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 /drivers | |
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>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/hid/hid-core.c | 101 |
1 files changed, 98 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 | ||