aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/hid/usbhid/hid-quirks.c163
-rw-r--r--include/linux/hid.h1
2 files changed, 150 insertions, 14 deletions
diff --git a/drivers/hid/usbhid/hid-quirks.c b/drivers/hid/usbhid/hid-quirks.c
index c4fdccdda858..a5fc8b5144ef 100644
--- a/drivers/hid/usbhid/hid-quirks.c
+++ b/drivers/hid/usbhid/hid-quirks.c
@@ -447,22 +447,152 @@ static const struct hid_blacklist {
447 447
448 { 0, 0 } 448 { 0, 0 }
449}; 449};
450
451/* Dynamic HID quirks list - specified at runtime */
452struct quirks_list_struct {
453 struct hid_blacklist hid_bl_item;
454 struct list_head node;
455};
456
457static LIST_HEAD(dquirks_list);
458static DECLARE_RWSEM(dquirks_rwsem);
459
460/* Runtime ("dynamic") quirks manipulation functions */
461
450/** 462/**
451* usbhid_exists_squirk: return any static quirks for a USB HID device 463 * usbhid_exists_dquirk: find any dynamic quirks for a USB HID device
452* @idVendor: the 16-bit USB vendor ID, in native byteorder 464 * @idVendor: the 16-bit USB vendor ID, in native byteorder
453* @idProduct: the 16-bit USB product ID, in native byteorder 465 * @idProduct: the 16-bit USB product ID, in native byteorder
454* 466 *
455* Description: 467 * Description:
456* Given a USB vendor ID and product ID, return a pointer to 468 * Scans dquirks_list for a matching dynamic quirk and returns
457* the hid_blacklist entry associated with that device. 469 * the pointer to the relevant struct hid_blacklist if found.
458* 470 * Must be called with a read lock held on dquirks_rwsem.
459* Returns: pointer if quirk found, or NULL if no quirks found. 471 *
460*/ 472 * Returns: NULL if no quirk found, struct hid_blacklist * if found.
473 */
474static struct hid_blacklist *usbhid_exists_dquirk(const u16 idVendor,
475 const u16 idProduct)
476{
477 struct quirks_list_struct *q;
478 struct hid_blacklist *bl_entry = NULL;
479
480 WARN_ON(idVendor == 0);
481
482 list_for_each_entry(q, &dquirks_list, node) {
483 if (q->hid_bl_item.idVendor == idVendor &&
484 q->hid_bl_item.idProduct == idProduct) {
485 bl_entry = &q->hid_bl_item;
486 break;
487 }
488 }
489
490 if (bl_entry != NULL)
491 dbg("Found dynamic quirk 0x%x for USB HID vendor 0x%hx prod 0x%hx\n",
492 bl_entry->quirks, bl_entry->idVendor,
493 bl_entry->idProduct);
494
495 return bl_entry;
496}
497
498
499/**
500 * usbhid_modify_dquirk: add/replace a HID quirk
501 * @idVendor: the 16-bit USB vendor ID, in native byteorder
502 * @idProduct: the 16-bit USB product ID, in native byteorder
503 * @quirks: the u32 quirks value to add/replace
504 *
505 * Description:
506 * If an dynamic quirk exists in memory for this (idVendor,
507 * idProduct) pair, replace its quirks value with what was
508 * provided. Otherwise, add the quirk to the dynamic quirks list.
509 *
510 * Returns: 0 OK, -error on failure.
511 */
512int usbhid_modify_dquirk(const u16 idVendor, const u16 idProduct,
513 const u32 quirks)
514{
515 struct quirks_list_struct *q_new, *q;
516 int list_edited = 0;
517
518 if (!idVendor) {
519 dbg("Cannot add a quirk with idVendor = 0");
520 return -EINVAL;
521 }
522
523 q_new = kmalloc(sizeof(struct quirks_list_struct), GFP_KERNEL);
524 if (!q_new) {
525 dbg("Could not allocate quirks_list_struct");
526 return -ENOMEM;
527 }
528
529 q_new->hid_bl_item.idVendor = idVendor;
530 q_new->hid_bl_item.idProduct = idProduct;
531 q_new->hid_bl_item.quirks = quirks;
532
533 down_write(&dquirks_rwsem);
534
535 list_for_each_entry(q, &dquirks_list, node) {
536
537 if (q->hid_bl_item.idVendor == idVendor &&
538 q->hid_bl_item.idProduct == idProduct) {
539
540 list_replace(&q->node, &q_new->node);
541 kfree(q);
542 list_edited = 1;
543 break;
544
545 }
546
547 }
548
549 if (!list_edited)
550 list_add_tail(&q_new->node, &dquirks_list);
551
552 up_write(&dquirks_rwsem);
553
554 return 0;
555}
556
557
558/**
559 * usbhid_remove_all_dquirks: remove all runtime HID quirks from memory
560 *
561 * Description:
562 * Free all memory associated with dynamic quirks - called before
563 * module unload.
564 *
565 */
566static void usbhid_remove_all_dquirks(void)
567{
568 struct quirks_list_struct *q, *temp;
569
570 down_write(&dquirks_rwsem);
571 list_for_each_entry_safe(q, temp, &dquirks_list, node) {
572 list_del(&q->node);
573 kfree(q);
574 }
575 up_write(&dquirks_rwsem);
576
577}
578
579
580/**
581 * usbhid_exists_squirk: return any static quirks for a USB HID device
582 * @idVendor: the 16-bit USB vendor ID, in native byteorder
583 * @idProduct: the 16-bit USB product ID, in native byteorder
584 *
585 * Description:
586 * Given a USB vendor ID and product ID, return a pointer to
587 * the hid_blacklist entry associated with that device.
588 *
589 * Returns: pointer if quirk found, or NULL if no quirks found.
590 */
461static const struct hid_blacklist *usbhid_exists_squirk(const u16 idVendor, 591static const struct hid_blacklist *usbhid_exists_squirk(const u16 idVendor,
462 const u16 idProduct) 592 const u16 idProduct)
463{ 593{
464 const struct hid_blacklist *bl_entry = NULL; 594 const struct hid_blacklist *bl_entry = NULL;
465 int n = 0; 595 int n = 0;
466 596
467 for (; hid_blacklist[n].idVendor; n++) 597 for (; hid_blacklist[n].idVendor; n++)
468 if (hid_blacklist[n].idVendor == idVendor && 598 if (hid_blacklist[n].idVendor == idVendor &&
@@ -502,9 +632,14 @@ u32 usbhid_lookup_quirk(const u16 idVendor, const u16 idProduct)
502 idProduct <= USB_DEVICE_ID_CODEMERCS_IOW_LAST) 632 idProduct <= USB_DEVICE_ID_CODEMERCS_IOW_LAST)
503 return HID_QUIRK_IGNORE; 633 return HID_QUIRK_IGNORE;
504 634
505 bl_entry = usbhid_exists_squirk(idVendor, idProduct); 635 down_read(&dquirks_rwsem);
636 bl_entry = usbhid_exists_dquirk(idVendor, idProduct);
637 if (!bl_entry)
638 bl_entry = usbhid_exists_squirk(idVendor, idProduct);
506 if (bl_entry) 639 if (bl_entry)
507 quirks = bl_entry->quirks; 640 quirks = bl_entry->quirks;
641 up_read(&dquirks_rwsem);
642
508 return quirks; 643 return quirks;
509} 644}
510 645
diff --git a/include/linux/hid.h b/include/linux/hid.h
index 23e0dcf8ec41..4ba456d71f6b 100644
--- a/include/linux/hid.h
+++ b/include/linux/hid.h
@@ -496,6 +496,7 @@ void hid_free_device(struct hid_device *device);
496struct hid_device *hid_parse_report(__u8 *start, unsigned size); 496struct hid_device *hid_parse_report(__u8 *start, unsigned size);
497 497
498u32 usbhid_lookup_quirk(const u16 idVendor, const u16 idProduct); 498u32 usbhid_lookup_quirk(const u16 idVendor, const u16 idProduct);
499int usbhid_modify_dquirk(const u16 idVendor, const u16 idProduct, const u32 quirks);
499 500
500#ifdef CONFIG_HID_FF 501#ifdef CONFIG_HID_FF
501int hid_ff_init(struct hid_device *hid); 502int hid_ff_init(struct hid_device *hid);