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 /drivers/hid | |
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>
Diffstat (limited to 'drivers/hid')
-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 | { |