diff options
author | Kay Sievers <kay.sievers@vrfy.org> | 2007-02-16 11:33:36 -0500 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2007-04-27 13:57:28 -0400 |
commit | b8c5cec23d5c33b767a1cddebd4f8813a9563e3c (patch) | |
tree | cffec2c5df58866aa6e7ed5540c2718a166c3246 /drivers/base/bus.c | |
parent | a456b7023e0abf80bb03b0bdf5471b48878e5c49 (diff) |
Driver core: udev triggered device-<>driver binding
We get two per-bus sysfs files:
ls-l /sys/subsystem/usb
drwxr-xr-x 2 root root 0 2007-02-16 16:42 devices
drwxr-xr-x 7 root root 0 2007-02-16 14:55 drivers
-rw-r--r-- 1 root root 4096 2007-02-16 16:42 drivers_autoprobe
--w------- 1 root root 4096 2007-02-16 16:42 drivers_probe
The flag "drivers_autoprobe" controls the behavior of the bus to bind
devices by default, or just initialize the device and leave it alone.
The command "drivers_probe" accepts a bus_id and the bus tries to bind a
driver to this device.
Systems who want to control the driver binding with udev, switch off the
bus initiated probing:
echo 0 > /sys/subsystem/usb/drivers_autoprobe
echo 0 > /sys/subsystem/pcmcia/drivers_autoprobe
...
and initiate the probing with udev rules like:
ACTION=="add", SUBSYSTEM=="usb", ATTR{subsystem/drivers_probe}="$kernel"
ACTION=="add", SUBSYSTEM=="pcmcia", ATTR{subsystem/drivers_probe}="$kernel"
...
Custom driver binding can happen in earlier rules by something like:
ACTION=="add", SUBSYSTEM=="usb", \
ATTRS{idVendor}=="1234", ATTRS{idProduct}=="5678" \
ATTR{subsystem/drivers/<custom-driver>/bind}="$kernel"
This is intended to solve the modprobe.conf mess with "install-rules", custom
bind/unbind-scripts and all the weird things people invented over the years.
It should also provide the functionality "libusual" was supposed to do.
With udev, one can just write a udev rule to drive all USB-disks at the
third port of USB-hub by the "ub" driver, and everything else by
usb-storage. One can also instruct udev to bind different wireless
drivers to identical cards - just selected by the pcmcia slot-number, and
whatever ...
To use the mentioned rules, it needs udev version 106, to be able to
write ATTR{}="$kernel" to sysfs files.
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/base/bus.c')
-rw-r--r-- | drivers/base/bus.c | 84 |
1 files changed, 78 insertions, 6 deletions
diff --git a/drivers/base/bus.c b/drivers/base/bus.c index 253868e03c70..9df2e6dff519 100644 --- a/drivers/base/bus.c +++ b/drivers/base/bus.c | |||
@@ -27,6 +27,9 @@ | |||
27 | #define to_driver(obj) container_of(obj, struct device_driver, kobj) | 27 | #define to_driver(obj) container_of(obj, struct device_driver, kobj) |
28 | 28 | ||
29 | 29 | ||
30 | static int __must_check bus_rescan_devices_helper(struct device *dev, | ||
31 | void *data); | ||
32 | |||
30 | static ssize_t | 33 | static ssize_t |
31 | drv_attr_show(struct kobject * kobj, struct attribute * attr, char * buf) | 34 | drv_attr_show(struct kobject * kobj, struct attribute * attr, char * buf) |
32 | { | 35 | { |
@@ -133,7 +136,6 @@ static decl_subsys(bus, &ktype_bus, NULL); | |||
133 | 136 | ||
134 | 137 | ||
135 | #ifdef CONFIG_HOTPLUG | 138 | #ifdef CONFIG_HOTPLUG |
136 | |||
137 | /* Manually detach a device from its associated driver. */ | 139 | /* Manually detach a device from its associated driver. */ |
138 | static int driver_helper(struct device *dev, void *data) | 140 | static int driver_helper(struct device *dev, void *data) |
139 | { | 141 | { |
@@ -199,6 +201,33 @@ static ssize_t driver_bind(struct device_driver *drv, | |||
199 | } | 201 | } |
200 | static DRIVER_ATTR(bind, S_IWUSR, NULL, driver_bind); | 202 | static DRIVER_ATTR(bind, S_IWUSR, NULL, driver_bind); |
201 | 203 | ||
204 | static ssize_t show_drivers_autoprobe(struct bus_type *bus, char *buf) | ||
205 | { | ||
206 | return sprintf(buf, "%d\n", bus->drivers_autoprobe); | ||
207 | } | ||
208 | |||
209 | static ssize_t store_drivers_autoprobe(struct bus_type *bus, | ||
210 | const char *buf, size_t count) | ||
211 | { | ||
212 | if (buf[0] == '0') | ||
213 | bus->drivers_autoprobe = 0; | ||
214 | else | ||
215 | bus->drivers_autoprobe = 1; | ||
216 | return count; | ||
217 | } | ||
218 | |||
219 | static ssize_t store_drivers_probe(struct bus_type *bus, | ||
220 | const char *buf, size_t count) | ||
221 | { | ||
222 | struct device *dev; | ||
223 | |||
224 | dev = bus_find_device(bus, NULL, (void *)buf, driver_helper); | ||
225 | if (!dev) | ||
226 | return -ENODEV; | ||
227 | if (bus_rescan_devices_helper(dev, NULL) != 0) | ||
228 | return -EINVAL; | ||
229 | return count; | ||
230 | } | ||
202 | #endif | 231 | #endif |
203 | 232 | ||
204 | static struct device * next_device(struct klist_iter * i) | 233 | static struct device * next_device(struct klist_iter * i) |
@@ -425,7 +454,8 @@ int bus_attach_device(struct device * dev) | |||
425 | 454 | ||
426 | if (bus) { | 455 | if (bus) { |
427 | dev->is_registered = 1; | 456 | dev->is_registered = 1; |
428 | ret = device_attach(dev); | 457 | if (bus->drivers_autoprobe) |
458 | ret = device_attach(dev); | ||
429 | if (ret >= 0) { | 459 | if (ret >= 0) { |
430 | klist_add_tail(&dev->knode_bus, &bus->klist_devices); | 460 | klist_add_tail(&dev->knode_bus, &bus->klist_devices); |
431 | ret = 0; | 461 | ret = 0; |
@@ -515,9 +545,41 @@ static void remove_bind_files(struct device_driver *drv) | |||
515 | driver_remove_file(drv, &driver_attr_bind); | 545 | driver_remove_file(drv, &driver_attr_bind); |
516 | driver_remove_file(drv, &driver_attr_unbind); | 546 | driver_remove_file(drv, &driver_attr_unbind); |
517 | } | 547 | } |
548 | |||
549 | static int add_probe_files(struct bus_type *bus) | ||
550 | { | ||
551 | int retval; | ||
552 | |||
553 | bus->drivers_probe_attr.attr.name = "drivers_probe"; | ||
554 | bus->drivers_probe_attr.attr.mode = S_IWUSR; | ||
555 | bus->drivers_probe_attr.attr.owner = bus->owner; | ||
556 | bus->drivers_probe_attr.store = store_drivers_probe; | ||
557 | retval = bus_create_file(bus, &bus->drivers_probe_attr); | ||
558 | if (retval) | ||
559 | goto out; | ||
560 | |||
561 | bus->drivers_autoprobe_attr.attr.name = "drivers_autoprobe"; | ||
562 | bus->drivers_autoprobe_attr.attr.mode = S_IWUSR | S_IRUGO; | ||
563 | bus->drivers_autoprobe_attr.attr.owner = bus->owner; | ||
564 | bus->drivers_autoprobe_attr.show = show_drivers_autoprobe; | ||
565 | bus->drivers_autoprobe_attr.store = store_drivers_autoprobe; | ||
566 | retval = bus_create_file(bus, &bus->drivers_autoprobe_attr); | ||
567 | if (retval) | ||
568 | bus_remove_file(bus, &bus->drivers_probe_attr); | ||
569 | out: | ||
570 | return retval; | ||
571 | } | ||
572 | |||
573 | static void remove_probe_files(struct bus_type *bus) | ||
574 | { | ||
575 | bus_remove_file(bus, &bus->drivers_autoprobe_attr); | ||
576 | bus_remove_file(bus, &bus->drivers_probe_attr); | ||
577 | } | ||
518 | #else | 578 | #else |
519 | static inline int add_bind_files(struct device_driver *drv) { return 0; } | 579 | static inline int add_bind_files(struct device_driver *drv) { return 0; } |
520 | static inline void remove_bind_files(struct device_driver *drv) {} | 580 | static inline void remove_bind_files(struct device_driver *drv) {} |
581 | static inline int add_probe_files(struct bus_type *bus) { return 0; } | ||
582 | static inline void remove_probe_files(struct bus_type *bus) {} | ||
521 | #endif | 583 | #endif |
522 | 584 | ||
523 | /** | 585 | /** |
@@ -541,9 +603,11 @@ int bus_add_driver(struct device_driver *drv) | |||
541 | if ((error = kobject_register(&drv->kobj))) | 603 | if ((error = kobject_register(&drv->kobj))) |
542 | goto out_put_bus; | 604 | goto out_put_bus; |
543 | 605 | ||
544 | error = driver_attach(drv); | 606 | if (drv->bus->drivers_autoprobe) { |
545 | if (error) | 607 | error = driver_attach(drv); |
546 | goto out_unregister; | 608 | if (error) |
609 | goto out_unregister; | ||
610 | } | ||
547 | klist_add_tail(&drv->knode_bus, &bus->klist_drivers); | 611 | klist_add_tail(&drv->knode_bus, &bus->klist_drivers); |
548 | module_add_driver(drv->owner, drv); | 612 | module_add_driver(drv->owner, drv); |
549 | 613 | ||
@@ -762,6 +826,12 @@ int bus_register(struct bus_type * bus) | |||
762 | 826 | ||
763 | klist_init(&bus->klist_devices, klist_devices_get, klist_devices_put); | 827 | klist_init(&bus->klist_devices, klist_devices_get, klist_devices_put); |
764 | klist_init(&bus->klist_drivers, NULL, NULL); | 828 | klist_init(&bus->klist_drivers, NULL, NULL); |
829 | |||
830 | bus->drivers_autoprobe = 1; | ||
831 | retval = add_probe_files(bus); | ||
832 | if (retval) | ||
833 | goto bus_probe_files_fail; | ||
834 | |||
765 | retval = bus_add_attrs(bus); | 835 | retval = bus_add_attrs(bus); |
766 | if (retval) | 836 | if (retval) |
767 | goto bus_attrs_fail; | 837 | goto bus_attrs_fail; |
@@ -770,6 +840,8 @@ int bus_register(struct bus_type * bus) | |||
770 | return 0; | 840 | return 0; |
771 | 841 | ||
772 | bus_attrs_fail: | 842 | bus_attrs_fail: |
843 | remove_probe_files(bus); | ||
844 | bus_probe_files_fail: | ||
773 | kset_unregister(&bus->drivers); | 845 | kset_unregister(&bus->drivers); |
774 | bus_drivers_fail: | 846 | bus_drivers_fail: |
775 | kset_unregister(&bus->devices); | 847 | kset_unregister(&bus->devices); |
@@ -779,7 +851,6 @@ out: | |||
779 | return retval; | 851 | return retval; |
780 | } | 852 | } |
781 | 853 | ||
782 | |||
783 | /** | 854 | /** |
784 | * bus_unregister - remove a bus from the system | 855 | * bus_unregister - remove a bus from the system |
785 | * @bus: bus. | 856 | * @bus: bus. |
@@ -791,6 +862,7 @@ void bus_unregister(struct bus_type * bus) | |||
791 | { | 862 | { |
792 | pr_debug("bus %s: unregistering\n", bus->name); | 863 | pr_debug("bus %s: unregistering\n", bus->name); |
793 | bus_remove_attrs(bus); | 864 | bus_remove_attrs(bus); |
865 | remove_probe_files(bus); | ||
794 | kset_unregister(&bus->drivers); | 866 | kset_unregister(&bus->drivers); |
795 | kset_unregister(&bus->devices); | 867 | kset_unregister(&bus->devices); |
796 | subsystem_unregister(&bus->subsys); | 868 | subsystem_unregister(&bus->subsys); |