xref: /openbmc/linux/drivers/hid/hid-logitech-dj.c (revision 6c71a0574249f5e5a45fe055ab5f837023d5eeca)
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