diff options
author | Jiri Slaby <jirislaby@gmail.com> | 2008-11-24 10:20:09 -0500 |
---|---|---|
committer | Jiri Kosina <jkosina@suse.cz> | 2009-01-03 19:00:52 -0500 |
commit | 3a6f82f7a22cf19687f556997c6978b31c109360 (patch) | |
tree | 0b3a34f0c2035beb21c6d15af33207d3057bcdaf | |
parent | 898089d08f983ef0fdb176267620543a7929826a (diff) |
HID: add dynids facility
Allow adding new devices to the hid drivers on the fly without
a need of kernel recompilation.
Now, one can test a driver e.g. by:
echo 0003:045E:00F0.0003 > ../generic-usb/unbind
echo 0003 045E 00F0 > new_id
from some driver subdir.
Signed-off-by: Jiri Slaby <jirislaby@gmail.com>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
-rw-r--r-- | drivers/hid/hid-core.c | 101 | ||||
-rw-r--r-- | include/linux/hid.h | 5 |
2 files changed, 103 insertions, 3 deletions
diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 344f8fdb2824..34cc3b0d01f6 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c | |||
@@ -1304,12 +1304,92 @@ static const struct hid_device_id hid_blacklist[] = { | |||
1304 | { } | 1304 | { } |
1305 | }; | 1305 | }; |
1306 | 1306 | ||
1307 | struct hid_dynid { | ||
1308 | struct list_head list; | ||
1309 | struct hid_device_id id; | ||
1310 | }; | ||
1311 | |||
1312 | /** | ||
1313 | * store_new_id - add a new HID device ID to this driver and re-probe devices | ||
1314 | * @driver: target device driver | ||
1315 | * @buf: buffer for scanning device ID data | ||
1316 | * @count: input size | ||
1317 | * | ||
1318 | * Adds a new dynamic hid device ID to this driver, | ||
1319 | * and causes the driver to probe for all devices again. | ||
1320 | */ | ||
1321 | static ssize_t store_new_id(struct device_driver *drv, const char *buf, | ||
1322 | size_t count) | ||
1323 | { | ||
1324 | struct hid_driver *hdrv = container_of(drv, struct hid_driver, driver); | ||
1325 | struct hid_dynid *dynid; | ||
1326 | __u32 bus, vendor, product; | ||
1327 | unsigned long driver_data = 0; | ||
1328 | int ret; | ||
1329 | |||
1330 | ret = sscanf(buf, "%x %x %x %lx", | ||
1331 | &bus, &vendor, &product, &driver_data); | ||
1332 | if (ret < 3) | ||
1333 | return -EINVAL; | ||
1334 | |||
1335 | dynid = kzalloc(sizeof(*dynid), GFP_KERNEL); | ||
1336 | if (!dynid) | ||
1337 | return -ENOMEM; | ||
1338 | |||
1339 | dynid->id.bus = bus; | ||
1340 | dynid->id.vendor = vendor; | ||
1341 | dynid->id.product = product; | ||
1342 | dynid->id.driver_data = driver_data; | ||
1343 | |||
1344 | spin_lock(&hdrv->dyn_lock); | ||
1345 | list_add_tail(&dynid->list, &hdrv->dyn_list); | ||
1346 | spin_unlock(&hdrv->dyn_lock); | ||
1347 | |||
1348 | ret = 0; | ||
1349 | if (get_driver(&hdrv->driver)) { | ||
1350 | ret = driver_attach(&hdrv->driver); | ||
1351 | put_driver(&hdrv->driver); | ||
1352 | } | ||
1353 | |||
1354 | return ret ? : count; | ||
1355 | } | ||
1356 | static DRIVER_ATTR(new_id, S_IWUSR, NULL, store_new_id); | ||
1357 | |||
1358 | static void hid_free_dynids(struct hid_driver *hdrv) | ||
1359 | { | ||
1360 | struct hid_dynid *dynid, *n; | ||
1361 | |||
1362 | spin_lock(&hdrv->dyn_lock); | ||
1363 | list_for_each_entry_safe(dynid, n, &hdrv->dyn_list, list) { | ||
1364 | list_del(&dynid->list); | ||
1365 | kfree(dynid); | ||
1366 | } | ||
1367 | spin_unlock(&hdrv->dyn_lock); | ||
1368 | } | ||
1369 | |||
1370 | static const struct hid_device_id *hid_match_device(struct hid_device *hdev, | ||
1371 | struct hid_driver *hdrv) | ||
1372 | { | ||
1373 | struct hid_dynid *dynid; | ||
1374 | |||
1375 | spin_lock(&hdrv->dyn_lock); | ||
1376 | list_for_each_entry(dynid, &hdrv->dyn_list, list) { | ||
1377 | if (hid_match_one_id(hdev, &dynid->id)) { | ||
1378 | spin_unlock(&hdrv->dyn_lock); | ||
1379 | return &dynid->id; | ||
1380 | } | ||
1381 | } | ||
1382 | spin_unlock(&hdrv->dyn_lock); | ||
1383 | |||
1384 | return hid_match_id(hdev, hdrv->id_table); | ||
1385 | } | ||
1386 | |||
1307 | static int hid_bus_match(struct device *dev, struct device_driver *drv) | 1387 | static int hid_bus_match(struct device *dev, struct device_driver *drv) |
1308 | { | 1388 | { |
1309 | struct hid_driver *hdrv = container_of(drv, struct hid_driver, driver); | 1389 | struct hid_driver *hdrv = container_of(drv, struct hid_driver, driver); |
1310 | struct hid_device *hdev = container_of(dev, struct hid_device, dev); | 1390 | struct hid_device *hdev = container_of(dev, struct hid_device, dev); |
1311 | 1391 | ||
1312 | if (!hid_match_id(hdev, hdrv->id_table)) | 1392 | if (!hid_match_device(hdev, hdrv)) |
1313 | return 0; | 1393 | return 0; |
1314 | 1394 | ||
1315 | /* generic wants all non-blacklisted */ | 1395 | /* generic wants all non-blacklisted */ |
@@ -1328,7 +1408,7 @@ static int hid_device_probe(struct device *dev) | |||
1328 | int ret = 0; | 1408 | int ret = 0; |
1329 | 1409 | ||
1330 | if (!hdev->driver) { | 1410 | if (!hdev->driver) { |
1331 | id = hid_match_id(hdev, hdrv->id_table); | 1411 | id = hid_match_device(hdev, hdrv); |
1332 | if (id == NULL) | 1412 | if (id == NULL) |
1333 | return -ENODEV; | 1413 | return -ENODEV; |
1334 | 1414 | ||
@@ -1695,18 +1775,33 @@ EXPORT_SYMBOL_GPL(hid_destroy_device); | |||
1695 | int __hid_register_driver(struct hid_driver *hdrv, struct module *owner, | 1775 | int __hid_register_driver(struct hid_driver *hdrv, struct module *owner, |
1696 | const char *mod_name) | 1776 | const char *mod_name) |
1697 | { | 1777 | { |
1778 | int ret; | ||
1779 | |||
1698 | hdrv->driver.name = hdrv->name; | 1780 | hdrv->driver.name = hdrv->name; |
1699 | hdrv->driver.bus = &hid_bus_type; | 1781 | hdrv->driver.bus = &hid_bus_type; |
1700 | hdrv->driver.owner = owner; | 1782 | hdrv->driver.owner = owner; |
1701 | hdrv->driver.mod_name = mod_name; | 1783 | hdrv->driver.mod_name = mod_name; |
1702 | 1784 | ||
1703 | return driver_register(&hdrv->driver); | 1785 | INIT_LIST_HEAD(&hdrv->dyn_list); |
1786 | spin_lock_init(&hdrv->dyn_lock); | ||
1787 | |||
1788 | ret = driver_register(&hdrv->driver); | ||
1789 | if (ret) | ||
1790 | return ret; | ||
1791 | |||
1792 | ret = driver_create_file(&hdrv->driver, &driver_attr_new_id); | ||
1793 | if (ret) | ||
1794 | driver_unregister(&hdrv->driver); | ||
1795 | |||
1796 | return ret; | ||
1704 | } | 1797 | } |
1705 | EXPORT_SYMBOL_GPL(__hid_register_driver); | 1798 | EXPORT_SYMBOL_GPL(__hid_register_driver); |
1706 | 1799 | ||
1707 | void hid_unregister_driver(struct hid_driver *hdrv) | 1800 | void hid_unregister_driver(struct hid_driver *hdrv) |
1708 | { | 1801 | { |
1802 | driver_remove_file(&hdrv->driver, &driver_attr_new_id); | ||
1709 | driver_unregister(&hdrv->driver); | 1803 | driver_unregister(&hdrv->driver); |
1804 | hid_free_dynids(hdrv); | ||
1710 | } | 1805 | } |
1711 | EXPORT_SYMBOL_GPL(hid_unregister_driver); | 1806 | EXPORT_SYMBOL_GPL(hid_unregister_driver); |
1712 | 1807 | ||
diff --git a/include/linux/hid.h b/include/linux/hid.h index 2c20f20283b2..215035bbb288 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h | |||
@@ -531,6 +531,8 @@ struct hid_usage_id { | |||
531 | * @name: driver name (e.g. "Footech_bar-wheel") | 531 | * @name: driver name (e.g. "Footech_bar-wheel") |
532 | * @id_table: which devices is this driver for (must be non-NULL for probe | 532 | * @id_table: which devices is this driver for (must be non-NULL for probe |
533 | * to be called) | 533 | * to be called) |
534 | * @dyn_list: list of dynamically added device ids | ||
535 | * @dyn_lock: lock protecting @dyn_list | ||
534 | * @probe: new device inserted | 536 | * @probe: new device inserted |
535 | * @remove: device removed (NULL if not a hot-plug capable driver) | 537 | * @remove: device removed (NULL if not a hot-plug capable driver) |
536 | * @report_table: on which reports to call raw_event (NULL means all) | 538 | * @report_table: on which reports to call raw_event (NULL means all) |
@@ -558,6 +560,9 @@ struct hid_driver { | |||
558 | char *name; | 560 | char *name; |
559 | const struct hid_device_id *id_table; | 561 | const struct hid_device_id *id_table; |
560 | 562 | ||
563 | struct list_head dyn_list; | ||
564 | spinlock_t dyn_lock; | ||
565 | |||
561 | int (*probe)(struct hid_device *dev, const struct hid_device_id *id); | 566 | int (*probe)(struct hid_device *dev, const struct hid_device_id *id); |
562 | void (*remove)(struct hid_device *dev); | 567 | void (*remove)(struct hid_device *dev); |
563 | 568 | ||