diff options
Diffstat (limited to 'drivers/hid/hid-logitech-dj.c')
-rw-r--r-- | drivers/hid/hid-logitech-dj.c | 397 |
1 files changed, 300 insertions, 97 deletions
diff --git a/drivers/hid/hid-logitech-dj.c b/drivers/hid/hid-logitech-dj.c index 71f569292cab..c917ab61aafa 100644 --- a/drivers/hid/hid-logitech-dj.c +++ b/drivers/hid/hid-logitech-dj.c | |||
@@ -26,9 +26,104 @@ | |||
26 | #include <linux/hid.h> | 26 | #include <linux/hid.h> |
27 | #include <linux/module.h> | 27 | #include <linux/module.h> |
28 | #include <linux/usb.h> | 28 | #include <linux/usb.h> |
29 | #include <linux/kfifo.h> | ||
29 | #include <asm/unaligned.h> | 30 | #include <asm/unaligned.h> |
30 | #include "hid-ids.h" | 31 | #include "hid-ids.h" |
31 | #include "hid-logitech-dj.h" | 32 | |
33 | #define DJ_MAX_PAIRED_DEVICES 6 | ||
34 | #define DJ_MAX_NUMBER_NOTIFICATIONS 8 | ||
35 | #define DJ_RECEIVER_INDEX 0 | ||
36 | #define DJ_DEVICE_INDEX_MIN 1 | ||
37 | #define DJ_DEVICE_INDEX_MAX 6 | ||
38 | |||
39 | #define DJREPORT_SHORT_LENGTH 15 | ||
40 | #define DJREPORT_LONG_LENGTH 32 | ||
41 | |||
42 | #define REPORT_ID_DJ_SHORT 0x20 | ||
43 | #define REPORT_ID_DJ_LONG 0x21 | ||
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 | ||
54 | #define REPORT_TYPE_RFREPORT_LAST 0x1F | ||
55 | |||
56 | /* Command Switch to DJ mode */ | ||
57 | #define REPORT_TYPE_CMD_SWITCH 0x80 | ||
58 | #define CMD_SWITCH_PARAM_DEVBITFIELD 0x00 | ||
59 | #define CMD_SWITCH_PARAM_TIMEOUT_SECONDS 0x01 | ||
60 | #define TIMEOUT_NO_KEEPALIVE 0x00 | ||
61 | |||
62 | /* Command to Get the list of Paired devices */ | ||
63 | #define REPORT_TYPE_CMD_GET_PAIRED_DEVICES 0x81 | ||
64 | |||
65 | /* Device Paired Notification */ | ||
66 | #define REPORT_TYPE_NOTIF_DEVICE_PAIRED 0x41 | ||
67 | #define SPFUNCTION_MORE_NOTIF_EXPECTED 0x01 | ||
68 | #define SPFUNCTION_DEVICE_LIST_EMPTY 0x02 | ||
69 | #define DEVICE_PAIRED_PARAM_SPFUNCTION 0x00 | ||
70 | #define DEVICE_PAIRED_PARAM_EQUAD_ID_LSB 0x01 | ||
71 | #define DEVICE_PAIRED_PARAM_EQUAD_ID_MSB 0x02 | ||
72 | #define DEVICE_PAIRED_RF_REPORT_TYPE 0x03 | ||
73 | |||
74 | /* Device Un-Paired Notification */ | ||
75 | #define REPORT_TYPE_NOTIF_DEVICE_UNPAIRED 0x40 | ||
76 | |||
77 | |||
78 | /* Connection Status Notification */ | ||
79 | #define REPORT_TYPE_NOTIF_CONNECTION_STATUS 0x42 | ||
80 | #define CONNECTION_STATUS_PARAM_STATUS 0x00 | ||
81 | #define STATUS_LINKLOSS 0x01 | ||
82 | |||
83 | /* Error Notification */ | ||
84 | #define REPORT_TYPE_NOTIF_ERROR 0x7F | ||
85 | #define NOTIF_ERROR_PARAM_ETYPE 0x00 | ||
86 | #define ETYPE_KEEPALIVE_TIMEOUT 0x01 | ||
87 | |||
88 | /* supported DJ HID && RF report types */ | ||
89 | #define REPORT_TYPE_KEYBOARD 0x01 | ||
90 | #define REPORT_TYPE_MOUSE 0x02 | ||
91 | #define REPORT_TYPE_CONSUMER_CONTROL 0x03 | ||
92 | #define REPORT_TYPE_SYSTEM_CONTROL 0x04 | ||
93 | #define REPORT_TYPE_MEDIA_CENTER 0x08 | ||
94 | #define REPORT_TYPE_LEDS 0x0E | ||
95 | |||
96 | /* RF Report types bitfield */ | ||
97 | #define STD_KEYBOARD 0x00000002 | ||
98 | #define STD_MOUSE 0x00000004 | ||
99 | #define MULTIMEDIA 0x00000008 | ||
100 | #define POWER_KEYS 0x00000010 | ||
101 | #define MEDIA_CENTER 0x00000100 | ||
102 | #define KBD_LEDS 0x00004000 | ||
103 | |||
104 | struct dj_report { | ||
105 | u8 report_id; | ||
106 | u8 device_index; | ||
107 | u8 report_type; | ||
108 | u8 report_params[DJREPORT_SHORT_LENGTH - 3]; | ||
109 | }; | ||
110 | |||
111 | struct dj_receiver_dev { | ||
112 | struct hid_device *hdev; | ||
113 | struct dj_device *paired_dj_devices[DJ_MAX_PAIRED_DEVICES + | ||
114 | DJ_DEVICE_INDEX_MIN]; | ||
115 | struct work_struct work; | ||
116 | struct kfifo notif_fifo; | ||
117 | spinlock_t lock; | ||
118 | bool querying_devices; | ||
119 | }; | ||
120 | |||
121 | struct dj_device { | ||
122 | struct hid_device *hdev; | ||
123 | struct dj_receiver_dev *dj_receiver_dev; | ||
124 | u32 reports_supported; | ||
125 | u8 device_index; | ||
126 | }; | ||
32 | 127 | ||
33 | /* Keyboard descriptor (1) */ | 128 | /* Keyboard descriptor (1) */ |
34 | static const char kbd_descriptor[] = { | 129 | static const char kbd_descriptor[] = { |
@@ -156,6 +251,57 @@ static const char media_descriptor[] = { | |||
156 | 0xc0, /* EndCollection */ | 251 | 0xc0, /* EndCollection */ |
157 | }; /* */ | 252 | }; /* */ |
158 | 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 | |||
159 | /* 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) */ |
160 | #define MAX_REPORT_SIZE 8 | 306 | #define MAX_REPORT_SIZE 8 |
161 | 307 | ||
@@ -165,7 +311,8 @@ static const char media_descriptor[] = { | |||
165 | sizeof(mse_descriptor) + \ | 311 | sizeof(mse_descriptor) + \ |
166 | sizeof(consumer_descriptor) + \ | 312 | sizeof(consumer_descriptor) + \ |
167 | sizeof(syscontrol_descriptor) + \ | 313 | sizeof(syscontrol_descriptor) + \ |
168 | sizeof(media_descriptor)) | 314 | sizeof(media_descriptor) + \ |
315 | sizeof(hidpp_descriptor)) | ||
169 | 316 | ||
170 | /* 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. |
171 | * | 318 | * |
@@ -256,11 +403,15 @@ static void logi_dj_recv_add_djhid_device(struct dj_receiver_dev *djrcv_dev, | |||
256 | dj_hiddev->dev.parent = &djrcv_hdev->dev; | 403 | dj_hiddev->dev.parent = &djrcv_hdev->dev; |
257 | dj_hiddev->bus = BUS_USB; | 404 | dj_hiddev->bus = BUS_USB; |
258 | dj_hiddev->vendor = le16_to_cpu(usbdev->descriptor.idVendor); | 405 | dj_hiddev->vendor = le16_to_cpu(usbdev->descriptor.idVendor); |
259 | dj_hiddev->product = le16_to_cpu(usbdev->descriptor.idProduct); | 406 | dj_hiddev->product = |
407 | (dj_report->report_params[DEVICE_PAIRED_PARAM_EQUAD_ID_MSB] | ||
408 | << 8) | | ||
409 | dj_report->report_params[DEVICE_PAIRED_PARAM_EQUAD_ID_LSB]; | ||
260 | snprintf(dj_hiddev->name, sizeof(dj_hiddev->name), | 410 | snprintf(dj_hiddev->name, sizeof(dj_hiddev->name), |
261 | "Logitech Unifying Device. Wireless PID:%02x%02x", | 411 | "Logitech Unifying Device. Wireless PID:%04x", |
262 | dj_report->report_params[DEVICE_PAIRED_PARAM_EQUAD_ID_MSB], | 412 | dj_hiddev->product); |
263 | dj_report->report_params[DEVICE_PAIRED_PARAM_EQUAD_ID_LSB]); | 413 | |
414 | dj_hiddev->group = HID_GROUP_LOGITECH_DJ_DEVICE; | ||
264 | 415 | ||
265 | usb_make_path(usbdev, dj_hiddev->phys, sizeof(dj_hiddev->phys)); | 416 | usb_make_path(usbdev, dj_hiddev->phys, sizeof(dj_hiddev->phys)); |
266 | snprintf(tmpstr, sizeof(tmpstr), ":%d", dj_report->device_index); | 417 | snprintf(tmpstr, sizeof(tmpstr), ":%d", dj_report->device_index); |
@@ -422,6 +573,13 @@ static void logi_dj_recv_forward_report(struct dj_receiver_dev *djrcv_dev, | |||
422 | } | 573 | } |
423 | } | 574 | } |
424 | 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 | } | ||
425 | 583 | ||
426 | 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, |
427 | struct dj_report *dj_report) | 585 | struct dj_report *dj_report) |
@@ -472,7 +630,9 @@ static int logi_dj_recv_query_paired_devices(struct dj_receiver_dev *djrcv_dev) | |||
472 | static int logi_dj_recv_switch_to_dj_mode(struct dj_receiver_dev *djrcv_dev, | 630 | static int logi_dj_recv_switch_to_dj_mode(struct dj_receiver_dev *djrcv_dev, |
473 | unsigned timeout) | 631 | unsigned timeout) |
474 | { | 632 | { |
633 | struct hid_device *hdev = djrcv_dev->hdev; | ||
475 | struct dj_report *dj_report; | 634 | struct dj_report *dj_report; |
635 | u8 *buf; | ||
476 | int retval; | 636 | int retval; |
477 | 637 | ||
478 | dj_report = kzalloc(sizeof(struct dj_report), GFP_KERNEL); | 638 | dj_report = kzalloc(sizeof(struct dj_report), GFP_KERNEL); |
@@ -484,7 +644,6 @@ static int logi_dj_recv_switch_to_dj_mode(struct dj_receiver_dev *djrcv_dev, | |||
484 | dj_report->report_params[CMD_SWITCH_PARAM_DEVBITFIELD] = 0x3F; | 644 | dj_report->report_params[CMD_SWITCH_PARAM_DEVBITFIELD] = 0x3F; |
485 | dj_report->report_params[CMD_SWITCH_PARAM_TIMEOUT_SECONDS] = (u8)timeout; | 645 | dj_report->report_params[CMD_SWITCH_PARAM_TIMEOUT_SECONDS] = (u8)timeout; |
486 | retval = logi_dj_recv_send_report(djrcv_dev, dj_report); | 646 | retval = logi_dj_recv_send_report(djrcv_dev, dj_report); |
487 | kfree(dj_report); | ||
488 | 647 | ||
489 | /* | 648 | /* |
490 | * Ugly sleep to work around a USB 3.0 bug when the receiver is still | 649 | * Ugly sleep to work around a USB 3.0 bug when the receiver is still |
@@ -493,6 +652,30 @@ static int logi_dj_recv_switch_to_dj_mode(struct dj_receiver_dev *djrcv_dev, | |||
493 | */ | 652 | */ |
494 | msleep(50); | 653 | msleep(50); |
495 | 654 | ||
655 | /* | ||
656 | * Magical bits to set up hidpp notifications when the dj devices | ||
657 | * are connected/disconnected. | ||
658 | * | ||
659 | * We can reuse dj_report because HIDPP_REPORT_SHORT_LENGTH is smaller | ||
660 | * than DJREPORT_SHORT_LENGTH. | ||
661 | */ | ||
662 | buf = (u8 *)dj_report; | ||
663 | |||
664 | memset(buf, 0, HIDPP_REPORT_SHORT_LENGTH); | ||
665 | |||
666 | buf[0] = REPORT_ID_HIDPP_SHORT; | ||
667 | buf[1] = 0xFF; | ||
668 | buf[2] = 0x80; | ||
669 | buf[3] = 0x00; | ||
670 | buf[4] = 0x00; | ||
671 | buf[5] = 0x09; | ||
672 | buf[6] = 0x00; | ||
673 | |||
674 | hid_hw_raw_request(hdev, REPORT_ID_HIDPP_SHORT, buf, | ||
675 | HIDPP_REPORT_SHORT_LENGTH, HID_OUTPUT_REPORT, | ||
676 | HID_REQ_SET_REPORT); | ||
677 | |||
678 | kfree(dj_report); | ||
496 | return retval; | 679 | return retval; |
497 | } | 680 | } |
498 | 681 | ||
@@ -509,6 +692,9 @@ static void logi_dj_ll_close(struct hid_device *hid) | |||
509 | dbg_hid("%s:%s\n", __func__, hid->phys); | 692 | dbg_hid("%s:%s\n", __func__, hid->phys); |
510 | } | 693 | } |
511 | 694 | ||
695 | static u8 unifying_name_query[] = {0x10, 0xff, 0x83, 0xb5, 0x40, 0x00, 0x00}; | ||
696 | static u8 unifying_name_answer[] = {0x11, 0xff, 0x83, 0xb5}; | ||
697 | |||
512 | static int logi_dj_ll_raw_request(struct hid_device *hid, | 698 | static int logi_dj_ll_raw_request(struct hid_device *hid, |
513 | unsigned char reportnum, __u8 *buf, | 699 | unsigned char reportnum, __u8 *buf, |
514 | size_t count, unsigned char report_type, | 700 | size_t count, unsigned char report_type, |
@@ -519,6 +705,22 @@ static int logi_dj_ll_raw_request(struct hid_device *hid, | |||
519 | u8 *out_buf; | 705 | u8 *out_buf; |
520 | int ret; | 706 | int ret; |
521 | 707 | ||
708 | if ((buf[0] == REPORT_ID_HIDPP_SHORT) || | ||
709 | (buf[0] == REPORT_ID_HIDPP_LONG)) { | ||
710 | if (count < 2) | ||
711 | return -EINVAL; | ||
712 | |||
713 | /* special case where we should not overwrite | ||
714 | * the device_index */ | ||
715 | if (count == 7 && !memcmp(buf, unifying_name_query, | ||
716 | sizeof(unifying_name_query))) | ||
717 | buf[4] |= djdev->device_index - 1; | ||
718 | else | ||
719 | buf[1] = djdev->device_index; | ||
720 | return hid_hw_raw_request(djrcv_dev->hdev, reportnum, buf, | ||
721 | count, report_type, reqtype); | ||
722 | } | ||
723 | |||
522 | if (buf[0] != REPORT_TYPE_LEDS) | 724 | if (buf[0] != REPORT_TYPE_LEDS) |
523 | return -EINVAL; | 725 | return -EINVAL; |
524 | 726 | ||
@@ -597,6 +799,8 @@ static int logi_dj_ll_parse(struct hid_device *hid) | |||
597 | __func__, djdev->reports_supported); | 799 | __func__, djdev->reports_supported); |
598 | } | 800 | } |
599 | 801 | ||
802 | rdcat(rdesc, &rsize, hidpp_descriptor, sizeof(hidpp_descriptor)); | ||
803 | |||
600 | retval = hid_parse_report(hid, rdesc, rsize); | 804 | retval = hid_parse_report(hid, rdesc, rsize); |
601 | kfree(rdesc); | 805 | kfree(rdesc); |
602 | 806 | ||
@@ -624,8 +828,7 @@ static struct hid_ll_driver logi_dj_ll_driver = { | |||
624 | .raw_request = logi_dj_ll_raw_request, | 828 | .raw_request = logi_dj_ll_raw_request, |
625 | }; | 829 | }; |
626 | 830 | ||
627 | 831 | static int logi_dj_dj_event(struct hid_device *hdev, | |
628 | static int logi_dj_raw_event(struct hid_device *hdev, | ||
629 | struct hid_report *report, u8 *data, | 832 | struct hid_report *report, u8 *data, |
630 | int size) | 833 | int size) |
631 | { | 834 | { |
@@ -633,36 +836,24 @@ static int logi_dj_raw_event(struct hid_device *hdev, | |||
633 | struct dj_report *dj_report = (struct dj_report *) data; | 836 | struct dj_report *dj_report = (struct dj_report *) data; |
634 | unsigned long flags; | 837 | unsigned long flags; |
635 | 838 | ||
636 | dbg_hid("%s, size:%d\n", __func__, size); | 839 | /* |
637 | 840 | * Here we receive all data coming from iface 2, there are 3 cases: | |
638 | /* Here we receive all data coming from iface 2, there are 4 cases: | ||
639 | * | ||
640 | * 1) Data should continue its normal processing i.e. data does not | ||
641 | * come from the DJ collection, in which case we do nothing and | ||
642 | * return 0, so hid-core can continue normal processing (will forward | ||
643 | * to associated hidraw device) | ||
644 | * | 841 | * |
645 | * 2) Data is from DJ collection, and is intended for this driver i. e. | 842 | * 1) Data is intended for this driver i. e. data contains arrival, |
646 | * data contains arrival, departure, etc notifications, in which case | 843 | * departure, etc notifications, in which case we queue them for delayed |
647 | * we queue them for delayed processing by the work queue. We return 1 | 844 | * processing by the work queue. We return 1 to hid-core as no further |
648 | * to hid-core as no further processing is required from it. | 845 | * processing is required from it. |
649 | * | 846 | * |
650 | * 3) Data is from DJ collection, and informs a connection change, | 847 | * 2) Data informs a connection change, if the change means rf link |
651 | * if the change means rf link loss, then we must send a null report | 848 | * loss, then we must send a null report to the upper layer to discard |
652 | * to the upper layer to discard potentially pressed keys that may be | 849 | * potentially pressed keys that may be repeated forever by the input |
653 | * repeated forever by the input layer. Return 1 to hid-core as no | 850 | * layer. Return 1 to hid-core as no further processing is required. |
654 | * further processing is required. | ||
655 | * | 851 | * |
656 | * 4) Data is from DJ collection and is an actual input event from | 852 | * 3) Data is an actual input event from a paired DJ device in which |
657 | * a paired DJ device in which case we forward it to the correct hid | 853 | * case we forward it to the correct hid device (via hid_input_report() |
658 | * device (via hid_input_report() ) and return 1 so hid-core does not do | 854 | * ) and return 1 so hid-core does not anything else with it. |
659 | * anything else with it. | ||
660 | */ | 855 | */ |
661 | 856 | ||
662 | /* case 1) */ | ||
663 | if (data[0] != REPORT_ID_DJ_SHORT) | ||
664 | return false; | ||
665 | |||
666 | if ((dj_report->device_index < DJ_DEVICE_INDEX_MIN) || | 857 | if ((dj_report->device_index < DJ_DEVICE_INDEX_MIN) || |
667 | (dj_report->device_index > DJ_DEVICE_INDEX_MAX)) { | 858 | (dj_report->device_index > DJ_DEVICE_INDEX_MAX)) { |
668 | /* | 859 | /* |
@@ -707,6 +898,80 @@ out: | |||
707 | return true; | 898 | return true; |
708 | } | 899 | } |
709 | 900 | ||
901 | static int logi_dj_hidpp_event(struct hid_device *hdev, | ||
902 | struct hid_report *report, u8 *data, | ||
903 | int size) | ||
904 | { | ||
905 | struct dj_receiver_dev *djrcv_dev = hid_get_drvdata(hdev); | ||
906 | struct dj_report *dj_report = (struct dj_report *) data; | ||
907 | unsigned long flags; | ||
908 | u8 device_index = dj_report->device_index; | ||
909 | |||
910 | if (device_index == HIDPP_RECEIVER_INDEX) { | ||
911 | /* special case were the device wants to know its unifying | ||
912 | * name */ | ||
913 | if (size == HIDPP_REPORT_LONG_LENGTH && | ||
914 | !memcmp(data, unifying_name_answer, | ||
915 | sizeof(unifying_name_answer)) && | ||
916 | ((data[4] & 0xF0) == 0x40)) | ||
917 | device_index = (data[4] & 0x0F) + 1; | ||
918 | else | ||
919 | return false; | ||
920 | } | ||
921 | |||
922 | /* | ||
923 | * Data is from the HID++ collection, in this case, we forward the | ||
924 | * data to the corresponding child dj device and return 0 to hid-core | ||
925 | * so he data also goes to the hidraw device of the receiver. This | ||
926 | * allows a user space application to implement the full HID++ routing | ||
927 | * via the receiver. | ||
928 | */ | ||
929 | |||
930 | if ((device_index < DJ_DEVICE_INDEX_MIN) || | ||
931 | (device_index > DJ_DEVICE_INDEX_MAX)) { | ||
932 | /* | ||
933 | * Device index is wrong, bail out. | ||
934 | * This driver can ignore safely the receiver notifications, | ||
935 | * so ignore those reports too. | ||
936 | */ | ||
937 | dev_err(&hdev->dev, "%s: invalid device index:%d\n", | ||
938 | __func__, dj_report->device_index); | ||
939 | return false; | ||
940 | } | ||
941 | |||
942 | spin_lock_irqsave(&djrcv_dev->lock, flags); | ||
943 | |||
944 | if (!djrcv_dev->paired_dj_devices[device_index]) | ||
945 | /* received an event for an unknown device, bail out */ | ||
946 | goto out; | ||
947 | |||
948 | logi_dj_recv_forward_hidpp(djrcv_dev->paired_dj_devices[device_index], | ||
949 | data, size); | ||
950 | |||
951 | out: | ||
952 | spin_unlock_irqrestore(&djrcv_dev->lock, flags); | ||
953 | |||
954 | return false; | ||
955 | } | ||
956 | |||
957 | static int logi_dj_raw_event(struct hid_device *hdev, | ||
958 | struct hid_report *report, u8 *data, | ||
959 | int size) | ||
960 | { | ||
961 | dbg_hid("%s, size:%d\n", __func__, size); | ||
962 | |||
963 | switch (data[0]) { | ||
964 | case REPORT_ID_DJ_SHORT: | ||
965 | return logi_dj_dj_event(hdev, report, data, size); | ||
966 | case REPORT_ID_HIDPP_SHORT: | ||
967 | /* intentional fallthrough */ | ||
968 | case REPORT_ID_HIDPP_LONG: | ||
969 | return logi_dj_hidpp_event(hdev, report, data, size); | ||
970 | } | ||
971 | |||
972 | return false; | ||
973 | } | ||
974 | |||
710 | static int logi_dj_probe(struct hid_device *hdev, | 975 | static int logi_dj_probe(struct hid_device *hdev, |
711 | const struct hid_device_id *id) | 976 | const struct hid_device_id *id) |
712 | { | 977 | { |
@@ -714,9 +979,6 @@ static int logi_dj_probe(struct hid_device *hdev, | |||
714 | struct dj_receiver_dev *djrcv_dev; | 979 | struct dj_receiver_dev *djrcv_dev; |
715 | int retval; | 980 | int retval; |
716 | 981 | ||
717 | if (is_dj_device((struct dj_device *)hdev->driver_data)) | ||
718 | return -ENODEV; | ||
719 | |||
720 | dbg_hid("%s called for ifnum %d\n", __func__, | 982 | dbg_hid("%s called for ifnum %d\n", __func__, |
721 | intf->cur_altsetting->desc.bInterfaceNumber); | 983 | intf->cur_altsetting->desc.bInterfaceNumber); |
722 | 984 | ||
@@ -869,22 +1131,6 @@ static void logi_dj_remove(struct hid_device *hdev) | |||
869 | hid_set_drvdata(hdev, NULL); | 1131 | hid_set_drvdata(hdev, NULL); |
870 | } | 1132 | } |
871 | 1133 | ||
872 | static int logi_djdevice_probe(struct hid_device *hdev, | ||
873 | const struct hid_device_id *id) | ||
874 | { | ||
875 | int ret; | ||
876 | struct dj_device *dj_dev = hdev->driver_data; | ||
877 | |||
878 | if (!is_dj_device(dj_dev)) | ||
879 | return -ENODEV; | ||
880 | |||
881 | ret = hid_parse(hdev); | ||
882 | if (!ret) | ||
883 | ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT); | ||
884 | |||
885 | return ret; | ||
886 | } | ||
887 | |||
888 | static const struct hid_device_id logi_dj_receivers[] = { | 1134 | static const struct hid_device_id logi_dj_receivers[] = { |
889 | {HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, | 1135 | {HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, |
890 | USB_DEVICE_ID_LOGITECH_UNIFYING_RECEIVER)}, | 1136 | USB_DEVICE_ID_LOGITECH_UNIFYING_RECEIVER)}, |
@@ -906,51 +1152,8 @@ static struct hid_driver logi_djreceiver_driver = { | |||
906 | #endif | 1152 | #endif |
907 | }; | 1153 | }; |
908 | 1154 | ||
1155 | module_hid_driver(logi_djreceiver_driver); | ||
909 | 1156 | ||
910 | static const struct hid_device_id logi_dj_devices[] = { | ||
911 | {HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, | ||
912 | USB_DEVICE_ID_LOGITECH_UNIFYING_RECEIVER)}, | ||
913 | {HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, | ||
914 | USB_DEVICE_ID_LOGITECH_UNIFYING_RECEIVER_2)}, | ||
915 | {} | ||
916 | }; | ||
917 | |||
918 | static struct hid_driver logi_djdevice_driver = { | ||
919 | .name = "logitech-djdevice", | ||
920 | .id_table = logi_dj_devices, | ||
921 | .probe = logi_djdevice_probe, | ||
922 | }; | ||
923 | |||
924 | |||
925 | static int __init logi_dj_init(void) | ||
926 | { | ||
927 | int retval; | ||
928 | |||
929 | dbg_hid("Logitech-DJ:%s\n", __func__); | ||
930 | |||
931 | retval = hid_register_driver(&logi_djreceiver_driver); | ||
932 | if (retval) | ||
933 | return retval; | ||
934 | |||
935 | retval = hid_register_driver(&logi_djdevice_driver); | ||
936 | if (retval) | ||
937 | hid_unregister_driver(&logi_djreceiver_driver); | ||
938 | |||
939 | return retval; | ||
940 | |||
941 | } | ||
942 | |||
943 | static void __exit logi_dj_exit(void) | ||
944 | { | ||
945 | dbg_hid("Logitech-DJ:%s\n", __func__); | ||
946 | |||
947 | hid_unregister_driver(&logi_djdevice_driver); | ||
948 | hid_unregister_driver(&logi_djreceiver_driver); | ||
949 | |||
950 | } | ||
951 | |||
952 | module_init(logi_dj_init); | ||
953 | module_exit(logi_dj_exit); | ||
954 | MODULE_LICENSE("GPL"); | 1157 | MODULE_LICENSE("GPL"); |
955 | MODULE_AUTHOR("Logitech"); | 1158 | MODULE_AUTHOR("Logitech"); |
956 | MODULE_AUTHOR("Nestor Lopez Casado"); | 1159 | MODULE_AUTHOR("Nestor Lopez Casado"); |