diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/pci/pci-driver.c |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'drivers/pci/pci-driver.c')
-rw-r--r-- | drivers/pci/pci-driver.c | 531 |
1 files changed, 531 insertions, 0 deletions
diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c new file mode 100644 index 000000000000..37b7961efc44 --- /dev/null +++ b/drivers/pci/pci-driver.c | |||
@@ -0,0 +1,531 @@ | |||
1 | /* | ||
2 | * drivers/pci/pci-driver.c | ||
3 | * | ||
4 | */ | ||
5 | |||
6 | #include <linux/pci.h> | ||
7 | #include <linux/module.h> | ||
8 | #include <linux/init.h> | ||
9 | #include <linux/device.h> | ||
10 | #include <linux/pci-dynids.h> | ||
11 | #include "pci.h" | ||
12 | |||
13 | /* | ||
14 | * Registration of PCI drivers and handling of hot-pluggable devices. | ||
15 | */ | ||
16 | |||
17 | /* | ||
18 | * Dynamic device IDs are disabled for !CONFIG_HOTPLUG | ||
19 | */ | ||
20 | |||
21 | #ifdef CONFIG_HOTPLUG | ||
22 | /** | ||
23 | * pci_device_probe_dynamic() | ||
24 | * | ||
25 | * Walk the dynamic ID list looking for a match. | ||
26 | * returns 0 and sets pci_dev->driver when drv claims pci_dev, else error. | ||
27 | */ | ||
28 | static int | ||
29 | pci_device_probe_dynamic(struct pci_driver *drv, struct pci_dev *pci_dev) | ||
30 | { | ||
31 | int error = -ENODEV; | ||
32 | struct list_head *pos; | ||
33 | struct dynid *dynid; | ||
34 | |||
35 | spin_lock(&drv->dynids.lock); | ||
36 | list_for_each(pos, &drv->dynids.list) { | ||
37 | dynid = list_entry(pos, struct dynid, node); | ||
38 | if (pci_match_one_device(&dynid->id, pci_dev)) { | ||
39 | spin_unlock(&drv->dynids.lock); | ||
40 | error = drv->probe(pci_dev, &dynid->id); | ||
41 | if (error >= 0) { | ||
42 | pci_dev->driver = drv; | ||
43 | return 0; | ||
44 | } | ||
45 | return error; | ||
46 | } | ||
47 | } | ||
48 | spin_unlock(&drv->dynids.lock); | ||
49 | return error; | ||
50 | } | ||
51 | |||
52 | /** | ||
53 | * store_new_id | ||
54 | * | ||
55 | * Adds a new dynamic pci device ID to this driver, | ||
56 | * and causes the driver to probe for all devices again. | ||
57 | */ | ||
58 | static inline ssize_t | ||
59 | store_new_id(struct device_driver *driver, const char *buf, size_t count) | ||
60 | { | ||
61 | struct dynid *dynid; | ||
62 | struct bus_type * bus; | ||
63 | struct pci_driver *pdrv = to_pci_driver(driver); | ||
64 | __u32 vendor=PCI_ANY_ID, device=PCI_ANY_ID, subvendor=PCI_ANY_ID, | ||
65 | subdevice=PCI_ANY_ID, class=0, class_mask=0; | ||
66 | unsigned long driver_data=0; | ||
67 | int fields=0; | ||
68 | |||
69 | fields = sscanf(buf, "%x %x %x %x %x %x %lux", | ||
70 | &vendor, &device, &subvendor, &subdevice, | ||
71 | &class, &class_mask, &driver_data); | ||
72 | if (fields < 0) | ||
73 | return -EINVAL; | ||
74 | |||
75 | dynid = kmalloc(sizeof(*dynid), GFP_KERNEL); | ||
76 | if (!dynid) | ||
77 | return -ENOMEM; | ||
78 | |||
79 | memset(dynid, 0, sizeof(*dynid)); | ||
80 | INIT_LIST_HEAD(&dynid->node); | ||
81 | dynid->id.vendor = vendor; | ||
82 | dynid->id.device = device; | ||
83 | dynid->id.subvendor = subvendor; | ||
84 | dynid->id.subdevice = subdevice; | ||
85 | dynid->id.class = class; | ||
86 | dynid->id.class_mask = class_mask; | ||
87 | dynid->id.driver_data = pdrv->dynids.use_driver_data ? | ||
88 | driver_data : 0UL; | ||
89 | |||
90 | spin_lock(&pdrv->dynids.lock); | ||
91 | list_add_tail(&pdrv->dynids.list, &dynid->node); | ||
92 | spin_unlock(&pdrv->dynids.lock); | ||
93 | |||
94 | bus = get_bus(pdrv->driver.bus); | ||
95 | if (bus) { | ||
96 | if (get_driver(&pdrv->driver)) { | ||
97 | down_write(&bus->subsys.rwsem); | ||
98 | driver_attach(&pdrv->driver); | ||
99 | up_write(&bus->subsys.rwsem); | ||
100 | put_driver(&pdrv->driver); | ||
101 | } | ||
102 | put_bus(bus); | ||
103 | } | ||
104 | |||
105 | return count; | ||
106 | } | ||
107 | |||
108 | static DRIVER_ATTR(new_id, S_IWUSR, NULL, store_new_id); | ||
109 | static inline void | ||
110 | pci_init_dynids(struct pci_dynids *dynids) | ||
111 | { | ||
112 | spin_lock_init(&dynids->lock); | ||
113 | INIT_LIST_HEAD(&dynids->list); | ||
114 | } | ||
115 | |||
116 | static void | ||
117 | pci_free_dynids(struct pci_driver *drv) | ||
118 | { | ||
119 | struct list_head *pos, *n; | ||
120 | struct dynid *dynid; | ||
121 | |||
122 | spin_lock(&drv->dynids.lock); | ||
123 | list_for_each_safe(pos, n, &drv->dynids.list) { | ||
124 | dynid = list_entry(pos, struct dynid, node); | ||
125 | list_del(&dynid->node); | ||
126 | kfree(dynid); | ||
127 | } | ||
128 | spin_unlock(&drv->dynids.lock); | ||
129 | } | ||
130 | |||
131 | static int | ||
132 | pci_create_newid_file(struct pci_driver *drv) | ||
133 | { | ||
134 | int error = 0; | ||
135 | if (drv->probe != NULL) | ||
136 | error = sysfs_create_file(&drv->driver.kobj, | ||
137 | &driver_attr_new_id.attr); | ||
138 | return error; | ||
139 | } | ||
140 | |||
141 | static int | ||
142 | pci_bus_match_dynids(const struct pci_dev *pci_dev, struct pci_driver *pci_drv) | ||
143 | { | ||
144 | struct list_head *pos; | ||
145 | struct dynid *dynid; | ||
146 | |||
147 | spin_lock(&pci_drv->dynids.lock); | ||
148 | list_for_each(pos, &pci_drv->dynids.list) { | ||
149 | dynid = list_entry(pos, struct dynid, node); | ||
150 | if (pci_match_one_device(&dynid->id, pci_dev)) { | ||
151 | spin_unlock(&pci_drv->dynids.lock); | ||
152 | return 1; | ||
153 | } | ||
154 | } | ||
155 | spin_unlock(&pci_drv->dynids.lock); | ||
156 | return 0; | ||
157 | } | ||
158 | |||
159 | #else /* !CONFIG_HOTPLUG */ | ||
160 | static inline int pci_device_probe_dynamic(struct pci_driver *drv, struct pci_dev *pci_dev) | ||
161 | { | ||
162 | return -ENODEV; | ||
163 | } | ||
164 | static inline void pci_init_dynids(struct pci_dynids *dynids) {} | ||
165 | static inline void pci_free_dynids(struct pci_driver *drv) {} | ||
166 | static inline int pci_create_newid_file(struct pci_driver *drv) | ||
167 | { | ||
168 | return 0; | ||
169 | } | ||
170 | static inline int pci_bus_match_dynids(const struct pci_dev *pci_dev, struct pci_driver *pci_drv) | ||
171 | { | ||
172 | return 0; | ||
173 | } | ||
174 | #endif | ||
175 | |||
176 | /** | ||
177 | * pci_match_device - Tell if a PCI device structure has a matching | ||
178 | * PCI device id structure | ||
179 | * @ids: array of PCI device id structures to search in | ||
180 | * @dev: the PCI device structure to match against | ||
181 | * | ||
182 | * Used by a driver to check whether a PCI device present in the | ||
183 | * system is in its list of supported devices.Returns the matching | ||
184 | * pci_device_id structure or %NULL if there is no match. | ||
185 | */ | ||
186 | const struct pci_device_id * | ||
187 | pci_match_device(const struct pci_device_id *ids, const struct pci_dev *dev) | ||
188 | { | ||
189 | while (ids->vendor || ids->subvendor || ids->class_mask) { | ||
190 | if (pci_match_one_device(ids, dev)) | ||
191 | return ids; | ||
192 | ids++; | ||
193 | } | ||
194 | return NULL; | ||
195 | } | ||
196 | |||
197 | /** | ||
198 | * pci_device_probe_static() | ||
199 | * | ||
200 | * returns 0 and sets pci_dev->driver when drv claims pci_dev, else error. | ||
201 | */ | ||
202 | static int | ||
203 | pci_device_probe_static(struct pci_driver *drv, struct pci_dev *pci_dev) | ||
204 | { | ||
205 | int error = -ENODEV; | ||
206 | const struct pci_device_id *id; | ||
207 | |||
208 | if (!drv->id_table) | ||
209 | return error; | ||
210 | id = pci_match_device(drv->id_table, pci_dev); | ||
211 | if (id) | ||
212 | error = drv->probe(pci_dev, id); | ||
213 | if (error >= 0) { | ||
214 | pci_dev->driver = drv; | ||
215 | error = 0; | ||
216 | } | ||
217 | return error; | ||
218 | } | ||
219 | |||
220 | /** | ||
221 | * __pci_device_probe() | ||
222 | * | ||
223 | * returns 0 on success, else error. | ||
224 | * side-effect: pci_dev->driver is set to drv when drv claims pci_dev. | ||
225 | */ | ||
226 | static int | ||
227 | __pci_device_probe(struct pci_driver *drv, struct pci_dev *pci_dev) | ||
228 | { | ||
229 | int error = 0; | ||
230 | |||
231 | if (!pci_dev->driver && drv->probe) { | ||
232 | error = pci_device_probe_static(drv, pci_dev); | ||
233 | if (error == -ENODEV) | ||
234 | error = pci_device_probe_dynamic(drv, pci_dev); | ||
235 | } | ||
236 | return error; | ||
237 | } | ||
238 | |||
239 | static int pci_device_probe(struct device * dev) | ||
240 | { | ||
241 | int error = 0; | ||
242 | struct pci_driver *drv; | ||
243 | struct pci_dev *pci_dev; | ||
244 | |||
245 | drv = to_pci_driver(dev->driver); | ||
246 | pci_dev = to_pci_dev(dev); | ||
247 | pci_dev_get(pci_dev); | ||
248 | error = __pci_device_probe(drv, pci_dev); | ||
249 | if (error) | ||
250 | pci_dev_put(pci_dev); | ||
251 | |||
252 | return error; | ||
253 | } | ||
254 | |||
255 | static int pci_device_remove(struct device * dev) | ||
256 | { | ||
257 | struct pci_dev * pci_dev = to_pci_dev(dev); | ||
258 | struct pci_driver * drv = pci_dev->driver; | ||
259 | |||
260 | if (drv) { | ||
261 | if (drv->remove) | ||
262 | drv->remove(pci_dev); | ||
263 | pci_dev->driver = NULL; | ||
264 | } | ||
265 | |||
266 | /* | ||
267 | * We would love to complain here if pci_dev->is_enabled is set, that | ||
268 | * the driver should have called pci_disable_device(), but the | ||
269 | * unfortunate fact is there are too many odd BIOS and bridge setups | ||
270 | * that don't like drivers doing that all of the time. | ||
271 | * Oh well, we can dream of sane hardware when we sleep, no matter how | ||
272 | * horrible the crap we have to deal with is when we are awake... | ||
273 | */ | ||
274 | |||
275 | pci_dev_put(pci_dev); | ||
276 | return 0; | ||
277 | } | ||
278 | |||
279 | static int pci_device_suspend(struct device * dev, pm_message_t state) | ||
280 | { | ||
281 | struct pci_dev * pci_dev = to_pci_dev(dev); | ||
282 | struct pci_driver * drv = pci_dev->driver; | ||
283 | int i = 0; | ||
284 | |||
285 | if (drv && drv->suspend) | ||
286 | i = drv->suspend(pci_dev, state); | ||
287 | else | ||
288 | pci_save_state(pci_dev); | ||
289 | return i; | ||
290 | } | ||
291 | |||
292 | |||
293 | /* | ||
294 | * Default resume method for devices that have no driver provided resume, | ||
295 | * or not even a driver at all. | ||
296 | */ | ||
297 | static void pci_default_resume(struct pci_dev *pci_dev) | ||
298 | { | ||
299 | /* restore the PCI config space */ | ||
300 | pci_restore_state(pci_dev); | ||
301 | /* if the device was enabled before suspend, reenable */ | ||
302 | if (pci_dev->is_enabled) | ||
303 | pci_enable_device(pci_dev); | ||
304 | /* if the device was busmaster before the suspend, make it busmaster again */ | ||
305 | if (pci_dev->is_busmaster) | ||
306 | pci_set_master(pci_dev); | ||
307 | } | ||
308 | |||
309 | static int pci_device_resume(struct device * dev) | ||
310 | { | ||
311 | struct pci_dev * pci_dev = to_pci_dev(dev); | ||
312 | struct pci_driver * drv = pci_dev->driver; | ||
313 | |||
314 | if (drv && drv->resume) | ||
315 | drv->resume(pci_dev); | ||
316 | else | ||
317 | pci_default_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 a negative value on error, otherwise 0. | ||
376 | * If no error occured, the driver remains registered even if | ||
377 | * no device was claimed during registration. | ||
378 | */ | ||
379 | int pci_register_driver(struct pci_driver *drv) | ||
380 | { | ||
381 | int error; | ||
382 | |||
383 | /* initialize common driver fields */ | ||
384 | drv->driver.name = drv->name; | ||
385 | drv->driver.bus = &pci_bus_type; | ||
386 | drv->driver.probe = pci_device_probe; | ||
387 | drv->driver.remove = pci_device_remove; | ||
388 | drv->driver.owner = drv->owner; | ||
389 | drv->driver.kobj.ktype = &pci_driver_kobj_type; | ||
390 | pci_init_dynids(&drv->dynids); | ||
391 | |||
392 | /* register with core */ | ||
393 | error = driver_register(&drv->driver); | ||
394 | |||
395 | if (!error) | ||
396 | pci_populate_driver_dir(drv); | ||
397 | |||
398 | return error; | ||
399 | } | ||
400 | |||
401 | /** | ||
402 | * pci_unregister_driver - unregister a pci driver | ||
403 | * @drv: the driver structure to unregister | ||
404 | * | ||
405 | * Deletes the driver structure from the list of registered PCI drivers, | ||
406 | * gives it a chance to clean up by calling its remove() function for | ||
407 | * each device it was responsible for, and marks those devices as | ||
408 | * driverless. | ||
409 | */ | ||
410 | |||
411 | void | ||
412 | pci_unregister_driver(struct pci_driver *drv) | ||
413 | { | ||
414 | driver_unregister(&drv->driver); | ||
415 | pci_free_dynids(drv); | ||
416 | } | ||
417 | |||
418 | static struct pci_driver pci_compat_driver = { | ||
419 | .name = "compat" | ||
420 | }; | ||
421 | |||
422 | /** | ||
423 | * pci_dev_driver - get the pci_driver of a device | ||
424 | * @dev: the device to query | ||
425 | * | ||
426 | * Returns the appropriate pci_driver structure or %NULL if there is no | ||
427 | * registered driver for the device. | ||
428 | */ | ||
429 | struct pci_driver * | ||
430 | pci_dev_driver(const struct pci_dev *dev) | ||
431 | { | ||
432 | if (dev->driver) | ||
433 | return dev->driver; | ||
434 | else { | ||
435 | int i; | ||
436 | for(i=0; i<=PCI_ROM_RESOURCE; i++) | ||
437 | if (dev->resource[i].flags & IORESOURCE_BUSY) | ||
438 | return &pci_compat_driver; | ||
439 | } | ||
440 | return NULL; | ||
441 | } | ||
442 | |||
443 | /** | ||
444 | * pci_bus_match - Tell if a PCI device structure has a matching PCI device id structure | ||
445 | * @ids: array of PCI device id structures to search in | ||
446 | * @dev: the PCI device structure to match against | ||
447 | * | ||
448 | * Used by a driver to check whether a PCI device present in the | ||
449 | * system is in its list of supported devices.Returns the matching | ||
450 | * pci_device_id structure or %NULL if there is no match. | ||
451 | */ | ||
452 | static int pci_bus_match(struct device * dev, struct device_driver * drv) | ||
453 | { | ||
454 | const struct pci_dev * pci_dev = to_pci_dev(dev); | ||
455 | struct pci_driver * pci_drv = to_pci_driver(drv); | ||
456 | const struct pci_device_id * ids = pci_drv->id_table; | ||
457 | const struct pci_device_id *found_id; | ||
458 | |||
459 | if (!ids) | ||
460 | return 0; | ||
461 | |||
462 | found_id = pci_match_device(ids, pci_dev); | ||
463 | if (found_id) | ||
464 | return 1; | ||
465 | |||
466 | return pci_bus_match_dynids(pci_dev, pci_drv); | ||
467 | } | ||
468 | |||
469 | /** | ||
470 | * pci_dev_get - increments the reference count of the pci device structure | ||
471 | * @dev: the device being referenced | ||
472 | * | ||
473 | * Each live reference to a device should be refcounted. | ||
474 | * | ||
475 | * Drivers for PCI devices should normally record such references in | ||
476 | * their probe() methods, when they bind to a device, and release | ||
477 | * them by calling pci_dev_put(), in their disconnect() methods. | ||
478 | * | ||
479 | * A pointer to the device with the incremented reference counter is returned. | ||
480 | */ | ||
481 | struct pci_dev *pci_dev_get(struct pci_dev *dev) | ||
482 | { | ||
483 | if (dev) | ||
484 | get_device(&dev->dev); | ||
485 | return dev; | ||
486 | } | ||
487 | |||
488 | /** | ||
489 | * pci_dev_put - release a use of the pci device structure | ||
490 | * @dev: device that's been disconnected | ||
491 | * | ||
492 | * Must be called when a user of a device is finished with it. When the last | ||
493 | * user of the device calls this function, the memory of the device is freed. | ||
494 | */ | ||
495 | void pci_dev_put(struct pci_dev *dev) | ||
496 | { | ||
497 | if (dev) | ||
498 | put_device(&dev->dev); | ||
499 | } | ||
500 | |||
501 | #ifndef CONFIG_HOTPLUG | ||
502 | int pci_hotplug (struct device *dev, char **envp, int num_envp, | ||
503 | char *buffer, int buffer_size) | ||
504 | { | ||
505 | return -ENODEV; | ||
506 | } | ||
507 | #endif | ||
508 | |||
509 | struct bus_type pci_bus_type = { | ||
510 | .name = "pci", | ||
511 | .match = pci_bus_match, | ||
512 | .hotplug = pci_hotplug, | ||
513 | .suspend = pci_device_suspend, | ||
514 | .resume = pci_device_resume, | ||
515 | .dev_attrs = pci_dev_attrs, | ||
516 | }; | ||
517 | |||
518 | static int __init pci_driver_init(void) | ||
519 | { | ||
520 | return bus_register(&pci_bus_type); | ||
521 | } | ||
522 | |||
523 | postcore_initcall(pci_driver_init); | ||
524 | |||
525 | EXPORT_SYMBOL(pci_match_device); | ||
526 | EXPORT_SYMBOL(pci_register_driver); | ||
527 | EXPORT_SYMBOL(pci_unregister_driver); | ||
528 | EXPORT_SYMBOL(pci_dev_driver); | ||
529 | EXPORT_SYMBOL(pci_bus_type); | ||
530 | EXPORT_SYMBOL(pci_dev_get); | ||
531 | EXPORT_SYMBOL(pci_dev_put); | ||