Skip to content

Commit 925f0f3

Browse files
bentissJiri Kosina
authored andcommitted
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 <[email protected]> Tested-by: Andrew de los Reyes <[email protected]> Signed-off-by: Jiri Kosina <[email protected]>
1 parent ab94e56 commit 925f0f3

File tree

1 file changed

+160
-28
lines changed

1 file changed

+160
-28
lines changed

drivers/hid/hid-logitech-dj.c

Lines changed: 160 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,15 @@
4242
#define REPORT_ID_DJ_SHORT 0x20
4343
#define REPORT_ID_DJ_LONG 0x21
4444

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
4554
#define REPORT_TYPE_RFREPORT_LAST 0x1F
4655

4756
/* Command Switch to DJ mode */
@@ -242,6 +251,57 @@ static const char media_descriptor[] = {
242251
0xc0, /* EndCollection */
243252
}; /* */
244253

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+
245305
/* Maximum size of all defined hid reports in bytes (including report id) */
246306
#define MAX_REPORT_SIZE 8
247307

@@ -251,7 +311,8 @@ static const char media_descriptor[] = {
251311
sizeof(mse_descriptor) + \
252312
sizeof(consumer_descriptor) + \
253313
sizeof(syscontrol_descriptor) + \
254-
sizeof(media_descriptor))
314+
sizeof(media_descriptor) + \
315+
sizeof(hidpp_descriptor))
255316

256317
/* Number of possible hid report types that can be created by this driver.
257318
*
@@ -512,6 +573,13 @@ static void logi_dj_recv_forward_report(struct dj_receiver_dev *djrcv_dev,
512573
}
513574
}
514575

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+
}
515583

516584
static int logi_dj_recv_send_report(struct dj_receiver_dev *djrcv_dev,
517585
struct dj_report *dj_report)
@@ -609,6 +677,16 @@ static int logi_dj_ll_raw_request(struct hid_device *hid,
609677
u8 *out_buf;
610678
int ret;
611679

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+
612690
if (buf[0] != REPORT_TYPE_LEDS)
613691
return -EINVAL;
614692

@@ -687,6 +765,8 @@ static int logi_dj_ll_parse(struct hid_device *hid)
687765
__func__, djdev->reports_supported);
688766
}
689767

768+
rdcat(rdesc, &rsize, hidpp_descriptor, sizeof(hidpp_descriptor));
769+
690770
retval = hid_parse_report(hid, rdesc, rsize);
691771
kfree(rdesc);
692772

@@ -714,45 +794,32 @@ static struct hid_ll_driver logi_dj_ll_driver = {
714794
.raw_request = logi_dj_ll_raw_request,
715795
};
716796

717-
718-
static int logi_dj_raw_event(struct hid_device *hdev,
797+
static int logi_dj_dj_event(struct hid_device *hdev,
719798
struct hid_report *report, u8 *data,
720799
int size)
721800
{
722801
struct dj_receiver_dev *djrcv_dev = hid_get_drvdata(hdev);
723802
struct dj_report *dj_report = (struct dj_report *) data;
724803
unsigned long flags;
725804

726-
dbg_hid("%s, size:%d\n", __func__, size);
727-
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)
805+
/*
806+
* Here we receive all data coming from iface 2, there are 3 cases:
734807
*
735-
* 2) Data is from DJ collection, and is intended for this driver i. e.
736-
* data contains arrival, departure, etc notifications, in which case
737-
* we queue them for delayed processing by the work queue. We return 1
738-
* to hid-core as no further processing is required from it.
808+
* 1) Data is intended for this driver i. e. data contains arrival,
809+
* departure, etc notifications, in which case we queue them for delayed
810+
* processing by the work queue. We return 1 to hid-core as no further
811+
* processing is required from it.
739812
*
740-
* 3) Data is from DJ collection, and informs a connection change,
741-
* if the change means rf link loss, then we must send a null report
742-
* to the upper layer to discard potentially pressed keys that may be
743-
* repeated forever by the input layer. Return 1 to hid-core as no
744-
* further processing is required.
813+
* 2) Data informs a connection change, if the change means rf link
814+
* loss, then we must send a null report to the upper layer to discard
815+
* potentially pressed keys that may be repeated forever by the input
816+
* layer. Return 1 to hid-core as no further processing is required.
745817
*
746-
* 4) Data is from DJ collection and is an actual input event from
747-
* a paired DJ device in which case we forward it to the correct hid
748-
* device (via hid_input_report() ) and return 1 so hid-core does not do
749-
* anything else with it.
818+
* 3) Data is an actual input event from a paired DJ device in which
819+
* case we forward it to the correct hid device (via hid_input_report()
820+
* ) and return 1 so hid-core does not anything else with it.
750821
*/
751822

752-
/* case 1) */
753-
if (data[0] != REPORT_ID_DJ_SHORT)
754-
return false;
755-
756823
if ((dj_report->device_index < DJ_DEVICE_INDEX_MIN) ||
757824
(dj_report->device_index > DJ_DEVICE_INDEX_MAX)) {
758825
/*
@@ -797,6 +864,71 @@ static int logi_dj_raw_event(struct hid_device *hdev,
797864
return true;
798865
}
799866

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+
800932
static int logi_dj_probe(struct hid_device *hdev,
801933
const struct hid_device_id *id)
802934
{

0 commit comments

Comments
 (0)