Go to most recent revision | Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
428 | giacomo | 1 | /* |
2 | * $Id: proc.c,v 1.1 2004-01-28 18:32:17 giacomo Exp $ |
||
3 | * |
||
4 | * Procfs interface for the PCI bus. |
||
5 | * |
||
6 | * Copyright (c) 1997--1999 Martin Mares <mj@ucw.cz> |
||
7 | */ |
||
8 | |||
9 | #include <linux/init.h> |
||
10 | #include <linux/pci.h> |
||
11 | #include <linux/module.h> |
||
12 | #include <linux/proc_fs.h> |
||
13 | #include <linux/seq_file.h> |
||
14 | #include <linux/smp_lock.h> |
||
15 | |||
16 | #include <asm/uaccess.h> |
||
17 | #include <asm/byteorder.h> |
||
18 | |||
19 | #define PCI_CFG_SPACE_SIZE 256 |
||
20 | |||
21 | static int proc_initialized; /* = 0 */ |
||
22 | |||
23 | static loff_t |
||
24 | proc_bus_pci_lseek(struct file *file, loff_t off, int whence) |
||
25 | { |
||
26 | loff_t new = -1; |
||
27 | |||
28 | lock_kernel(); |
||
29 | switch (whence) { |
||
30 | case 0: |
||
31 | new = off; |
||
32 | break; |
||
33 | case 1: |
||
34 | new = file->f_pos + off; |
||
35 | break; |
||
36 | case 2: |
||
37 | new = PCI_CFG_SPACE_SIZE + off; |
||
38 | break; |
||
39 | } |
||
40 | unlock_kernel(); |
||
41 | if (new < 0 || new > PCI_CFG_SPACE_SIZE) |
||
42 | return -EINVAL; |
||
43 | return (file->f_pos = new); |
||
44 | } |
||
45 | |||
46 | static ssize_t |
||
47 | proc_bus_pci_read(struct file *file, char __user *buf, size_t nbytes, loff_t *ppos) |
||
48 | { |
||
49 | const struct inode *ino = file->f_dentry->d_inode; |
||
50 | const struct proc_dir_entry *dp = PDE(ino); |
||
51 | struct pci_dev *dev = dp->data; |
||
52 | unsigned int pos = *ppos; |
||
53 | unsigned int cnt, size; |
||
54 | |||
55 | /* |
||
56 | * Normal users can read only the standardized portion of the |
||
57 | * configuration space as several chips lock up when trying to read |
||
58 | * undefined locations (think of Intel PIIX4 as a typical example). |
||
59 | */ |
||
60 | |||
61 | if (capable(CAP_SYS_ADMIN)) |
||
62 | size = PCI_CFG_SPACE_SIZE; |
||
63 | else if (dev->hdr_type == PCI_HEADER_TYPE_CARDBUS) |
||
64 | size = 128; |
||
65 | else |
||
66 | size = 64; |
||
67 | |||
68 | if (pos >= size) |
||
69 | return 0; |
||
70 | if (nbytes >= size) |
||
71 | nbytes = size; |
||
72 | if (pos + nbytes > size) |
||
73 | nbytes = size - pos; |
||
74 | cnt = nbytes; |
||
75 | |||
76 | if (!access_ok(VERIFY_WRITE, buf, cnt)) |
||
77 | return -EINVAL; |
||
78 | |||
79 | if ((pos & 1) && cnt) { |
||
80 | unsigned char val; |
||
81 | pci_read_config_byte(dev, pos, &val); |
||
82 | __put_user(val, buf); |
||
83 | buf++; |
||
84 | pos++; |
||
85 | cnt--; |
||
86 | } |
||
87 | |||
88 | if ((pos & 3) && cnt > 2) { |
||
89 | unsigned short val; |
||
90 | pci_read_config_word(dev, pos, &val); |
||
91 | __put_user(cpu_to_le16(val), (unsigned short *) buf); |
||
92 | buf += 2; |
||
93 | pos += 2; |
||
94 | cnt -= 2; |
||
95 | } |
||
96 | |||
97 | while (cnt >= 4) { |
||
98 | unsigned int val; |
||
99 | pci_read_config_dword(dev, pos, &val); |
||
100 | __put_user(cpu_to_le32(val), (unsigned int *) buf); |
||
101 | buf += 4; |
||
102 | pos += 4; |
||
103 | cnt -= 4; |
||
104 | } |
||
105 | |||
106 | if (cnt >= 2) { |
||
107 | unsigned short val; |
||
108 | pci_read_config_word(dev, pos, &val); |
||
109 | __put_user(cpu_to_le16(val), (unsigned short *) buf); |
||
110 | buf += 2; |
||
111 | pos += 2; |
||
112 | cnt -= 2; |
||
113 | } |
||
114 | |||
115 | if (cnt) { |
||
116 | unsigned char val; |
||
117 | pci_read_config_byte(dev, pos, &val); |
||
118 | __put_user(val, buf); |
||
119 | buf++; |
||
120 | pos++; |
||
121 | cnt--; |
||
122 | } |
||
123 | |||
124 | *ppos = pos; |
||
125 | return nbytes; |
||
126 | } |
||
127 | |||
128 | static ssize_t |
||
129 | proc_bus_pci_write(struct file *file, const char __user *buf, size_t nbytes, loff_t *ppos) |
||
130 | { |
||
131 | const struct inode *ino = file->f_dentry->d_inode; |
||
132 | const struct proc_dir_entry *dp = PDE(ino); |
||
133 | struct pci_dev *dev = dp->data; |
||
134 | int pos = *ppos; |
||
135 | int cnt; |
||
136 | |||
137 | if (pos >= PCI_CFG_SPACE_SIZE) |
||
138 | return 0; |
||
139 | if (nbytes >= PCI_CFG_SPACE_SIZE) |
||
140 | nbytes = PCI_CFG_SPACE_SIZE; |
||
141 | if (pos + nbytes > PCI_CFG_SPACE_SIZE) |
||
142 | nbytes = PCI_CFG_SPACE_SIZE - pos; |
||
143 | cnt = nbytes; |
||
144 | |||
145 | if (!access_ok(VERIFY_READ, buf, cnt)) |
||
146 | return -EINVAL; |
||
147 | |||
148 | if ((pos & 1) && cnt) { |
||
149 | unsigned char val; |
||
150 | __get_user(val, buf); |
||
151 | pci_write_config_byte(dev, pos, val); |
||
152 | buf++; |
||
153 | pos++; |
||
154 | cnt--; |
||
155 | } |
||
156 | |||
157 | if ((pos & 3) && cnt > 2) { |
||
158 | unsigned short val; |
||
159 | __get_user(val, (unsigned short *) buf); |
||
160 | pci_write_config_word(dev, pos, le16_to_cpu(val)); |
||
161 | buf += 2; |
||
162 | pos += 2; |
||
163 | cnt -= 2; |
||
164 | } |
||
165 | |||
166 | while (cnt >= 4) { |
||
167 | unsigned int val; |
||
168 | __get_user(val, (unsigned int *) buf); |
||
169 | pci_write_config_dword(dev, pos, le32_to_cpu(val)); |
||
170 | buf += 4; |
||
171 | pos += 4; |
||
172 | cnt -= 4; |
||
173 | } |
||
174 | |||
175 | if (cnt >= 2) { |
||
176 | unsigned short val; |
||
177 | __get_user(val, (unsigned short *) buf); |
||
178 | pci_write_config_word(dev, pos, le16_to_cpu(val)); |
||
179 | buf += 2; |
||
180 | pos += 2; |
||
181 | cnt -= 2; |
||
182 | } |
||
183 | |||
184 | if (cnt) { |
||
185 | unsigned char val; |
||
186 | __get_user(val, buf); |
||
187 | pci_write_config_byte(dev, pos, val); |
||
188 | buf++; |
||
189 | pos++; |
||
190 | cnt--; |
||
191 | } |
||
192 | |||
193 | *ppos = pos; |
||
194 | return nbytes; |
||
195 | } |
||
196 | |||
197 | struct pci_filp_private { |
||
198 | enum pci_mmap_state mmap_state; |
||
199 | int write_combine; |
||
200 | }; |
||
201 | |||
202 | static int proc_bus_pci_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) |
||
203 | { |
||
204 | const struct proc_dir_entry *dp = PDE(inode); |
||
205 | struct pci_dev *dev = dp->data; |
||
206 | #ifdef HAVE_PCI_MMAP |
||
207 | struct pci_filp_private *fpriv = file->private_data; |
||
208 | #endif /* HAVE_PCI_MMAP */ |
||
209 | int ret = 0; |
||
210 | |||
211 | switch (cmd) { |
||
212 | case PCIIOC_CONTROLLER: |
||
213 | ret = pci_domain_nr(dev->bus); |
||
214 | break; |
||
215 | |||
216 | #ifdef HAVE_PCI_MMAP |
||
217 | case PCIIOC_MMAP_IS_IO: |
||
218 | fpriv->mmap_state = pci_mmap_io; |
||
219 | break; |
||
220 | |||
221 | case PCIIOC_MMAP_IS_MEM: |
||
222 | fpriv->mmap_state = pci_mmap_mem; |
||
223 | break; |
||
224 | |||
225 | case PCIIOC_WRITE_COMBINE: |
||
226 | if (arg) |
||
227 | fpriv->write_combine = 1; |
||
228 | else |
||
229 | fpriv->write_combine = 0; |
||
230 | break; |
||
231 | |||
232 | #endif /* HAVE_PCI_MMAP */ |
||
233 | |||
234 | default: |
||
235 | ret = -EINVAL; |
||
236 | break; |
||
237 | }; |
||
238 | |||
239 | return ret; |
||
240 | } |
||
241 | |||
242 | #ifdef HAVE_PCI_MMAP |
||
243 | static int proc_bus_pci_mmap(struct file *file, struct vm_area_struct *vma) |
||
244 | { |
||
245 | struct inode *inode = file->f_dentry->d_inode; |
||
246 | const struct proc_dir_entry *dp = PDE(inode); |
||
247 | struct pci_dev *dev = dp->data; |
||
248 | struct pci_filp_private *fpriv = file->private_data; |
||
249 | int ret; |
||
250 | |||
251 | if (!capable(CAP_SYS_RAWIO)) |
||
252 | return -EPERM; |
||
253 | |||
254 | ret = pci_mmap_page_range(dev, vma, |
||
255 | fpriv->mmap_state, |
||
256 | fpriv->write_combine); |
||
257 | if (ret < 0) |
||
258 | return ret; |
||
259 | |||
260 | return 0; |
||
261 | } |
||
262 | |||
263 | static int proc_bus_pci_open(struct inode *inode, struct file *file) |
||
264 | { |
||
265 | struct pci_filp_private *fpriv = kmalloc(sizeof(*fpriv), GFP_KERNEL); |
||
266 | |||
267 | if (!fpriv) |
||
268 | return -ENOMEM; |
||
269 | |||
270 | fpriv->mmap_state = pci_mmap_io; |
||
271 | fpriv->write_combine = 0; |
||
272 | |||
273 | file->private_data = fpriv; |
||
274 | |||
275 | return 0; |
||
276 | } |
||
277 | |||
278 | static int proc_bus_pci_release(struct inode *inode, struct file *file) |
||
279 | { |
||
280 | kfree(file->private_data); |
||
281 | file->private_data = NULL; |
||
282 | |||
283 | return 0; |
||
284 | } |
||
285 | #endif /* HAVE_PCI_MMAP */ |
||
286 | |||
287 | static struct file_operations proc_bus_pci_operations = { |
||
288 | .llseek = proc_bus_pci_lseek, |
||
289 | .read = proc_bus_pci_read, |
||
290 | .write = proc_bus_pci_write, |
||
291 | .ioctl = proc_bus_pci_ioctl, |
||
292 | #ifdef HAVE_PCI_MMAP |
||
293 | .open = proc_bus_pci_open, |
||
294 | .release = proc_bus_pci_release, |
||
295 | .mmap = proc_bus_pci_mmap, |
||
296 | #ifdef HAVE_ARCH_PCI_GET_UNMAPPED_AREA |
||
297 | .get_unmapped_area = get_pci_unmapped_area, |
||
298 | #endif /* HAVE_ARCH_PCI_GET_UNMAPPED_AREA */ |
||
299 | #endif /* HAVE_PCI_MMAP */ |
||
300 | }; |
||
301 | |||
302 | #if BITS_PER_LONG == 32 |
||
303 | #define LONG_FORMAT "\t%08lx" |
||
304 | #else |
||
305 | #define LONG_FORMAT "\t%16lx" |
||
306 | #endif |
||
307 | |||
308 | /* iterator */ |
||
309 | static void *pci_seq_start(struct seq_file *m, loff_t *pos) |
||
310 | { |
||
311 | struct pci_dev *dev = NULL; |
||
312 | loff_t n = *pos; |
||
313 | |||
314 | dev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, dev); |
||
315 | while (n--) { |
||
316 | dev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, dev); |
||
317 | if (dev == NULL) |
||
318 | goto exit; |
||
319 | } |
||
320 | exit: |
||
321 | return dev; |
||
322 | } |
||
323 | |||
324 | static void *pci_seq_next(struct seq_file *m, void *v, loff_t *pos) |
||
325 | { |
||
326 | struct pci_dev *dev = v; |
||
327 | |||
328 | (*pos)++; |
||
329 | dev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, dev); |
||
330 | return dev; |
||
331 | } |
||
332 | |||
333 | static void pci_seq_stop(struct seq_file *m, void *v) |
||
334 | { |
||
335 | if (v) { |
||
336 | struct pci_dev *dev = v; |
||
337 | pci_dev_put(dev); |
||
338 | } |
||
339 | } |
||
340 | |||
341 | static int show_device(struct seq_file *m, void *v) |
||
342 | { |
||
343 | const struct pci_dev *dev = v; |
||
344 | const struct pci_driver *drv; |
||
345 | int i; |
||
346 | |||
347 | if (dev == NULL) |
||
348 | return 0; |
||
349 | |||
350 | drv = pci_dev_driver(dev); |
||
351 | seq_printf(m, "%02x%02x\t%04x%04x\t%x", |
||
352 | dev->bus->number, |
||
353 | dev->devfn, |
||
354 | dev->vendor, |
||
355 | dev->device, |
||
356 | dev->irq); |
||
357 | /* Here should be 7 and not PCI_NUM_RESOURCES as we need to preserve compatibility */ |
||
358 | for(i=0; i<7; i++) |
||
359 | seq_printf(m, LONG_FORMAT, |
||
360 | dev->resource[i].start | |
||
361 | (dev->resource[i].flags & PCI_REGION_FLAG_MASK)); |
||
362 | for(i=0; i<7; i++) |
||
363 | seq_printf(m, LONG_FORMAT, |
||
364 | dev->resource[i].start < dev->resource[i].end ? |
||
365 | dev->resource[i].end - dev->resource[i].start + 1 : 0); |
||
366 | seq_putc(m, '\t'); |
||
367 | if (drv) |
||
368 | seq_printf(m, "%s", drv->name); |
||
369 | seq_putc(m, '\n'); |
||
370 | return 0; |
||
371 | } |
||
372 | |||
373 | static struct seq_operations proc_bus_pci_devices_op = { |
||
374 | .start = pci_seq_start, |
||
375 | .next = pci_seq_next, |
||
376 | .stop = pci_seq_stop, |
||
377 | .show = show_device |
||
378 | }; |
||
379 | |||
380 | struct proc_dir_entry *proc_bus_pci_dir; |
||
381 | |||
382 | int pci_proc_attach_device(struct pci_dev *dev) |
||
383 | { |
||
384 | struct pci_bus *bus = dev->bus; |
||
385 | struct proc_dir_entry *de, *e; |
||
386 | char name[16]; |
||
387 | |||
388 | if (!proc_initialized) |
||
389 | return -EACCES; |
||
390 | |||
391 | if (!(de = bus->procdir)) { |
||
392 | if (pci_name_bus(name, bus)) |
||
393 | return -EEXIST; |
||
394 | de = bus->procdir = proc_mkdir(name, proc_bus_pci_dir); |
||
395 | if (!de) |
||
396 | return -ENOMEM; |
||
397 | } |
||
398 | sprintf(name, "%02x.%x", PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn)); |
||
399 | e = dev->procent = create_proc_entry(name, S_IFREG | S_IRUGO | S_IWUSR, de); |
||
400 | if (!e) |
||
401 | return -ENOMEM; |
||
402 | e->proc_fops = &proc_bus_pci_operations; |
||
403 | e->data = dev; |
||
404 | e->size = PCI_CFG_SPACE_SIZE; |
||
405 | |||
406 | return 0; |
||
407 | } |
||
408 | |||
409 | int pci_proc_detach_device(struct pci_dev *dev) |
||
410 | { |
||
411 | struct proc_dir_entry *e; |
||
412 | |||
413 | if ((e = dev->procent)) { |
||
414 | if (atomic_read(&e->count)) |
||
415 | return -EBUSY; |
||
416 | remove_proc_entry(e->name, dev->bus->procdir); |
||
417 | dev->procent = NULL; |
||
418 | } |
||
419 | return 0; |
||
420 | } |
||
421 | |||
422 | int pci_proc_attach_bus(struct pci_bus* bus) |
||
423 | { |
||
424 | struct proc_dir_entry *de = bus->procdir; |
||
425 | |||
426 | if (!proc_initialized) |
||
427 | return -EACCES; |
||
428 | |||
429 | if (!de) { |
||
430 | char name[16]; |
||
431 | sprintf(name, "%02x", bus->number); |
||
432 | de = bus->procdir = proc_mkdir(name, proc_bus_pci_dir); |
||
433 | if (!de) |
||
434 | return -ENOMEM; |
||
435 | } |
||
436 | return 0; |
||
437 | } |
||
438 | |||
439 | int pci_proc_detach_bus(struct pci_bus* bus) |
||
440 | { |
||
441 | struct proc_dir_entry *de = bus->procdir; |
||
442 | if (de) |
||
443 | remove_proc_entry(de->name, proc_bus_pci_dir); |
||
444 | return 0; |
||
445 | } |
||
446 | |||
447 | #ifdef CONFIG_PCI_LEGACY_PROC |
||
448 | |||
449 | /* |
||
450 | * Backward compatible /proc/pci interface. |
||
451 | */ |
||
452 | |||
453 | /* |
||
454 | * Convert some of the configuration space registers of the device at |
||
455 | * address (bus,devfn) into a string (possibly several lines each). |
||
456 | * The configuration string is stored starting at buf[len]. If the |
||
457 | * string would exceed the size of the buffer (SIZE), 0 is returned. |
||
458 | */ |
||
459 | static int show_dev_config(struct seq_file *m, void *v) |
||
460 | { |
||
461 | struct pci_dev *dev = v; |
||
462 | struct pci_dev *first_dev; |
||
463 | struct pci_driver *drv; |
||
464 | u32 class_rev; |
||
465 | unsigned char latency, min_gnt, max_lat, *class; |
||
466 | int reg; |
||
467 | |||
468 | first_dev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, NULL); |
||
469 | if (dev == first_dev) |
||
470 | seq_puts(m, "PCI devices found:\n"); |
||
471 | pci_dev_put(first_dev); |
||
472 | |||
473 | drv = pci_dev_driver(dev); |
||
474 | |||
475 | pci_read_config_dword(dev, PCI_CLASS_REVISION, &class_rev); |
||
476 | pci_read_config_byte (dev, PCI_LATENCY_TIMER, &latency); |
||
477 | pci_read_config_byte (dev, PCI_MIN_GNT, &min_gnt); |
||
478 | pci_read_config_byte (dev, PCI_MAX_LAT, &max_lat); |
||
479 | seq_printf(m, " Bus %2d, device %3d, function %2d:\n", |
||
480 | dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn)); |
||
481 | class = pci_class_name(class_rev >> 16); |
||
482 | if (class) |
||
483 | seq_printf(m, " %s", class); |
||
484 | else |
||
485 | seq_printf(m, " Class %04x", class_rev >> 16); |
||
486 | #ifdef CONFIG_PCI_NAMES |
||
487 | seq_printf(m, ": %s", dev->pretty_name); |
||
488 | #else |
||
489 | seq_printf(m, ": PCI device %04x:%04x", dev->vendor, dev->device); |
||
490 | #endif |
||
491 | seq_printf(m, " (rev %d).\n", class_rev & 0xff); |
||
492 | |||
493 | if (dev->irq) |
||
494 | seq_printf(m, " IRQ %d.\n", dev->irq); |
||
495 | |||
496 | if (latency || min_gnt || max_lat) { |
||
497 | seq_printf(m, " Master Capable. "); |
||
498 | if (latency) |
||
499 | seq_printf(m, "Latency=%d. ", latency); |
||
500 | else |
||
501 | seq_puts(m, "No bursts. "); |
||
502 | if (min_gnt) |
||
503 | seq_printf(m, "Min Gnt=%d.", min_gnt); |
||
504 | if (max_lat) |
||
505 | seq_printf(m, "Max Lat=%d.", max_lat); |
||
506 | seq_putc(m, '\n'); |
||
507 | } |
||
508 | |||
509 | for (reg = 0; reg < 6; reg++) { |
||
510 | struct resource *res = dev->resource + reg; |
||
511 | unsigned long base, end, flags; |
||
512 | |||
513 | base = res->start; |
||
514 | end = res->end; |
||
515 | flags = res->flags; |
||
516 | if (!end) |
||
517 | continue; |
||
518 | |||
519 | if (flags & PCI_BASE_ADDRESS_SPACE_IO) { |
||
520 | seq_printf(m, " I/O at 0x%lx [0x%lx].\n", |
||
521 | base, end); |
||
522 | } else { |
||
523 | const char *pref, *type = "unknown"; |
||
524 | |||
525 | if (flags & PCI_BASE_ADDRESS_MEM_PREFETCH) |
||
526 | pref = "P"; |
||
527 | else |
||
528 | pref = "Non-p"; |
||
529 | switch (flags & PCI_BASE_ADDRESS_MEM_TYPE_MASK) { |
||
530 | case PCI_BASE_ADDRESS_MEM_TYPE_32: |
||
531 | type = "32 bit"; break; |
||
532 | case PCI_BASE_ADDRESS_MEM_TYPE_1M: |
||
533 | type = "20 bit"; break; |
||
534 | case PCI_BASE_ADDRESS_MEM_TYPE_64: |
||
535 | type = "64 bit"; break; |
||
536 | } |
||
537 | seq_printf(m, " %srefetchable %s memory at " |
||
538 | "0x%lx [0x%lx].\n", pref, type, |
||
539 | base, |
||
540 | end); |
||
541 | } |
||
542 | } |
||
543 | return 0; |
||
544 | } |
||
545 | |||
546 | static struct seq_operations proc_pci_op = { |
||
547 | .start = pci_seq_start, |
||
548 | .next = pci_seq_next, |
||
549 | .stop = pci_seq_stop, |
||
550 | .show = show_dev_config |
||
551 | }; |
||
552 | |||
553 | static int proc_pci_open(struct inode *inode, struct file *file) |
||
554 | { |
||
555 | return seq_open(file, &proc_pci_op); |
||
556 | } |
||
557 | static struct file_operations proc_pci_operations = { |
||
558 | .open = proc_pci_open, |
||
559 | .read = seq_read, |
||
560 | .llseek = seq_lseek, |
||
561 | .release = seq_release, |
||
562 | }; |
||
563 | |||
564 | static void legacy_proc_init(void) |
||
565 | { |
||
566 | struct proc_dir_entry * entry = create_proc_entry("pci", 0, NULL); |
||
567 | if (entry) |
||
568 | entry->proc_fops = &proc_pci_operations; |
||
569 | } |
||
570 | |||
571 | #else |
||
572 | |||
573 | static void legacy_proc_init(void) |
||
574 | { |
||
575 | |||
576 | } |
||
577 | |||
578 | #endif /* CONFIG_PCI_LEGACY_PROC */ |
||
579 | |||
580 | static int proc_bus_pci_dev_open(struct inode *inode, struct file *file) |
||
581 | { |
||
582 | return seq_open(file, &proc_bus_pci_devices_op); |
||
583 | } |
||
584 | static struct file_operations proc_bus_pci_dev_operations = { |
||
585 | .open = proc_bus_pci_dev_open, |
||
586 | .read = seq_read, |
||
587 | .llseek = seq_lseek, |
||
588 | .release = seq_release, |
||
589 | }; |
||
590 | |||
591 | static int __init pci_proc_init(void) |
||
592 | { |
||
593 | struct proc_dir_entry *entry; |
||
594 | struct pci_dev *dev = NULL; |
||
595 | proc_bus_pci_dir = proc_mkdir("pci", proc_bus); |
||
596 | entry = create_proc_entry("devices", 0, proc_bus_pci_dir); |
||
597 | if (entry) |
||
598 | entry->proc_fops = &proc_bus_pci_dev_operations; |
||
599 | proc_initialized = 1; |
||
600 | while ((dev = pci_find_device(PCI_ANY_ID, PCI_ANY_ID, dev)) != NULL) { |
||
601 | pci_proc_attach_device(dev); |
||
602 | } |
||
603 | legacy_proc_init(); |
||
604 | return 0; |
||
605 | } |
||
606 | |||
607 | __initcall(pci_proc_init); |
||
608 | |||
609 | #ifdef CONFIG_HOTPLUG |
||
610 | EXPORT_SYMBOL(pci_proc_attach_device); |
||
611 | EXPORT_SYMBOL(pci_proc_attach_bus); |
||
612 | EXPORT_SYMBOL(pci_proc_detach_bus); |
||
613 | EXPORT_SYMBOL(proc_bus_pci_dir); |
||
614 | #endif |
||
615 |