diff options
| author | Benjamin Tissoires <benjamin.tissoires@redhat.com> | 2014-09-30 13:18:29 -0400 |
|---|---|---|
| committer | Jiri Kosina <jkosina@suse.cz> | 2014-10-29 05:51:40 -0400 |
| commit | 925f0f3ed24f98b40c28627e74ff3e7f9d1e28bc (patch) | |
| tree | ed39744f58c2f57393194db8dcd29dff1dbbe78d | |
| parent | ab94e562ed45c99914fe874b7feaf75b80ceea84 (diff) | |
HID: logitech-dj: allow transfer of HID++ reports from/to the correct dj device
HID++ is a Logitech-specific protocol for communicating with HID
devices. DJ devices implement HID++, and so we can add the HID++
collection in the report descriptor and forward the incoming
reports from the receiver to the appropriate DJ device.
The same can be done in the other way, if someone calls a
.raw_request(), we can forward it to the correct dj device
by overriding the device_index in the HID++ report.
Signed-off-by: Benjamin Tisssoires <benjamin.tissoires@redhat.com>
Tested-by: Andrew de los Reyes <adlr@chromium.org>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
| -rw-r--r-- | drivers/hid/hid-logitech-dj.c | 188 |
1 files changed, 160 insertions, 28 deletions
diff --git a/drivers/hid/hid-logitech-dj.c b/drivers/hid/hid-logitech-dj.c index 45a7eacdfe98..feddacd87b8b 100644 --- a/drivers/hid/hid-logitech-dj.c +++ b/drivers/hid/hid-logitech-dj.c | |||
| @@ -42,6 +42,15 @@ | |||
| 42 | #define REPORT_ID_DJ_SHORT 0x20 | 42 | #define REPORT_ID_DJ_SHORT 0x20 |
| 43 | #define REPORT_ID_DJ_LONG 0x21 | 43 | #define REPORT_ID_DJ_LONG 0x21 |
| 44 | 44 | ||
| 45 | #define REPORT_ID_HIDPP_SHORT 0x10 | ||
| 46 | #define REPORT_ID_HIDPP_LONG 0x11 | ||
| 47 | |||
| 48 | #define HIDPP_REPORT_SHORT_LENGTH 7 | ||
| 49 | #define HIDPP_REPORT_LONG_LENGTH 20 | ||
| 50 | |||
| 51 | #define HIDPP_RECEIVER_INDEX 0xff | ||
| 52 | |||
| 53 | #define REPORT_TYPE_RFREPORT_FIRST 0x01 | ||
| 45 | #define REPORT_TYPE_RFREPORT_LAST 0x1F | 54 | #define REPORT_TYPE_RFREPORT_LAST 0x1F |
| 46 | 55 | ||
| 47 | /* Command Switch to DJ mode */ | 56 | /* Command Switch to DJ mode */ |
| @@ -242,6 +251,57 @@ static const char media_descriptor[] = { | |||
| 242 | 0xc0, /* EndCollection */ | 251 | 0xc0, /* EndCollection */ |
| 243 | }; /* */ | 252 | }; /* */ |
| 244 | 253 | ||
| 254 | /* HIDPP descriptor */ | ||
| 255 | static const char hidpp_descriptor[] = { | ||
| 256 | 0x06, 0x00, 0xff, /* Usage Page (Vendor Defined Page 1) */ | ||
| 257 | 0x09, 0x01, /* Usage (Vendor Usage 1) */ | ||
| 258 | 0xa1, 0x01, /* Collection (Application) */ | ||
| 259 | 0x85, 0x10, /* Report ID (16) */ | ||
| 260 | 0x75, 0x08, /* Report Size (8) */ | ||
| 261 | 0x95, 0x06, /* Report Count (6) */ | ||
| 262 | 0x15, 0x00, /* Logical Minimum (0) */ | ||
| 263 | 0x26, 0xff, 0x00, /* Logical Maximum (255) */ | ||
| 264 | 0x09, 0x01, /* Usage (Vendor Usage 1) */ | ||
| 265 | 0x81, 0x00, /* Input (Data,Arr,Abs) */ | ||
| 266 | 0x09, 0x01, /* Usage (Vendor Usage 1) */ | ||
| 267 | 0x91, 0x00, /* Output (Data,Arr,Abs) */ | ||
| 268 | 0xc0, /* End Collection */ | ||
| 269 | 0x06, 0x00, 0xff, /* Usage Page (Vendor Defined Page 1) */ | ||
| 270 | 0x09, 0x02, /* Usage (Vendor Usage 2) */ | ||
| 271 | 0xa1, 0x01, /* Collection (Application) */ | ||
| 272 | 0x85, 0x11, /* Report ID (17) */ | ||
| 273 | 0x75, 0x08, /* Report Size (8) */ | ||
| 274 | 0x95, 0x13, /* Report Count (19) */ | ||
| 275 | 0x15, 0x00, /* Logical Minimum (0) */ | ||
| 276 | 0x26, 0xff, 0x00, /* Logical Maximum (255) */ | ||
| 277 | 0x09, 0x02, /* Usage (Vendor Usage 2) */ | ||
| 278 | 0x81, 0x00, /* Input (Data,Arr,Abs) */ | ||
| 279 | 0x09, 0x02, /* Usage (Vendor Usage 2) */ | ||
| 280 | 0x91, 0x00, /* Output (Data,Arr,Abs) */ | ||
| 281 | 0xc0, /* End Collection */ | ||
| 282 | 0x06, 0x00, 0xff, /* Usage Page (Vendor Defined Page 1) */ | ||
| 283 | 0x09, 0x04, /* Usage (Vendor Usage 0x04) */ | ||
| 284 | 0xa1, 0x01, /* Collection (Application) */ | ||
| 285 | 0x85, 0x20, /* Report ID (32) */ | ||
| 286 | 0x75, 0x08, /* Report Size (8) */ | ||
| 287 | 0x95, 0x0e, /* Report Count (14) */ | ||
| 288 | 0x15, 0x00, /* Logical Minimum (0) */ | ||
| 289 | 0x26, 0xff, 0x00, /* Logical Maximum (255) */ | ||
| 290 | 0x09, 0x41, /* Usage (Vendor Usage 0x41) */ | ||
| 291 | 0x81, 0x00, /* Input (Data,Arr,Abs) */ | ||
| 292 | 0x09, 0x41, /* Usage (Vendor Usage 0x41) */ | ||
| 293 | 0x91, 0x00, /* Output (Data,Arr,Abs) */ | ||
| 294 | 0x85, 0x21, /* Report ID (33) */ | ||
| 295 | 0x95, 0x1f, /* Report Count (31) */ | ||
| 296 | 0x15, 0x00, /* Logical Minimum (0) */ | ||
| 297 | 0x26, 0xff, 0x00, /* Logical Maximum (255) */ | ||
| 298 | 0x09, 0x42, /* Usage (Vendor Usage 0x42) */ | ||
| 299 | 0x81, 0x00, /* Input (Data,Arr,Abs) */ | ||
| 300 | 0x09, 0x42, /* Usage (Vendor Usage 0x42) */ | ||
| 301 | 0x91, 0x00, /* Output (Data,Arr,Abs) */ | ||
| 302 | 0xc0, /* End Collection */ | ||
| 303 | }; | ||
| 304 | |||
| 245 | /* Maximum size of all defined hid reports in bytes (including report id) */ | 305 | /* Maximum size of all defined hid reports in bytes (including report id) */ |
| 246 | #define MAX_REPORT_SIZE 8 | 306 | #define MAX_REPORT_SIZE 8 |
| 247 | 307 | ||
| @@ -251,7 +311,8 @@ static const char media_descriptor[] = { | |||
| 251 | sizeof(mse_descriptor) + \ | 311 | sizeof(mse_descriptor) + \ |
| 252 | sizeof(consumer_descriptor) + \ | 312 | sizeof(consumer_descriptor) + \ |
| 253 | sizeof(syscontrol_descriptor) + \ | 313 | sizeof(syscontrol_descriptor) + \ |
| 254 | sizeof(media_descriptor)) | 314 | sizeof(media_descriptor) + \ |
| 315 | sizeof(hidpp_descriptor)) | ||
| 255 | 316 | ||
| 256 | /* Number of possible hid report types that can be created by this driver. | 317 | /* Number of possible hid report types that can be created by this driver. |
| 257 | * | 318 | * |
| @@ -512,6 +573,13 @@ static void logi_dj_recv_forward_report(struct dj_receiver_dev *djrcv_dev, | |||
| 512 | } | 573 | } |
| 513 | } | 574 | } |
| 514 | 575 | ||
| 576 | static void logi_dj_recv_forward_hidpp(struct dj_device *dj_dev, u8 *data, | ||
| 577 | int size) | ||
| 578 | { | ||
| 579 | /* We are called from atomic context (tasklet && djrcv->lock held) */ | ||
| 580 | if (hid_input_report(dj_dev->hdev, HID_INPUT_REPORT, data, size, 1)) | ||
| 581 | dbg_hid("hid_input_report error\n"); | ||
| 582 | } | ||
| 515 | 583 | ||
| 516 | static int logi_dj_recv_send_report(struct dj_receiver_dev *djrcv_dev, | 584 | static int logi_dj_recv_send_report(struct dj_receiver_dev *djrcv_dev, |
| 517 | struct dj_report *dj_report) | 585 | struct dj_report *dj_report) |
| @@ -609,6 +677,16 @@ static int logi_dj_ll_raw_request(struct hid_device *hid, | |||
| 609 | u8 *out_buf; | 677 | u8 *out_buf; |
| 610 | int ret; | 678 | int ret; |
| 611 | 679 | ||
| 680 | if ((buf[0] == REPORT_ID_HIDPP_SHORT) || | ||
| 681 | (buf[0] == REPORT_ID_HIDPP_LONG)) { | ||
| 682 | if (count < 2) | ||
| 683 | return -EINVAL; | ||
| 684 | |||
| 685 | buf[1] = djdev->device_index; | ||
| 686 | return hid_hw_raw_request(djrcv_dev->hdev, reportnum, buf, | ||
| 687 | count, report_type, reqtype); | ||
| 688 | } | ||
| 689 | |||
| 612 | if (buf[0] != REPORT_TYPE_LEDS) | 690 | if (buf[0] != REPORT_TYPE_LEDS) |
| 613 | return -EINVAL; | 691 | return -EINVAL; |
| 614 | 692 | ||
| @@ -687,6 +765,8 @@ static int logi_dj_ll_parse(struct hid_device *hid) | |||
| 687 | __func__, djdev->reports_supported); | 765 | __func__, djdev->reports_supported); |
| 688 | } | 766 | } |
| 689 | 767 | ||
| 768 | rdcat(rdesc, &rsize, hidpp_descriptor, sizeof(hidpp_descriptor)); | ||
| 769 | |||
| 690 | retval = hid_parse_report(hid, rdesc, rsize); | 770 | retval = hid_parse_report(hid, rdesc, rsize); |
| 691 | kfree(rdesc); | 771 | kfree(rdesc); |
| 692 | 772 | ||
| @@ -714,8 +794,7 @@ static struct hid_ll_driver logi_dj_ll_driver = { | |||
| 714 | .raw_request = logi_dj_ll_raw_request, | 794 | .raw_request = logi_dj_ll_raw_request, |
| 715 | }; | 795 | }; |
| 716 | 796 | ||
| 717 | 797 | static int logi_dj_dj_event(struct hid_device *hdev, | |
| 718 | static int logi_dj_raw_event(struct hid_device *hdev, | ||
| 719 | struct hid_report *report, u8 *data, | 798 | struct hid_report *report, u8 *data, |
| 720 | int size) | 799 | int size) |
| 721 | { | 800 | { |
| @@ -723,36 +802,24 @@ static int logi_dj_raw_event(struct hid_device *hdev, | |||
| 723 | struct dj_report *dj_report = (struct dj_report *) data; | 802 | struct dj_report *dj_report = (struct dj_report *) data; |
| 724 | unsigned long flags; | 803 | unsigned long flags; |
| 725 | 804 | ||
| 726 | dbg_hid("%s, size:%d\n", __func__, size); | 805 | /* |
| 727 | 806 | * Here we receive all data coming from iface 2, there are 3 cases: | |
| 728 | /* Here we receive all data coming from iface 2, there are 4 cases: | ||
| 729 | * | ||
| 730 | * 1) Data should continue its normal processing i.e. data does not | ||
| 731 | * come from the DJ collection, in which case we do nothing and | ||
| 732 | * return 0, so hid-core can continue normal processing (will forward | ||
| 733 | * to associated hidraw device) | ||
| 734 | * | 807 | * |
| 735 | * 2) Data is from DJ collection, and is intended for this driver i. e. | 808 | * 1) Data is intended for this driver i. e. data contains arrival, |
| 736 | * data contains arrival, departure, etc notifications, in which case | 809 | * departure, etc notifications, in which case we queue them for delayed |
| 737 | * we queue them for delayed processing by the work queue. We return 1 | 810 | * processing by the work queue. We return 1 to hid-core as no further |
| 738 | * to hid-core as no further processing is required from it. | 811 | * processing is required from it. |
| 739 | * | 812 | * |
| 740 | * 3) Data is from DJ collection, and informs a connection change, | 813 | * 2) Data informs a connection change, if the change means rf link |
| 741 | * if the change means rf link loss, then we must send a null report | 814 | * loss, then we must send a null report to the upper layer to discard |
| 742 | * to the upper layer to discard potentially pressed keys that may be | 815 | * potentially pressed keys that may be repeated forever by the input |
| 743 | * repeated forever by the input layer. Return 1 to hid-core as no | 816 | * layer. Return 1 to hid-core as no further processing is required. |
| 744 | * further processing is required. | ||
| 745 | * | 817 | * |
| 746 | * 4) Data is from DJ collection and is an actual input event from | 818 | * 3) Data is an actual input event from a paired DJ device in which |
| 747 | * a paired DJ device in which case we forward it to the correct hid | 819 | * case we forward it to the correct hid device (via hid_input_report() |
| 748 | * device (via hid_input_report() ) and return 1 so hid-core does not do | 820 | * ) and return 1 so hid-core does not anything else with it. |
| 749 | * anything else with it. | ||
| 750 | */ | 821 | */ |
| 751 | 822 | ||
| 752 | /* case 1) */ | ||
| 753 | if (data[0] != REPORT_ID_DJ_SHORT) | ||
| 754 | return false; | ||
| 755 | |||
| 756 | if ((dj_report->device_index < DJ_DEVICE_INDEX_MIN) || | 823 | if ((dj_report->device_index < DJ_DEVICE_INDEX_MIN) || |
| 757 | (dj_report->device_index > DJ_DEVICE_INDEX_MAX)) { | 824 | (dj_report->device_index > DJ_DEVICE_INDEX_MAX)) { |
| 758 | /* | 825 | /* |
| @@ -797,6 +864,71 @@ out: | |||
| 797 | return true; | 864 | return true; |
| 798 | } | 865 | } |
| 799 | 866 | ||
| 867 | static int logi_dj_hidpp_event(struct hid_device *hdev, | ||
| 868 | struct hid_report *report, u8 *data, | ||
| 869 | int size) | ||
| 870 | { | ||
| 871 | struct dj_receiver_dev *djrcv_dev = hid_get_drvdata(hdev); | ||
| 872 | struct dj_report *dj_report = (struct dj_report *) data; | ||
| 873 | unsigned long flags; | ||
| 874 | u8 device_index = dj_report->device_index; | ||
| 875 | |||
| 876 | if (device_index == HIDPP_RECEIVER_INDEX) | ||
| 877 | return false; | ||
| 878 | |||
| 879 | /* | ||
| 880 | * Data is from the HID++ collection, in this case, we forward the | ||
| 881 | * data to the corresponding child dj device and return 0 to hid-core | ||
| 882 | * so he data also goes to the hidraw device of the receiver. This | ||
| 883 | * allows a user space application to implement the full HID++ routing | ||
| 884 | * via the receiver. | ||
| 885 | */ | ||
| 886 | |||
| 887 | if ((device_index < DJ_DEVICE_INDEX_MIN) || | ||
| 888 | (device_index > DJ_DEVICE_INDEX_MAX)) { | ||
| 889 | /* | ||
| 890 | * Device index is wrong, bail out. | ||
| 891 | * This driver can ignore safely the receiver notifications, | ||
| 892 | * so ignore those reports too. | ||
| 893 | */ | ||
| 894 | dev_err(&hdev->dev, "%s: invalid device index:%d\n", | ||
| 895 | __func__, dj_report->device_index); | ||
| 896 | return false; | ||
| 897 | } | ||
| 898 | |||
| 899 | spin_lock_irqsave(&djrcv_dev->lock, flags); | ||
| 900 | |||
| 901 | if (!djrcv_dev->paired_dj_devices[device_index]) | ||
| 902 | /* received an event for an unknown device, bail out */ | ||
| 903 | goto out; | ||
| 904 | |||
| 905 | logi_dj_recv_forward_hidpp(djrcv_dev->paired_dj_devices[device_index], | ||
| 906 | data, size); | ||
| 907 | |||
| 908 | out: | ||
| 909 | spin_unlock_irqrestore(&djrcv_dev->lock, flags); | ||
| 910 | |||
| 911 | return false; | ||
| 912 | } | ||
| 913 | |||
| 914 | static int logi_dj_raw_event(struct hid_device *hdev, | ||
| 915 | struct hid_report *report, u8 *data, | ||
| 916 | int size) | ||
| 917 | { | ||
| 918 | dbg_hid("%s, size:%d\n", __func__, size); | ||
| 919 | |||
| 920 | switch (data[0]) { | ||
| 921 | case REPORT_ID_DJ_SHORT: | ||
| 922 | return logi_dj_dj_event(hdev, report, data, size); | ||
| 923 | case REPORT_ID_HIDPP_SHORT: | ||
| 924 | /* intentional fallthrough */ | ||
| 925 | case REPORT_ID_HIDPP_LONG: | ||
| 926 | return logi_dj_hidpp_event(hdev, report, data, size); | ||
| 927 | } | ||
| 928 | |||
| 929 | return false; | ||
| 930 | } | ||
| 931 | |||
| 800 | static int logi_dj_probe(struct hid_device *hdev, | 932 | static int logi_dj_probe(struct hid_device *hdev, |
| 801 | const struct hid_device_id *id) | 933 | const struct hid_device_id *id) |
| 802 | { | 934 | { |
