~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~ [ freetext search ] ~ [ file search ] ~

Linux Cross Reference
Linux/drivers/pci/proc.c

Version: ~ [ 2.4.0 ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  1 /*
  2  *      $Id: proc.c,v 1.13 1998/05/12 07:36:07 mj Exp $
  3  *
  4  *      Procfs interface for the PCI bus.
  5  *
  6  *      Copyright (c) 1997--1999 Martin Mares <mj@suse.cz>
  7  */
  8 
  9 #include <linux/types.h>
 10 #include <linux/kernel.h>
 11 #include <linux/pci.h>
 12 #include <linux/proc_fs.h>
 13 #include <linux/init.h>
 14 
 15 #include <asm/uaccess.h>
 16 #include <asm/byteorder.h>
 17 
 18 #define PCI_CFG_SPACE_SIZE 256
 19 
 20 static loff_t
 21 proc_bus_pci_lseek(struct file *file, loff_t off, int whence)
 22 {
 23         loff_t new;
 24 
 25         switch (whence) {
 26         case 0:
 27                 new = off;
 28                 break;
 29         case 1:
 30                 new = file->f_pos + off;
 31                 break;
 32         case 2:
 33                 new = PCI_CFG_SPACE_SIZE + off;
 34                 break;
 35         default:
 36                 return -EINVAL;
 37         }
 38         if (new < 0 || new > PCI_CFG_SPACE_SIZE)
 39                 return -EINVAL;
 40         return (file->f_pos = new);
 41 }
 42 
 43 static ssize_t
 44 proc_bus_pci_read(struct file *file, char *buf, size_t nbytes, loff_t *ppos)
 45 {
 46         const struct inode *ino = file->f_dentry->d_inode;
 47         const struct proc_dir_entry *dp = ino->u.generic_ip;
 48         struct pci_dev *dev = dp->data;
 49         unsigned int pos = *ppos;
 50         unsigned int cnt, size;
 51 
 52         /*
 53          * Normal users can read only the standardized portion of the
 54          * configuration space as several chips lock up when trying to read
 55          * undefined locations (think of Intel PIIX4 as a typical example).
 56          */
 57 
 58         if (capable(CAP_SYS_ADMIN))
 59                 size = PCI_CFG_SPACE_SIZE;
 60         else if (dev->hdr_type == PCI_HEADER_TYPE_CARDBUS)
 61                 size = 128;
 62         else
 63                 size = 64;
 64 
 65         if (pos >= size)
 66                 return 0;
 67         if (nbytes >= size)
 68                 nbytes = size;
 69         if (pos + nbytes > size)
 70                 nbytes = size - pos;
 71         cnt = nbytes;
 72 
 73         if (!access_ok(VERIFY_WRITE, buf, cnt))
 74                 return -EINVAL;
 75 
 76         if ((pos & 1) && cnt) {
 77                 unsigned char val;
 78                 pci_read_config_byte(dev, pos, &val);
 79                 __put_user(val, buf);
 80                 buf++;
 81                 pos++;
 82                 cnt--;
 83         }
 84 
 85         if ((pos & 3) && cnt > 2) {
 86                 unsigned short val;
 87                 pci_read_config_word(dev, pos, &val);
 88                 __put_user(cpu_to_le16(val), (unsigned short *) buf);
 89                 buf += 2;
 90                 pos += 2;
 91                 cnt -= 2;
 92         }
 93 
 94         while (cnt >= 4) {
 95                 unsigned int val;
 96                 pci_read_config_dword(dev, pos, &val);
 97                 __put_user(cpu_to_le32(val), (unsigned int *) buf);
 98                 buf += 4;
 99                 pos += 4;
100                 cnt -= 4;
101         }
102 
103         if (cnt >= 2) {
104                 unsigned short val;
105                 pci_read_config_word(dev, pos, &val);
106                 __put_user(cpu_to_le16(val), (unsigned short *) buf);
107                 buf += 2;
108                 pos += 2;
109                 cnt -= 2;
110         }
111 
112         if (cnt) {
113                 unsigned char val;
114                 pci_read_config_byte(dev, pos, &val);
115                 __put_user(val, buf);
116                 buf++;
117                 pos++;
118                 cnt--;
119         }
120 
121         *ppos = pos;
122         return nbytes;
123 }
124 
125 static ssize_t
126 proc_bus_pci_write(struct file *file, const char *buf, size_t nbytes, loff_t *ppos)
127 {
128         const struct inode *ino = file->f_dentry->d_inode;
129         const struct proc_dir_entry *dp = ino->u.generic_ip;
130         struct pci_dev *dev = dp->data;
131         int pos = *ppos;
132         int cnt;
133 
134         if (pos >= PCI_CFG_SPACE_SIZE)
135                 return 0;
136         if (nbytes >= PCI_CFG_SPACE_SIZE)
137                 nbytes = PCI_CFG_SPACE_SIZE;
138         if (pos + nbytes > PCI_CFG_SPACE_SIZE)
139                 nbytes = PCI_CFG_SPACE_SIZE - pos;
140         cnt = nbytes;
141 
142         if (!access_ok(VERIFY_READ, buf, cnt))
143                 return -EINVAL;
144 
145         if ((pos & 1) && cnt) {
146                 unsigned char val;
147                 __get_user(val, buf);
148                 pci_write_config_byte(dev, pos, val);
149                 buf++;
150                 pos++;
151                 cnt--;
152         }
153 
154         if ((pos & 3) && cnt > 2) {
155                 unsigned short val;
156                 __get_user(val, (unsigned short *) buf);
157                 pci_write_config_word(dev, pos, le16_to_cpu(val));
158                 buf += 2;
159                 pos += 2;
160                 cnt -= 2;
161         }
162 
163         while (cnt >= 4) {
164                 unsigned int val;
165                 __get_user(val, (unsigned int *) buf);
166                 pci_write_config_dword(dev, pos, le32_to_cpu(val));
167                 buf += 4;
168                 pos += 4;
169                 cnt -= 4;
170         }
171 
172         if (cnt >= 2) {
173                 unsigned short val;
174                 __get_user(val, (unsigned short *) buf);
175                 pci_write_config_word(dev, pos, le16_to_cpu(val));
176                 buf += 2;
177                 pos += 2;
178                 cnt -= 2;
179         }
180 
181         if (cnt) {
182                 unsigned char val;
183                 __get_user(val, buf);
184                 pci_write_config_byte(dev, pos, val);
185                 buf++;
186                 pos++;
187                 cnt--;
188         }
189 
190         *ppos = pos;
191         return nbytes;
192 }
193 
194 static struct file_operations proc_bus_pci_operations = {
195         llseek: proc_bus_pci_lseek,
196         read:   proc_bus_pci_read,
197         write:  proc_bus_pci_write,
198 };
199 
200 #if BITS_PER_LONG == 32
201 #define LONG_FORMAT "\t%08lx"
202 #else
203 #define LONG_FORMAT "\t%16lx"
204 #endif
205 
206 static int
207 get_pci_dev_info(char *buf, char **start, off_t pos, int count)
208 {
209         const struct pci_dev *dev;
210         off_t at = 0;
211         int len, i, cnt;
212 
213         cnt = 0;
214         pci_for_each_dev(dev) {
215                 const struct pci_driver *drv = pci_dev_driver(dev);
216                 len = sprintf(buf, "%02x%02x\t%04x%04x\t%x",
217                         dev->bus->number,
218                         dev->devfn,
219                         dev->vendor,
220                         dev->device,
221                         dev->irq);
222                 /* Here should be 7 and not PCI_NUM_RESOURCES as we need to preserve compatibility */
223                 for(i=0; i<7; i++)
224                         len += sprintf(buf+len, LONG_FORMAT,
225                                        dev->resource[i].start | (dev->resource[i].flags & PCI_REGION_FLAG_MASK));
226                 for(i=0; i<7; i++)
227                         len += sprintf(buf+len, LONG_FORMAT, dev->resource[i].start < dev->resource[i].end ?
228                                        dev->resource[i].end - dev->resource[i].start + 1 : 0);
229                 buf[len++] = '\t';
230                 if (drv)
231                         len += sprintf(buf+len, "%s", drv->name);
232                 buf[len++] = '\n';
233                 at += len;
234                 if (at >= pos) {
235                         if (!*start) {
236                                 *start = buf + (pos - (at - len));
237                                 cnt = at - pos;
238                         } else
239                                 cnt += len;
240                         buf += len;
241                         if (cnt >= count)
242                                 /*
243                                  * proc_file_read() gives us 1KB of slack so it's OK if the
244                                  * above printfs write a little beyond the buffer end (we
245                                  * never write more than 1KB beyond the buffer end).
246                                  */
247                                 break;
248                 }
249         }
250         return (count > cnt) ? cnt : count;
251 }
252 
253 static struct proc_dir_entry *proc_bus_pci_dir;
254 
255 int pci_proc_attach_device(struct pci_dev *dev)
256 {
257         struct pci_bus *bus = dev->bus;
258         struct proc_dir_entry *de, *e;
259         char name[16];
260 
261         if (!(de = bus->procdir)) {
262                 sprintf(name, "%02x", bus->number);
263                 de = bus->procdir = proc_mkdir(name, proc_bus_pci_dir);
264                 if (!de)
265                         return -ENOMEM;
266         }
267         sprintf(name, "%02x.%x", PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn));
268         e = dev->procent = create_proc_entry(name, S_IFREG | S_IRUGO | S_IWUSR, de);
269         if (!e)
270                 return -ENOMEM;
271         e->proc_fops = &proc_bus_pci_operations;
272         e->data = dev;
273         e->size = PCI_CFG_SPACE_SIZE;
274         return 0;
275 }
276 
277 int pci_proc_detach_device(struct pci_dev *dev)
278 {
279         struct proc_dir_entry *e;
280 
281         if ((e = dev->procent)) {
282                 if (atomic_read(&e->count))
283                         return -EBUSY;
284                 remove_proc_entry(e->name, dev->bus->procdir);
285                 dev->procent = NULL;
286         }
287         return 0;
288 }
289 
290 
291 /*
292  *  Backward compatible /proc/pci interface.
293  */
294 
295 /*
296  * Convert some of the configuration space registers of the device at
297  * address (bus,devfn) into a string (possibly several lines each).
298  * The configuration string is stored starting at buf[len].  If the
299  * string would exceed the size of the buffer (SIZE), 0 is returned.
300  */
301 static int sprint_dev_config(struct pci_dev *dev, char *buf, int size)
302 {
303         u32 class_rev;
304         unsigned char latency, min_gnt, max_lat, *class;
305         int reg, len = 0;
306 
307         pci_read_config_dword(dev, PCI_CLASS_REVISION, &class_rev);
308         pci_read_config_byte (dev, PCI_LATENCY_TIMER, &latency);
309         pci_read_config_byte (dev, PCI_MIN_GNT, &min_gnt);
310         pci_read_config_byte (dev, PCI_MAX_LAT, &max_lat);
311         if (len + 160 > size)
312                 return -1;
313         len += sprintf(buf + len, "  Bus %2d, device %3d, function %2d:\n",
314                        dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn));
315         class = pci_class_name(class_rev >> 16);
316         if (class)
317                 len += sprintf(buf+len, "    %s", class);
318         else
319                 len += sprintf(buf+len, "    Class %04x", class_rev >> 16);
320         len += sprintf(buf+len, ": %s (rev %d).\n", dev->name, class_rev & 0xff);
321 
322         if (dev->irq) {
323                 if (len + 40 > size)
324                         return -1;
325                 len += sprintf(buf + len, "      IRQ %d.\n", dev->irq);
326         }
327 
328         if (latency || min_gnt || max_lat) {
329                 if (len + 80 > size)
330                         return -1;
331                 len += sprintf(buf + len, "      Master Capable.  ");
332                 if (latency)
333                   len += sprintf(buf + len, "Latency=%d.  ", latency);
334                 else
335                   len += sprintf(buf + len, "No bursts.  ");
336                 if (min_gnt)
337                   len += sprintf(buf + len, "Min Gnt=%d.", min_gnt);
338                 if (max_lat)
339                   len += sprintf(buf + len, "Max Lat=%d.", max_lat);
340                 len += sprintf(buf + len, "\n");
341         }
342 
343         for (reg = 0; reg < 6; reg++) {
344                 struct resource *res = dev->resource + reg;
345                 unsigned long base, end, flags;
346 
347                 if (len + 40 > size)
348                         return -1;
349                 base = res->start;
350                 end = res->end;
351                 flags = res->flags;
352                 if (!end)
353                         continue;
354 
355                 if (flags & PCI_BASE_ADDRESS_SPACE_IO) {
356                         len += sprintf(buf + len,
357                                        "      I/O at 0x%lx [0x%lx].\n",
358                                        base, end);
359                 } else {
360                         const char *pref, *type = "unknown";
361 
362                         if (flags & PCI_BASE_ADDRESS_MEM_PREFETCH)
363                                 pref = "P";
364                         else
365                                 pref = "Non-p";
366                         switch (flags & PCI_BASE_ADDRESS_MEM_TYPE_MASK) {
367                               case PCI_BASE_ADDRESS_MEM_TYPE_32:
368                                 type = "32 bit"; break;
369                               case PCI_BASE_ADDRESS_MEM_TYPE_1M:
370                                 type = "20 bit"; break;
371                               case PCI_BASE_ADDRESS_MEM_TYPE_64:
372                                 type = "64 bit"; break;
373                         }
374                         len += sprintf(buf + len,
375                                        "      %srefetchable %s memory at "
376                                        "0x%lx [0x%lx].\n", pref, type,
377                                        base,
378                                        end);
379                 }
380         }
381 
382         return len;
383 }
384 
385 /*
386  * Return list of PCI devices as a character string for /proc/pci.
387  * BUF is a buffer that is PAGE_SIZE bytes long.
388  */
389 static int pci_read_proc(char *buf, char **start, off_t off,
390                                 int count, int *eof, void *data)
391 {
392         int nprinted, len, begin = 0;
393         struct pci_dev *dev;
394 
395         len = sprintf(buf, "PCI devices found:\n");
396 
397         *eof = 1;
398         pci_for_each_dev(dev) {
399                 nprinted = sprint_dev_config(dev, buf + len, PAGE_SIZE - len);
400                 if (nprinted < 0) {
401                         *eof = 0;
402                         break;
403                 }
404                 len += nprinted;
405                 if (len+begin < off) {
406                         begin += len;
407                         len = 0;
408                 }
409                 if (len+begin >= off+count)
410                         break;
411         }
412         off -= begin;
413         *start = buf + off;
414         len -= off;
415         if (len>count)
416                 len = count;
417         if (len<0)
418                 len = 0;
419         return len;
420 }
421 
422 static int __init pci_proc_init(void)
423 {
424         if (pci_present()) {
425                 struct pci_dev *dev;
426                 proc_bus_pci_dir = proc_mkdir("pci", proc_bus);
427                 create_proc_info_entry("devices", 0, proc_bus_pci_dir,
428                                         get_pci_dev_info);
429                 pci_for_each_dev(dev) {
430                         pci_proc_attach_device(dev);
431                 }
432                 create_proc_read_entry("pci", 0, NULL, pci_read_proc, NULL);
433         }
434         return 0;
435 }
436 
437 __initcall(pci_proc_init);
438 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~ [ freetext search ] ~ [ file search ] ~

This page was automatically generated by the LXR engine.
Visit the LXR main site for more information.