145051539SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2534a7b8eSNestor Lopez Casado /*
3c08ce255SFilipe Laíns * HID driver for Logitech receivers
4534a7b8eSNestor Lopez Casado *
5534a7b8eSNestor Lopez Casado * Copyright (c) 2011 Logitech
6534a7b8eSNestor Lopez Casado */
7534a7b8eSNestor Lopez Casado
8534a7b8eSNestor Lopez Casado
9534a7b8eSNestor Lopez Casado
10534a7b8eSNestor Lopez Casado #include <linux/device.h>
11534a7b8eSNestor Lopez Casado #include <linux/hid.h>
12534a7b8eSNestor Lopez Casado #include <linux/module.h>
138cb3746aSBenjamin Tissoires #include <linux/kfifo.h>
1482c0beb8SBenjamin Tissoires #include <linux/delay.h>
15da12b224SHans de Goede #include <linux/usb.h> /* For to_usb_interface for kvm extra intf check */
1644d27f7dSJonathan Nieder #include <asm/unaligned.h>
17534a7b8eSNestor Lopez Casado #include "hid-ids.h"
188cb3746aSBenjamin Tissoires
198c9d734cSFilipe Laíns #define DJ_MAX_PAIRED_DEVICES 7
204fcad95aSBenjamin Tissoires #define DJ_MAX_NUMBER_NOTIFS 8
218cb3746aSBenjamin Tissoires #define DJ_RECEIVER_INDEX 0
228cb3746aSBenjamin Tissoires #define DJ_DEVICE_INDEX_MIN 1
238c9d734cSFilipe Laíns #define DJ_DEVICE_INDEX_MAX 7
248cb3746aSBenjamin Tissoires
258cb3746aSBenjamin Tissoires #define DJREPORT_SHORT_LENGTH 15
268cb3746aSBenjamin Tissoires #define DJREPORT_LONG_LENGTH 32
278cb3746aSBenjamin Tissoires
288cb3746aSBenjamin Tissoires #define REPORT_ID_DJ_SHORT 0x20
298cb3746aSBenjamin Tissoires #define REPORT_ID_DJ_LONG 0x21
308cb3746aSBenjamin Tissoires
31925f0f3eSBenjamin Tissoires #define REPORT_ID_HIDPP_SHORT 0x10
32925f0f3eSBenjamin Tissoires #define REPORT_ID_HIDPP_LONG 0x11
333a9a2c86SHans de Goede #define REPORT_ID_HIDPP_VERY_LONG 0x12
34925f0f3eSBenjamin Tissoires
35925f0f3eSBenjamin Tissoires #define HIDPP_REPORT_SHORT_LENGTH 7
36925f0f3eSBenjamin Tissoires #define HIDPP_REPORT_LONG_LENGTH 20
37925f0f3eSBenjamin Tissoires
38925f0f3eSBenjamin Tissoires #define HIDPP_RECEIVER_INDEX 0xff
39925f0f3eSBenjamin Tissoires
40925f0f3eSBenjamin Tissoires #define REPORT_TYPE_RFREPORT_FIRST 0x01
418cb3746aSBenjamin Tissoires #define REPORT_TYPE_RFREPORT_LAST 0x1F
428cb3746aSBenjamin Tissoires
438cb3746aSBenjamin Tissoires /* Command Switch to DJ mode */
448cb3746aSBenjamin Tissoires #define REPORT_TYPE_CMD_SWITCH 0x80
458cb3746aSBenjamin Tissoires #define CMD_SWITCH_PARAM_DEVBITFIELD 0x00
468cb3746aSBenjamin Tissoires #define CMD_SWITCH_PARAM_TIMEOUT_SECONDS 0x01
478cb3746aSBenjamin Tissoires #define TIMEOUT_NO_KEEPALIVE 0x00
488cb3746aSBenjamin Tissoires
498cb3746aSBenjamin Tissoires /* Command to Get the list of Paired devices */
508cb3746aSBenjamin Tissoires #define REPORT_TYPE_CMD_GET_PAIRED_DEVICES 0x81
518cb3746aSBenjamin Tissoires
528cb3746aSBenjamin Tissoires /* Device Paired Notification */
538cb3746aSBenjamin Tissoires #define REPORT_TYPE_NOTIF_DEVICE_PAIRED 0x41
548cb3746aSBenjamin Tissoires #define SPFUNCTION_MORE_NOTIF_EXPECTED 0x01
558cb3746aSBenjamin Tissoires #define SPFUNCTION_DEVICE_LIST_EMPTY 0x02
568cb3746aSBenjamin Tissoires #define DEVICE_PAIRED_PARAM_SPFUNCTION 0x00
578cb3746aSBenjamin Tissoires #define DEVICE_PAIRED_PARAM_EQUAD_ID_LSB 0x01
588cb3746aSBenjamin Tissoires #define DEVICE_PAIRED_PARAM_EQUAD_ID_MSB 0x02
598cb3746aSBenjamin Tissoires #define DEVICE_PAIRED_RF_REPORT_TYPE 0x03
608cb3746aSBenjamin Tissoires
618cb3746aSBenjamin Tissoires /* Device Un-Paired Notification */
628cb3746aSBenjamin Tissoires #define REPORT_TYPE_NOTIF_DEVICE_UNPAIRED 0x40
638cb3746aSBenjamin Tissoires
648cb3746aSBenjamin Tissoires /* Connection Status Notification */
658cb3746aSBenjamin Tissoires #define REPORT_TYPE_NOTIF_CONNECTION_STATUS 0x42
668cb3746aSBenjamin Tissoires #define CONNECTION_STATUS_PARAM_STATUS 0x00
678cb3746aSBenjamin Tissoires #define STATUS_LINKLOSS 0x01
688cb3746aSBenjamin Tissoires
698cb3746aSBenjamin Tissoires /* Error Notification */
708cb3746aSBenjamin Tissoires #define REPORT_TYPE_NOTIF_ERROR 0x7F
718cb3746aSBenjamin Tissoires #define NOTIF_ERROR_PARAM_ETYPE 0x00
728cb3746aSBenjamin Tissoires #define ETYPE_KEEPALIVE_TIMEOUT 0x01
738cb3746aSBenjamin Tissoires
748cb3746aSBenjamin Tissoires /* supported DJ HID && RF report types */
758cb3746aSBenjamin Tissoires #define REPORT_TYPE_KEYBOARD 0x01
768cb3746aSBenjamin Tissoires #define REPORT_TYPE_MOUSE 0x02
778cb3746aSBenjamin Tissoires #define REPORT_TYPE_CONSUMER_CONTROL 0x03
788cb3746aSBenjamin Tissoires #define REPORT_TYPE_SYSTEM_CONTROL 0x04
798cb3746aSBenjamin Tissoires #define REPORT_TYPE_MEDIA_CENTER 0x08
808cb3746aSBenjamin Tissoires #define REPORT_TYPE_LEDS 0x0E
818cb3746aSBenjamin Tissoires
828cb3746aSBenjamin Tissoires /* RF Report types bitfield */
83a17dd1f2SBenjamin Tissoires #define STD_KEYBOARD BIT(1)
84a17dd1f2SBenjamin Tissoires #define STD_MOUSE BIT(2)
85a17dd1f2SBenjamin Tissoires #define MULTIMEDIA BIT(3)
86a17dd1f2SBenjamin Tissoires #define POWER_KEYS BIT(4)
87434f7709SHans de Goede #define KBD_MOUSE BIT(5)
88a17dd1f2SBenjamin Tissoires #define MEDIA_CENTER BIT(8)
89a17dd1f2SBenjamin Tissoires #define KBD_LEDS BIT(14)
906d3c3f03SHans de Goede /* Fake (bitnr > NUMBER_OF_HID_REPORTS) bit to track HID++ capability */
916d3c3f03SHans de Goede #define HIDPP BIT_ULL(63)
928cb3746aSBenjamin Tissoires
9374808f91SHans de Goede /* HID++ Device Connected Notification */
9474808f91SHans de Goede #define REPORT_TYPE_NOTIF_DEVICE_CONNECTED 0x41
9574808f91SHans de Goede #define HIDPP_PARAM_PROTO_TYPE 0x00
9674808f91SHans de Goede #define HIDPP_PARAM_DEVICE_INFO 0x01
9774808f91SHans de Goede #define HIDPP_PARAM_EQUAD_LSB 0x02
9874808f91SHans de Goede #define HIDPP_PARAM_EQUAD_MSB 0x03
99c9121cf6SHans de Goede #define HIDPP_PARAM_27MHZ_DEVID 0x03
10074808f91SHans de Goede #define HIDPP_DEVICE_TYPE_MASK GENMASK(3, 0)
10174808f91SHans de Goede #define HIDPP_LINK_STATUS_MASK BIT(6)
102f2113c30SHans de Goede #define HIDPP_MANUFACTURER_MASK BIT(7)
1037383354aSHans de Goede #define HIDPP_27MHZ_SECURE_MASK BIT(7)
10474808f91SHans de Goede
105de76b1d3SHans de Goede #define HIDPP_DEVICE_TYPE_KEYBOARD 1
106de76b1d3SHans de Goede #define HIDPP_DEVICE_TYPE_MOUSE 2
107de76b1d3SHans de Goede
10874808f91SHans de Goede #define HIDPP_SET_REGISTER 0x80
109c0340412SBenjamin Tissoires #define HIDPP_GET_LONG_REGISTER 0x83
11074808f91SHans de Goede #define HIDPP_REG_CONNECTION_STATE 0x02
111c0340412SBenjamin Tissoires #define HIDPP_REG_PAIRING_INFORMATION 0xB5
112c0340412SBenjamin Tissoires #define HIDPP_PAIRING_INFORMATION 0x20
11374808f91SHans de Goede #define HIDPP_FAKE_DEVICE_ARRIVAL 0x02
11474808f91SHans de Goede
11574808f91SHans de Goede enum recvr_type {
11674808f91SHans de Goede recvr_type_dj,
11774808f91SHans de Goede recvr_type_hidpp,
118f5fb57a7SBenjamin Tissoires recvr_type_gaming_hidpp,
1193ed224e2SHans de Goede recvr_type_mouse_only,
120c9121cf6SHans de Goede recvr_type_27mhz,
121f2113c30SHans de Goede recvr_type_bluetooth,
122434f7709SHans de Goede recvr_type_dinovo,
12374808f91SHans de Goede };
124c0340412SBenjamin Tissoires
1258cb3746aSBenjamin Tissoires struct dj_report {
1268cb3746aSBenjamin Tissoires u8 report_id;
1278cb3746aSBenjamin Tissoires u8 device_index;
1288cb3746aSBenjamin Tissoires u8 report_type;
1298cb3746aSBenjamin Tissoires u8 report_params[DJREPORT_SHORT_LENGTH - 3];
1308cb3746aSBenjamin Tissoires };
1318cb3746aSBenjamin Tissoires
1327bb56a5fSBenjamin Tissoires struct hidpp_event {
1337bb56a5fSBenjamin Tissoires u8 report_id;
1347bb56a5fSBenjamin Tissoires u8 device_index;
1357bb56a5fSBenjamin Tissoires u8 sub_id;
1367bb56a5fSBenjamin Tissoires u8 params[HIDPP_REPORT_LONG_LENGTH - 3U];
1377bb56a5fSBenjamin Tissoires } __packed;
1387bb56a5fSBenjamin Tissoires
1398cb3746aSBenjamin Tissoires struct dj_receiver_dev {
140a1d97ccbSHans de Goede struct hid_device *mouse;
141a1d97ccbSHans de Goede struct hid_device *keyboard;
1420ee75544SHans de Goede struct hid_device *hidpp;
1438cb3746aSBenjamin Tissoires struct dj_device *paired_dj_devices[DJ_MAX_PAIRED_DEVICES +
1448cb3746aSBenjamin Tissoires DJ_DEVICE_INDEX_MIN];
145a1d97ccbSHans de Goede struct list_head list;
146a1d97ccbSHans de Goede struct kref kref;
1478cb3746aSBenjamin Tissoires struct work_struct work;
1488cb3746aSBenjamin Tissoires struct kfifo notif_fifo;
149b6aeeddeSHans de Goede unsigned long last_query; /* in jiffies */
150a1d97ccbSHans de Goede bool ready;
15174808f91SHans de Goede enum recvr_type type;
15274808f91SHans de Goede unsigned int unnumbered_application;
1538cb3746aSBenjamin Tissoires spinlock_t lock;
1548cb3746aSBenjamin Tissoires };
1558cb3746aSBenjamin Tissoires
1568cb3746aSBenjamin Tissoires struct dj_device {
1578cb3746aSBenjamin Tissoires struct hid_device *hdev;
1588cb3746aSBenjamin Tissoires struct dj_receiver_dev *dj_receiver_dev;
1596d3c3f03SHans de Goede u64 reports_supported;
1608cb3746aSBenjamin Tissoires u8 device_index;
1618cb3746aSBenjamin Tissoires };
162534a7b8eSNestor Lopez Casado
1634fcad95aSBenjamin Tissoires #define WORKITEM_TYPE_EMPTY 0
1644fcad95aSBenjamin Tissoires #define WORKITEM_TYPE_PAIRED 1
1654fcad95aSBenjamin Tissoires #define WORKITEM_TYPE_UNPAIRED 2
1664fcad95aSBenjamin Tissoires #define WORKITEM_TYPE_UNKNOWN 255
1674fcad95aSBenjamin Tissoires
1684fcad95aSBenjamin Tissoires struct dj_workitem {
1694fcad95aSBenjamin Tissoires u8 type; /* WORKITEM_TYPE_* */
1704fcad95aSBenjamin Tissoires u8 device_index;
171de76b1d3SHans de Goede u8 device_type;
1724fcad95aSBenjamin Tissoires u8 quad_id_msb;
1734fcad95aSBenjamin Tissoires u8 quad_id_lsb;
1746d3c3f03SHans de Goede u64 reports_supported;
1754fcad95aSBenjamin Tissoires };
1764fcad95aSBenjamin Tissoires
177534a7b8eSNestor Lopez Casado /* Keyboard descriptor (1) */
178534a7b8eSNestor Lopez Casado static const char kbd_descriptor[] = {
179534a7b8eSNestor Lopez Casado 0x05, 0x01, /* USAGE_PAGE (generic Desktop) */
180534a7b8eSNestor Lopez Casado 0x09, 0x06, /* USAGE (Keyboard) */
181534a7b8eSNestor Lopez Casado 0xA1, 0x01, /* COLLECTION (Application) */
182534a7b8eSNestor Lopez Casado 0x85, 0x01, /* REPORT_ID (1) */
183534a7b8eSNestor Lopez Casado 0x95, 0x08, /* REPORT_COUNT (8) */
184534a7b8eSNestor Lopez Casado 0x75, 0x01, /* REPORT_SIZE (1) */
185534a7b8eSNestor Lopez Casado 0x15, 0x00, /* LOGICAL_MINIMUM (0) */
186534a7b8eSNestor Lopez Casado 0x25, 0x01, /* LOGICAL_MAXIMUM (1) */
187534a7b8eSNestor Lopez Casado 0x05, 0x07, /* USAGE_PAGE (Keyboard) */
188534a7b8eSNestor Lopez Casado 0x19, 0xE0, /* USAGE_MINIMUM (Left Control) */
189534a7b8eSNestor Lopez Casado 0x29, 0xE7, /* USAGE_MAXIMUM (Right GUI) */
190534a7b8eSNestor Lopez Casado 0x81, 0x02, /* INPUT (Data,Var,Abs) */
191534a7b8eSNestor Lopez Casado 0x95, 0x06, /* REPORT_COUNT (6) */
192534a7b8eSNestor Lopez Casado 0x75, 0x08, /* REPORT_SIZE (8) */
193534a7b8eSNestor Lopez Casado 0x15, 0x00, /* LOGICAL_MINIMUM (0) */
194534a7b8eSNestor Lopez Casado 0x26, 0xFF, 0x00, /* LOGICAL_MAXIMUM (255) */
195534a7b8eSNestor Lopez Casado 0x05, 0x07, /* USAGE_PAGE (Keyboard) */
196534a7b8eSNestor Lopez Casado 0x19, 0x00, /* USAGE_MINIMUM (no event) */
197534a7b8eSNestor Lopez Casado 0x2A, 0xFF, 0x00, /* USAGE_MAXIMUM (reserved) */
198534a7b8eSNestor Lopez Casado 0x81, 0x00, /* INPUT (Data,Ary,Abs) */
1990e40d356SBenjamin Tissoires 0x85, 0x0e, /* REPORT_ID (14) */
2000e40d356SBenjamin Tissoires 0x05, 0x08, /* USAGE PAGE (LED page) */
2010e40d356SBenjamin Tissoires 0x95, 0x05, /* REPORT COUNT (5) */
2020e40d356SBenjamin Tissoires 0x75, 0x01, /* REPORT SIZE (1) */
2030e40d356SBenjamin Tissoires 0x15, 0x00, /* LOGICAL_MINIMUM (0) */
2040e40d356SBenjamin Tissoires 0x25, 0x01, /* LOGICAL_MAXIMUM (1) */
2050e40d356SBenjamin Tissoires 0x19, 0x01, /* USAGE MINIMUM (1) */
2060e40d356SBenjamin Tissoires 0x29, 0x05, /* USAGE MAXIMUM (5) */
2070e40d356SBenjamin Tissoires 0x91, 0x02, /* OUTPUT (Data, Variable, Absolute) */
2080e40d356SBenjamin Tissoires 0x95, 0x01, /* REPORT COUNT (1) */
2090e40d356SBenjamin Tissoires 0x75, 0x03, /* REPORT SIZE (3) */
2100e40d356SBenjamin Tissoires 0x91, 0x01, /* OUTPUT (Constant) */
211534a7b8eSNestor Lopez Casado 0xC0
212534a7b8eSNestor Lopez Casado };
213534a7b8eSNestor Lopez Casado
214534a7b8eSNestor Lopez Casado /* Mouse descriptor (2) */
215534a7b8eSNestor Lopez Casado static const char mse_descriptor[] = {
216534a7b8eSNestor Lopez Casado 0x05, 0x01, /* USAGE_PAGE (Generic Desktop) */
217534a7b8eSNestor Lopez Casado 0x09, 0x02, /* USAGE (Mouse) */
218534a7b8eSNestor Lopez Casado 0xA1, 0x01, /* COLLECTION (Application) */
219534a7b8eSNestor Lopez Casado 0x85, 0x02, /* REPORT_ID = 2 */
220534a7b8eSNestor Lopez Casado 0x09, 0x01, /* USAGE (pointer) */
221534a7b8eSNestor Lopez Casado 0xA1, 0x00, /* COLLECTION (physical) */
222534a7b8eSNestor Lopez Casado 0x05, 0x09, /* USAGE_PAGE (buttons) */
223534a7b8eSNestor Lopez Casado 0x19, 0x01, /* USAGE_MIN (1) */
224534a7b8eSNestor Lopez Casado 0x29, 0x10, /* USAGE_MAX (16) */
225534a7b8eSNestor Lopez Casado 0x15, 0x00, /* LOGICAL_MIN (0) */
226534a7b8eSNestor Lopez Casado 0x25, 0x01, /* LOGICAL_MAX (1) */
227534a7b8eSNestor Lopez Casado 0x95, 0x10, /* REPORT_COUNT (16) */
228534a7b8eSNestor Lopez Casado 0x75, 0x01, /* REPORT_SIZE (1) */
229534a7b8eSNestor Lopez Casado 0x81, 0x02, /* INPUT (data var abs) */
230534a7b8eSNestor Lopez Casado 0x05, 0x01, /* USAGE_PAGE (generic desktop) */
231534a7b8eSNestor Lopez Casado 0x16, 0x01, 0xF8, /* LOGICAL_MIN (-2047) */
232534a7b8eSNestor Lopez Casado 0x26, 0xFF, 0x07, /* LOGICAL_MAX (2047) */
233534a7b8eSNestor Lopez Casado 0x75, 0x0C, /* REPORT_SIZE (12) */
234534a7b8eSNestor Lopez Casado 0x95, 0x02, /* REPORT_COUNT (2) */
235534a7b8eSNestor Lopez Casado 0x09, 0x30, /* USAGE (X) */
236534a7b8eSNestor Lopez Casado 0x09, 0x31, /* USAGE (Y) */
237534a7b8eSNestor Lopez Casado 0x81, 0x06, /* INPUT */
238534a7b8eSNestor Lopez Casado 0x15, 0x81, /* LOGICAL_MIN (-127) */
239534a7b8eSNestor Lopez Casado 0x25, 0x7F, /* LOGICAL_MAX (127) */
240534a7b8eSNestor Lopez Casado 0x75, 0x08, /* REPORT_SIZE (8) */
241534a7b8eSNestor Lopez Casado 0x95, 0x01, /* REPORT_COUNT (1) */
242534a7b8eSNestor Lopez Casado 0x09, 0x38, /* USAGE (wheel) */
243534a7b8eSNestor Lopez Casado 0x81, 0x06, /* INPUT */
244534a7b8eSNestor Lopez Casado 0x05, 0x0C, /* USAGE_PAGE(consumer) */
245534a7b8eSNestor Lopez Casado 0x0A, 0x38, 0x02, /* USAGE(AC Pan) */
246534a7b8eSNestor Lopez Casado 0x95, 0x01, /* REPORT_COUNT (1) */
247534a7b8eSNestor Lopez Casado 0x81, 0x06, /* INPUT */
248534a7b8eSNestor Lopez Casado 0xC0, /* END_COLLECTION */
249534a7b8eSNestor Lopez Casado 0xC0, /* END_COLLECTION */
250534a7b8eSNestor Lopez Casado };
251534a7b8eSNestor Lopez Casado
252c9121cf6SHans de Goede /* Mouse descriptor (2) for 27 MHz receiver, only 8 buttons */
253c9121cf6SHans de Goede static const char mse_27mhz_descriptor[] = {
254c9121cf6SHans de Goede 0x05, 0x01, /* USAGE_PAGE (Generic Desktop) */
255c9121cf6SHans de Goede 0x09, 0x02, /* USAGE (Mouse) */
256c9121cf6SHans de Goede 0xA1, 0x01, /* COLLECTION (Application) */
257c9121cf6SHans de Goede 0x85, 0x02, /* REPORT_ID = 2 */
258c9121cf6SHans de Goede 0x09, 0x01, /* USAGE (pointer) */
259c9121cf6SHans de Goede 0xA1, 0x00, /* COLLECTION (physical) */
260c9121cf6SHans de Goede 0x05, 0x09, /* USAGE_PAGE (buttons) */
261c9121cf6SHans de Goede 0x19, 0x01, /* USAGE_MIN (1) */
262c9121cf6SHans de Goede 0x29, 0x08, /* USAGE_MAX (8) */
263c9121cf6SHans de Goede 0x15, 0x00, /* LOGICAL_MIN (0) */
264c9121cf6SHans de Goede 0x25, 0x01, /* LOGICAL_MAX (1) */
265c9121cf6SHans de Goede 0x95, 0x08, /* REPORT_COUNT (8) */
266c9121cf6SHans de Goede 0x75, 0x01, /* REPORT_SIZE (1) */
267c9121cf6SHans de Goede 0x81, 0x02, /* INPUT (data var abs) */
268c9121cf6SHans de Goede 0x05, 0x01, /* USAGE_PAGE (generic desktop) */
269c9121cf6SHans de Goede 0x16, 0x01, 0xF8, /* LOGICAL_MIN (-2047) */
270c9121cf6SHans de Goede 0x26, 0xFF, 0x07, /* LOGICAL_MAX (2047) */
271c9121cf6SHans de Goede 0x75, 0x0C, /* REPORT_SIZE (12) */
272c9121cf6SHans de Goede 0x95, 0x02, /* REPORT_COUNT (2) */
273c9121cf6SHans de Goede 0x09, 0x30, /* USAGE (X) */
274c9121cf6SHans de Goede 0x09, 0x31, /* USAGE (Y) */
275c9121cf6SHans de Goede 0x81, 0x06, /* INPUT */
276c9121cf6SHans de Goede 0x15, 0x81, /* LOGICAL_MIN (-127) */
277c9121cf6SHans de Goede 0x25, 0x7F, /* LOGICAL_MAX (127) */
278c9121cf6SHans de Goede 0x75, 0x08, /* REPORT_SIZE (8) */
279c9121cf6SHans de Goede 0x95, 0x01, /* REPORT_COUNT (1) */
280c9121cf6SHans de Goede 0x09, 0x38, /* USAGE (wheel) */
281c9121cf6SHans de Goede 0x81, 0x06, /* INPUT */
282c9121cf6SHans de Goede 0x05, 0x0C, /* USAGE_PAGE(consumer) */
283c9121cf6SHans de Goede 0x0A, 0x38, 0x02, /* USAGE(AC Pan) */
284c9121cf6SHans de Goede 0x95, 0x01, /* REPORT_COUNT (1) */
285c9121cf6SHans de Goede 0x81, 0x06, /* INPUT */
286c9121cf6SHans de Goede 0xC0, /* END_COLLECTION */
287c9121cf6SHans de Goede 0xC0, /* END_COLLECTION */
288c9121cf6SHans de Goede };
289c9121cf6SHans de Goede
290f2113c30SHans de Goede /* Mouse descriptor (2) for Bluetooth receiver, low-res hwheel, 12 buttons */
291f2113c30SHans de Goede static const char mse_bluetooth_descriptor[] = {
292f2113c30SHans de Goede 0x05, 0x01, /* USAGE_PAGE (Generic Desktop) */
293f2113c30SHans de Goede 0x09, 0x02, /* USAGE (Mouse) */
294f2113c30SHans de Goede 0xA1, 0x01, /* COLLECTION (Application) */
295f2113c30SHans de Goede 0x85, 0x02, /* REPORT_ID = 2 */
296f2113c30SHans de Goede 0x09, 0x01, /* USAGE (pointer) */
297f2113c30SHans de Goede 0xA1, 0x00, /* COLLECTION (physical) */
298f2113c30SHans de Goede 0x05, 0x09, /* USAGE_PAGE (buttons) */
299f2113c30SHans de Goede 0x19, 0x01, /* USAGE_MIN (1) */
300f2113c30SHans de Goede 0x29, 0x08, /* USAGE_MAX (8) */
301f2113c30SHans de Goede 0x15, 0x00, /* LOGICAL_MIN (0) */
302f2113c30SHans de Goede 0x25, 0x01, /* LOGICAL_MAX (1) */
303f2113c30SHans de Goede 0x95, 0x08, /* REPORT_COUNT (8) */
304f2113c30SHans de Goede 0x75, 0x01, /* REPORT_SIZE (1) */
305f2113c30SHans de Goede 0x81, 0x02, /* INPUT (data var abs) */
306f2113c30SHans de Goede 0x05, 0x01, /* USAGE_PAGE (generic desktop) */
307f2113c30SHans de Goede 0x16, 0x01, 0xF8, /* LOGICAL_MIN (-2047) */
308f2113c30SHans de Goede 0x26, 0xFF, 0x07, /* LOGICAL_MAX (2047) */
309f2113c30SHans de Goede 0x75, 0x0C, /* REPORT_SIZE (12) */
310f2113c30SHans de Goede 0x95, 0x02, /* REPORT_COUNT (2) */
311f2113c30SHans de Goede 0x09, 0x30, /* USAGE (X) */
312f2113c30SHans de Goede 0x09, 0x31, /* USAGE (Y) */
313f2113c30SHans de Goede 0x81, 0x06, /* INPUT */
314f2113c30SHans de Goede 0x15, 0x81, /* LOGICAL_MIN (-127) */
315f2113c30SHans de Goede 0x25, 0x7F, /* LOGICAL_MAX (127) */
316f2113c30SHans de Goede 0x75, 0x08, /* REPORT_SIZE (8) */
317f2113c30SHans de Goede 0x95, 0x01, /* REPORT_COUNT (1) */
318f2113c30SHans de Goede 0x09, 0x38, /* USAGE (wheel) */
319f2113c30SHans de Goede 0x81, 0x06, /* INPUT */
320f2113c30SHans de Goede 0x05, 0x0C, /* USAGE_PAGE(consumer) */
321f2113c30SHans de Goede 0x0A, 0x38, 0x02, /* USAGE(AC Pan) */
322f2113c30SHans de Goede 0x15, 0xF9, /* LOGICAL_MIN (-7) */
323f2113c30SHans de Goede 0x25, 0x07, /* LOGICAL_MAX (7) */
324f2113c30SHans de Goede 0x75, 0x04, /* REPORT_SIZE (4) */
325f2113c30SHans de Goede 0x95, 0x01, /* REPORT_COUNT (1) */
326f2113c30SHans de Goede 0x81, 0x06, /* INPUT */
327f2113c30SHans de Goede 0x05, 0x09, /* USAGE_PAGE (buttons) */
328f2113c30SHans de Goede 0x19, 0x09, /* USAGE_MIN (9) */
329f2113c30SHans de Goede 0x29, 0x0C, /* USAGE_MAX (12) */
330f2113c30SHans de Goede 0x15, 0x00, /* LOGICAL_MIN (0) */
331f2113c30SHans de Goede 0x25, 0x01, /* LOGICAL_MAX (1) */
332f2113c30SHans de Goede 0x75, 0x01, /* REPORT_SIZE (1) */
333f2113c30SHans de Goede 0x95, 0x04, /* REPORT_COUNT (4) */
334eec231e0SHans de Goede 0x81, 0x02, /* INPUT (Data,Var,Abs) */
335f2113c30SHans de Goede 0xC0, /* END_COLLECTION */
336f2113c30SHans de Goede 0xC0, /* END_COLLECTION */
337f2113c30SHans de Goede };
338f2113c30SHans de Goede
339434f7709SHans de Goede /* Mouse descriptor (5) for Bluetooth receiver, normal-res hwheel, 8 buttons */
340434f7709SHans de Goede static const char mse5_bluetooth_descriptor[] = {
341434f7709SHans de Goede 0x05, 0x01, /* USAGE_PAGE (Generic Desktop) */
342434f7709SHans de Goede 0x09, 0x02, /* Usage (Mouse) */
343434f7709SHans de Goede 0xa1, 0x01, /* Collection (Application) */
344434f7709SHans de Goede 0x85, 0x05, /* Report ID (5) */
345434f7709SHans de Goede 0x09, 0x01, /* Usage (Pointer) */
346434f7709SHans de Goede 0xa1, 0x00, /* Collection (Physical) */
347434f7709SHans de Goede 0x05, 0x09, /* Usage Page (Button) */
348434f7709SHans de Goede 0x19, 0x01, /* Usage Minimum (1) */
349434f7709SHans de Goede 0x29, 0x08, /* Usage Maximum (8) */
350434f7709SHans de Goede 0x15, 0x00, /* Logical Minimum (0) */
351434f7709SHans de Goede 0x25, 0x01, /* Logical Maximum (1) */
352434f7709SHans de Goede 0x95, 0x08, /* Report Count (8) */
353434f7709SHans de Goede 0x75, 0x01, /* Report Size (1) */
354434f7709SHans de Goede 0x81, 0x02, /* Input (Data,Var,Abs) */
355434f7709SHans de Goede 0x05, 0x01, /* Usage Page (Generic Desktop) */
356434f7709SHans de Goede 0x16, 0x01, 0xf8, /* Logical Minimum (-2047) */
357434f7709SHans de Goede 0x26, 0xff, 0x07, /* Logical Maximum (2047) */
358434f7709SHans de Goede 0x75, 0x0c, /* Report Size (12) */
359434f7709SHans de Goede 0x95, 0x02, /* Report Count (2) */
360434f7709SHans de Goede 0x09, 0x30, /* Usage (X) */
361434f7709SHans de Goede 0x09, 0x31, /* Usage (Y) */
362434f7709SHans de Goede 0x81, 0x06, /* Input (Data,Var,Rel) */
363434f7709SHans de Goede 0x15, 0x81, /* Logical Minimum (-127) */
364434f7709SHans de Goede 0x25, 0x7f, /* Logical Maximum (127) */
365434f7709SHans de Goede 0x75, 0x08, /* Report Size (8) */
366434f7709SHans de Goede 0x95, 0x01, /* Report Count (1) */
367434f7709SHans de Goede 0x09, 0x38, /* Usage (Wheel) */
368434f7709SHans de Goede 0x81, 0x06, /* Input (Data,Var,Rel) */
369434f7709SHans de Goede 0x05, 0x0c, /* Usage Page (Consumer Devices) */
370434f7709SHans de Goede 0x0a, 0x38, 0x02, /* Usage (AC Pan) */
371434f7709SHans de Goede 0x15, 0x81, /* Logical Minimum (-127) */
372434f7709SHans de Goede 0x25, 0x7f, /* Logical Maximum (127) */
373434f7709SHans de Goede 0x75, 0x08, /* Report Size (8) */
374434f7709SHans de Goede 0x95, 0x01, /* Report Count (1) */
375434f7709SHans de Goede 0x81, 0x06, /* Input (Data,Var,Rel) */
376434f7709SHans de Goede 0xc0, /* End Collection */
377434f7709SHans de Goede 0xc0, /* End Collection */
378434f7709SHans de Goede };
379434f7709SHans de Goede
380f5fb57a7SBenjamin Tissoires /* Gaming Mouse descriptor (2) */
381f5fb57a7SBenjamin Tissoires static const char mse_high_res_descriptor[] = {
382f5fb57a7SBenjamin Tissoires 0x05, 0x01, /* USAGE_PAGE (Generic Desktop) */
383f5fb57a7SBenjamin Tissoires 0x09, 0x02, /* USAGE (Mouse) */
384f5fb57a7SBenjamin Tissoires 0xA1, 0x01, /* COLLECTION (Application) */
385f5fb57a7SBenjamin Tissoires 0x85, 0x02, /* REPORT_ID = 2 */
386f5fb57a7SBenjamin Tissoires 0x09, 0x01, /* USAGE (pointer) */
387f5fb57a7SBenjamin Tissoires 0xA1, 0x00, /* COLLECTION (physical) */
388f5fb57a7SBenjamin Tissoires 0x05, 0x09, /* USAGE_PAGE (buttons) */
389f5fb57a7SBenjamin Tissoires 0x19, 0x01, /* USAGE_MIN (1) */
390f5fb57a7SBenjamin Tissoires 0x29, 0x10, /* USAGE_MAX (16) */
391f5fb57a7SBenjamin Tissoires 0x15, 0x00, /* LOGICAL_MIN (0) */
392f5fb57a7SBenjamin Tissoires 0x25, 0x01, /* LOGICAL_MAX (1) */
393f5fb57a7SBenjamin Tissoires 0x95, 0x10, /* REPORT_COUNT (16) */
394f5fb57a7SBenjamin Tissoires 0x75, 0x01, /* REPORT_SIZE (1) */
395f5fb57a7SBenjamin Tissoires 0x81, 0x02, /* INPUT (data var abs) */
396f5fb57a7SBenjamin Tissoires 0x05, 0x01, /* USAGE_PAGE (generic desktop) */
397f5fb57a7SBenjamin Tissoires 0x16, 0x01, 0x80, /* LOGICAL_MIN (-32767) */
398f5fb57a7SBenjamin Tissoires 0x26, 0xFF, 0x7F, /* LOGICAL_MAX (32767) */
399f5fb57a7SBenjamin Tissoires 0x75, 0x10, /* REPORT_SIZE (16) */
400f5fb57a7SBenjamin Tissoires 0x95, 0x02, /* REPORT_COUNT (2) */
401f5fb57a7SBenjamin Tissoires 0x09, 0x30, /* USAGE (X) */
402f5fb57a7SBenjamin Tissoires 0x09, 0x31, /* USAGE (Y) */
403f5fb57a7SBenjamin Tissoires 0x81, 0x06, /* INPUT */
404f5fb57a7SBenjamin Tissoires 0x15, 0x81, /* LOGICAL_MIN (-127) */
405f5fb57a7SBenjamin Tissoires 0x25, 0x7F, /* LOGICAL_MAX (127) */
406f5fb57a7SBenjamin Tissoires 0x75, 0x08, /* REPORT_SIZE (8) */
407f5fb57a7SBenjamin Tissoires 0x95, 0x01, /* REPORT_COUNT (1) */
408f5fb57a7SBenjamin Tissoires 0x09, 0x38, /* USAGE (wheel) */
409f5fb57a7SBenjamin Tissoires 0x81, 0x06, /* INPUT */
410f5fb57a7SBenjamin Tissoires 0x05, 0x0C, /* USAGE_PAGE(consumer) */
411f5fb57a7SBenjamin Tissoires 0x0A, 0x38, 0x02, /* USAGE(AC Pan) */
412f5fb57a7SBenjamin Tissoires 0x95, 0x01, /* REPORT_COUNT (1) */
413f5fb57a7SBenjamin Tissoires 0x81, 0x06, /* INPUT */
414f5fb57a7SBenjamin Tissoires 0xC0, /* END_COLLECTION */
415f5fb57a7SBenjamin Tissoires 0xC0, /* END_COLLECTION */
416f5fb57a7SBenjamin Tissoires };
417f5fb57a7SBenjamin Tissoires
418534a7b8eSNestor Lopez Casado /* Consumer Control descriptor (3) */
419534a7b8eSNestor Lopez Casado static const char consumer_descriptor[] = {
420534a7b8eSNestor Lopez Casado 0x05, 0x0C, /* USAGE_PAGE (Consumer Devices) */
421534a7b8eSNestor Lopez Casado 0x09, 0x01, /* USAGE (Consumer Control) */
422534a7b8eSNestor Lopez Casado 0xA1, 0x01, /* COLLECTION (Application) */
423534a7b8eSNestor Lopez Casado 0x85, 0x03, /* REPORT_ID = 3 */
424534a7b8eSNestor Lopez Casado 0x75, 0x10, /* REPORT_SIZE (16) */
425534a7b8eSNestor Lopez Casado 0x95, 0x02, /* REPORT_COUNT (2) */
426534a7b8eSNestor Lopez Casado 0x15, 0x01, /* LOGICAL_MIN (1) */
4275b6cc127SOlivier Gay 0x26, 0xFF, 0x02, /* LOGICAL_MAX (767) */
428534a7b8eSNestor Lopez Casado 0x19, 0x01, /* USAGE_MIN (1) */
4295b6cc127SOlivier Gay 0x2A, 0xFF, 0x02, /* USAGE_MAX (767) */
430534a7b8eSNestor Lopez Casado 0x81, 0x00, /* INPUT (Data Ary Abs) */
431534a7b8eSNestor Lopez Casado 0xC0, /* END_COLLECTION */
432534a7b8eSNestor Lopez Casado }; /* */
433534a7b8eSNestor Lopez Casado
434534a7b8eSNestor Lopez Casado /* System control descriptor (4) */
435534a7b8eSNestor Lopez Casado static const char syscontrol_descriptor[] = {
436534a7b8eSNestor Lopez Casado 0x05, 0x01, /* USAGE_PAGE (Generic Desktop) */
437534a7b8eSNestor Lopez Casado 0x09, 0x80, /* USAGE (System Control) */
438534a7b8eSNestor Lopez Casado 0xA1, 0x01, /* COLLECTION (Application) */
439534a7b8eSNestor Lopez Casado 0x85, 0x04, /* REPORT_ID = 4 */
440534a7b8eSNestor Lopez Casado 0x75, 0x02, /* REPORT_SIZE (2) */
441534a7b8eSNestor Lopez Casado 0x95, 0x01, /* REPORT_COUNT (1) */
442534a7b8eSNestor Lopez Casado 0x15, 0x01, /* LOGICAL_MIN (1) */
443534a7b8eSNestor Lopez Casado 0x25, 0x03, /* LOGICAL_MAX (3) */
444534a7b8eSNestor Lopez Casado 0x09, 0x82, /* USAGE (System Sleep) */
445534a7b8eSNestor Lopez Casado 0x09, 0x81, /* USAGE (System Power Down) */
446534a7b8eSNestor Lopez Casado 0x09, 0x83, /* USAGE (System Wake Up) */
447534a7b8eSNestor Lopez Casado 0x81, 0x60, /* INPUT (Data Ary Abs NPrf Null) */
448534a7b8eSNestor Lopez Casado 0x75, 0x06, /* REPORT_SIZE (6) */
449534a7b8eSNestor Lopez Casado 0x81, 0x03, /* INPUT (Cnst Var Abs) */
450534a7b8eSNestor Lopez Casado 0xC0, /* END_COLLECTION */
451534a7b8eSNestor Lopez Casado };
452534a7b8eSNestor Lopez Casado
453534a7b8eSNestor Lopez Casado /* Media descriptor (8) */
454534a7b8eSNestor Lopez Casado static const char media_descriptor[] = {
455534a7b8eSNestor Lopez Casado 0x06, 0xbc, 0xff, /* Usage Page 0xffbc */
456534a7b8eSNestor Lopez Casado 0x09, 0x88, /* Usage 0x0088 */
457534a7b8eSNestor Lopez Casado 0xa1, 0x01, /* BeginCollection */
458534a7b8eSNestor Lopez Casado 0x85, 0x08, /* Report ID 8 */
459534a7b8eSNestor Lopez Casado 0x19, 0x01, /* Usage Min 0x0001 */
460534a7b8eSNestor Lopez Casado 0x29, 0xff, /* Usage Max 0x00ff */
461534a7b8eSNestor Lopez Casado 0x15, 0x01, /* Logical Min 1 */
462534a7b8eSNestor Lopez Casado 0x26, 0xff, 0x00, /* Logical Max 255 */
463534a7b8eSNestor Lopez Casado 0x75, 0x08, /* Report Size 8 */
464534a7b8eSNestor Lopez Casado 0x95, 0x01, /* Report Count 1 */
465534a7b8eSNestor Lopez Casado 0x81, 0x00, /* Input */
466534a7b8eSNestor Lopez Casado 0xc0, /* EndCollection */
467534a7b8eSNestor Lopez Casado }; /* */
468534a7b8eSNestor Lopez Casado
469925f0f3eSBenjamin Tissoires /* HIDPP descriptor */
470925f0f3eSBenjamin Tissoires static const char hidpp_descriptor[] = {
471925f0f3eSBenjamin Tissoires 0x06, 0x00, 0xff, /* Usage Page (Vendor Defined Page 1) */
472925f0f3eSBenjamin Tissoires 0x09, 0x01, /* Usage (Vendor Usage 1) */
473925f0f3eSBenjamin Tissoires 0xa1, 0x01, /* Collection (Application) */
474925f0f3eSBenjamin Tissoires 0x85, 0x10, /* Report ID (16) */
475925f0f3eSBenjamin Tissoires 0x75, 0x08, /* Report Size (8) */
476925f0f3eSBenjamin Tissoires 0x95, 0x06, /* Report Count (6) */
477925f0f3eSBenjamin Tissoires 0x15, 0x00, /* Logical Minimum (0) */
478925f0f3eSBenjamin Tissoires 0x26, 0xff, 0x00, /* Logical Maximum (255) */
479925f0f3eSBenjamin Tissoires 0x09, 0x01, /* Usage (Vendor Usage 1) */
480925f0f3eSBenjamin Tissoires 0x81, 0x00, /* Input (Data,Arr,Abs) */
481925f0f3eSBenjamin Tissoires 0x09, 0x01, /* Usage (Vendor Usage 1) */
482925f0f3eSBenjamin Tissoires 0x91, 0x00, /* Output (Data,Arr,Abs) */
483925f0f3eSBenjamin Tissoires 0xc0, /* End Collection */
484925f0f3eSBenjamin Tissoires 0x06, 0x00, 0xff, /* Usage Page (Vendor Defined Page 1) */
485925f0f3eSBenjamin Tissoires 0x09, 0x02, /* Usage (Vendor Usage 2) */
486925f0f3eSBenjamin Tissoires 0xa1, 0x01, /* Collection (Application) */
487925f0f3eSBenjamin Tissoires 0x85, 0x11, /* Report ID (17) */
488925f0f3eSBenjamin Tissoires 0x75, 0x08, /* Report Size (8) */
489925f0f3eSBenjamin Tissoires 0x95, 0x13, /* Report Count (19) */
490925f0f3eSBenjamin Tissoires 0x15, 0x00, /* Logical Minimum (0) */
491925f0f3eSBenjamin Tissoires 0x26, 0xff, 0x00, /* Logical Maximum (255) */
492925f0f3eSBenjamin Tissoires 0x09, 0x02, /* Usage (Vendor Usage 2) */
493925f0f3eSBenjamin Tissoires 0x81, 0x00, /* Input (Data,Arr,Abs) */
494925f0f3eSBenjamin Tissoires 0x09, 0x02, /* Usage (Vendor Usage 2) */
495925f0f3eSBenjamin Tissoires 0x91, 0x00, /* Output (Data,Arr,Abs) */
496925f0f3eSBenjamin Tissoires 0xc0, /* End Collection */
497925f0f3eSBenjamin Tissoires 0x06, 0x00, 0xff, /* Usage Page (Vendor Defined Page 1) */
498925f0f3eSBenjamin Tissoires 0x09, 0x04, /* Usage (Vendor Usage 0x04) */
499925f0f3eSBenjamin Tissoires 0xa1, 0x01, /* Collection (Application) */
500925f0f3eSBenjamin Tissoires 0x85, 0x20, /* Report ID (32) */
501925f0f3eSBenjamin Tissoires 0x75, 0x08, /* Report Size (8) */
502925f0f3eSBenjamin Tissoires 0x95, 0x0e, /* Report Count (14) */
503925f0f3eSBenjamin Tissoires 0x15, 0x00, /* Logical Minimum (0) */
504925f0f3eSBenjamin Tissoires 0x26, 0xff, 0x00, /* Logical Maximum (255) */
505925f0f3eSBenjamin Tissoires 0x09, 0x41, /* Usage (Vendor Usage 0x41) */
506925f0f3eSBenjamin Tissoires 0x81, 0x00, /* Input (Data,Arr,Abs) */
507925f0f3eSBenjamin Tissoires 0x09, 0x41, /* Usage (Vendor Usage 0x41) */
508925f0f3eSBenjamin Tissoires 0x91, 0x00, /* Output (Data,Arr,Abs) */
509925f0f3eSBenjamin Tissoires 0x85, 0x21, /* Report ID (33) */
510925f0f3eSBenjamin Tissoires 0x95, 0x1f, /* Report Count (31) */
511925f0f3eSBenjamin Tissoires 0x15, 0x00, /* Logical Minimum (0) */
512925f0f3eSBenjamin Tissoires 0x26, 0xff, 0x00, /* Logical Maximum (255) */
513925f0f3eSBenjamin Tissoires 0x09, 0x42, /* Usage (Vendor Usage 0x42) */
514925f0f3eSBenjamin Tissoires 0x81, 0x00, /* Input (Data,Arr,Abs) */
515925f0f3eSBenjamin Tissoires 0x09, 0x42, /* Usage (Vendor Usage 0x42) */
516925f0f3eSBenjamin Tissoires 0x91, 0x00, /* Output (Data,Arr,Abs) */
517925f0f3eSBenjamin Tissoires 0xc0, /* End Collection */
518925f0f3eSBenjamin Tissoires };
519925f0f3eSBenjamin Tissoires
520534a7b8eSNestor Lopez Casado /* Maximum size of all defined hid reports in bytes (including report id) */
521534a7b8eSNestor Lopez Casado #define MAX_REPORT_SIZE 8
522534a7b8eSNestor Lopez Casado
5232a039bf5SHenrik Rydberg /* Make sure all descriptors are present here */
5242a039bf5SHenrik Rydberg #define MAX_RDESC_SIZE \
5252a039bf5SHenrik Rydberg (sizeof(kbd_descriptor) + \
526f2113c30SHans de Goede sizeof(mse_bluetooth_descriptor) + \
527434f7709SHans de Goede sizeof(mse5_bluetooth_descriptor) + \
5282a039bf5SHenrik Rydberg sizeof(consumer_descriptor) + \
5292a039bf5SHenrik Rydberg sizeof(syscontrol_descriptor) + \
530925f0f3eSBenjamin Tissoires sizeof(media_descriptor) + \
531925f0f3eSBenjamin Tissoires sizeof(hidpp_descriptor))
5322a039bf5SHenrik Rydberg
533534a7b8eSNestor Lopez Casado /* Number of possible hid report types that can be created by this driver.
534534a7b8eSNestor Lopez Casado *
535534a7b8eSNestor Lopez Casado * Right now, RF report types have the same report types (or report id's)
536534a7b8eSNestor Lopez Casado * than the hid report created from those RF reports. In the future
537534a7b8eSNestor Lopez Casado * this doesnt have to be true.
538534a7b8eSNestor Lopez Casado *
539534a7b8eSNestor Lopez Casado * For instance, RF report type 0x01 which has a size of 8 bytes, corresponds
540534a7b8eSNestor Lopez Casado * to hid report id 0x01, this is standard keyboard. Same thing applies to mice
541534a7b8eSNestor Lopez Casado * reports and consumer control, etc. If a new RF report is created, it doesn't
542534a7b8eSNestor Lopez Casado * has to have the same report id as its corresponding hid report, so an
543534a7b8eSNestor Lopez Casado * translation may have to take place for future report types.
544534a7b8eSNestor Lopez Casado */
545534a7b8eSNestor Lopez Casado #define NUMBER_OF_HID_REPORTS 32
546534a7b8eSNestor Lopez Casado static const u8 hid_reportid_size_map[NUMBER_OF_HID_REPORTS] = {
547534a7b8eSNestor Lopez Casado [1] = 8, /* Standard keyboard */
548534a7b8eSNestor Lopez Casado [2] = 8, /* Standard mouse */
549534a7b8eSNestor Lopez Casado [3] = 5, /* Consumer control */
550534a7b8eSNestor Lopez Casado [4] = 2, /* System control */
551534a7b8eSNestor Lopez Casado [8] = 2, /* Media Center */
552534a7b8eSNestor Lopez Casado };
553534a7b8eSNestor Lopez Casado
554534a7b8eSNestor Lopez Casado
555534a7b8eSNestor Lopez Casado #define LOGITECH_DJ_INTERFACE_NUMBER 0x02
556534a7b8eSNestor Lopez Casado
557662eee8dSThomas Weißschuh static const struct hid_ll_driver logi_dj_ll_driver;
558534a7b8eSNestor Lopez Casado
559c63e0e37SNestor Lopez Casado static int logi_dj_recv_query_paired_devices(struct dj_receiver_dev *djrcv_dev);
560a1d97ccbSHans de Goede static void delayedwork_callback(struct work_struct *work);
561a1d97ccbSHans de Goede
562a1d97ccbSHans de Goede static LIST_HEAD(dj_hdev_list);
563a1d97ccbSHans de Goede static DEFINE_MUTEX(dj_hdev_list_lock);
564a1d97ccbSHans de Goede
recvr_type_is_bluetooth(enum recvr_type type)565434f7709SHans de Goede static bool recvr_type_is_bluetooth(enum recvr_type type)
566434f7709SHans de Goede {
567434f7709SHans de Goede return type == recvr_type_bluetooth || type == recvr_type_dinovo;
568434f7709SHans de Goede }
569434f7709SHans de Goede
570a1d97ccbSHans de Goede /*
571a1d97ccbSHans de Goede * dj/HID++ receivers are really a single logical entity, but for BIOS/Windows
572a1d97ccbSHans de Goede * compatibility they have multiple USB interfaces. On HID++ receivers we need
573a1d97ccbSHans de Goede * to listen for input reports on both interfaces. The functions below are used
574a1d97ccbSHans de Goede * to create a single struct dj_receiver_dev for all interfaces belonging to
575a1d97ccbSHans de Goede * a single USB-device / receiver.
576a1d97ccbSHans de Goede */
dj_find_receiver_dev(struct hid_device * hdev,enum recvr_type type)577f2113c30SHans de Goede static struct dj_receiver_dev *dj_find_receiver_dev(struct hid_device *hdev,
578f2113c30SHans de Goede enum recvr_type type)
579a1d97ccbSHans de Goede {
580a1d97ccbSHans de Goede struct dj_receiver_dev *djrcv_dev;
581f2113c30SHans de Goede char sep;
582f2113c30SHans de Goede
583f2113c30SHans de Goede /*
584f2113c30SHans de Goede * The bluetooth receiver contains a built-in hub and has separate
585f2113c30SHans de Goede * USB-devices for the keyboard and mouse interfaces.
586f2113c30SHans de Goede */
587434f7709SHans de Goede sep = recvr_type_is_bluetooth(type) ? '.' : '/';
588a1d97ccbSHans de Goede
589a1d97ccbSHans de Goede /* Try to find an already-probed interface from the same device */
590a1d97ccbSHans de Goede list_for_each_entry(djrcv_dev, &dj_hdev_list, list) {
591a1d97ccbSHans de Goede if (djrcv_dev->mouse &&
592f2113c30SHans de Goede hid_compare_device_paths(hdev, djrcv_dev->mouse, sep)) {
593a1d97ccbSHans de Goede kref_get(&djrcv_dev->kref);
594a1d97ccbSHans de Goede return djrcv_dev;
595a1d97ccbSHans de Goede }
596a1d97ccbSHans de Goede if (djrcv_dev->keyboard &&
597f2113c30SHans de Goede hid_compare_device_paths(hdev, djrcv_dev->keyboard, sep)) {
598a1d97ccbSHans de Goede kref_get(&djrcv_dev->kref);
599a1d97ccbSHans de Goede return djrcv_dev;
600a1d97ccbSHans de Goede }
601a1d97ccbSHans de Goede if (djrcv_dev->hidpp &&
602f2113c30SHans de Goede hid_compare_device_paths(hdev, djrcv_dev->hidpp, sep)) {
603a1d97ccbSHans de Goede kref_get(&djrcv_dev->kref);
604a1d97ccbSHans de Goede return djrcv_dev;
605a1d97ccbSHans de Goede }
606a1d97ccbSHans de Goede }
607a1d97ccbSHans de Goede
608a1d97ccbSHans de Goede return NULL;
609a1d97ccbSHans de Goede }
610a1d97ccbSHans de Goede
dj_release_receiver_dev(struct kref * kref)611a1d97ccbSHans de Goede static void dj_release_receiver_dev(struct kref *kref)
612a1d97ccbSHans de Goede {
613a1d97ccbSHans de Goede struct dj_receiver_dev *djrcv_dev = container_of(kref, struct dj_receiver_dev, kref);
614a1d97ccbSHans de Goede
615a1d97ccbSHans de Goede list_del(&djrcv_dev->list);
616a1d97ccbSHans de Goede kfifo_free(&djrcv_dev->notif_fifo);
617a1d97ccbSHans de Goede kfree(djrcv_dev);
618a1d97ccbSHans de Goede }
619a1d97ccbSHans de Goede
dj_put_receiver_dev(struct hid_device * hdev)620a1d97ccbSHans de Goede static void dj_put_receiver_dev(struct hid_device *hdev)
621a1d97ccbSHans de Goede {
622a1d97ccbSHans de Goede struct dj_receiver_dev *djrcv_dev = hid_get_drvdata(hdev);
623a1d97ccbSHans de Goede
624a1d97ccbSHans de Goede mutex_lock(&dj_hdev_list_lock);
625a1d97ccbSHans de Goede
626a1d97ccbSHans de Goede if (djrcv_dev->mouse == hdev)
627a1d97ccbSHans de Goede djrcv_dev->mouse = NULL;
628a1d97ccbSHans de Goede if (djrcv_dev->keyboard == hdev)
629a1d97ccbSHans de Goede djrcv_dev->keyboard = NULL;
630a1d97ccbSHans de Goede if (djrcv_dev->hidpp == hdev)
631a1d97ccbSHans de Goede djrcv_dev->hidpp = NULL;
632a1d97ccbSHans de Goede
633a1d97ccbSHans de Goede kref_put(&djrcv_dev->kref, dj_release_receiver_dev);
634a1d97ccbSHans de Goede
635a1d97ccbSHans de Goede mutex_unlock(&dj_hdev_list_lock);
636a1d97ccbSHans de Goede }
637a1d97ccbSHans de Goede
dj_get_receiver_dev(struct hid_device * hdev,enum recvr_type type,unsigned int application,bool is_hidpp)638a1d97ccbSHans de Goede static struct dj_receiver_dev *dj_get_receiver_dev(struct hid_device *hdev,
63974808f91SHans de Goede enum recvr_type type,
640a1d97ccbSHans de Goede unsigned int application,
641a1d97ccbSHans de Goede bool is_hidpp)
642a1d97ccbSHans de Goede {
643a1d97ccbSHans de Goede struct dj_receiver_dev *djrcv_dev;
644a1d97ccbSHans de Goede
645a1d97ccbSHans de Goede mutex_lock(&dj_hdev_list_lock);
646a1d97ccbSHans de Goede
647f2113c30SHans de Goede djrcv_dev = dj_find_receiver_dev(hdev, type);
648a1d97ccbSHans de Goede if (!djrcv_dev) {
649a1d97ccbSHans de Goede djrcv_dev = kzalloc(sizeof(*djrcv_dev), GFP_KERNEL);
650a1d97ccbSHans de Goede if (!djrcv_dev)
651a1d97ccbSHans de Goede goto out;
652a1d97ccbSHans de Goede
653a1d97ccbSHans de Goede INIT_WORK(&djrcv_dev->work, delayedwork_callback);
654a1d97ccbSHans de Goede spin_lock_init(&djrcv_dev->lock);
655a1d97ccbSHans de Goede if (kfifo_alloc(&djrcv_dev->notif_fifo,
656a1d97ccbSHans de Goede DJ_MAX_NUMBER_NOTIFS * sizeof(struct dj_workitem),
657a1d97ccbSHans de Goede GFP_KERNEL)) {
658a1d97ccbSHans de Goede kfree(djrcv_dev);
659a1d97ccbSHans de Goede djrcv_dev = NULL;
660a1d97ccbSHans de Goede goto out;
661a1d97ccbSHans de Goede }
662a1d97ccbSHans de Goede kref_init(&djrcv_dev->kref);
663a1d97ccbSHans de Goede list_add_tail(&djrcv_dev->list, &dj_hdev_list);
664b6aeeddeSHans de Goede djrcv_dev->last_query = jiffies;
66574808f91SHans de Goede djrcv_dev->type = type;
666a1d97ccbSHans de Goede }
667a1d97ccbSHans de Goede
668a1d97ccbSHans de Goede if (application == HID_GD_KEYBOARD)
669a1d97ccbSHans de Goede djrcv_dev->keyboard = hdev;
670a1d97ccbSHans de Goede if (application == HID_GD_MOUSE)
671a1d97ccbSHans de Goede djrcv_dev->mouse = hdev;
672a1d97ccbSHans de Goede if (is_hidpp)
673a1d97ccbSHans de Goede djrcv_dev->hidpp = hdev;
674a1d97ccbSHans de Goede
675a1d97ccbSHans de Goede hid_set_drvdata(hdev, djrcv_dev);
676a1d97ccbSHans de Goede out:
677a1d97ccbSHans de Goede mutex_unlock(&dj_hdev_list_lock);
678a1d97ccbSHans de Goede return djrcv_dev;
679a1d97ccbSHans de Goede }
680534a7b8eSNestor Lopez Casado
logi_dj_recv_destroy_djhid_device(struct dj_receiver_dev * djrcv_dev,struct dj_workitem * workitem)681534a7b8eSNestor Lopez Casado static void logi_dj_recv_destroy_djhid_device(struct dj_receiver_dev *djrcv_dev,
6824fcad95aSBenjamin Tissoires struct dj_workitem *workitem)
683534a7b8eSNestor Lopez Casado {
684534a7b8eSNestor Lopez Casado /* Called in delayed work context */
685534a7b8eSNestor Lopez Casado struct dj_device *dj_dev;
686534a7b8eSNestor Lopez Casado unsigned long flags;
687534a7b8eSNestor Lopez Casado
688534a7b8eSNestor Lopez Casado spin_lock_irqsave(&djrcv_dev->lock, flags);
6894fcad95aSBenjamin Tissoires dj_dev = djrcv_dev->paired_dj_devices[workitem->device_index];
6904fcad95aSBenjamin Tissoires djrcv_dev->paired_dj_devices[workitem->device_index] = NULL;
691534a7b8eSNestor Lopez Casado spin_unlock_irqrestore(&djrcv_dev->lock, flags);
692534a7b8eSNestor Lopez Casado
693534a7b8eSNestor Lopez Casado if (dj_dev != NULL) {
694534a7b8eSNestor Lopez Casado hid_destroy_device(dj_dev->hdev);
695534a7b8eSNestor Lopez Casado kfree(dj_dev);
696534a7b8eSNestor Lopez Casado } else {
6970ee75544SHans de Goede hid_err(djrcv_dev->hidpp, "%s: can't destroy a NULL device\n",
698534a7b8eSNestor Lopez Casado __func__);
699534a7b8eSNestor Lopez Casado }
700534a7b8eSNestor Lopez Casado }
701534a7b8eSNestor Lopez Casado
logi_dj_recv_add_djhid_device(struct dj_receiver_dev * djrcv_dev,struct dj_workitem * workitem)702534a7b8eSNestor Lopez Casado static void logi_dj_recv_add_djhid_device(struct dj_receiver_dev *djrcv_dev,
7034fcad95aSBenjamin Tissoires struct dj_workitem *workitem)
704534a7b8eSNestor Lopez Casado {
705534a7b8eSNestor Lopez Casado /* Called in delayed work context */
7060ee75544SHans de Goede struct hid_device *djrcv_hdev = djrcv_dev->hidpp;
707534a7b8eSNestor Lopez Casado struct hid_device *dj_hiddev;
708534a7b8eSNestor Lopez Casado struct dj_device *dj_dev;
7094fcad95aSBenjamin Tissoires u8 device_index = workitem->device_index;
710f41d766cSHans de Goede unsigned long flags;
711534a7b8eSNestor Lopez Casado
712534a7b8eSNestor Lopez Casado /* Device index goes from 1 to 6, we need 3 bytes to store the
713534a7b8eSNestor Lopez Casado * semicolon, the index, and a null terminator
714534a7b8eSNestor Lopez Casado */
715534a7b8eSNestor Lopez Casado unsigned char tmpstr[3];
716534a7b8eSNestor Lopez Casado
717f41d766cSHans de Goede /* We are the only one ever adding a device, no need to lock */
7184fcad95aSBenjamin Tissoires if (djrcv_dev->paired_dj_devices[device_index]) {
719c63e0e37SNestor Lopez Casado /* The device is already known. No need to reallocate it. */
720c63e0e37SNestor Lopez Casado dbg_hid("%s: device is already known\n", __func__);
721c63e0e37SNestor Lopez Casado return;
722c63e0e37SNestor Lopez Casado }
723c63e0e37SNestor Lopez Casado
724534a7b8eSNestor Lopez Casado dj_hiddev = hid_allocate_device();
725534a7b8eSNestor Lopez Casado if (IS_ERR(dj_hiddev)) {
726aca22a35SHans de Goede hid_err(djrcv_hdev, "%s: hid_allocate_dev failed\n", __func__);
727534a7b8eSNestor Lopez Casado return;
728534a7b8eSNestor Lopez Casado }
729534a7b8eSNestor Lopez Casado
730534a7b8eSNestor Lopez Casado dj_hiddev->ll_driver = &logi_dj_ll_driver;
731534a7b8eSNestor Lopez Casado
732534a7b8eSNestor Lopez Casado dj_hiddev->dev.parent = &djrcv_hdev->dev;
733534a7b8eSNestor Lopez Casado dj_hiddev->bus = BUS_USB;
73482c0beb8SBenjamin Tissoires dj_hiddev->vendor = djrcv_hdev->vendor;
7354fcad95aSBenjamin Tissoires dj_hiddev->product = (workitem->quad_id_msb << 8) |
7364fcad95aSBenjamin Tissoires workitem->quad_id_lsb;
737de76b1d3SHans de Goede if (workitem->device_type) {
738de76b1d3SHans de Goede const char *type_str = "Device";
739de76b1d3SHans de Goede
740de76b1d3SHans de Goede switch (workitem->device_type) {
741de76b1d3SHans de Goede case 0x01: type_str = "Keyboard"; break;
742de76b1d3SHans de Goede case 0x02: type_str = "Mouse"; break;
743de76b1d3SHans de Goede case 0x03: type_str = "Numpad"; break;
744de76b1d3SHans de Goede case 0x04: type_str = "Presenter"; break;
745de76b1d3SHans de Goede case 0x07: type_str = "Remote Control"; break;
746de76b1d3SHans de Goede case 0x08: type_str = "Trackball"; break;
747de76b1d3SHans de Goede case 0x09: type_str = "Touchpad"; break;
748de76b1d3SHans de Goede }
749de76b1d3SHans de Goede snprintf(dj_hiddev->name, sizeof(dj_hiddev->name),
750de76b1d3SHans de Goede "Logitech Wireless %s PID:%04x",
751de76b1d3SHans de Goede type_str, dj_hiddev->product);
752de76b1d3SHans de Goede } else {
753534a7b8eSNestor Lopez Casado snprintf(dj_hiddev->name, sizeof(dj_hiddev->name),
754c08ce255SFilipe Laíns "Logitech Wireless Device PID:%04x",
755d610274bSBenjamin Tissoires dj_hiddev->product);
756de76b1d3SHans de Goede }
757d610274bSBenjamin Tissoires
758c9121cf6SHans de Goede if (djrcv_dev->type == recvr_type_27mhz)
759c9121cf6SHans de Goede dj_hiddev->group = HID_GROUP_LOGITECH_27MHZ_DEVICE;
760c9121cf6SHans de Goede else
761d610274bSBenjamin Tissoires dj_hiddev->group = HID_GROUP_LOGITECH_DJ_DEVICE;
762534a7b8eSNestor Lopez Casado
76382c0beb8SBenjamin Tissoires memcpy(dj_hiddev->phys, djrcv_hdev->phys, sizeof(djrcv_hdev->phys));
7644fcad95aSBenjamin Tissoires snprintf(tmpstr, sizeof(tmpstr), ":%d", device_index);
765534a7b8eSNestor Lopez Casado strlcat(dj_hiddev->phys, tmpstr, sizeof(dj_hiddev->phys));
766534a7b8eSNestor Lopez Casado
767534a7b8eSNestor Lopez Casado dj_dev = kzalloc(sizeof(struct dj_device), GFP_KERNEL);
768534a7b8eSNestor Lopez Casado
769534a7b8eSNestor Lopez Casado if (!dj_dev) {
770aca22a35SHans de Goede hid_err(djrcv_hdev, "%s: failed allocating dj_dev\n", __func__);
771534a7b8eSNestor Lopez Casado goto dj_device_allocate_fail;
772534a7b8eSNestor Lopez Casado }
773534a7b8eSNestor Lopez Casado
7744fcad95aSBenjamin Tissoires dj_dev->reports_supported = workitem->reports_supported;
775534a7b8eSNestor Lopez Casado dj_dev->hdev = dj_hiddev;
776534a7b8eSNestor Lopez Casado dj_dev->dj_receiver_dev = djrcv_dev;
7774fcad95aSBenjamin Tissoires dj_dev->device_index = device_index;
778534a7b8eSNestor Lopez Casado dj_hiddev->driver_data = dj_dev;
779534a7b8eSNestor Lopez Casado
780f41d766cSHans de Goede spin_lock_irqsave(&djrcv_dev->lock, flags);
7814fcad95aSBenjamin Tissoires djrcv_dev->paired_dj_devices[device_index] = dj_dev;
782f41d766cSHans de Goede spin_unlock_irqrestore(&djrcv_dev->lock, flags);
783534a7b8eSNestor Lopez Casado
784534a7b8eSNestor Lopez Casado if (hid_add_device(dj_hiddev)) {
785aca22a35SHans de Goede hid_err(djrcv_hdev, "%s: failed adding dj_device\n", __func__);
786534a7b8eSNestor Lopez Casado goto hid_add_device_fail;
787534a7b8eSNestor Lopez Casado }
788534a7b8eSNestor Lopez Casado
789534a7b8eSNestor Lopez Casado return;
790534a7b8eSNestor Lopez Casado
791534a7b8eSNestor Lopez Casado hid_add_device_fail:
792f41d766cSHans de Goede spin_lock_irqsave(&djrcv_dev->lock, flags);
7934fcad95aSBenjamin Tissoires djrcv_dev->paired_dj_devices[device_index] = NULL;
794f41d766cSHans de Goede spin_unlock_irqrestore(&djrcv_dev->lock, flags);
795534a7b8eSNestor Lopez Casado kfree(dj_dev);
796534a7b8eSNestor Lopez Casado dj_device_allocate_fail:
797534a7b8eSNestor Lopez Casado hid_destroy_device(dj_hiddev);
798534a7b8eSNestor Lopez Casado }
799534a7b8eSNestor Lopez Casado
delayedwork_callback(struct work_struct * work)800534a7b8eSNestor Lopez Casado static void delayedwork_callback(struct work_struct *work)
801534a7b8eSNestor Lopez Casado {
802534a7b8eSNestor Lopez Casado struct dj_receiver_dev *djrcv_dev =
803534a7b8eSNestor Lopez Casado container_of(work, struct dj_receiver_dev, work);
804534a7b8eSNestor Lopez Casado
8054fcad95aSBenjamin Tissoires struct dj_workitem workitem;
806534a7b8eSNestor Lopez Casado unsigned long flags;
807534a7b8eSNestor Lopez Casado int count;
808c63e0e37SNestor Lopez Casado int retval;
809534a7b8eSNestor Lopez Casado
810534a7b8eSNestor Lopez Casado dbg_hid("%s\n", __func__);
811534a7b8eSNestor Lopez Casado
812534a7b8eSNestor Lopez Casado spin_lock_irqsave(&djrcv_dev->lock, flags);
813534a7b8eSNestor Lopez Casado
814a1d97ccbSHans de Goede /*
815a1d97ccbSHans de Goede * Since we attach to multiple interfaces, we may get scheduled before
816a1d97ccbSHans de Goede * we are bound to the HID++ interface, catch this.
817a1d97ccbSHans de Goede */
818a1d97ccbSHans de Goede if (!djrcv_dev->ready) {
819a1d97ccbSHans de Goede pr_warn("%s: delayedwork queued before hidpp interface was enumerated\n",
820a1d97ccbSHans de Goede __func__);
821a1d97ccbSHans de Goede spin_unlock_irqrestore(&djrcv_dev->lock, flags);
822a1d97ccbSHans de Goede return;
823a1d97ccbSHans de Goede }
824a1d97ccbSHans de Goede
8254fcad95aSBenjamin Tissoires count = kfifo_out(&djrcv_dev->notif_fifo, &workitem, sizeof(workitem));
826534a7b8eSNestor Lopez Casado
8274fcad95aSBenjamin Tissoires if (count != sizeof(workitem)) {
828534a7b8eSNestor Lopez Casado spin_unlock_irqrestore(&djrcv_dev->lock, flags);
829534a7b8eSNestor Lopez Casado return;
830534a7b8eSNestor Lopez Casado }
831534a7b8eSNestor Lopez Casado
832e316aa6eSHans de Goede if (!kfifo_is_empty(&djrcv_dev->notif_fifo))
833e316aa6eSHans de Goede schedule_work(&djrcv_dev->work);
834534a7b8eSNestor Lopez Casado
835534a7b8eSNestor Lopez Casado spin_unlock_irqrestore(&djrcv_dev->lock, flags);
836534a7b8eSNestor Lopez Casado
8374fcad95aSBenjamin Tissoires switch (workitem.type) {
8384fcad95aSBenjamin Tissoires case WORKITEM_TYPE_PAIRED:
8394fcad95aSBenjamin Tissoires logi_dj_recv_add_djhid_device(djrcv_dev, &workitem);
840534a7b8eSNestor Lopez Casado break;
8414fcad95aSBenjamin Tissoires case WORKITEM_TYPE_UNPAIRED:
8424fcad95aSBenjamin Tissoires logi_dj_recv_destroy_djhid_device(djrcv_dev, &workitem);
8434fcad95aSBenjamin Tissoires break;
8444fcad95aSBenjamin Tissoires case WORKITEM_TYPE_UNKNOWN:
8454fcad95aSBenjamin Tissoires retval = logi_dj_recv_query_paired_devices(djrcv_dev);
8464fcad95aSBenjamin Tissoires if (retval) {
8470ee75544SHans de Goede hid_err(djrcv_dev->hidpp, "%s: logi_dj_recv_query_paired_devices error: %d\n",
8484fcad95aSBenjamin Tissoires __func__, retval);
8494fcad95aSBenjamin Tissoires }
8504fcad95aSBenjamin Tissoires break;
8514fcad95aSBenjamin Tissoires case WORKITEM_TYPE_EMPTY:
8524fcad95aSBenjamin Tissoires dbg_hid("%s: device list is empty\n", __func__);
8534fcad95aSBenjamin Tissoires break;
8544fcad95aSBenjamin Tissoires }
8554fcad95aSBenjamin Tissoires }
8564fcad95aSBenjamin Tissoires
857b6aeeddeSHans de Goede /*
858b6aeeddeSHans de Goede * Sometimes we receive reports for which we do not have a paired dj_device
859b6aeeddeSHans de Goede * associated with the device_index or report-type to forward the report to.
860b6aeeddeSHans de Goede * This means that the original "device paired" notification corresponding
861b6aeeddeSHans de Goede * to the dj_device never arrived to this driver. Possible reasons for this are:
862b6aeeddeSHans de Goede * 1) hid-core discards all packets coming from a device during probe().
863b6aeeddeSHans de Goede * 2) if the receiver is plugged into a KVM switch then the pairing reports
864b6aeeddeSHans de Goede * are only forwarded to it if the focus is on this PC.
865b6aeeddeSHans de Goede * This function deals with this by re-asking the receiver for the list of
866b6aeeddeSHans de Goede * connected devices in the delayed work callback.
867b6aeeddeSHans de Goede * This function MUST be called with djrcv->lock held.
868b6aeeddeSHans de Goede */
logi_dj_recv_queue_unknown_work(struct dj_receiver_dev * djrcv_dev)869b6aeeddeSHans de Goede static void logi_dj_recv_queue_unknown_work(struct dj_receiver_dev *djrcv_dev)
870b6aeeddeSHans de Goede {
871b6aeeddeSHans de Goede struct dj_workitem workitem = { .type = WORKITEM_TYPE_UNKNOWN };
872b6aeeddeSHans de Goede
87353ed4cf6SChris Packham /* Rate limit queries done because of unhandled reports to 2/sec */
874b6aeeddeSHans de Goede if (time_before(jiffies, djrcv_dev->last_query + HZ / 2))
875b6aeeddeSHans de Goede return;
876b6aeeddeSHans de Goede
877b6aeeddeSHans de Goede kfifo_in(&djrcv_dev->notif_fifo, &workitem, sizeof(workitem));
878b6aeeddeSHans de Goede schedule_work(&djrcv_dev->work);
879b6aeeddeSHans de Goede }
880b6aeeddeSHans de Goede
logi_dj_recv_queue_notification(struct dj_receiver_dev * djrcv_dev,struct dj_report * dj_report)8814fcad95aSBenjamin Tissoires static void logi_dj_recv_queue_notification(struct dj_receiver_dev *djrcv_dev,
8824fcad95aSBenjamin Tissoires struct dj_report *dj_report)
8834fcad95aSBenjamin Tissoires {
8844fcad95aSBenjamin Tissoires /* We are called from atomic context (tasklet && djrcv->lock held) */
8854fcad95aSBenjamin Tissoires struct dj_workitem workitem = {
8864fcad95aSBenjamin Tissoires .device_index = dj_report->device_index,
8874fcad95aSBenjamin Tissoires };
8884fcad95aSBenjamin Tissoires
8894fcad95aSBenjamin Tissoires switch (dj_report->report_type) {
8904fcad95aSBenjamin Tissoires case REPORT_TYPE_NOTIF_DEVICE_PAIRED:
8914fcad95aSBenjamin Tissoires workitem.type = WORKITEM_TYPE_PAIRED;
8924fcad95aSBenjamin Tissoires if (dj_report->report_params[DEVICE_PAIRED_PARAM_SPFUNCTION] &
8934fcad95aSBenjamin Tissoires SPFUNCTION_DEVICE_LIST_EMPTY) {
8944fcad95aSBenjamin Tissoires workitem.type = WORKITEM_TYPE_EMPTY;
8954fcad95aSBenjamin Tissoires break;
8964fcad95aSBenjamin Tissoires }
897df561f66SGustavo A. R. Silva fallthrough;
898534a7b8eSNestor Lopez Casado case REPORT_TYPE_NOTIF_DEVICE_UNPAIRED:
8994fcad95aSBenjamin Tissoires workitem.quad_id_msb =
9004fcad95aSBenjamin Tissoires dj_report->report_params[DEVICE_PAIRED_PARAM_EQUAD_ID_MSB];
9014fcad95aSBenjamin Tissoires workitem.quad_id_lsb =
9024fcad95aSBenjamin Tissoires dj_report->report_params[DEVICE_PAIRED_PARAM_EQUAD_ID_LSB];
9034fcad95aSBenjamin Tissoires workitem.reports_supported = get_unaligned_le32(
9044fcad95aSBenjamin Tissoires dj_report->report_params +
9054fcad95aSBenjamin Tissoires DEVICE_PAIRED_RF_REPORT_TYPE);
9066d3c3f03SHans de Goede workitem.reports_supported |= HIDPP;
9074fcad95aSBenjamin Tissoires if (dj_report->report_type == REPORT_TYPE_NOTIF_DEVICE_UNPAIRED)
9084fcad95aSBenjamin Tissoires workitem.type = WORKITEM_TYPE_UNPAIRED;
909534a7b8eSNestor Lopez Casado break;
910534a7b8eSNestor Lopez Casado default:
911b6aeeddeSHans de Goede logi_dj_recv_queue_unknown_work(djrcv_dev);
912b6aeeddeSHans de Goede return;
913534a7b8eSNestor Lopez Casado }
914534a7b8eSNestor Lopez Casado
9154fcad95aSBenjamin Tissoires kfifo_in(&djrcv_dev->notif_fifo, &workitem, sizeof(workitem));
916e316aa6eSHans de Goede schedule_work(&djrcv_dev->work);
917534a7b8eSNestor Lopez Casado }
918534a7b8eSNestor Lopez Casado
919ee5e5841SHans de Goede /*
920ee5e5841SHans de Goede * Some quad/bluetooth keyboards have a builtin touchpad in this case we see
921ee5e5841SHans de Goede * only 1 paired device with a device_type of REPORT_TYPE_KEYBOARD. For the
922ee5e5841SHans de Goede * touchpad to work we must also forward mouse input reports to the dj_hiddev
923ee5e5841SHans de Goede * created for the keyboard (instead of forwarding them to a second paired
924ee5e5841SHans de Goede * device with a device_type of REPORT_TYPE_MOUSE as we normally would).
925434f7709SHans de Goede *
926434f7709SHans de Goede * On Dinovo receivers the keyboard's touchpad and an optional paired actual
927434f7709SHans de Goede * mouse send separate input reports, INPUT(2) aka STD_MOUSE for the mouse
928434f7709SHans de Goede * and INPUT(5) aka KBD_MOUSE for the keyboard's touchpad.
929434f7709SHans de Goede *
930434f7709SHans de Goede * On MX5x00 receivers (which can also be paired with a Dinovo keyboard)
931434f7709SHans de Goede * INPUT(2) is used for both an optional paired actual mouse and for the
932434f7709SHans de Goede * keyboard's touchpad.
933ee5e5841SHans de Goede */
934ee5e5841SHans de Goede static const u16 kbd_builtin_touchpad_ids[] = {
935ee5e5841SHans de Goede 0xb309, /* Dinovo Edge */
936b4c00e79SHans de Goede 0xb30c, /* Dinovo Mini */
937ee5e5841SHans de Goede };
938ee5e5841SHans de Goede
logi_hidpp_dev_conn_notif_equad(struct hid_device * hdev,struct hidpp_event * hidpp_report,struct dj_workitem * workitem)9393ed224e2SHans de Goede static void logi_hidpp_dev_conn_notif_equad(struct hid_device *hdev,
9403ed224e2SHans de Goede struct hidpp_event *hidpp_report,
94174808f91SHans de Goede struct dj_workitem *workitem)
94274808f91SHans de Goede {
9433ed224e2SHans de Goede struct dj_receiver_dev *djrcv_dev = hid_get_drvdata(hdev);
944ee5e5841SHans de Goede int i, id;
9453ed224e2SHans de Goede
94674808f91SHans de Goede workitem->type = WORKITEM_TYPE_PAIRED;
947de76b1d3SHans de Goede workitem->device_type = hidpp_report->params[HIDPP_PARAM_DEVICE_INFO] &
948de76b1d3SHans de Goede HIDPP_DEVICE_TYPE_MASK;
94974808f91SHans de Goede workitem->quad_id_msb = hidpp_report->params[HIDPP_PARAM_EQUAD_MSB];
95074808f91SHans de Goede workitem->quad_id_lsb = hidpp_report->params[HIDPP_PARAM_EQUAD_LSB];
951de76b1d3SHans de Goede switch (workitem->device_type) {
95274808f91SHans de Goede case REPORT_TYPE_KEYBOARD:
95374808f91SHans de Goede workitem->reports_supported |= STD_KEYBOARD | MULTIMEDIA |
9546d3c3f03SHans de Goede POWER_KEYS | MEDIA_CENTER |
9556d3c3f03SHans de Goede HIDPP;
956ee5e5841SHans de Goede id = (workitem->quad_id_msb << 8) | workitem->quad_id_lsb;
957ee5e5841SHans de Goede for (i = 0; i < ARRAY_SIZE(kbd_builtin_touchpad_ids); i++) {
958ee5e5841SHans de Goede if (id == kbd_builtin_touchpad_ids[i]) {
959434f7709SHans de Goede if (djrcv_dev->type == recvr_type_dinovo)
960434f7709SHans de Goede workitem->reports_supported |= KBD_MOUSE;
961434f7709SHans de Goede else
962ee5e5841SHans de Goede workitem->reports_supported |= STD_MOUSE;
963ee5e5841SHans de Goede break;
964ee5e5841SHans de Goede }
965ee5e5841SHans de Goede }
96674808f91SHans de Goede break;
96774808f91SHans de Goede case REPORT_TYPE_MOUSE:
9684fb8d6e8SYaraslau Furman workitem->reports_supported |= STD_MOUSE | HIDPP | MULTIMEDIA;
96974808f91SHans de Goede break;
97074808f91SHans de Goede }
97174808f91SHans de Goede }
97274808f91SHans de Goede
logi_hidpp_dev_conn_notif_27mhz(struct hid_device * hdev,struct hidpp_event * hidpp_report,struct dj_workitem * workitem)973c9121cf6SHans de Goede static void logi_hidpp_dev_conn_notif_27mhz(struct hid_device *hdev,
974c9121cf6SHans de Goede struct hidpp_event *hidpp_report,
975c9121cf6SHans de Goede struct dj_workitem *workitem)
976c9121cf6SHans de Goede {
977c9121cf6SHans de Goede workitem->type = WORKITEM_TYPE_PAIRED;
978c9121cf6SHans de Goede workitem->quad_id_lsb = hidpp_report->params[HIDPP_PARAM_27MHZ_DEVID];
979c9121cf6SHans de Goede switch (hidpp_report->device_index) {
980c9121cf6SHans de Goede case 1: /* Index 1 is always a mouse */
981c9121cf6SHans de Goede case 2: /* Index 2 is always a mouse */
982de76b1d3SHans de Goede workitem->device_type = HIDPP_DEVICE_TYPE_MOUSE;
9836d3c3f03SHans de Goede workitem->reports_supported |= STD_MOUSE | HIDPP;
984c9121cf6SHans de Goede break;
985c9121cf6SHans de Goede case 3: /* Index 3 is always the keyboard */
9867383354aSHans de Goede if (hidpp_report->params[HIDPP_PARAM_DEVICE_INFO] & HIDPP_27MHZ_SECURE_MASK) {
9877383354aSHans de Goede hid_info(hdev, "Keyboard connection is encrypted\n");
9887383354aSHans de Goede } else {
9897383354aSHans de Goede hid_warn(hdev, "Keyboard events are send over the air in plain-text / unencrypted\n");
9907383354aSHans de Goede hid_warn(hdev, "See: https://gitlab.freedesktop.org/jwrdegoede/logitech-27mhz-keyboard-encryption-setup/\n");
9917383354aSHans de Goede }
9927383354aSHans de Goede fallthrough;
993c9121cf6SHans de Goede case 4: /* Index 4 is used for an optional separate numpad */
994de76b1d3SHans de Goede workitem->device_type = HIDPP_DEVICE_TYPE_KEYBOARD;
995c9121cf6SHans de Goede workitem->reports_supported |= STD_KEYBOARD | MULTIMEDIA |
9966d3c3f03SHans de Goede POWER_KEYS | HIDPP;
997c9121cf6SHans de Goede break;
998c9121cf6SHans de Goede default:
999c9121cf6SHans de Goede hid_warn(hdev, "%s: unexpected device-index %d", __func__,
1000c9121cf6SHans de Goede hidpp_report->device_index);
1001c9121cf6SHans de Goede }
1002c9121cf6SHans de Goede }
1003c9121cf6SHans de Goede
logi_hidpp_recv_queue_notif(struct hid_device * hdev,struct hidpp_event * hidpp_report)100474808f91SHans de Goede static void logi_hidpp_recv_queue_notif(struct hid_device *hdev,
100574808f91SHans de Goede struct hidpp_event *hidpp_report)
100674808f91SHans de Goede {
100774808f91SHans de Goede /* We are called from atomic context (tasklet && djrcv->lock held) */
100874808f91SHans de Goede struct dj_receiver_dev *djrcv_dev = hid_get_drvdata(hdev);
100974808f91SHans de Goede const char *device_type = "UNKNOWN";
101074808f91SHans de Goede struct dj_workitem workitem = {
101174808f91SHans de Goede .type = WORKITEM_TYPE_EMPTY,
101274808f91SHans de Goede .device_index = hidpp_report->device_index,
101374808f91SHans de Goede };
101474808f91SHans de Goede
101574808f91SHans de Goede switch (hidpp_report->params[HIDPP_PARAM_PROTO_TYPE]) {
101674808f91SHans de Goede case 0x01:
101774808f91SHans de Goede device_type = "Bluetooth";
1018f2113c30SHans de Goede /* Bluetooth connect packet contents is the same as (e)QUAD */
10193ed224e2SHans de Goede logi_hidpp_dev_conn_notif_equad(hdev, hidpp_report, &workitem);
1020f2113c30SHans de Goede if (!(hidpp_report->params[HIDPP_PARAM_DEVICE_INFO] &
1021f2113c30SHans de Goede HIDPP_MANUFACTURER_MASK)) {
1022f2113c30SHans de Goede hid_info(hdev, "Non Logitech device connected on slot %d\n",
1023f2113c30SHans de Goede hidpp_report->device_index);
1024f2113c30SHans de Goede workitem.reports_supported &= ~HIDPP;
1025f2113c30SHans de Goede }
102674808f91SHans de Goede break;
102774808f91SHans de Goede case 0x02:
102874808f91SHans de Goede device_type = "27 Mhz";
1029c9121cf6SHans de Goede logi_hidpp_dev_conn_notif_27mhz(hdev, hidpp_report, &workitem);
103074808f91SHans de Goede break;
103174808f91SHans de Goede case 0x03:
103274808f91SHans de Goede device_type = "QUAD or eQUAD";
10333ed224e2SHans de Goede logi_hidpp_dev_conn_notif_equad(hdev, hidpp_report, &workitem);
103474808f91SHans de Goede break;
103574808f91SHans de Goede case 0x04:
103674808f91SHans de Goede device_type = "eQUAD step 4 DJ";
10373ed224e2SHans de Goede logi_hidpp_dev_conn_notif_equad(hdev, hidpp_report, &workitem);
103874808f91SHans de Goede break;
103974808f91SHans de Goede case 0x05:
104074808f91SHans de Goede device_type = "DFU Lite";
104174808f91SHans de Goede break;
104274808f91SHans de Goede case 0x06:
104374808f91SHans de Goede device_type = "eQUAD step 4 Lite";
10443ed224e2SHans de Goede logi_hidpp_dev_conn_notif_equad(hdev, hidpp_report, &workitem);
104574808f91SHans de Goede break;
104674808f91SHans de Goede case 0x07:
104774808f91SHans de Goede device_type = "eQUAD step 4 Gaming";
1048992ff2ccSBenjamin Tissoires logi_hidpp_dev_conn_notif_equad(hdev, hidpp_report, &workitem);
1049ef07c116SFilipe Laíns workitem.reports_supported |= STD_KEYBOARD;
105074808f91SHans de Goede break;
105174808f91SHans de Goede case 0x08:
105274808f91SHans de Goede device_type = "eQUAD step 4 for gamepads";
105374808f91SHans de Goede break;
105474808f91SHans de Goede case 0x0a:
105574808f91SHans de Goede device_type = "eQUAD nano Lite";
10563ed224e2SHans de Goede logi_hidpp_dev_conn_notif_equad(hdev, hidpp_report, &workitem);
105774808f91SHans de Goede break;
105874808f91SHans de Goede case 0x0c:
10595722f338SFilipe Laíns device_type = "eQUAD Lightspeed 1";
10605722f338SFilipe Laíns logi_hidpp_dev_conn_notif_equad(hdev, hidpp_report, &workitem);
10615722f338SFilipe Laíns workitem.reports_supported |= STD_KEYBOARD;
10625722f338SFilipe Laíns break;
10635722f338SFilipe Laíns case 0x0d:
1064fab3a956SFilipe Laíns device_type = "eQUAD Lightspeed 1.1";
1065fab3a956SFilipe Laíns logi_hidpp_dev_conn_notif_equad(hdev, hidpp_report, &workitem);
1066fab3a956SFilipe Laíns workitem.reports_supported |= STD_KEYBOARD;
1067fab3a956SFilipe Laíns break;
1068fab3a956SFilipe Laíns case 0x0f:
106925666e8cSLucas Zampieri case 0x11:
1070fab3a956SFilipe Laíns device_type = "eQUAD Lightspeed 1.2";
10713ed224e2SHans de Goede logi_hidpp_dev_conn_notif_equad(hdev, hidpp_report, &workitem);
1072f5fb57a7SBenjamin Tissoires workitem.reports_supported |= STD_KEYBOARD;
107374808f91SHans de Goede break;
107474808f91SHans de Goede }
107574808f91SHans de Goede
10768c9d734cSFilipe Laíns /* custom receiver device (eg. powerplay) */
10778c9d734cSFilipe Laíns if (hidpp_report->device_index == 7) {
10788c9d734cSFilipe Laíns workitem.reports_supported |= HIDPP;
10798c9d734cSFilipe Laíns }
10808c9d734cSFilipe Laíns
108174808f91SHans de Goede if (workitem.type == WORKITEM_TYPE_EMPTY) {
108274808f91SHans de Goede hid_warn(hdev,
108374808f91SHans de Goede "unusable device of type %s (0x%02x) connected on slot %d",
108474808f91SHans de Goede device_type,
108574808f91SHans de Goede hidpp_report->params[HIDPP_PARAM_PROTO_TYPE],
108674808f91SHans de Goede hidpp_report->device_index);
108774808f91SHans de Goede return;
108874808f91SHans de Goede }
108974808f91SHans de Goede
109074808f91SHans de Goede hid_info(hdev, "device of type %s (0x%02x) connected on slot %d",
109174808f91SHans de Goede device_type, hidpp_report->params[HIDPP_PARAM_PROTO_TYPE],
109274808f91SHans de Goede hidpp_report->device_index);
109374808f91SHans de Goede
109474808f91SHans de Goede kfifo_in(&djrcv_dev->notif_fifo, &workitem, sizeof(workitem));
1095e316aa6eSHans de Goede schedule_work(&djrcv_dev->work);
109674808f91SHans de Goede }
109774808f91SHans de Goede
logi_dj_recv_forward_null_report(struct dj_receiver_dev * djrcv_dev,struct dj_report * dj_report)1098534a7b8eSNestor Lopez Casado static void logi_dj_recv_forward_null_report(struct dj_receiver_dev *djrcv_dev,
1099534a7b8eSNestor Lopez Casado struct dj_report *dj_report)
1100534a7b8eSNestor Lopez Casado {
1101534a7b8eSNestor Lopez Casado /* We are called from atomic context (tasklet && djrcv->lock held) */
1102534a7b8eSNestor Lopez Casado unsigned int i;
1103534a7b8eSNestor Lopez Casado u8 reportbuffer[MAX_REPORT_SIZE];
1104534a7b8eSNestor Lopez Casado struct dj_device *djdev;
1105534a7b8eSNestor Lopez Casado
1106534a7b8eSNestor Lopez Casado djdev = djrcv_dev->paired_dj_devices[dj_report->device_index];
1107534a7b8eSNestor Lopez Casado
1108534a7b8eSNestor Lopez Casado memset(reportbuffer, 0, sizeof(reportbuffer));
1109534a7b8eSNestor Lopez Casado
1110534a7b8eSNestor Lopez Casado for (i = 0; i < NUMBER_OF_HID_REPORTS; i++) {
1111534a7b8eSNestor Lopez Casado if (djdev->reports_supported & (1 << i)) {
1112534a7b8eSNestor Lopez Casado reportbuffer[0] = i;
1113534a7b8eSNestor Lopez Casado if (hid_input_report(djdev->hdev,
1114534a7b8eSNestor Lopez Casado HID_INPUT_REPORT,
1115534a7b8eSNestor Lopez Casado reportbuffer,
1116534a7b8eSNestor Lopez Casado hid_reportid_size_map[i], 1)) {
1117534a7b8eSNestor Lopez Casado dbg_hid("hid_input_report error sending null "
1118534a7b8eSNestor Lopez Casado "report\n");
1119534a7b8eSNestor Lopez Casado }
1120534a7b8eSNestor Lopez Casado }
1121534a7b8eSNestor Lopez Casado }
1122534a7b8eSNestor Lopez Casado }
1123534a7b8eSNestor Lopez Casado
logi_dj_recv_forward_dj(struct dj_receiver_dev * djrcv_dev,struct dj_report * dj_report)112483898234SBenjamin Tissoires static void logi_dj_recv_forward_dj(struct dj_receiver_dev *djrcv_dev,
1125534a7b8eSNestor Lopez Casado struct dj_report *dj_report)
1126534a7b8eSNestor Lopez Casado {
1127534a7b8eSNestor Lopez Casado /* We are called from atomic context (tasklet && djrcv->lock held) */
1128534a7b8eSNestor Lopez Casado struct dj_device *dj_device;
1129534a7b8eSNestor Lopez Casado
1130534a7b8eSNestor Lopez Casado dj_device = djrcv_dev->paired_dj_devices[dj_report->device_index];
1131534a7b8eSNestor Lopez Casado
1132534a7b8eSNestor Lopez Casado if ((dj_report->report_type > ARRAY_SIZE(hid_reportid_size_map) - 1) ||
1133534a7b8eSNestor Lopez Casado (hid_reportid_size_map[dj_report->report_type] == 0)) {
1134534a7b8eSNestor Lopez Casado dbg_hid("invalid report type:%x\n", dj_report->report_type);
1135534a7b8eSNestor Lopez Casado return;
1136534a7b8eSNestor Lopez Casado }
1137534a7b8eSNestor Lopez Casado
1138534a7b8eSNestor Lopez Casado if (hid_input_report(dj_device->hdev,
1139534a7b8eSNestor Lopez Casado HID_INPUT_REPORT, &dj_report->report_type,
1140534a7b8eSNestor Lopez Casado hid_reportid_size_map[dj_report->report_type], 1)) {
1141534a7b8eSNestor Lopez Casado dbg_hid("hid_input_report error\n");
1142534a7b8eSNestor Lopez Casado }
1143534a7b8eSNestor Lopez Casado }
1144534a7b8eSNestor Lopez Casado
logi_dj_recv_forward_report(struct dj_device * dj_dev,u8 * data,int size)114583898234SBenjamin Tissoires static void logi_dj_recv_forward_report(struct dj_device *dj_dev, u8 *data,
1146925f0f3eSBenjamin Tissoires int size)
1147925f0f3eSBenjamin Tissoires {
1148925f0f3eSBenjamin Tissoires /* We are called from atomic context (tasklet && djrcv->lock held) */
1149925f0f3eSBenjamin Tissoires if (hid_input_report(dj_dev->hdev, HID_INPUT_REPORT, data, size, 1))
1150925f0f3eSBenjamin Tissoires dbg_hid("hid_input_report error\n");
1151925f0f3eSBenjamin Tissoires }
1152534a7b8eSNestor Lopez Casado
logi_dj_recv_forward_input_report(struct hid_device * hdev,u8 * data,int size)115374808f91SHans de Goede static void logi_dj_recv_forward_input_report(struct hid_device *hdev,
115474808f91SHans de Goede u8 *data, int size)
115574808f91SHans de Goede {
115674808f91SHans de Goede struct dj_receiver_dev *djrcv_dev = hid_get_drvdata(hdev);
115774808f91SHans de Goede struct dj_device *dj_dev;
115874808f91SHans de Goede unsigned long flags;
115974808f91SHans de Goede u8 report = data[0];
116074808f91SHans de Goede int i;
116174808f91SHans de Goede
116274808f91SHans de Goede if (report > REPORT_TYPE_RFREPORT_LAST) {
1163640d4ea8SColin Ian King hid_err(hdev, "Unexpected input report number %d\n", report);
116474808f91SHans de Goede return;
116574808f91SHans de Goede }
116674808f91SHans de Goede
116774808f91SHans de Goede spin_lock_irqsave(&djrcv_dev->lock, flags);
116874808f91SHans de Goede for (i = 0; i < (DJ_MAX_PAIRED_DEVICES + DJ_DEVICE_INDEX_MIN); i++) {
116974808f91SHans de Goede dj_dev = djrcv_dev->paired_dj_devices[i];
117074808f91SHans de Goede if (dj_dev && (dj_dev->reports_supported & BIT(report))) {
117174808f91SHans de Goede logi_dj_recv_forward_report(dj_dev, data, size);
117274808f91SHans de Goede spin_unlock_irqrestore(&djrcv_dev->lock, flags);
117374808f91SHans de Goede return;
117474808f91SHans de Goede }
117574808f91SHans de Goede }
117674808f91SHans de Goede
117774808f91SHans de Goede logi_dj_recv_queue_unknown_work(djrcv_dev);
117874808f91SHans de Goede spin_unlock_irqrestore(&djrcv_dev->lock, flags);
117974808f91SHans de Goede
118074808f91SHans de Goede dbg_hid("No dj-devs handling input report number %d\n", report);
118174808f91SHans de Goede }
118274808f91SHans de Goede
logi_dj_recv_send_report(struct dj_receiver_dev * djrcv_dev,struct dj_report * dj_report)1183534a7b8eSNestor Lopez Casado static int logi_dj_recv_send_report(struct dj_receiver_dev *djrcv_dev,
1184534a7b8eSNestor Lopez Casado struct dj_report *dj_report)
1185534a7b8eSNestor Lopez Casado {
11860ee75544SHans de Goede struct hid_device *hdev = djrcv_dev->hidpp;
1187dcd9006bSBenjamin Tissoires struct hid_report *report;
1188dcd9006bSBenjamin Tissoires struct hid_report_enum *output_report_enum;
1189dcd9006bSBenjamin Tissoires u8 *data = (u8 *)(&dj_report->device_index);
1190297502abSKees Cook unsigned int i;
1191534a7b8eSNestor Lopez Casado
1192dcd9006bSBenjamin Tissoires output_report_enum = &hdev->report_enum[HID_OUTPUT_REPORT];
1193dcd9006bSBenjamin Tissoires report = output_report_enum->report_id_hash[REPORT_ID_DJ_SHORT];
1194dcd9006bSBenjamin Tissoires
1195dcd9006bSBenjamin Tissoires if (!report) {
1196aca22a35SHans de Goede hid_err(hdev, "%s: unable to find dj report\n", __func__);
1197534a7b8eSNestor Lopez Casado return -ENODEV;
1198534a7b8eSNestor Lopez Casado }
1199534a7b8eSNestor Lopez Casado
1200297502abSKees Cook for (i = 0; i < DJREPORT_SHORT_LENGTH - 1; i++)
1201dcd9006bSBenjamin Tissoires report->field[0]->value[i] = data[i];
1202534a7b8eSNestor Lopez Casado
120383a44ac8SJiri Kosina hid_hw_request(hdev, report, HID_REQ_SET_REPORT);
1204dcd9006bSBenjamin Tissoires
1205dcd9006bSBenjamin Tissoires return 0;
1206534a7b8eSNestor Lopez Casado }
1207534a7b8eSNestor Lopez Casado
logi_dj_recv_query_hidpp_devices(struct dj_receiver_dev * djrcv_dev)120874808f91SHans de Goede static int logi_dj_recv_query_hidpp_devices(struct dj_receiver_dev *djrcv_dev)
120974808f91SHans de Goede {
121039d21e7eSColin Ian King static const u8 template[] = {
121139d21e7eSColin Ian King REPORT_ID_HIDPP_SHORT,
121274808f91SHans de Goede HIDPP_RECEIVER_INDEX,
121374808f91SHans de Goede HIDPP_SET_REGISTER,
121474808f91SHans de Goede HIDPP_REG_CONNECTION_STATE,
121574808f91SHans de Goede HIDPP_FAKE_DEVICE_ARRIVAL,
121639d21e7eSColin Ian King 0x00, 0x00
121739d21e7eSColin Ian King };
121874808f91SHans de Goede u8 *hidpp_report;
121974808f91SHans de Goede int retval;
122074808f91SHans de Goede
122174808f91SHans de Goede hidpp_report = kmemdup(template, sizeof(template), GFP_KERNEL);
122274808f91SHans de Goede if (!hidpp_report)
122374808f91SHans de Goede return -ENOMEM;
122474808f91SHans de Goede
122574808f91SHans de Goede retval = hid_hw_raw_request(djrcv_dev->hidpp,
122674808f91SHans de Goede REPORT_ID_HIDPP_SHORT,
122774808f91SHans de Goede hidpp_report, sizeof(template),
122874808f91SHans de Goede HID_OUTPUT_REPORT,
122974808f91SHans de Goede HID_REQ_SET_REPORT);
123074808f91SHans de Goede
123174808f91SHans de Goede kfree(hidpp_report);
12326fb08f1aSHans de Goede return (retval < 0) ? retval : 0;
123374808f91SHans de Goede }
123474808f91SHans de Goede
logi_dj_recv_query_paired_devices(struct dj_receiver_dev * djrcv_dev)1235534a7b8eSNestor Lopez Casado static int logi_dj_recv_query_paired_devices(struct dj_receiver_dev *djrcv_dev)
1236534a7b8eSNestor Lopez Casado {
1237d8dc3494SMarc Dionne struct dj_report *dj_report;
1238d8dc3494SMarc Dionne int retval;
1239534a7b8eSNestor Lopez Casado
1240b6aeeddeSHans de Goede djrcv_dev->last_query = jiffies;
1241b6aeeddeSHans de Goede
124274808f91SHans de Goede if (djrcv_dev->type != recvr_type_dj)
124374808f91SHans de Goede return logi_dj_recv_query_hidpp_devices(djrcv_dev);
124474808f91SHans de Goede
12458a55ade7SAlan Cox dj_report = kzalloc(sizeof(struct dj_report), GFP_KERNEL);
1246d8dc3494SMarc Dionne if (!dj_report)
1247d8dc3494SMarc Dionne return -ENOMEM;
1248d8dc3494SMarc Dionne dj_report->report_id = REPORT_ID_DJ_SHORT;
12497f02bb62SMazin Rezk dj_report->device_index = HIDPP_RECEIVER_INDEX;
1250d8dc3494SMarc Dionne dj_report->report_type = REPORT_TYPE_CMD_GET_PAIRED_DEVICES;
1251d8dc3494SMarc Dionne retval = logi_dj_recv_send_report(djrcv_dev, dj_report);
1252d8dc3494SMarc Dionne kfree(dj_report);
1253d8dc3494SMarc Dionne return retval;
1254534a7b8eSNestor Lopez Casado }
1255534a7b8eSNestor Lopez Casado
1256c63e0e37SNestor Lopez Casado
logi_dj_recv_switch_to_dj_mode(struct dj_receiver_dev * djrcv_dev,unsigned timeout)1257534a7b8eSNestor Lopez Casado static int logi_dj_recv_switch_to_dj_mode(struct dj_receiver_dev *djrcv_dev,
1258534a7b8eSNestor Lopez Casado unsigned timeout)
1259534a7b8eSNestor Lopez Casado {
12600ee75544SHans de Goede struct hid_device *hdev = djrcv_dev->hidpp;
1261d8dc3494SMarc Dionne struct dj_report *dj_report;
12626a9ddc89SBenjamin Tissoires u8 *buf;
126374808f91SHans de Goede int retval = 0;
1264534a7b8eSNestor Lopez Casado
12658a55ade7SAlan Cox dj_report = kzalloc(sizeof(struct dj_report), GFP_KERNEL);
1266d8dc3494SMarc Dionne if (!dj_report)
1267d8dc3494SMarc Dionne return -ENOMEM;
126874808f91SHans de Goede
126974808f91SHans de Goede if (djrcv_dev->type == recvr_type_dj) {
1270d8dc3494SMarc Dionne dj_report->report_id = REPORT_ID_DJ_SHORT;
12717f02bb62SMazin Rezk dj_report->device_index = HIDPP_RECEIVER_INDEX;
1272d8dc3494SMarc Dionne dj_report->report_type = REPORT_TYPE_CMD_SWITCH;
1273d8dc3494SMarc Dionne dj_report->report_params[CMD_SWITCH_PARAM_DEVBITFIELD] = 0x3F;
127474808f91SHans de Goede dj_report->report_params[CMD_SWITCH_PARAM_TIMEOUT_SECONDS] =
127574808f91SHans de Goede (u8)timeout;
127674808f91SHans de Goede
1277d8dc3494SMarc Dionne retval = logi_dj_recv_send_report(djrcv_dev, dj_report);
127842c22dbfSBenjamin Tisssoires
127942c22dbfSBenjamin Tisssoires /*
128074808f91SHans de Goede * Ugly sleep to work around a USB 3.0 bug when the receiver is
128174808f91SHans de Goede * still processing the "switch-to-dj" command while we send an
128274808f91SHans de Goede * other command.
128342c22dbfSBenjamin Tisssoires * 50 msec should gives enough time to the receiver to be ready.
128442c22dbfSBenjamin Tisssoires */
128542c22dbfSBenjamin Tisssoires msleep(50);
12866f20d326SNikita Zhandarovich
1287*f677ca8cSJosé Expósito if (retval) {
1288*f677ca8cSJosé Expósito kfree(dj_report);
12896f20d326SNikita Zhandarovich return retval;
129074808f91SHans de Goede }
1291*f677ca8cSJosé Expósito }
129242c22dbfSBenjamin Tisssoires
12936a9ddc89SBenjamin Tissoires /*
12946a9ddc89SBenjamin Tissoires * Magical bits to set up hidpp notifications when the dj devices
12956a9ddc89SBenjamin Tissoires * are connected/disconnected.
12966a9ddc89SBenjamin Tissoires *
12976a9ddc89SBenjamin Tissoires * We can reuse dj_report because HIDPP_REPORT_SHORT_LENGTH is smaller
12986a9ddc89SBenjamin Tissoires * than DJREPORT_SHORT_LENGTH.
12996a9ddc89SBenjamin Tissoires */
13006a9ddc89SBenjamin Tissoires buf = (u8 *)dj_report;
13016a9ddc89SBenjamin Tissoires
13026a9ddc89SBenjamin Tissoires memset(buf, 0, HIDPP_REPORT_SHORT_LENGTH);
13036a9ddc89SBenjamin Tissoires
13046a9ddc89SBenjamin Tissoires buf[0] = REPORT_ID_HIDPP_SHORT;
13057f02bb62SMazin Rezk buf[1] = HIDPP_RECEIVER_INDEX;
13066a9ddc89SBenjamin Tissoires buf[2] = 0x80;
13076a9ddc89SBenjamin Tissoires buf[3] = 0x00;
13086a9ddc89SBenjamin Tissoires buf[4] = 0x00;
13096a9ddc89SBenjamin Tissoires buf[5] = 0x09;
13106a9ddc89SBenjamin Tissoires buf[6] = 0x00;
13116a9ddc89SBenjamin Tissoires
13126f20d326SNikita Zhandarovich retval = hid_hw_raw_request(hdev, REPORT_ID_HIDPP_SHORT, buf,
13136a9ddc89SBenjamin Tissoires HIDPP_REPORT_SHORT_LENGTH, HID_OUTPUT_REPORT,
13146a9ddc89SBenjamin Tissoires HID_REQ_SET_REPORT);
13156a9ddc89SBenjamin Tissoires
13166a9ddc89SBenjamin Tissoires kfree(dj_report);
1317d8dc3494SMarc Dionne return retval;
1318534a7b8eSNestor Lopez Casado }
1319534a7b8eSNestor Lopez Casado
1320534a7b8eSNestor Lopez Casado
logi_dj_ll_open(struct hid_device * hid)1321534a7b8eSNestor Lopez Casado static int logi_dj_ll_open(struct hid_device *hid)
1322534a7b8eSNestor Lopez Casado {
1323534a7b8eSNestor Lopez Casado dbg_hid("%s: %s\n", __func__, hid->phys);
1324534a7b8eSNestor Lopez Casado return 0;
1325534a7b8eSNestor Lopez Casado
1326534a7b8eSNestor Lopez Casado }
1327534a7b8eSNestor Lopez Casado
logi_dj_ll_close(struct hid_device * hid)1328534a7b8eSNestor Lopez Casado static void logi_dj_ll_close(struct hid_device *hid)
1329534a7b8eSNestor Lopez Casado {
1330534a7b8eSNestor Lopez Casado dbg_hid("%s: %s\n", __func__, hid->phys);
1331534a7b8eSNestor Lopez Casado }
1332534a7b8eSNestor Lopez Casado
13338dba3026SBenjamin Tissoires /*
13348dba3026SBenjamin Tissoires * Register 0xB5 is "pairing information". It is solely intended for the
13358dba3026SBenjamin Tissoires * receiver, so do not overwrite the device index.
13368dba3026SBenjamin Tissoires */
1337c0340412SBenjamin Tissoires static u8 unifying_pairing_query[] = { REPORT_ID_HIDPP_SHORT,
1338c0340412SBenjamin Tissoires HIDPP_RECEIVER_INDEX,
1339c0340412SBenjamin Tissoires HIDPP_GET_LONG_REGISTER,
1340c0340412SBenjamin Tissoires HIDPP_REG_PAIRING_INFORMATION };
1341c0340412SBenjamin Tissoires static u8 unifying_pairing_answer[] = { REPORT_ID_HIDPP_LONG,
1342c0340412SBenjamin Tissoires HIDPP_RECEIVER_INDEX,
1343c0340412SBenjamin Tissoires HIDPP_GET_LONG_REGISTER,
1344c0340412SBenjamin Tissoires HIDPP_REG_PAIRING_INFORMATION };
134533797820SBenjamin Tissoires
logi_dj_ll_raw_request(struct hid_device * hid,unsigned char reportnum,__u8 * buf,size_t count,unsigned char report_type,int reqtype)1346bd27e202SBenjamin Tissoires static int logi_dj_ll_raw_request(struct hid_device *hid,
1347bd27e202SBenjamin Tissoires unsigned char reportnum, __u8 *buf,
1348bd27e202SBenjamin Tissoires size_t count, unsigned char report_type,
1349bd27e202SBenjamin Tissoires int reqtype)
1350534a7b8eSNestor Lopez Casado {
13510e40d356SBenjamin Tissoires struct dj_device *djdev = hid->driver_data;
13520e40d356SBenjamin Tissoires struct dj_receiver_dev *djrcv_dev = djdev->dj_receiver_dev;
13530e40d356SBenjamin Tissoires u8 *out_buf;
13540e40d356SBenjamin Tissoires int ret;
1355534a7b8eSNestor Lopez Casado
1356925f0f3eSBenjamin Tissoires if ((buf[0] == REPORT_ID_HIDPP_SHORT) ||
13573a9a2c86SHans de Goede (buf[0] == REPORT_ID_HIDPP_LONG) ||
13583a9a2c86SHans de Goede (buf[0] == REPORT_ID_HIDPP_VERY_LONG)) {
1359925f0f3eSBenjamin Tissoires if (count < 2)
1360925f0f3eSBenjamin Tissoires return -EINVAL;
1361925f0f3eSBenjamin Tissoires
136233797820SBenjamin Tissoires /* special case where we should not overwrite
136333797820SBenjamin Tissoires * the device_index */
13648dba3026SBenjamin Tissoires if (count == 7 && !memcmp(buf, unifying_pairing_query,
13658dba3026SBenjamin Tissoires sizeof(unifying_pairing_query)))
13668dba3026SBenjamin Tissoires buf[4] = (buf[4] & 0xf0) | (djdev->device_index - 1);
136733797820SBenjamin Tissoires else
1368925f0f3eSBenjamin Tissoires buf[1] = djdev->device_index;
13690ee75544SHans de Goede return hid_hw_raw_request(djrcv_dev->hidpp, reportnum, buf,
1370925f0f3eSBenjamin Tissoires count, report_type, reqtype);
1371925f0f3eSBenjamin Tissoires }
1372925f0f3eSBenjamin Tissoires
13730e40d356SBenjamin Tissoires if (buf[0] != REPORT_TYPE_LEDS)
13740e40d356SBenjamin Tissoires return -EINVAL;
13750e40d356SBenjamin Tissoires
137674808f91SHans de Goede if (djrcv_dev->type != recvr_type_dj && count >= 2) {
137774808f91SHans de Goede if (!djrcv_dev->keyboard) {
137874808f91SHans de Goede hid_warn(hid, "Received REPORT_TYPE_LEDS request before the keyboard interface was enumerated\n");
137974808f91SHans de Goede return 0;
138074808f91SHans de Goede }
138174808f91SHans de Goede /* usbhid overrides the report ID and ignores the first byte */
138274808f91SHans de Goede return hid_hw_raw_request(djrcv_dev->keyboard, 0, buf, count,
138374808f91SHans de Goede report_type, reqtype);
138474808f91SHans de Goede }
138574808f91SHans de Goede
13860e40d356SBenjamin Tissoires out_buf = kzalloc(DJREPORT_SHORT_LENGTH, GFP_ATOMIC);
13870e40d356SBenjamin Tissoires if (!out_buf)
13880e40d356SBenjamin Tissoires return -ENOMEM;
13890e40d356SBenjamin Tissoires
139051217e69SJiri Kosina if (count > DJREPORT_SHORT_LENGTH - 2)
13910e40d356SBenjamin Tissoires count = DJREPORT_SHORT_LENGTH - 2;
13920e40d356SBenjamin Tissoires
13930e40d356SBenjamin Tissoires out_buf[0] = REPORT_ID_DJ_SHORT;
13940e40d356SBenjamin Tissoires out_buf[1] = djdev->device_index;
13950e40d356SBenjamin Tissoires memcpy(out_buf + 2, buf, count);
13960e40d356SBenjamin Tissoires
13970ee75544SHans de Goede ret = hid_hw_raw_request(djrcv_dev->hidpp, out_buf[0], out_buf,
1398bd27e202SBenjamin Tissoires DJREPORT_SHORT_LENGTH, report_type, reqtype);
13990e40d356SBenjamin Tissoires
14000e40d356SBenjamin Tissoires kfree(out_buf);
14010e40d356SBenjamin Tissoires return ret;
1402534a7b8eSNestor Lopez Casado }
1403534a7b8eSNestor Lopez Casado
rdcat(char * rdesc,unsigned int * rsize,const char * data,unsigned int size)14046d603326SDan Carpenter static void rdcat(char *rdesc, unsigned int *rsize, const char *data, unsigned int size)
14052a039bf5SHenrik Rydberg {
14066d603326SDan Carpenter memcpy(rdesc + *rsize, data, size);
14072a039bf5SHenrik Rydberg *rsize += size;
14082a039bf5SHenrik Rydberg }
14092a039bf5SHenrik Rydberg
logi_dj_ll_parse(struct hid_device * hid)1410534a7b8eSNestor Lopez Casado static int logi_dj_ll_parse(struct hid_device *hid)
1411534a7b8eSNestor Lopez Casado {
1412534a7b8eSNestor Lopez Casado struct dj_device *djdev = hid->driver_data;
14132a039bf5SHenrik Rydberg unsigned int rsize = 0;
14142a039bf5SHenrik Rydberg char *rdesc;
1415534a7b8eSNestor Lopez Casado int retval;
1416534a7b8eSNestor Lopez Casado
1417534a7b8eSNestor Lopez Casado dbg_hid("%s\n", __func__);
1418534a7b8eSNestor Lopez Casado
1419534a7b8eSNestor Lopez Casado djdev->hdev->version = 0x0111;
1420534a7b8eSNestor Lopez Casado djdev->hdev->country = 0x00;
1421534a7b8eSNestor Lopez Casado
14222a039bf5SHenrik Rydberg rdesc = kmalloc(MAX_RDESC_SIZE, GFP_KERNEL);
14232a039bf5SHenrik Rydberg if (!rdesc)
14242a039bf5SHenrik Rydberg return -ENOMEM;
14252a039bf5SHenrik Rydberg
1426534a7b8eSNestor Lopez Casado if (djdev->reports_supported & STD_KEYBOARD) {
14276d3c3f03SHans de Goede dbg_hid("%s: sending a kbd descriptor, reports_supported: %llx\n",
1428534a7b8eSNestor Lopez Casado __func__, djdev->reports_supported);
14296d603326SDan Carpenter rdcat(rdesc, &rsize, kbd_descriptor, sizeof(kbd_descriptor));
1430534a7b8eSNestor Lopez Casado }
1431534a7b8eSNestor Lopez Casado
1432534a7b8eSNestor Lopez Casado if (djdev->reports_supported & STD_MOUSE) {
14336d3c3f03SHans de Goede dbg_hid("%s: sending a mouse descriptor, reports_supported: %llx\n",
14346d3c3f03SHans de Goede __func__, djdev->reports_supported);
14353ed224e2SHans de Goede if (djdev->dj_receiver_dev->type == recvr_type_gaming_hidpp ||
14363ed224e2SHans de Goede djdev->dj_receiver_dev->type == recvr_type_mouse_only)
1437f5fb57a7SBenjamin Tissoires rdcat(rdesc, &rsize, mse_high_res_descriptor,
1438f5fb57a7SBenjamin Tissoires sizeof(mse_high_res_descriptor));
1439c9121cf6SHans de Goede else if (djdev->dj_receiver_dev->type == recvr_type_27mhz)
1440c9121cf6SHans de Goede rdcat(rdesc, &rsize, mse_27mhz_descriptor,
1441c9121cf6SHans de Goede sizeof(mse_27mhz_descriptor));
1442434f7709SHans de Goede else if (recvr_type_is_bluetooth(djdev->dj_receiver_dev->type))
1443f2113c30SHans de Goede rdcat(rdesc, &rsize, mse_bluetooth_descriptor,
1444f2113c30SHans de Goede sizeof(mse_bluetooth_descriptor));
1445f5fb57a7SBenjamin Tissoires else
1446f5fb57a7SBenjamin Tissoires rdcat(rdesc, &rsize, mse_descriptor,
1447f5fb57a7SBenjamin Tissoires sizeof(mse_descriptor));
1448534a7b8eSNestor Lopez Casado }
1449534a7b8eSNestor Lopez Casado
1450434f7709SHans de Goede if (djdev->reports_supported & KBD_MOUSE) {
1451434f7709SHans de Goede dbg_hid("%s: sending a kbd-mouse descriptor, reports_supported: %llx\n",
1452434f7709SHans de Goede __func__, djdev->reports_supported);
1453434f7709SHans de Goede rdcat(rdesc, &rsize, mse5_bluetooth_descriptor,
1454434f7709SHans de Goede sizeof(mse5_bluetooth_descriptor));
1455434f7709SHans de Goede }
1456434f7709SHans de Goede
1457534a7b8eSNestor Lopez Casado if (djdev->reports_supported & MULTIMEDIA) {
14586d3c3f03SHans de Goede dbg_hid("%s: sending a multimedia report descriptor: %llx\n",
1459534a7b8eSNestor Lopez Casado __func__, djdev->reports_supported);
14606d603326SDan Carpenter rdcat(rdesc, &rsize, consumer_descriptor, sizeof(consumer_descriptor));
1461534a7b8eSNestor Lopez Casado }
1462534a7b8eSNestor Lopez Casado
1463534a7b8eSNestor Lopez Casado if (djdev->reports_supported & POWER_KEYS) {
14646d3c3f03SHans de Goede dbg_hid("%s: sending a power keys report descriptor: %llx\n",
1465534a7b8eSNestor Lopez Casado __func__, djdev->reports_supported);
14666d603326SDan Carpenter rdcat(rdesc, &rsize, syscontrol_descriptor, sizeof(syscontrol_descriptor));
1467534a7b8eSNestor Lopez Casado }
1468534a7b8eSNestor Lopez Casado
1469534a7b8eSNestor Lopez Casado if (djdev->reports_supported & MEDIA_CENTER) {
14706d3c3f03SHans de Goede dbg_hid("%s: sending a media center report descriptor: %llx\n",
1471534a7b8eSNestor Lopez Casado __func__, djdev->reports_supported);
14726d603326SDan Carpenter rdcat(rdesc, &rsize, media_descriptor, sizeof(media_descriptor));
1473534a7b8eSNestor Lopez Casado }
1474534a7b8eSNestor Lopez Casado
1475534a7b8eSNestor Lopez Casado if (djdev->reports_supported & KBD_LEDS) {
14766d3c3f03SHans de Goede dbg_hid("%s: need to send kbd leds report descriptor: %llx\n",
1477534a7b8eSNestor Lopez Casado __func__, djdev->reports_supported);
1478534a7b8eSNestor Lopez Casado }
1479534a7b8eSNestor Lopez Casado
14806d3c3f03SHans de Goede if (djdev->reports_supported & HIDPP) {
1481630dd6eaSFilipe Laíns dbg_hid("%s: sending a HID++ descriptor, reports_supported: %llx\n",
1482630dd6eaSFilipe Laíns __func__, djdev->reports_supported);
14836d3c3f03SHans de Goede rdcat(rdesc, &rsize, hidpp_descriptor,
14846d3c3f03SHans de Goede sizeof(hidpp_descriptor));
14856d3c3f03SHans de Goede }
1486925f0f3eSBenjamin Tissoires
14872a039bf5SHenrik Rydberg retval = hid_parse_report(hid, rdesc, rsize);
14882a039bf5SHenrik Rydberg kfree(rdesc);
14892a039bf5SHenrik Rydberg
14902a039bf5SHenrik Rydberg return retval;
1491534a7b8eSNestor Lopez Casado }
1492534a7b8eSNestor Lopez Casado
logi_dj_ll_start(struct hid_device * hid)1493534a7b8eSNestor Lopez Casado static int logi_dj_ll_start(struct hid_device *hid)
1494534a7b8eSNestor Lopez Casado {
1495534a7b8eSNestor Lopez Casado dbg_hid("%s\n", __func__);
1496534a7b8eSNestor Lopez Casado return 0;
1497534a7b8eSNestor Lopez Casado }
1498534a7b8eSNestor Lopez Casado
logi_dj_ll_stop(struct hid_device * hid)1499534a7b8eSNestor Lopez Casado static void logi_dj_ll_stop(struct hid_device *hid)
1500534a7b8eSNestor Lopez Casado {
1501534a7b8eSNestor Lopez Casado dbg_hid("%s\n", __func__);
1502534a7b8eSNestor Lopez Casado }
1503534a7b8eSNestor Lopez Casado
logi_dj_ll_may_wakeup(struct hid_device * hid)1504622d97cfSHans de Goede static bool logi_dj_ll_may_wakeup(struct hid_device *hid)
1505622d97cfSHans de Goede {
1506622d97cfSHans de Goede struct dj_device *djdev = hid->driver_data;
1507622d97cfSHans de Goede struct dj_receiver_dev *djrcv_dev = djdev->dj_receiver_dev;
1508622d97cfSHans de Goede
1509622d97cfSHans de Goede return hid_hw_may_wakeup(djrcv_dev->hidpp);
1510622d97cfSHans de Goede }
1511534a7b8eSNestor Lopez Casado
1512662eee8dSThomas Weißschuh static const struct hid_ll_driver logi_dj_ll_driver = {
1513534a7b8eSNestor Lopez Casado .parse = logi_dj_ll_parse,
1514534a7b8eSNestor Lopez Casado .start = logi_dj_ll_start,
1515534a7b8eSNestor Lopez Casado .stop = logi_dj_ll_stop,
1516534a7b8eSNestor Lopez Casado .open = logi_dj_ll_open,
1517534a7b8eSNestor Lopez Casado .close = logi_dj_ll_close,
1518bd27e202SBenjamin Tissoires .raw_request = logi_dj_ll_raw_request,
1519622d97cfSHans de Goede .may_wakeup = logi_dj_ll_may_wakeup,
1520534a7b8eSNestor Lopez Casado };
1521534a7b8eSNestor Lopez Casado
logi_dj_dj_event(struct hid_device * hdev,struct hid_report * report,u8 * data,int size)1522925f0f3eSBenjamin Tissoires static int logi_dj_dj_event(struct hid_device *hdev,
1523534a7b8eSNestor Lopez Casado struct hid_report *report, u8 *data,
1524534a7b8eSNestor Lopez Casado int size)
1525534a7b8eSNestor Lopez Casado {
1526534a7b8eSNestor Lopez Casado struct dj_receiver_dev *djrcv_dev = hid_get_drvdata(hdev);
1527534a7b8eSNestor Lopez Casado struct dj_report *dj_report = (struct dj_report *) data;
1528534a7b8eSNestor Lopez Casado unsigned long flags;
1529534a7b8eSNestor Lopez Casado
1530925f0f3eSBenjamin Tissoires /*
1531925f0f3eSBenjamin Tissoires * Here we receive all data coming from iface 2, there are 3 cases:
1532534a7b8eSNestor Lopez Casado *
1533925f0f3eSBenjamin Tissoires * 1) Data is intended for this driver i. e. data contains arrival,
1534925f0f3eSBenjamin Tissoires * departure, etc notifications, in which case we queue them for delayed
1535925f0f3eSBenjamin Tissoires * processing by the work queue. We return 1 to hid-core as no further
1536925f0f3eSBenjamin Tissoires * processing is required from it.
1537534a7b8eSNestor Lopez Casado *
1538925f0f3eSBenjamin Tissoires * 2) Data informs a connection change, if the change means rf link
1539925f0f3eSBenjamin Tissoires * loss, then we must send a null report to the upper layer to discard
1540925f0f3eSBenjamin Tissoires * potentially pressed keys that may be repeated forever by the input
1541925f0f3eSBenjamin Tissoires * layer. Return 1 to hid-core as no further processing is required.
1542534a7b8eSNestor Lopez Casado *
1543925f0f3eSBenjamin Tissoires * 3) Data is an actual input event from a paired DJ device in which
1544925f0f3eSBenjamin Tissoires * case we forward it to the correct hid device (via hid_input_report()
1545925f0f3eSBenjamin Tissoires * ) and return 1 so hid-core does not anything else with it.
1546534a7b8eSNestor Lopez Casado */
15475abfe85cSBenjamin Tissoires
1548ad3e14d7SJiri Kosina if ((dj_report->device_index < DJ_DEVICE_INDEX_MIN) ||
1549ad3e14d7SJiri Kosina (dj_report->device_index > DJ_DEVICE_INDEX_MAX)) {
15505abfe85cSBenjamin Tissoires /*
15515abfe85cSBenjamin Tissoires * Device index is wrong, bail out.
15525abfe85cSBenjamin Tissoires * This driver can ignore safely the receiver notifications,
15535abfe85cSBenjamin Tissoires * so ignore those reports too.
15545abfe85cSBenjamin Tissoires */
15555abfe85cSBenjamin Tissoires if (dj_report->device_index != DJ_RECEIVER_INDEX)
1556aca22a35SHans de Goede hid_err(hdev, "%s: invalid device index:%d\n",
1557ad3e14d7SJiri Kosina __func__, dj_report->device_index);
1558ad3e14d7SJiri Kosina return false;
1559ad3e14d7SJiri Kosina }
1560534a7b8eSNestor Lopez Casado
1561534a7b8eSNestor Lopez Casado spin_lock_irqsave(&djrcv_dev->lock, flags);
1562368d4e59SBenjamin Tissoires
1563368d4e59SBenjamin Tissoires if (!djrcv_dev->paired_dj_devices[dj_report->device_index]) {
1564368d4e59SBenjamin Tissoires /* received an event for an unknown device, bail out */
1565368d4e59SBenjamin Tissoires logi_dj_recv_queue_notification(djrcv_dev, dj_report);
1566368d4e59SBenjamin Tissoires goto out;
1567368d4e59SBenjamin Tissoires }
1568368d4e59SBenjamin Tissoires
1569534a7b8eSNestor Lopez Casado switch (dj_report->report_type) {
1570534a7b8eSNestor Lopez Casado case REPORT_TYPE_NOTIF_DEVICE_PAIRED:
1571368d4e59SBenjamin Tissoires /* pairing notifications are handled above the switch */
1572368d4e59SBenjamin Tissoires break;
1573534a7b8eSNestor Lopez Casado case REPORT_TYPE_NOTIF_DEVICE_UNPAIRED:
1574534a7b8eSNestor Lopez Casado logi_dj_recv_queue_notification(djrcv_dev, dj_report);
1575534a7b8eSNestor Lopez Casado break;
1576534a7b8eSNestor Lopez Casado case REPORT_TYPE_NOTIF_CONNECTION_STATUS:
1577534a7b8eSNestor Lopez Casado if (dj_report->report_params[CONNECTION_STATUS_PARAM_STATUS] ==
1578534a7b8eSNestor Lopez Casado STATUS_LINKLOSS) {
1579534a7b8eSNestor Lopez Casado logi_dj_recv_forward_null_report(djrcv_dev, dj_report);
1580534a7b8eSNestor Lopez Casado }
1581534a7b8eSNestor Lopez Casado break;
1582534a7b8eSNestor Lopez Casado default:
158383898234SBenjamin Tissoires logi_dj_recv_forward_dj(djrcv_dev, dj_report);
1584534a7b8eSNestor Lopez Casado }
1585368d4e59SBenjamin Tissoires
1586368d4e59SBenjamin Tissoires out:
1587534a7b8eSNestor Lopez Casado spin_unlock_irqrestore(&djrcv_dev->lock, flags);
1588534a7b8eSNestor Lopez Casado
15895abfe85cSBenjamin Tissoires return true;
1590534a7b8eSNestor Lopez Casado }
1591534a7b8eSNestor Lopez Casado
logi_dj_hidpp_event(struct hid_device * hdev,struct hid_report * report,u8 * data,int size)1592925f0f3eSBenjamin Tissoires static int logi_dj_hidpp_event(struct hid_device *hdev,
1593925f0f3eSBenjamin Tissoires struct hid_report *report, u8 *data,
1594925f0f3eSBenjamin Tissoires int size)
1595925f0f3eSBenjamin Tissoires {
1596925f0f3eSBenjamin Tissoires struct dj_receiver_dev *djrcv_dev = hid_get_drvdata(hdev);
15977bb56a5fSBenjamin Tissoires struct hidpp_event *hidpp_report = (struct hidpp_event *) data;
159874808f91SHans de Goede struct dj_device *dj_dev;
1599925f0f3eSBenjamin Tissoires unsigned long flags;
16007bb56a5fSBenjamin Tissoires u8 device_index = hidpp_report->device_index;
1601925f0f3eSBenjamin Tissoires
160233797820SBenjamin Tissoires if (device_index == HIDPP_RECEIVER_INDEX) {
160333797820SBenjamin Tissoires /* special case were the device wants to know its unifying
160433797820SBenjamin Tissoires * name */
160533797820SBenjamin Tissoires if (size == HIDPP_REPORT_LONG_LENGTH &&
16068dba3026SBenjamin Tissoires !memcmp(data, unifying_pairing_answer,
16078dba3026SBenjamin Tissoires sizeof(unifying_pairing_answer)))
160833797820SBenjamin Tissoires device_index = (data[4] & 0x0F) + 1;
160933797820SBenjamin Tissoires else
1610925f0f3eSBenjamin Tissoires return false;
161133797820SBenjamin Tissoires }
1612925f0f3eSBenjamin Tissoires
1613925f0f3eSBenjamin Tissoires /*
1614925f0f3eSBenjamin Tissoires * Data is from the HID++ collection, in this case, we forward the
1615925f0f3eSBenjamin Tissoires * data to the corresponding child dj device and return 0 to hid-core
1616925f0f3eSBenjamin Tissoires * so he data also goes to the hidraw device of the receiver. This
1617925f0f3eSBenjamin Tissoires * allows a user space application to implement the full HID++ routing
1618925f0f3eSBenjamin Tissoires * via the receiver.
1619925f0f3eSBenjamin Tissoires */
1620925f0f3eSBenjamin Tissoires
1621925f0f3eSBenjamin Tissoires if ((device_index < DJ_DEVICE_INDEX_MIN) ||
1622925f0f3eSBenjamin Tissoires (device_index > DJ_DEVICE_INDEX_MAX)) {
1623925f0f3eSBenjamin Tissoires /*
1624925f0f3eSBenjamin Tissoires * Device index is wrong, bail out.
1625925f0f3eSBenjamin Tissoires * This driver can ignore safely the receiver notifications,
1626925f0f3eSBenjamin Tissoires * so ignore those reports too.
1627925f0f3eSBenjamin Tissoires */
1628aca22a35SHans de Goede hid_err(hdev, "%s: invalid device index:%d\n", __func__,
1629aca22a35SHans de Goede hidpp_report->device_index);
1630925f0f3eSBenjamin Tissoires return false;
1631925f0f3eSBenjamin Tissoires }
1632925f0f3eSBenjamin Tissoires
1633925f0f3eSBenjamin Tissoires spin_lock_irqsave(&djrcv_dev->lock, flags);
1634925f0f3eSBenjamin Tissoires
163574808f91SHans de Goede dj_dev = djrcv_dev->paired_dj_devices[device_index];
1636c9121cf6SHans de Goede
1637c9121cf6SHans de Goede /*
1638c9121cf6SHans de Goede * With 27 MHz receivers, we do not get an explicit unpair event,
1639c9121cf6SHans de Goede * remove the old device if the user has paired a *different* device.
1640c9121cf6SHans de Goede */
1641c9121cf6SHans de Goede if (djrcv_dev->type == recvr_type_27mhz && dj_dev &&
1642c9121cf6SHans de Goede hidpp_report->sub_id == REPORT_TYPE_NOTIF_DEVICE_CONNECTED &&
1643c9121cf6SHans de Goede hidpp_report->params[HIDPP_PARAM_PROTO_TYPE] == 0x02 &&
1644c9121cf6SHans de Goede hidpp_report->params[HIDPP_PARAM_27MHZ_DEVID] !=
1645c9121cf6SHans de Goede dj_dev->hdev->product) {
1646c9121cf6SHans de Goede struct dj_workitem workitem = {
1647c9121cf6SHans de Goede .device_index = hidpp_report->device_index,
1648c9121cf6SHans de Goede .type = WORKITEM_TYPE_UNPAIRED,
1649c9121cf6SHans de Goede };
1650c9121cf6SHans de Goede kfifo_in(&djrcv_dev->notif_fifo, &workitem, sizeof(workitem));
1651c9121cf6SHans de Goede /* logi_hidpp_recv_queue_notif will queue the work */
1652c9121cf6SHans de Goede dj_dev = NULL;
1653c9121cf6SHans de Goede }
1654c9121cf6SHans de Goede
165574808f91SHans de Goede if (dj_dev) {
165674808f91SHans de Goede logi_dj_recv_forward_report(dj_dev, data, size);
165774808f91SHans de Goede } else {
165874808f91SHans de Goede if (hidpp_report->sub_id == REPORT_TYPE_NOTIF_DEVICE_CONNECTED)
165974808f91SHans de Goede logi_hidpp_recv_queue_notif(hdev, hidpp_report);
166074808f91SHans de Goede else
166174808f91SHans de Goede logi_dj_recv_queue_unknown_work(djrcv_dev);
166274808f91SHans de Goede }
1663925f0f3eSBenjamin Tissoires
1664925f0f3eSBenjamin Tissoires spin_unlock_irqrestore(&djrcv_dev->lock, flags);
1665925f0f3eSBenjamin Tissoires
1666925f0f3eSBenjamin Tissoires return false;
1667925f0f3eSBenjamin Tissoires }
1668925f0f3eSBenjamin Tissoires
logi_dj_raw_event(struct hid_device * hdev,struct hid_report * report,u8 * data,int size)1669925f0f3eSBenjamin Tissoires static int logi_dj_raw_event(struct hid_device *hdev,
1670925f0f3eSBenjamin Tissoires struct hid_report *report, u8 *data,
1671925f0f3eSBenjamin Tissoires int size)
1672925f0f3eSBenjamin Tissoires {
167374808f91SHans de Goede struct dj_receiver_dev *djrcv_dev = hid_get_drvdata(hdev);
1674925f0f3eSBenjamin Tissoires dbg_hid("%s, size:%d\n", __func__, size);
1675925f0f3eSBenjamin Tissoires
1676da12b224SHans de Goede if (!djrcv_dev)
1677da12b224SHans de Goede return 0;
1678da12b224SHans de Goede
167974808f91SHans de Goede if (!hdev->report_enum[HID_INPUT_REPORT].numbered) {
168074808f91SHans de Goede
168174808f91SHans de Goede if (djrcv_dev->unnumbered_application == HID_GD_KEYBOARD) {
168274808f91SHans de Goede /*
168374808f91SHans de Goede * For the keyboard, we can reuse the same report by
168474808f91SHans de Goede * using the second byte which is constant in the USB
168574808f91SHans de Goede * HID report descriptor.
168674808f91SHans de Goede */
168774808f91SHans de Goede data[1] = data[0];
168874808f91SHans de Goede data[0] = REPORT_TYPE_KEYBOARD;
168974808f91SHans de Goede
169074808f91SHans de Goede logi_dj_recv_forward_input_report(hdev, data, size);
169174808f91SHans de Goede
169274808f91SHans de Goede /* restore previous state */
169374808f91SHans de Goede data[0] = data[1];
169474808f91SHans de Goede data[1] = 0;
169574808f91SHans de Goede }
16963ed224e2SHans de Goede /*
16973ed224e2SHans de Goede * Mouse-only receivers send unnumbered mouse data. The 27 MHz
1698a3028797SJiri Kosina * receiver uses 6 byte packets, the nano receiver 8 bytes.
16993ed224e2SHans de Goede */
17001f944ac6SHans de Goede if (djrcv_dev->unnumbered_application == HID_GD_MOUSE &&
1701a3028797SJiri Kosina size <= 8) {
1702a3028797SJiri Kosina u8 mouse_report[9];
17031f944ac6SHans de Goede
17041f944ac6SHans de Goede /* Prepend report id */
17051f944ac6SHans de Goede mouse_report[0] = REPORT_TYPE_MOUSE;
17063ed224e2SHans de Goede memcpy(mouse_report + 1, data, size);
17073ed224e2SHans de Goede logi_dj_recv_forward_input_report(hdev, mouse_report,
17083ed224e2SHans de Goede size + 1);
17091f944ac6SHans de Goede }
171074808f91SHans de Goede
171174808f91SHans de Goede return false;
171274808f91SHans de Goede }
171374808f91SHans de Goede
1714925f0f3eSBenjamin Tissoires switch (data[0]) {
1715925f0f3eSBenjamin Tissoires case REPORT_ID_DJ_SHORT:
1716f254ae93SPeter Wu if (size != DJREPORT_SHORT_LENGTH) {
1717aca22a35SHans de Goede hid_err(hdev, "Short DJ report bad size (%d)", size);
1718f254ae93SPeter Wu return false;
1719f254ae93SPeter Wu }
1720925f0f3eSBenjamin Tissoires return logi_dj_dj_event(hdev, report, data, size);
1721f5fb57a7SBenjamin Tissoires case REPORT_ID_DJ_LONG:
1722f5fb57a7SBenjamin Tissoires if (size != DJREPORT_LONG_LENGTH) {
1723aca22a35SHans de Goede hid_err(hdev, "Long DJ report bad size (%d)", size);
1724f5fb57a7SBenjamin Tissoires return false;
1725f5fb57a7SBenjamin Tissoires }
1726f5fb57a7SBenjamin Tissoires return logi_dj_dj_event(hdev, report, data, size);
1727925f0f3eSBenjamin Tissoires case REPORT_ID_HIDPP_SHORT:
1728f254ae93SPeter Wu if (size != HIDPP_REPORT_SHORT_LENGTH) {
1729aca22a35SHans de Goede hid_err(hdev, "Short HID++ report bad size (%d)", size);
1730f254ae93SPeter Wu return false;
1731f254ae93SPeter Wu }
1732f254ae93SPeter Wu return logi_dj_hidpp_event(hdev, report, data, size);
1733925f0f3eSBenjamin Tissoires case REPORT_ID_HIDPP_LONG:
1734f254ae93SPeter Wu if (size != HIDPP_REPORT_LONG_LENGTH) {
1735aca22a35SHans de Goede hid_err(hdev, "Long HID++ report bad size (%d)", size);
1736f254ae93SPeter Wu return false;
1737f254ae93SPeter Wu }
1738925f0f3eSBenjamin Tissoires return logi_dj_hidpp_event(hdev, report, data, size);
1739925f0f3eSBenjamin Tissoires }
1740925f0f3eSBenjamin Tissoires
174174808f91SHans de Goede logi_dj_recv_forward_input_report(hdev, data, size);
174274808f91SHans de Goede
1743925f0f3eSBenjamin Tissoires return false;
1744925f0f3eSBenjamin Tissoires }
1745925f0f3eSBenjamin Tissoires
logi_dj_probe(struct hid_device * hdev,const struct hid_device_id * id)1746534a7b8eSNestor Lopez Casado static int logi_dj_probe(struct hid_device *hdev,
1747534a7b8eSNestor Lopez Casado const struct hid_device_id *id)
1748534a7b8eSNestor Lopez Casado {
174982c0beb8SBenjamin Tissoires struct hid_report_enum *rep_enum;
175082c0beb8SBenjamin Tissoires struct hid_report *rep;
1751534a7b8eSNestor Lopez Casado struct dj_receiver_dev *djrcv_dev;
1752da12b224SHans de Goede struct usb_interface *intf;
1753da12b224SHans de Goede unsigned int no_dj_interfaces = 0;
175482c0beb8SBenjamin Tissoires bool has_hidpp = false;
1755a1d97ccbSHans de Goede unsigned long flags;
1756534a7b8eSNestor Lopez Casado int retval;
1757534a7b8eSNestor Lopez Casado
175882c0beb8SBenjamin Tissoires /*
175982c0beb8SBenjamin Tissoires * Call to usbhid to fetch the HID descriptors of the current
176082c0beb8SBenjamin Tissoires * interface subsequently call to the hid/hid-core to parse the
176182c0beb8SBenjamin Tissoires * fetched descriptors.
176282c0beb8SBenjamin Tissoires */
176382c0beb8SBenjamin Tissoires retval = hid_parse(hdev);
176482c0beb8SBenjamin Tissoires if (retval) {
1765aca22a35SHans de Goede hid_err(hdev, "%s: parse failed\n", __func__);
176682c0beb8SBenjamin Tissoires return retval;
1767534a7b8eSNestor Lopez Casado }
1768534a7b8eSNestor Lopez Casado
1769da12b224SHans de Goede /*
1770da12b224SHans de Goede * Some KVMs add an extra interface for e.g. mouse emulation. If we
1771da12b224SHans de Goede * treat these as logitech-dj interfaces then this causes input events
1772da12b224SHans de Goede * reported through this extra interface to not be reported correctly.
1773da12b224SHans de Goede * To avoid this, we treat these as generic-hid devices.
1774da12b224SHans de Goede */
1775da12b224SHans de Goede switch (id->driver_data) {
1776da12b224SHans de Goede case recvr_type_dj: no_dj_interfaces = 3; break;
1777da12b224SHans de Goede case recvr_type_hidpp: no_dj_interfaces = 2; break;
1778da12b224SHans de Goede case recvr_type_gaming_hidpp: no_dj_interfaces = 3; break;
17793ed224e2SHans de Goede case recvr_type_mouse_only: no_dj_interfaces = 2; break;
1780da12b224SHans de Goede case recvr_type_27mhz: no_dj_interfaces = 2; break;
1781f2113c30SHans de Goede case recvr_type_bluetooth: no_dj_interfaces = 2; break;
1782434f7709SHans de Goede case recvr_type_dinovo: no_dj_interfaces = 2; break;
1783da12b224SHans de Goede }
1784f83baa0cSGreg Kroah-Hartman if (hid_is_usb(hdev)) {
1785da12b224SHans de Goede intf = to_usb_interface(hdev->dev.parent);
1786da12b224SHans de Goede if (intf && intf->altsetting->desc.bInterfaceNumber >=
1787da12b224SHans de Goede no_dj_interfaces) {
1788da12b224SHans de Goede hdev->quirks |= HID_QUIRK_INPUT_PER_APP;
1789da12b224SHans de Goede return hid_hw_start(hdev, HID_CONNECT_DEFAULT);
1790da12b224SHans de Goede }
1791da12b224SHans de Goede }
1792da12b224SHans de Goede
179382c0beb8SBenjamin Tissoires rep_enum = &hdev->report_enum[HID_INPUT_REPORT];
179482c0beb8SBenjamin Tissoires
179574808f91SHans de Goede /* no input reports, bail out */
179674808f91SHans de Goede if (list_empty(&rep_enum->report_list))
179774808f91SHans de Goede return -ENODEV;
179874808f91SHans de Goede
179982c0beb8SBenjamin Tissoires /*
180082c0beb8SBenjamin Tissoires * Check for the HID++ application.
180182c0beb8SBenjamin Tissoires * Note: we should theoretically check for HID++ and DJ
180282c0beb8SBenjamin Tissoires * collections, but this will do.
180382c0beb8SBenjamin Tissoires */
180482c0beb8SBenjamin Tissoires list_for_each_entry(rep, &rep_enum->report_list, list) {
180582c0beb8SBenjamin Tissoires if (rep->application == 0xff000001)
180682c0beb8SBenjamin Tissoires has_hidpp = true;
180782c0beb8SBenjamin Tissoires }
180882c0beb8SBenjamin Tissoires
180982c0beb8SBenjamin Tissoires /*
181082c0beb8SBenjamin Tissoires * Ignore interfaces without DJ/HID++ collection, they will not carry
181182c0beb8SBenjamin Tissoires * any data, dont create any hid_device for them.
181282c0beb8SBenjamin Tissoires */
181374808f91SHans de Goede if (!has_hidpp && id->driver_data == recvr_type_dj)
181482c0beb8SBenjamin Tissoires return -ENODEV;
181582c0beb8SBenjamin Tissoires
1816a1d97ccbSHans de Goede /* get the current application attached to the node */
1817a1d97ccbSHans de Goede rep = list_first_entry(&rep_enum->report_list, struct hid_report, list);
181874808f91SHans de Goede djrcv_dev = dj_get_receiver_dev(hdev, id->driver_data,
1819a1d97ccbSHans de Goede rep->application, has_hidpp);
1820534a7b8eSNestor Lopez Casado if (!djrcv_dev) {
1821aca22a35SHans de Goede hid_err(hdev, "%s: dj_get_receiver_dev failed\n", __func__);
1822534a7b8eSNestor Lopez Casado return -ENOMEM;
1823534a7b8eSNestor Lopez Casado }
1824297502abSKees Cook
182574808f91SHans de Goede if (!rep_enum->numbered)
182674808f91SHans de Goede djrcv_dev->unnumbered_application = rep->application;
182774808f91SHans de Goede
1828534a7b8eSNestor Lopez Casado /* Starts the usb device and connects to upper interfaces hiddev and
1829534a7b8eSNestor Lopez Casado * hidraw */
183074808f91SHans de Goede retval = hid_hw_start(hdev, HID_CONNECT_HIDRAW|HID_CONNECT_HIDDEV);
1831534a7b8eSNestor Lopez Casado if (retval) {
1832aca22a35SHans de Goede hid_err(hdev, "%s: hid_hw_start returned error\n", __func__);
1833534a7b8eSNestor Lopez Casado goto hid_hw_start_fail;
1834534a7b8eSNestor Lopez Casado }
1835534a7b8eSNestor Lopez Casado
183674808f91SHans de Goede if (has_hidpp) {
1837534a7b8eSNestor Lopez Casado retval = logi_dj_recv_switch_to_dj_mode(djrcv_dev, 0);
1838534a7b8eSNestor Lopez Casado if (retval < 0) {
183974808f91SHans de Goede hid_err(hdev, "%s: logi_dj_recv_switch_to_dj_mode returned error:%d\n",
1840534a7b8eSNestor Lopez Casado __func__, retval);
1841534a7b8eSNestor Lopez Casado goto switch_to_dj_mode_fail;
1842534a7b8eSNestor Lopez Casado }
184374808f91SHans de Goede }
1844534a7b8eSNestor Lopez Casado
1845534a7b8eSNestor Lopez Casado /* This is enabling the polling urb on the IN endpoint */
1846ddf7540eSBenjamin Tissoires retval = hid_hw_open(hdev);
1847534a7b8eSNestor Lopez Casado if (retval < 0) {
1848aca22a35SHans de Goede hid_err(hdev, "%s: hid_hw_open returned error:%d\n",
1849ddf7540eSBenjamin Tissoires __func__, retval);
1850534a7b8eSNestor Lopez Casado goto llopen_failed;
1851534a7b8eSNestor Lopez Casado }
1852534a7b8eSNestor Lopez Casado
1853a9dd22b7SAndrew de los Reyes /* Allow incoming packets to arrive: */
1854a9dd22b7SAndrew de los Reyes hid_device_io_start(hdev);
1855a9dd22b7SAndrew de los Reyes
185674808f91SHans de Goede if (has_hidpp) {
1857a1d97ccbSHans de Goede spin_lock_irqsave(&djrcv_dev->lock, flags);
1858a1d97ccbSHans de Goede djrcv_dev->ready = true;
1859a1d97ccbSHans de Goede spin_unlock_irqrestore(&djrcv_dev->lock, flags);
1860534a7b8eSNestor Lopez Casado retval = logi_dj_recv_query_paired_devices(djrcv_dev);
1861534a7b8eSNestor Lopez Casado if (retval < 0) {
186274808f91SHans de Goede hid_err(hdev, "%s: logi_dj_recv_query_paired_devices error:%d\n",
186374808f91SHans de Goede __func__, retval);
18648ccff284SHans de Goede /*
18658ccff284SHans de Goede * This can happen with a KVM, let the probe succeed,
18668ccff284SHans de Goede * logi_dj_recv_queue_unknown_work will retry later.
18678ccff284SHans de Goede */
1868534a7b8eSNestor Lopez Casado }
186974808f91SHans de Goede }
1870534a7b8eSNestor Lopez Casado
18718ccff284SHans de Goede return 0;
1872534a7b8eSNestor Lopez Casado
1873534a7b8eSNestor Lopez Casado llopen_failed:
1874534a7b8eSNestor Lopez Casado switch_to_dj_mode_fail:
1875534a7b8eSNestor Lopez Casado hid_hw_stop(hdev);
1876534a7b8eSNestor Lopez Casado
1877534a7b8eSNestor Lopez Casado hid_hw_start_fail:
1878a1d97ccbSHans de Goede dj_put_receiver_dev(hdev);
1879534a7b8eSNestor Lopez Casado return retval;
1880534a7b8eSNestor Lopez Casado }
1881534a7b8eSNestor Lopez Casado
1882534a7b8eSNestor Lopez Casado #ifdef CONFIG_PM
logi_dj_reset_resume(struct hid_device * hdev)1883534a7b8eSNestor Lopez Casado static int logi_dj_reset_resume(struct hid_device *hdev)
1884534a7b8eSNestor Lopez Casado {
1885534a7b8eSNestor Lopez Casado int retval;
1886534a7b8eSNestor Lopez Casado struct dj_receiver_dev *djrcv_dev = hid_get_drvdata(hdev);
1887534a7b8eSNestor Lopez Casado
1888da12b224SHans de Goede if (!djrcv_dev || djrcv_dev->hidpp != hdev)
188974808f91SHans de Goede return 0;
189074808f91SHans de Goede
1891534a7b8eSNestor Lopez Casado retval = logi_dj_recv_switch_to_dj_mode(djrcv_dev, 0);
1892534a7b8eSNestor Lopez Casado if (retval < 0) {
1893aca22a35SHans de Goede hid_err(hdev, "%s: logi_dj_recv_switch_to_dj_mode returned error:%d\n",
1894534a7b8eSNestor Lopez Casado __func__, retval);
1895534a7b8eSNestor Lopez Casado }
1896534a7b8eSNestor Lopez Casado
1897534a7b8eSNestor Lopez Casado return 0;
1898534a7b8eSNestor Lopez Casado }
1899534a7b8eSNestor Lopez Casado #endif
1900534a7b8eSNestor Lopez Casado
logi_dj_remove(struct hid_device * hdev)1901534a7b8eSNestor Lopez Casado static void logi_dj_remove(struct hid_device *hdev)
1902534a7b8eSNestor Lopez Casado {
1903534a7b8eSNestor Lopez Casado struct dj_receiver_dev *djrcv_dev = hid_get_drvdata(hdev);
1904534a7b8eSNestor Lopez Casado struct dj_device *dj_dev;
1905a1d97ccbSHans de Goede unsigned long flags;
1906534a7b8eSNestor Lopez Casado int i;
1907534a7b8eSNestor Lopez Casado
1908534a7b8eSNestor Lopez Casado dbg_hid("%s\n", __func__);
1909534a7b8eSNestor Lopez Casado
1910da12b224SHans de Goede if (!djrcv_dev)
1911da12b224SHans de Goede return hid_hw_stop(hdev);
1912da12b224SHans de Goede
1913a1d97ccbSHans de Goede /*
1914a1d97ccbSHans de Goede * This ensures that if the work gets requeued from another
1915a1d97ccbSHans de Goede * interface of the same receiver it will be a no-op.
1916a1d97ccbSHans de Goede */
1917a1d97ccbSHans de Goede spin_lock_irqsave(&djrcv_dev->lock, flags);
1918a1d97ccbSHans de Goede djrcv_dev->ready = false;
1919a1d97ccbSHans de Goede spin_unlock_irqrestore(&djrcv_dev->lock, flags);
1920a1d97ccbSHans de Goede
1921534a7b8eSNestor Lopez Casado cancel_work_sync(&djrcv_dev->work);
1922534a7b8eSNestor Lopez Casado
1923ddf7540eSBenjamin Tissoires hid_hw_close(hdev);
1924534a7b8eSNestor Lopez Casado hid_hw_stop(hdev);
1925534a7b8eSNestor Lopez Casado
1926a1d97ccbSHans de Goede /*
1927a1d97ccbSHans de Goede * For proper operation we need access to all interfaces, so we destroy
1928a1d97ccbSHans de Goede * the paired devices when we're unbound from any interface.
1929a1d97ccbSHans de Goede *
1930a1d97ccbSHans de Goede * Note we may still be bound to other interfaces, sharing the same
1931a1d97ccbSHans de Goede * djrcv_dev, so we need locking here.
1932a1d97ccbSHans de Goede */
1933844580ffSNestor Lopez Casado for (i = 0; i < (DJ_MAX_PAIRED_DEVICES + DJ_DEVICE_INDEX_MIN); i++) {
1934a1d97ccbSHans de Goede spin_lock_irqsave(&djrcv_dev->lock, flags);
1935534a7b8eSNestor Lopez Casado dj_dev = djrcv_dev->paired_dj_devices[i];
1936a1d97ccbSHans de Goede djrcv_dev->paired_dj_devices[i] = NULL;
1937a1d97ccbSHans de Goede spin_unlock_irqrestore(&djrcv_dev->lock, flags);
1938534a7b8eSNestor Lopez Casado if (dj_dev != NULL) {
1939534a7b8eSNestor Lopez Casado hid_destroy_device(dj_dev->hdev);
1940534a7b8eSNestor Lopez Casado kfree(dj_dev);
1941534a7b8eSNestor Lopez Casado }
1942534a7b8eSNestor Lopez Casado }
1943534a7b8eSNestor Lopez Casado
1944a1d97ccbSHans de Goede dj_put_receiver_dev(hdev);
1945534a7b8eSNestor Lopez Casado }
1946534a7b8eSNestor Lopez Casado
1947534a7b8eSNestor Lopez Casado static const struct hid_device_id logi_dj_receivers[] = {
1948751cb651SHans de Goede { /* Logitech unifying receiver (0xc52b) */
1949751cb651SHans de Goede HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH,
195074808f91SHans de Goede USB_DEVICE_ID_LOGITECH_UNIFYING_RECEIVER),
195174808f91SHans de Goede .driver_data = recvr_type_dj},
1952751cb651SHans de Goede { /* Logitech unifying receiver (0xc532) */
1953751cb651SHans de Goede HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH,
195474808f91SHans de Goede USB_DEVICE_ID_LOGITECH_UNIFYING_RECEIVER_2),
195574808f91SHans de Goede .driver_data = recvr_type_dj},
1956751cb651SHans de Goede
1957751cb651SHans de Goede { /* Logitech Nano mouse only receiver (0xc52f) */
195874808f91SHans de Goede HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH,
195974808f91SHans de Goede USB_DEVICE_ID_LOGITECH_NANO_RECEIVER),
19603ed224e2SHans de Goede .driver_data = recvr_type_mouse_only},
1961751cb651SHans de Goede { /* Logitech Nano (non DJ) receiver (0xc534) */
196274808f91SHans de Goede HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH,
196374808f91SHans de Goede USB_DEVICE_ID_LOGITECH_NANO_RECEIVER_2),
196474808f91SHans de Goede .driver_data = recvr_type_hidpp},
1965751cb651SHans de Goede
1966992ff2ccSBenjamin Tissoires { /* Logitech G700(s) receiver (0xc531) */
1967992ff2ccSBenjamin Tissoires HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH,
1968751cb651SHans de Goede USB_DEVICE_ID_LOGITECH_G700_RECEIVER),
1969992ff2ccSBenjamin Tissoires .driver_data = recvr_type_gaming_hidpp},
1970e400071aSFilipe Laíns { /* Logitech G602 receiver (0xc537) */
1971e400071aSFilipe Laíns HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH,
1972e400071aSFilipe Laíns 0xc537),
1973e400071aSFilipe Laíns .driver_data = recvr_type_gaming_hidpp},
1974600ea54dSFilipe Laíns { /* Logitech lightspeed receiver (0xc539) */
1975f5fb57a7SBenjamin Tissoires HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH,
19765722f338SFilipe Laíns USB_DEVICE_ID_LOGITECH_NANO_RECEIVER_LIGHTSPEED_1),
19775722f338SFilipe Laíns .driver_data = recvr_type_gaming_hidpp},
1978d79f7badSFilipe Laíns { /* Logitech powerplay receiver (0xc53a) */
1979d79f7badSFilipe Laíns HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH,
1980d79f7badSFilipe Laíns USB_DEVICE_ID_LOGITECH_NANO_RECEIVER_POWERPLAY),
1981d79f7badSFilipe Laíns .driver_data = recvr_type_gaming_hidpp},
1982751cb651SHans de Goede { /* Logitech lightspeed receiver (0xc53f) */
1983751cb651SHans de Goede HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH,
1984751cb651SHans de Goede USB_DEVICE_ID_LOGITECH_NANO_RECEIVER_LIGHTSPEED_1_1),
1985751cb651SHans de Goede .driver_data = recvr_type_gaming_hidpp},
1986751cb651SHans de Goede
1987751cb651SHans de Goede { /* Logitech 27 MHz HID++ 1.0 receiver (0xc513) */
1988751cb651SHans de Goede HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_MX3000_RECEIVER),
1989751cb651SHans de Goede .driver_data = recvr_type_27mhz},
1990c9121cf6SHans de Goede { /* Logitech 27 MHz HID++ 1.0 receiver (0xc517) */
1991c9121cf6SHans de Goede HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH,
1992c9121cf6SHans de Goede USB_DEVICE_ID_S510_RECEIVER_2),
1993c9121cf6SHans de Goede .driver_data = recvr_type_27mhz},
19941f944ac6SHans de Goede { /* Logitech 27 MHz HID++ 1.0 mouse-only receiver (0xc51b) */
19951f944ac6SHans de Goede HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH,
19961f944ac6SHans de Goede USB_DEVICE_ID_LOGITECH_27MHZ_MOUSE_RECEIVER),
19971f944ac6SHans de Goede .driver_data = recvr_type_27mhz},
1998751cb651SHans de Goede
1999751cb651SHans de Goede { /* Logitech MX5000 HID++ / bluetooth receiver keyboard intf. (0xc70e) */
2000f2113c30SHans de Goede HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH,
2001751cb651SHans de Goede USB_DEVICE_ID_MX5000_RECEIVER_KBD_DEV),
2002f2113c30SHans de Goede .driver_data = recvr_type_bluetooth},
2003751cb651SHans de Goede { /* Logitech MX5000 HID++ / bluetooth receiver mouse intf. (0xc70a) */
2004f2113c30SHans de Goede HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH,
2005751cb651SHans de Goede USB_DEVICE_ID_MX5000_RECEIVER_MOUSE_DEV),
2006f2113c30SHans de Goede .driver_data = recvr_type_bluetooth},
2007751cb651SHans de Goede { /* Logitech MX5500 HID++ / bluetooth receiver keyboard intf. (0xc71b) */
2008b9a94fbeSHans de Goede HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH,
2009751cb651SHans de Goede USB_DEVICE_ID_MX5500_RECEIVER_KBD_DEV),
2010b9a94fbeSHans de Goede .driver_data = recvr_type_bluetooth},
2011751cb651SHans de Goede { /* Logitech MX5500 HID++ / bluetooth receiver mouse intf. (0xc71c) */
2012b9a94fbeSHans de Goede HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH,
2013751cb651SHans de Goede USB_DEVICE_ID_MX5500_RECEIVER_MOUSE_DEV),
2014b9a94fbeSHans de Goede .driver_data = recvr_type_bluetooth},
2015434f7709SHans de Goede
2016434f7709SHans de Goede { /* Logitech Dinovo Edge HID++ / bluetooth receiver keyboard intf. (0xc713) */
2017434f7709SHans de Goede HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH,
2018434f7709SHans de Goede USB_DEVICE_ID_DINOVO_EDGE_RECEIVER_KBD_DEV),
2019434f7709SHans de Goede .driver_data = recvr_type_dinovo},
2020434f7709SHans de Goede { /* Logitech Dinovo Edge HID++ / bluetooth receiver mouse intf. (0xc714) */
2021434f7709SHans de Goede HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH,
2022434f7709SHans de Goede USB_DEVICE_ID_DINOVO_EDGE_RECEIVER_MOUSE_DEV),
2023434f7709SHans de Goede .driver_data = recvr_type_dinovo},
2024434f7709SHans de Goede { /* Logitech DiNovo Mini HID++ / bluetooth receiver mouse intf. (0xc71e) */
2025434f7709SHans de Goede HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH,
2026434f7709SHans de Goede USB_DEVICE_ID_DINOVO_MINI_RECEIVER_KBD_DEV),
2027434f7709SHans de Goede .driver_data = recvr_type_dinovo},
2028434f7709SHans de Goede { /* Logitech DiNovo Mini HID++ / bluetooth receiver keyboard intf. (0xc71f) */
2029434f7709SHans de Goede HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH,
2030434f7709SHans de Goede USB_DEVICE_ID_DINOVO_MINI_RECEIVER_MOUSE_DEV),
2031434f7709SHans de Goede .driver_data = recvr_type_dinovo},
2032534a7b8eSNestor Lopez Casado {}
2033534a7b8eSNestor Lopez Casado };
2034534a7b8eSNestor Lopez Casado
2035534a7b8eSNestor Lopez Casado MODULE_DEVICE_TABLE(hid, logi_dj_receivers);
2036534a7b8eSNestor Lopez Casado
2037534a7b8eSNestor Lopez Casado static struct hid_driver logi_djreceiver_driver = {
2038534a7b8eSNestor Lopez Casado .name = "logitech-djreceiver",
2039534a7b8eSNestor Lopez Casado .id_table = logi_dj_receivers,
2040534a7b8eSNestor Lopez Casado .probe = logi_dj_probe,
2041534a7b8eSNestor Lopez Casado .remove = logi_dj_remove,
2042534a7b8eSNestor Lopez Casado .raw_event = logi_dj_raw_event,
2043534a7b8eSNestor Lopez Casado #ifdef CONFIG_PM
2044534a7b8eSNestor Lopez Casado .reset_resume = logi_dj_reset_resume,
2045534a7b8eSNestor Lopez Casado #endif
2046534a7b8eSNestor Lopez Casado };
2047534a7b8eSNestor Lopez Casado
2048ab94e562SBenjamin Tissoires module_hid_driver(logi_djreceiver_driver);
2049534a7b8eSNestor Lopez Casado
2050534a7b8eSNestor Lopez Casado MODULE_LICENSE("GPL");
2051534a7b8eSNestor Lopez Casado MODULE_AUTHOR("Logitech");
2052534a7b8eSNestor Lopez Casado MODULE_AUTHOR("Nestor Lopez Casado");
2053534a7b8eSNestor Lopez Casado MODULE_AUTHOR("nlopezcasad@logitech.com");
2054