Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
587 | giacomo | 1 | /* |
2 | * drivers/pci/pci-driver.c |
||
3 | * |
||
4 | */ |
||
5 | |||
6 | #include <linuxcomp.h> |
||
7 | |||
8 | #include <linux/pci.h> |
||
9 | #include <linux/module.h> |
||
10 | #include <linux/init.h> |
||
11 | #include <linux/device.h> |
||
12 | #include <linux/pci-dynids.h> |
||
13 | #include "pci.h" |
||
14 | |||
15 | /* |
||
16 | * Registration of PCI drivers and handling of hot-pluggable devices. |
||
17 | */ |
||
18 | |||
19 | /** |
||
20 | * pci_match_one_device - Tell if a PCI device structure has a matching |
||
21 | * PCI device id structure |
||
22 | * @id: single PCI device id structure to match |
||
23 | * @dev: the PCI device structure to match against |
||
24 | * |
||
25 | * Returns the matching pci_device_id structure or %NULL if there is no match. |
||
26 | */ |
||
27 | |||
28 | static inline const struct pci_device_id * |
||
29 | pci_match_one_device(const struct pci_device_id *id, const struct pci_dev *dev) |
||
30 | { |
||
31 | if ((id->vendor == PCI_ANY_ID || id->vendor == dev->vendor) && |
||
32 | (id->device == PCI_ANY_ID || id->device == dev->device) && |
||
33 | (id->subvendor == PCI_ANY_ID || id->subvendor == dev->subsystem_vendor) && |
||
34 | (id->subdevice == PCI_ANY_ID || id->subdevice == dev->subsystem_device) && |
||
35 | !((id->class ^ dev->class) & id->class_mask)) |
||
36 | return id; |
||
37 | |||
38 | return NULL; |
||
39 | } |
||
40 | |||
41 | /* |
||
42 | * Dynamic device IDs are disabled for !CONFIG_HOTPLUG |
||
43 | */ |
||
44 | |||
45 | #ifdef CONFIG_HOTPLUG |
||
46 | /** |
||
47 | * pci_device_probe_dynamic() |
||
48 | * |
||
49 | * Walk the dynamic ID list looking for a match. |
||
50 | * returns 0 and sets pci_dev->driver when drv claims pci_dev, else error. |
||
51 | */ |
||
52 | static int |
||
53 | pci_device_probe_dynamic(struct pci_driver *drv, struct pci_dev *pci_dev) |
||
54 | { |
||
55 | int error = -ENODEV; |
||
56 | struct list_head *pos; |
||
57 | struct dynid *dynid; |
||
58 | |||
59 | spin_lock(&drv->dynids.lock); |
||
60 | list_for_each(pos, &drv->dynids.list) { |
||
61 | dynid = list_entry(pos, struct dynid, node); |
||
62 | if (pci_match_one_device(&dynid->id, pci_dev)) { |
||
63 | spin_unlock(&drv->dynids.lock); |
||
64 | error = drv->probe(pci_dev, &dynid->id); |
||
65 | if (error >= 0) { |
||
66 | pci_dev->driver = drv; |
||
67 | return 0; |
||
68 | } |
||
69 | return error; |
||
70 | } |
||
71 | } |
||
72 | spin_unlock(&drv->dynids.lock); |
||
73 | return error; |
||
74 | } |
||
75 | |||
76 | static inline void |
||
77 | dynid_init(struct dynid *dynid) |
||
78 | { |
||
79 | memset(dynid, 0, sizeof(*dynid)); |
||
80 | INIT_LIST_HEAD(&dynid->node); |
||
81 | } |
||
82 | |||
83 | /** |
||
84 | * store_new_id |
||
85 | * |
||
86 | * Adds a new dynamic pci device ID to this driver, |
||
87 | * and causes the driver to probe for all devices again. |
||
88 | */ |
||
89 | static inline ssize_t |
||
90 | store_new_id(struct device_driver *driver, const char *buf, size_t count) |
||
91 | { |
||
92 | struct dynid *dynid; |
||
93 | struct bus_type * bus; |
||
94 | struct pci_driver *pdrv = to_pci_driver(driver); |
||
95 | __u32 vendor=PCI_ANY_ID, device=PCI_ANY_ID, subvendor=PCI_ANY_ID, |
||
96 | subdevice=PCI_ANY_ID, class=0, class_mask=0; |
||
97 | unsigned long driver_data=0; |
||
98 | int fields=0; |
||
99 | |||
100 | fields = sscanf(buf, "%x %x %x %x %x %x %lux", |
||
101 | &vendor, &device, &subvendor, &subdevice, |
||
102 | &class, &class_mask, &driver_data); |
||
103 | if (fields < 0) |
||
104 | return -EINVAL; |
||
105 | |||
106 | dynid = kmalloc(sizeof(*dynid), GFP_KERNEL); |
||
107 | if (!dynid) |
||
108 | return -ENOMEM; |
||
109 | dynid_init(dynid); |
||
110 | |||
111 | dynid->id.vendor = vendor; |
||
112 | dynid->id.device = device; |
||
113 | dynid->id.subvendor = subvendor; |
||
114 | dynid->id.subdevice = subdevice; |
||
115 | dynid->id.class = class; |
||
116 | dynid->id.class_mask = class_mask; |
||
117 | dynid->id.driver_data = pdrv->dynids.use_driver_data ? |
||
118 | driver_data : 0UL; |
||
119 | |||
120 | spin_lock(&pdrv->dynids.lock); |
||
121 | list_add_tail(&pdrv->dynids.list, &dynid->node); |
||
122 | spin_unlock(&pdrv->dynids.lock); |
||
123 | |||
124 | bus = get_bus(pdrv->driver.bus); |
||
125 | if (bus) { |
||
126 | if (get_driver(&pdrv->driver)) { |
||
127 | down_write(&bus->subsys.rwsem); |
||
128 | driver_attach(&pdrv->driver); |
||
129 | up_write(&bus->subsys.rwsem); |
||
130 | put_driver(&pdrv->driver); |
||
131 | } |
||
132 | put_bus(bus); |
||
133 | } |
||
134 | |||
135 | return count; |
||
136 | } |
||
137 | |||
138 | static DRIVER_ATTR(new_id, S_IWUSR, NULL, store_new_id); |
||
139 | static inline void |
||
140 | pci_init_dynids(struct pci_dynids *dynids) |
||
141 | { |
||
142 | memset(dynids, 0, sizeof(*dynids)); |
||
143 | spin_lock_init(&dynids->lock); |
||
144 | INIT_LIST_HEAD(&dynids->list); |
||
145 | } |
||
146 | |||
147 | static void |
||
148 | pci_free_dynids(struct pci_driver *drv) |
||
149 | { |
||
150 | struct list_head *pos, *n; |
||
151 | struct dynid *dynid; |
||
152 | |||
153 | spin_lock(&drv->dynids.lock); |
||
154 | list_for_each_safe(pos, n, &drv->dynids.list) { |
||
155 | dynid = list_entry(pos, struct dynid, node); |
||
156 | list_del(&dynid->node); |
||
157 | kfree(dynid); |
||
158 | } |
||
159 | spin_unlock(&drv->dynids.lock); |
||
160 | } |
||
161 | |||
162 | static int |
||
163 | pci_create_newid_file(struct pci_driver *drv) |
||
164 | { |
||
165 | int error = 0; |
||
166 | if (drv->probe != NULL) |
||
167 | error = sysfs_create_file(&drv->driver.kobj, |
||
168 | &driver_attr_new_id.attr); |
||
169 | return error; |
||
170 | } |
||
171 | |||
172 | static int |
||
173 | pci_bus_match_dynids(const struct pci_dev *pci_dev, struct pci_driver *pci_drv) |
||
174 | { |
||
175 | struct list_head *pos; |
||
176 | struct dynid *dynid; |
||
177 | |||
178 | spin_lock(&pci_drv->dynids.lock); |
||
179 | list_for_each(pos, &pci_drv->dynids.list) { |
||
180 | dynid = list_entry(pos, struct dynid, node); |
||
181 | if (pci_match_one_device(&dynid->id, pci_dev)) { |
||
182 | spin_unlock(&pci_drv->dynids.lock); |
||
183 | return 1; |
||
184 | } |
||
185 | } |
||
186 | spin_unlock(&pci_drv->dynids.lock); |
||
187 | return 0; |
||
188 | } |
||
189 | |||
190 | #else /* !CONFIG_HOTPLUG */ |
||
191 | static inline int pci_device_probe_dynamic(struct pci_driver *drv, struct pci_dev *pci_dev) |
||
192 | { |
||
193 | return -ENODEV; |
||
194 | } |
||
195 | static inline void dynid_init(struct dynid *dynid) {} |
||
196 | static inline void pci_init_dynids(struct pci_dynids *dynids) {} |
||
197 | static inline void pci_free_dynids(struct pci_driver *drv) {} |
||
198 | static inline int pci_create_newid_file(struct pci_driver *drv) |
||
199 | { |
||
200 | return 0; |
||
201 | } |
||
202 | static inline int pci_bus_match_dynids(const struct pci_dev *pci_dev, struct pci_driver *pci_drv) |
||
203 | { |
||
204 | return 0; |
||
205 | } |
||
206 | #endif |
||
207 | |||
208 | /** |
||
209 | * pci_match_device - Tell if a PCI device structure has a matching |
||
210 | * PCI device id structure |
||
211 | * @ids: array of PCI device id structures to search in |
||
212 | * @dev: the PCI device structure to match against |
||
213 | * |
||
214 | * Used by a driver to check whether a PCI device present in the |
||
215 | * system is in its list of supported devices.Returns the matching |
||
216 | * pci_device_id structure or %NULL if there is no match. |
||
217 | */ |
||
218 | const struct pci_device_id * |
||
219 | pci_match_device(const struct pci_device_id *ids, const struct pci_dev *dev) |
||
220 | { |
||
221 | while (ids->vendor || ids->subvendor || ids->class_mask) { |
||
222 | if (pci_match_one_device(ids, dev)) |
||
223 | return ids; |
||
224 | ids++; |
||
225 | } |
||
226 | return NULL; |
||
227 | } |
||
228 | |||
229 | /** |
||
230 | * pci_device_probe_static() |
||
231 | * |
||
232 | * returns 0 and sets pci_dev->driver when drv claims pci_dev, else error. |
||
233 | */ |
||
234 | static int |
||
235 | pci_device_probe_static(struct pci_driver *drv, struct pci_dev *pci_dev) |
||
236 | { |
||
237 | int error = -ENODEV; |
||
238 | const struct pci_device_id *id; |
||
239 | |||
240 | if (!drv->id_table) |
||
241 | return error; |
||
242 | id = pci_match_device(drv->id_table, pci_dev); |
||
243 | if (id) |
||
244 | error = drv->probe(pci_dev, id); |
||
245 | if (error >= 0) { |
||
246 | pci_dev->driver = drv; |
||
247 | return 0; |
||
248 | } |
||
249 | return error; |
||
250 | } |
||
251 | |||
252 | /** |
||
253 | * __pci_device_probe() |
||
254 | * |
||
255 | * returns 0 on success, else error. |
||
256 | * side-effect: pci_dev->driver is set to drv when drv claims pci_dev. |
||
257 | */ |
||
258 | static int |
||
259 | __pci_device_probe(struct pci_driver *drv, struct pci_dev *pci_dev) |
||
260 | { |
||
261 | int error = 0; |
||
262 | |||
263 | if (!pci_dev->driver && drv->probe) { |
||
264 | error = pci_device_probe_static(drv, pci_dev); |
||
265 | if (error == -ENODEV) |
||
266 | error = pci_device_probe_dynamic(drv, pci_dev); |
||
267 | } |
||
268 | return error; |
||
269 | } |
||
270 | |||
271 | static int pci_device_probe(struct device * dev) |
||
272 | { |
||
273 | int error = 0; |
||
274 | struct pci_driver *drv; |
||
275 | struct pci_dev *pci_dev; |
||
276 | |||
277 | drv = to_pci_driver(dev->driver); |
||
278 | pci_dev = to_pci_dev(dev); |
||
279 | pci_dev_get(pci_dev); |
||
280 | error = __pci_device_probe(drv, pci_dev); |
||
281 | if (error) |
||
282 | pci_dev_put(pci_dev); |
||
283 | |||
284 | return error; |
||
285 | } |
||
286 | |||
287 | static int pci_device_remove(struct device * dev) |
||
288 | { |
||
289 | struct pci_dev * pci_dev = to_pci_dev(dev); |
||
290 | struct pci_driver * drv = pci_dev->driver; |
||
291 | |||
292 | if (drv) { |
||
293 | if (drv->remove) |
||
294 | drv->remove(pci_dev); |
||
295 | pci_dev->driver = NULL; |
||
296 | } |
||
297 | pci_dev_put(pci_dev); |
||
298 | return 0; |
||
299 | } |
||
300 | |||
301 | static int pci_device_suspend(struct device * dev, u32 state) |
||
302 | { |
||
303 | struct pci_dev * pci_dev = to_pci_dev(dev); |
||
304 | struct pci_driver * drv = pci_dev->driver; |
||
305 | |||
306 | if (drv && drv->suspend) |
||
307 | return drv->suspend(pci_dev,state); |
||
308 | return 0; |
||
309 | } |
||
310 | |||
311 | static int pci_device_resume(struct device * dev) |
||
312 | { |
||
313 | struct pci_dev * pci_dev = to_pci_dev(dev); |
||
314 | struct pci_driver * drv = pci_dev->driver; |
||
315 | |||
316 | if (drv && drv->resume) |
||
317 | drv->resume(pci_dev); |
||
318 | return 0; |
||
319 | } |
||
320 | |||
321 | |||
322 | #define kobj_to_pci_driver(obj) container_of(obj, struct device_driver, kobj) |
||
323 | #define attr_to_driver_attribute(obj) container_of(obj, struct driver_attribute, attr) |
||
324 | |||
325 | static ssize_t |
||
326 | pci_driver_attr_show(struct kobject * kobj, struct attribute *attr, char *buf) |
||
327 | { |
||
328 | struct device_driver *driver = kobj_to_pci_driver(kobj); |
||
329 | struct driver_attribute *dattr = attr_to_driver_attribute(attr); |
||
330 | ssize_t ret = 0; |
||
331 | |||
332 | if (get_driver(driver)) { |
||
333 | if (dattr->show) |
||
334 | ret = dattr->show(driver, buf); |
||
335 | put_driver(driver); |
||
336 | } |
||
337 | return ret; |
||
338 | } |
||
339 | |||
340 | static ssize_t |
||
341 | pci_driver_attr_store(struct kobject * kobj, struct attribute *attr, |
||
342 | const char *buf, size_t count) |
||
343 | { |
||
344 | struct device_driver *driver = kobj_to_pci_driver(kobj); |
||
345 | struct driver_attribute *dattr = attr_to_driver_attribute(attr); |
||
346 | ssize_t ret = 0; |
||
347 | |||
348 | if (get_driver(driver)) { |
||
349 | if (dattr->store) |
||
350 | ret = dattr->store(driver, buf, count); |
||
351 | put_driver(driver); |
||
352 | } |
||
353 | return ret; |
||
354 | } |
||
355 | |||
356 | static struct sysfs_ops pci_driver_sysfs_ops = { |
||
357 | .show = pci_driver_attr_show, |
||
358 | .store = pci_driver_attr_store, |
||
359 | }; |
||
360 | static struct kobj_type pci_driver_kobj_type = { |
||
361 | .sysfs_ops = &pci_driver_sysfs_ops, |
||
362 | }; |
||
363 | |||
364 | static int |
||
365 | pci_populate_driver_dir(struct pci_driver *drv) |
||
366 | { |
||
367 | return pci_create_newid_file(drv); |
||
368 | } |
||
369 | |||
370 | /** |
||
371 | * pci_register_driver - register a new pci driver |
||
372 | * @drv: the driver structure to register |
||
373 | * |
||
374 | * Adds the driver structure to the list of registered drivers |
||
375 | * Returns the number of pci devices which were claimed by the driver |
||
376 | * during registration. The driver remains registered even if the |
||
377 | * return value is zero. |
||
378 | */ |
||
379 | int |
||
380 | pci_register_driver(struct pci_driver *drv) |
||
381 | { |
||
382 | int count = 0; |
||
383 | |||
384 | /* initialize common driver fields */ |
||
385 | drv->driver.name = drv->name; |
||
386 | drv->driver.bus = &pci_bus_type; |
||
387 | drv->driver.probe = pci_device_probe; |
||
388 | drv->driver.remove = pci_device_remove; |
||
389 | drv->driver.kobj.ktype = &pci_driver_kobj_type; |
||
390 | pci_init_dynids(&drv->dynids); |
||
391 | |||
392 | /* register with core */ |
||
393 | count = driver_register(&drv->driver); |
||
394 | |||
395 | if (count >= 0) { |
||
396 | pci_populate_driver_dir(drv); |
||
397 | } |
||
398 | |||
399 | return count ? count : 1; |
||
400 | } |
||
401 | |||
402 | /** |
||
403 | * pci_unregister_driver - unregister a pci driver |
||
404 | * @drv: the driver structure to unregister |
||
405 | * |
||
406 | * Deletes the driver structure from the list of registered PCI drivers, |
||
407 | * gives it a chance to clean up by calling its remove() function for |
||
408 | * each device it was responsible for, and marks those devices as |
||
409 | * driverless. |
||
410 | */ |
||
411 | |||
412 | void |
||
413 | pci_unregister_driver(struct pci_driver *drv) |
||
414 | { |
||
415 | driver_unregister(&drv->driver); |
||
416 | pci_free_dynids(drv); |
||
417 | } |
||
418 | |||
419 | static struct pci_driver pci_compat_driver = { |
||
420 | .name = "compat" |
||
421 | }; |
||
422 | |||
423 | /** |
||
424 | * pci_dev_driver - get the pci_driver of a device |
||
425 | * @dev: the device to query |
||
426 | * |
||
427 | * Returns the appropriate pci_driver structure or %NULL if there is no |
||
428 | * registered driver for the device. |
||
429 | */ |
||
430 | struct pci_driver * |
||
431 | pci_dev_driver(const struct pci_dev *dev) |
||
432 | { |
||
433 | if (dev->driver) |
||
434 | return dev->driver; |
||
435 | else { |
||
436 | int i; |
||
437 | for(i=0; i<=PCI_ROM_RESOURCE; i++) |
||
438 | if (dev->resource[i].flags & IORESOURCE_BUSY) |
||
439 | return &pci_compat_driver; |
||
440 | } |
||
441 | return NULL; |
||
442 | } |
||
443 | |||
444 | /** |
||
445 | * pci_bus_match - Tell if a PCI device structure has a matching PCI device id structure |
||
446 | * @ids: array of PCI device id structures to search in |
||
447 | * @dev: the PCI device structure to match against |
||
448 | * |
||
449 | * Used by a driver to check whether a PCI device present in the |
||
450 | * system is in its list of supported devices.Returns the matching |
||
451 | * pci_device_id structure or %NULL if there is no match. |
||
452 | */ |
||
453 | static int pci_bus_match(struct device * dev, struct device_driver * drv) |
||
454 | { |
||
455 | const struct pci_dev * pci_dev = to_pci_dev(dev); |
||
456 | struct pci_driver * pci_drv = to_pci_driver(drv); |
||
457 | const struct pci_device_id * ids = pci_drv->id_table; |
||
458 | const struct pci_device_id *found_id; |
||
459 | |||
460 | if (!ids) |
||
461 | return 0; |
||
462 | |||
463 | found_id = pci_match_device(ids, pci_dev); |
||
464 | if (found_id) |
||
465 | return 1; |
||
466 | |||
467 | return pci_bus_match_dynids(pci_dev, pci_drv); |
||
468 | } |
||
469 | |||
470 | /** |
||
471 | * pci_dev_get - increments the reference count of the pci device structure |
||
472 | * @dev: the device being referenced |
||
473 | * |
||
474 | * Each live reference to a device should be refcounted. |
||
475 | * |
||
476 | * Drivers for PCI devices should normally record such references in |
||
477 | * their probe() methods, when they bind to a device, and release |
||
478 | * them by calling pci_dev_put(), in their disconnect() methods. |
||
479 | * |
||
480 | * A pointer to the device with the incremented reference counter is returned. |
||
481 | */ |
||
482 | struct pci_dev *pci_dev_get(struct pci_dev *dev) |
||
483 | { |
||
484 | struct device *tmp; |
||
485 | |||
486 | if (!dev) |
||
487 | return NULL; |
||
488 | |||
489 | tmp = get_device(&dev->dev); |
||
490 | if (tmp) |
||
491 | return to_pci_dev(tmp); |
||
492 | else |
||
493 | return NULL; |
||
494 | } |
||
495 | |||
496 | /** |
||
497 | * pci_dev_put - release a use of the pci device structure |
||
498 | * @dev: device that's been disconnected |
||
499 | * |
||
500 | * Must be called when a user of a device is finished with it. When the last |
||
501 | * user of the device calls this function, the memory of the device is freed. |
||
502 | */ |
||
503 | void pci_dev_put(struct pci_dev *dev) |
||
504 | { |
||
505 | if (dev) |
||
506 | put_device(&dev->dev); |
||
507 | } |
||
508 | |||
509 | #ifndef CONFIG_HOTPLUG |
||
510 | int pci_hotplug (struct device *dev, char **envp, int num_envp, |
||
511 | char *buffer, int buffer_size) |
||
512 | { |
||
513 | return -ENODEV; |
||
514 | } |
||
515 | #endif |
||
516 | |||
517 | struct bus_type pci_bus_type = { |
||
518 | .name = "pci", |
||
519 | .match = pci_bus_match, |
||
520 | .hotplug = pci_hotplug, |
||
521 | .suspend = pci_device_suspend, |
||
522 | .resume = pci_device_resume, |
||
523 | }; |
||
524 | |||
525 | int __init pci_driver_init(void) |
||
526 | { |
||
527 | return bus_register(&pci_bus_type); |
||
528 | } |
||
529 | |||
530 | postcore_initcall(pci_driver_init); |
||
531 | |||
532 | EXPORT_SYMBOL(pci_match_device); |
||
533 | EXPORT_SYMBOL(pci_register_driver); |
||
534 | EXPORT_SYMBOL(pci_unregister_driver); |
||
535 | EXPORT_SYMBOL(pci_dev_driver); |
||
536 | EXPORT_SYMBOL(pci_bus_type); |
||
537 | EXPORT_SYMBOL(pci_dev_get); |
||
538 | EXPORT_SYMBOL(pci_dev_put); |