162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *  HID driver for Logitech receivers
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci *  Copyright (c) 2011 Logitech
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <linux/device.h>
1162306a36Sopenharmony_ci#include <linux/hid.h>
1262306a36Sopenharmony_ci#include <linux/module.h>
1362306a36Sopenharmony_ci#include <linux/kfifo.h>
1462306a36Sopenharmony_ci#include <linux/delay.h>
1562306a36Sopenharmony_ci#include <linux/usb.h> /* For to_usb_interface for kvm extra intf check */
1662306a36Sopenharmony_ci#include <asm/unaligned.h>
1762306a36Sopenharmony_ci#include "hid-ids.h"
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci#define DJ_MAX_PAIRED_DEVICES			7
2062306a36Sopenharmony_ci#define DJ_MAX_NUMBER_NOTIFS			8
2162306a36Sopenharmony_ci#define DJ_RECEIVER_INDEX			0
2262306a36Sopenharmony_ci#define DJ_DEVICE_INDEX_MIN			1
2362306a36Sopenharmony_ci#define DJ_DEVICE_INDEX_MAX			7
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci#define DJREPORT_SHORT_LENGTH			15
2662306a36Sopenharmony_ci#define DJREPORT_LONG_LENGTH			32
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci#define REPORT_ID_DJ_SHORT			0x20
2962306a36Sopenharmony_ci#define REPORT_ID_DJ_LONG			0x21
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci#define REPORT_ID_HIDPP_SHORT			0x10
3262306a36Sopenharmony_ci#define REPORT_ID_HIDPP_LONG			0x11
3362306a36Sopenharmony_ci#define REPORT_ID_HIDPP_VERY_LONG		0x12
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci#define HIDPP_REPORT_SHORT_LENGTH		7
3662306a36Sopenharmony_ci#define HIDPP_REPORT_LONG_LENGTH		20
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci#define HIDPP_RECEIVER_INDEX			0xff
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci#define REPORT_TYPE_RFREPORT_FIRST		0x01
4162306a36Sopenharmony_ci#define REPORT_TYPE_RFREPORT_LAST		0x1F
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci/* Command Switch to DJ mode */
4462306a36Sopenharmony_ci#define REPORT_TYPE_CMD_SWITCH			0x80
4562306a36Sopenharmony_ci#define CMD_SWITCH_PARAM_DEVBITFIELD		0x00
4662306a36Sopenharmony_ci#define CMD_SWITCH_PARAM_TIMEOUT_SECONDS	0x01
4762306a36Sopenharmony_ci#define TIMEOUT_NO_KEEPALIVE			0x00
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci/* Command to Get the list of Paired devices */
5062306a36Sopenharmony_ci#define REPORT_TYPE_CMD_GET_PAIRED_DEVICES	0x81
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci/* Device Paired Notification */
5362306a36Sopenharmony_ci#define REPORT_TYPE_NOTIF_DEVICE_PAIRED		0x41
5462306a36Sopenharmony_ci#define SPFUNCTION_MORE_NOTIF_EXPECTED		0x01
5562306a36Sopenharmony_ci#define SPFUNCTION_DEVICE_LIST_EMPTY		0x02
5662306a36Sopenharmony_ci#define DEVICE_PAIRED_PARAM_SPFUNCTION		0x00
5762306a36Sopenharmony_ci#define DEVICE_PAIRED_PARAM_EQUAD_ID_LSB	0x01
5862306a36Sopenharmony_ci#define DEVICE_PAIRED_PARAM_EQUAD_ID_MSB	0x02
5962306a36Sopenharmony_ci#define DEVICE_PAIRED_RF_REPORT_TYPE		0x03
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci/* Device Un-Paired Notification */
6262306a36Sopenharmony_ci#define REPORT_TYPE_NOTIF_DEVICE_UNPAIRED	0x40
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci/* Connection Status Notification */
6562306a36Sopenharmony_ci#define REPORT_TYPE_NOTIF_CONNECTION_STATUS	0x42
6662306a36Sopenharmony_ci#define CONNECTION_STATUS_PARAM_STATUS		0x00
6762306a36Sopenharmony_ci#define STATUS_LINKLOSS				0x01
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci/* Error Notification */
7062306a36Sopenharmony_ci#define REPORT_TYPE_NOTIF_ERROR			0x7F
7162306a36Sopenharmony_ci#define NOTIF_ERROR_PARAM_ETYPE			0x00
7262306a36Sopenharmony_ci#define ETYPE_KEEPALIVE_TIMEOUT			0x01
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci/* supported DJ HID && RF report types */
7562306a36Sopenharmony_ci#define REPORT_TYPE_KEYBOARD			0x01
7662306a36Sopenharmony_ci#define REPORT_TYPE_MOUSE			0x02
7762306a36Sopenharmony_ci#define REPORT_TYPE_CONSUMER_CONTROL		0x03
7862306a36Sopenharmony_ci#define REPORT_TYPE_SYSTEM_CONTROL		0x04
7962306a36Sopenharmony_ci#define REPORT_TYPE_MEDIA_CENTER		0x08
8062306a36Sopenharmony_ci#define REPORT_TYPE_LEDS			0x0E
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci/* RF Report types bitfield */
8362306a36Sopenharmony_ci#define STD_KEYBOARD				BIT(1)
8462306a36Sopenharmony_ci#define STD_MOUSE				BIT(2)
8562306a36Sopenharmony_ci#define MULTIMEDIA				BIT(3)
8662306a36Sopenharmony_ci#define POWER_KEYS				BIT(4)
8762306a36Sopenharmony_ci#define KBD_MOUSE				BIT(5)
8862306a36Sopenharmony_ci#define MEDIA_CENTER				BIT(8)
8962306a36Sopenharmony_ci#define KBD_LEDS				BIT(14)
9062306a36Sopenharmony_ci/* Fake (bitnr > NUMBER_OF_HID_REPORTS) bit to track HID++ capability */
9162306a36Sopenharmony_ci#define HIDPP					BIT_ULL(63)
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci/* HID++ Device Connected Notification */
9462306a36Sopenharmony_ci#define REPORT_TYPE_NOTIF_DEVICE_CONNECTED	0x41
9562306a36Sopenharmony_ci#define HIDPP_PARAM_PROTO_TYPE			0x00
9662306a36Sopenharmony_ci#define HIDPP_PARAM_DEVICE_INFO			0x01
9762306a36Sopenharmony_ci#define HIDPP_PARAM_EQUAD_LSB			0x02
9862306a36Sopenharmony_ci#define HIDPP_PARAM_EQUAD_MSB			0x03
9962306a36Sopenharmony_ci#define HIDPP_PARAM_27MHZ_DEVID			0x03
10062306a36Sopenharmony_ci#define HIDPP_DEVICE_TYPE_MASK			GENMASK(3, 0)
10162306a36Sopenharmony_ci#define HIDPP_LINK_STATUS_MASK			BIT(6)
10262306a36Sopenharmony_ci#define HIDPP_MANUFACTURER_MASK			BIT(7)
10362306a36Sopenharmony_ci#define HIDPP_27MHZ_SECURE_MASK			BIT(7)
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci#define HIDPP_DEVICE_TYPE_KEYBOARD		1
10662306a36Sopenharmony_ci#define HIDPP_DEVICE_TYPE_MOUSE			2
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci#define HIDPP_SET_REGISTER			0x80
10962306a36Sopenharmony_ci#define HIDPP_GET_LONG_REGISTER			0x83
11062306a36Sopenharmony_ci#define HIDPP_REG_CONNECTION_STATE		0x02
11162306a36Sopenharmony_ci#define HIDPP_REG_PAIRING_INFORMATION		0xB5
11262306a36Sopenharmony_ci#define HIDPP_PAIRING_INFORMATION		0x20
11362306a36Sopenharmony_ci#define HIDPP_FAKE_DEVICE_ARRIVAL		0x02
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_cienum recvr_type {
11662306a36Sopenharmony_ci	recvr_type_dj,
11762306a36Sopenharmony_ci	recvr_type_hidpp,
11862306a36Sopenharmony_ci	recvr_type_gaming_hidpp,
11962306a36Sopenharmony_ci	recvr_type_mouse_only,
12062306a36Sopenharmony_ci	recvr_type_27mhz,
12162306a36Sopenharmony_ci	recvr_type_bluetooth,
12262306a36Sopenharmony_ci	recvr_type_dinovo,
12362306a36Sopenharmony_ci};
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_cistruct dj_report {
12662306a36Sopenharmony_ci	u8 report_id;
12762306a36Sopenharmony_ci	u8 device_index;
12862306a36Sopenharmony_ci	u8 report_type;
12962306a36Sopenharmony_ci	u8 report_params[DJREPORT_SHORT_LENGTH - 3];
13062306a36Sopenharmony_ci};
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_cistruct hidpp_event {
13362306a36Sopenharmony_ci	u8 report_id;
13462306a36Sopenharmony_ci	u8 device_index;
13562306a36Sopenharmony_ci	u8 sub_id;
13662306a36Sopenharmony_ci	u8 params[HIDPP_REPORT_LONG_LENGTH - 3U];
13762306a36Sopenharmony_ci} __packed;
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_cistruct dj_receiver_dev {
14062306a36Sopenharmony_ci	struct hid_device *mouse;
14162306a36Sopenharmony_ci	struct hid_device *keyboard;
14262306a36Sopenharmony_ci	struct hid_device *hidpp;
14362306a36Sopenharmony_ci	struct dj_device *paired_dj_devices[DJ_MAX_PAIRED_DEVICES +
14462306a36Sopenharmony_ci					    DJ_DEVICE_INDEX_MIN];
14562306a36Sopenharmony_ci	struct list_head list;
14662306a36Sopenharmony_ci	struct kref kref;
14762306a36Sopenharmony_ci	struct work_struct work;
14862306a36Sopenharmony_ci	struct kfifo notif_fifo;
14962306a36Sopenharmony_ci	unsigned long last_query; /* in jiffies */
15062306a36Sopenharmony_ci	bool ready;
15162306a36Sopenharmony_ci	enum recvr_type type;
15262306a36Sopenharmony_ci	unsigned int unnumbered_application;
15362306a36Sopenharmony_ci	spinlock_t lock;
15462306a36Sopenharmony_ci};
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_cistruct dj_device {
15762306a36Sopenharmony_ci	struct hid_device *hdev;
15862306a36Sopenharmony_ci	struct dj_receiver_dev *dj_receiver_dev;
15962306a36Sopenharmony_ci	u64 reports_supported;
16062306a36Sopenharmony_ci	u8 device_index;
16162306a36Sopenharmony_ci};
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci#define WORKITEM_TYPE_EMPTY	0
16462306a36Sopenharmony_ci#define WORKITEM_TYPE_PAIRED	1
16562306a36Sopenharmony_ci#define WORKITEM_TYPE_UNPAIRED	2
16662306a36Sopenharmony_ci#define WORKITEM_TYPE_UNKNOWN	255
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_cistruct dj_workitem {
16962306a36Sopenharmony_ci	u8 type;		/* WORKITEM_TYPE_* */
17062306a36Sopenharmony_ci	u8 device_index;
17162306a36Sopenharmony_ci	u8 device_type;
17262306a36Sopenharmony_ci	u8 quad_id_msb;
17362306a36Sopenharmony_ci	u8 quad_id_lsb;
17462306a36Sopenharmony_ci	u64 reports_supported;
17562306a36Sopenharmony_ci};
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci/* Keyboard descriptor (1) */
17862306a36Sopenharmony_cistatic const char kbd_descriptor[] = {
17962306a36Sopenharmony_ci	0x05, 0x01,		/* USAGE_PAGE (generic Desktop)     */
18062306a36Sopenharmony_ci	0x09, 0x06,		/* USAGE (Keyboard)         */
18162306a36Sopenharmony_ci	0xA1, 0x01,		/* COLLECTION (Application)     */
18262306a36Sopenharmony_ci	0x85, 0x01,		/* REPORT_ID (1)            */
18362306a36Sopenharmony_ci	0x95, 0x08,		/*   REPORT_COUNT (8)           */
18462306a36Sopenharmony_ci	0x75, 0x01,		/*   REPORT_SIZE (1)            */
18562306a36Sopenharmony_ci	0x15, 0x00,		/*   LOGICAL_MINIMUM (0)        */
18662306a36Sopenharmony_ci	0x25, 0x01,		/*   LOGICAL_MAXIMUM (1)        */
18762306a36Sopenharmony_ci	0x05, 0x07,		/*   USAGE_PAGE (Keyboard)      */
18862306a36Sopenharmony_ci	0x19, 0xE0,		/*   USAGE_MINIMUM (Left Control)   */
18962306a36Sopenharmony_ci	0x29, 0xE7,		/*   USAGE_MAXIMUM (Right GUI)      */
19062306a36Sopenharmony_ci	0x81, 0x02,		/*   INPUT (Data,Var,Abs)       */
19162306a36Sopenharmony_ci	0x95, 0x06,		/*   REPORT_COUNT (6)           */
19262306a36Sopenharmony_ci	0x75, 0x08,		/*   REPORT_SIZE (8)            */
19362306a36Sopenharmony_ci	0x15, 0x00,		/*   LOGICAL_MINIMUM (0)        */
19462306a36Sopenharmony_ci	0x26, 0xFF, 0x00,	/*   LOGICAL_MAXIMUM (255)      */
19562306a36Sopenharmony_ci	0x05, 0x07,		/*   USAGE_PAGE (Keyboard)      */
19662306a36Sopenharmony_ci	0x19, 0x00,		/*   USAGE_MINIMUM (no event)       */
19762306a36Sopenharmony_ci	0x2A, 0xFF, 0x00,	/*   USAGE_MAXIMUM (reserved)       */
19862306a36Sopenharmony_ci	0x81, 0x00,		/*   INPUT (Data,Ary,Abs)       */
19962306a36Sopenharmony_ci	0x85, 0x0e,		/* REPORT_ID (14)               */
20062306a36Sopenharmony_ci	0x05, 0x08,		/*   USAGE PAGE (LED page)      */
20162306a36Sopenharmony_ci	0x95, 0x05,		/*   REPORT COUNT (5)           */
20262306a36Sopenharmony_ci	0x75, 0x01,		/*   REPORT SIZE (1)            */
20362306a36Sopenharmony_ci	0x15, 0x00,		/*   LOGICAL_MINIMUM (0)        */
20462306a36Sopenharmony_ci	0x25, 0x01,		/*   LOGICAL_MAXIMUM (1)        */
20562306a36Sopenharmony_ci	0x19, 0x01,		/*   USAGE MINIMUM (1)          */
20662306a36Sopenharmony_ci	0x29, 0x05,		/*   USAGE MAXIMUM (5)          */
20762306a36Sopenharmony_ci	0x91, 0x02,		/*   OUTPUT (Data, Variable, Absolute)  */
20862306a36Sopenharmony_ci	0x95, 0x01,		/*   REPORT COUNT (1)           */
20962306a36Sopenharmony_ci	0x75, 0x03,		/*   REPORT SIZE (3)            */
21062306a36Sopenharmony_ci	0x91, 0x01,		/*   OUTPUT (Constant)          */
21162306a36Sopenharmony_ci	0xC0
21262306a36Sopenharmony_ci};
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci/* Mouse descriptor (2)     */
21562306a36Sopenharmony_cistatic const char mse_descriptor[] = {
21662306a36Sopenharmony_ci	0x05, 0x01,		/*  USAGE_PAGE (Generic Desktop)        */
21762306a36Sopenharmony_ci	0x09, 0x02,		/*  USAGE (Mouse)                       */
21862306a36Sopenharmony_ci	0xA1, 0x01,		/*  COLLECTION (Application)            */
21962306a36Sopenharmony_ci	0x85, 0x02,		/*    REPORT_ID = 2                     */
22062306a36Sopenharmony_ci	0x09, 0x01,		/*    USAGE (pointer)                   */
22162306a36Sopenharmony_ci	0xA1, 0x00,		/*    COLLECTION (physical)             */
22262306a36Sopenharmony_ci	0x05, 0x09,		/*      USAGE_PAGE (buttons)            */
22362306a36Sopenharmony_ci	0x19, 0x01,		/*      USAGE_MIN (1)                   */
22462306a36Sopenharmony_ci	0x29, 0x10,		/*      USAGE_MAX (16)                  */
22562306a36Sopenharmony_ci	0x15, 0x00,		/*      LOGICAL_MIN (0)                 */
22662306a36Sopenharmony_ci	0x25, 0x01,		/*      LOGICAL_MAX (1)                 */
22762306a36Sopenharmony_ci	0x95, 0x10,		/*      REPORT_COUNT (16)               */
22862306a36Sopenharmony_ci	0x75, 0x01,		/*      REPORT_SIZE (1)                 */
22962306a36Sopenharmony_ci	0x81, 0x02,		/*      INPUT (data var abs)            */
23062306a36Sopenharmony_ci	0x05, 0x01,		/*      USAGE_PAGE (generic desktop)    */
23162306a36Sopenharmony_ci	0x16, 0x01, 0xF8,	/*      LOGICAL_MIN (-2047)             */
23262306a36Sopenharmony_ci	0x26, 0xFF, 0x07,	/*      LOGICAL_MAX (2047)              */
23362306a36Sopenharmony_ci	0x75, 0x0C,		/*      REPORT_SIZE (12)                */
23462306a36Sopenharmony_ci	0x95, 0x02,		/*      REPORT_COUNT (2)                */
23562306a36Sopenharmony_ci	0x09, 0x30,		/*      USAGE (X)                       */
23662306a36Sopenharmony_ci	0x09, 0x31,		/*      USAGE (Y)                       */
23762306a36Sopenharmony_ci	0x81, 0x06,		/*      INPUT                           */
23862306a36Sopenharmony_ci	0x15, 0x81,		/*      LOGICAL_MIN (-127)              */
23962306a36Sopenharmony_ci	0x25, 0x7F,		/*      LOGICAL_MAX (127)               */
24062306a36Sopenharmony_ci	0x75, 0x08,		/*      REPORT_SIZE (8)                 */
24162306a36Sopenharmony_ci	0x95, 0x01,		/*      REPORT_COUNT (1)                */
24262306a36Sopenharmony_ci	0x09, 0x38,		/*      USAGE (wheel)                   */
24362306a36Sopenharmony_ci	0x81, 0x06,		/*      INPUT                           */
24462306a36Sopenharmony_ci	0x05, 0x0C,		/*      USAGE_PAGE(consumer)            */
24562306a36Sopenharmony_ci	0x0A, 0x38, 0x02,	/*      USAGE(AC Pan)                   */
24662306a36Sopenharmony_ci	0x95, 0x01,		/*      REPORT_COUNT (1)                */
24762306a36Sopenharmony_ci	0x81, 0x06,		/*      INPUT                           */
24862306a36Sopenharmony_ci	0xC0,			/*    END_COLLECTION                    */
24962306a36Sopenharmony_ci	0xC0,			/*  END_COLLECTION                      */
25062306a36Sopenharmony_ci};
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci/* Mouse descriptor (2) for 27 MHz receiver, only 8 buttons */
25362306a36Sopenharmony_cistatic const char mse_27mhz_descriptor[] = {
25462306a36Sopenharmony_ci	0x05, 0x01,		/*  USAGE_PAGE (Generic Desktop)        */
25562306a36Sopenharmony_ci	0x09, 0x02,		/*  USAGE (Mouse)                       */
25662306a36Sopenharmony_ci	0xA1, 0x01,		/*  COLLECTION (Application)            */
25762306a36Sopenharmony_ci	0x85, 0x02,		/*    REPORT_ID = 2                     */
25862306a36Sopenharmony_ci	0x09, 0x01,		/*    USAGE (pointer)                   */
25962306a36Sopenharmony_ci	0xA1, 0x00,		/*    COLLECTION (physical)             */
26062306a36Sopenharmony_ci	0x05, 0x09,		/*      USAGE_PAGE (buttons)            */
26162306a36Sopenharmony_ci	0x19, 0x01,		/*      USAGE_MIN (1)                   */
26262306a36Sopenharmony_ci	0x29, 0x08,		/*      USAGE_MAX (8)                   */
26362306a36Sopenharmony_ci	0x15, 0x00,		/*      LOGICAL_MIN (0)                 */
26462306a36Sopenharmony_ci	0x25, 0x01,		/*      LOGICAL_MAX (1)                 */
26562306a36Sopenharmony_ci	0x95, 0x08,		/*      REPORT_COUNT (8)                */
26662306a36Sopenharmony_ci	0x75, 0x01,		/*      REPORT_SIZE (1)                 */
26762306a36Sopenharmony_ci	0x81, 0x02,		/*      INPUT (data var abs)            */
26862306a36Sopenharmony_ci	0x05, 0x01,		/*      USAGE_PAGE (generic desktop)    */
26962306a36Sopenharmony_ci	0x16, 0x01, 0xF8,	/*      LOGICAL_MIN (-2047)             */
27062306a36Sopenharmony_ci	0x26, 0xFF, 0x07,	/*      LOGICAL_MAX (2047)              */
27162306a36Sopenharmony_ci	0x75, 0x0C,		/*      REPORT_SIZE (12)                */
27262306a36Sopenharmony_ci	0x95, 0x02,		/*      REPORT_COUNT (2)                */
27362306a36Sopenharmony_ci	0x09, 0x30,		/*      USAGE (X)                       */
27462306a36Sopenharmony_ci	0x09, 0x31,		/*      USAGE (Y)                       */
27562306a36Sopenharmony_ci	0x81, 0x06,		/*      INPUT                           */
27662306a36Sopenharmony_ci	0x15, 0x81,		/*      LOGICAL_MIN (-127)              */
27762306a36Sopenharmony_ci	0x25, 0x7F,		/*      LOGICAL_MAX (127)               */
27862306a36Sopenharmony_ci	0x75, 0x08,		/*      REPORT_SIZE (8)                 */
27962306a36Sopenharmony_ci	0x95, 0x01,		/*      REPORT_COUNT (1)                */
28062306a36Sopenharmony_ci	0x09, 0x38,		/*      USAGE (wheel)                   */
28162306a36Sopenharmony_ci	0x81, 0x06,		/*      INPUT                           */
28262306a36Sopenharmony_ci	0x05, 0x0C,		/*      USAGE_PAGE(consumer)            */
28362306a36Sopenharmony_ci	0x0A, 0x38, 0x02,	/*      USAGE(AC Pan)                   */
28462306a36Sopenharmony_ci	0x95, 0x01,		/*      REPORT_COUNT (1)                */
28562306a36Sopenharmony_ci	0x81, 0x06,		/*      INPUT                           */
28662306a36Sopenharmony_ci	0xC0,			/*    END_COLLECTION                    */
28762306a36Sopenharmony_ci	0xC0,			/*  END_COLLECTION                      */
28862306a36Sopenharmony_ci};
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci/* Mouse descriptor (2) for Bluetooth receiver, low-res hwheel, 12 buttons */
29162306a36Sopenharmony_cistatic const char mse_bluetooth_descriptor[] = {
29262306a36Sopenharmony_ci	0x05, 0x01,		/*  USAGE_PAGE (Generic Desktop)        */
29362306a36Sopenharmony_ci	0x09, 0x02,		/*  USAGE (Mouse)                       */
29462306a36Sopenharmony_ci	0xA1, 0x01,		/*  COLLECTION (Application)            */
29562306a36Sopenharmony_ci	0x85, 0x02,		/*    REPORT_ID = 2                     */
29662306a36Sopenharmony_ci	0x09, 0x01,		/*    USAGE (pointer)                   */
29762306a36Sopenharmony_ci	0xA1, 0x00,		/*    COLLECTION (physical)             */
29862306a36Sopenharmony_ci	0x05, 0x09,		/*      USAGE_PAGE (buttons)            */
29962306a36Sopenharmony_ci	0x19, 0x01,		/*      USAGE_MIN (1)                   */
30062306a36Sopenharmony_ci	0x29, 0x08,		/*      USAGE_MAX (8)                   */
30162306a36Sopenharmony_ci	0x15, 0x00,		/*      LOGICAL_MIN (0)                 */
30262306a36Sopenharmony_ci	0x25, 0x01,		/*      LOGICAL_MAX (1)                 */
30362306a36Sopenharmony_ci	0x95, 0x08,		/*      REPORT_COUNT (8)                */
30462306a36Sopenharmony_ci	0x75, 0x01,		/*      REPORT_SIZE (1)                 */
30562306a36Sopenharmony_ci	0x81, 0x02,		/*      INPUT (data var abs)            */
30662306a36Sopenharmony_ci	0x05, 0x01,		/*      USAGE_PAGE (generic desktop)    */
30762306a36Sopenharmony_ci	0x16, 0x01, 0xF8,	/*      LOGICAL_MIN (-2047)             */
30862306a36Sopenharmony_ci	0x26, 0xFF, 0x07,	/*      LOGICAL_MAX (2047)              */
30962306a36Sopenharmony_ci	0x75, 0x0C,		/*      REPORT_SIZE (12)                */
31062306a36Sopenharmony_ci	0x95, 0x02,		/*      REPORT_COUNT (2)                */
31162306a36Sopenharmony_ci	0x09, 0x30,		/*      USAGE (X)                       */
31262306a36Sopenharmony_ci	0x09, 0x31,		/*      USAGE (Y)                       */
31362306a36Sopenharmony_ci	0x81, 0x06,		/*      INPUT                           */
31462306a36Sopenharmony_ci	0x15, 0x81,		/*      LOGICAL_MIN (-127)              */
31562306a36Sopenharmony_ci	0x25, 0x7F,		/*      LOGICAL_MAX (127)               */
31662306a36Sopenharmony_ci	0x75, 0x08,		/*      REPORT_SIZE (8)                 */
31762306a36Sopenharmony_ci	0x95, 0x01,		/*      REPORT_COUNT (1)                */
31862306a36Sopenharmony_ci	0x09, 0x38,		/*      USAGE (wheel)                   */
31962306a36Sopenharmony_ci	0x81, 0x06,		/*      INPUT                           */
32062306a36Sopenharmony_ci	0x05, 0x0C,		/*      USAGE_PAGE(consumer)            */
32162306a36Sopenharmony_ci	0x0A, 0x38, 0x02,	/*      USAGE(AC Pan)                   */
32262306a36Sopenharmony_ci	0x15, 0xF9,		/*      LOGICAL_MIN (-7)                */
32362306a36Sopenharmony_ci	0x25, 0x07,		/*      LOGICAL_MAX (7)                 */
32462306a36Sopenharmony_ci	0x75, 0x04,		/*      REPORT_SIZE (4)                 */
32562306a36Sopenharmony_ci	0x95, 0x01,		/*      REPORT_COUNT (1)                */
32662306a36Sopenharmony_ci	0x81, 0x06,		/*      INPUT                           */
32762306a36Sopenharmony_ci	0x05, 0x09,		/*      USAGE_PAGE (buttons)            */
32862306a36Sopenharmony_ci	0x19, 0x09,		/*      USAGE_MIN (9)                   */
32962306a36Sopenharmony_ci	0x29, 0x0C,		/*      USAGE_MAX (12)                  */
33062306a36Sopenharmony_ci	0x15, 0x00,		/*      LOGICAL_MIN (0)                 */
33162306a36Sopenharmony_ci	0x25, 0x01,		/*      LOGICAL_MAX (1)                 */
33262306a36Sopenharmony_ci	0x75, 0x01,		/*      REPORT_SIZE (1)                 */
33362306a36Sopenharmony_ci	0x95, 0x04,		/*      REPORT_COUNT (4)                */
33462306a36Sopenharmony_ci	0x81, 0x02,		/*      INPUT (Data,Var,Abs)            */
33562306a36Sopenharmony_ci	0xC0,			/*    END_COLLECTION                    */
33662306a36Sopenharmony_ci	0xC0,			/*  END_COLLECTION                      */
33762306a36Sopenharmony_ci};
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ci/* Mouse descriptor (5) for Bluetooth receiver, normal-res hwheel, 8 buttons */
34062306a36Sopenharmony_cistatic const char mse5_bluetooth_descriptor[] = {
34162306a36Sopenharmony_ci	0x05, 0x01,		/*  USAGE_PAGE (Generic Desktop)        */
34262306a36Sopenharmony_ci	0x09, 0x02,		/*  Usage (Mouse)                       */
34362306a36Sopenharmony_ci	0xa1, 0x01,		/*  Collection (Application)            */
34462306a36Sopenharmony_ci	0x85, 0x05,		/*   Report ID (5)                      */
34562306a36Sopenharmony_ci	0x09, 0x01,		/*   Usage (Pointer)                    */
34662306a36Sopenharmony_ci	0xa1, 0x00,		/*   Collection (Physical)              */
34762306a36Sopenharmony_ci	0x05, 0x09,		/*    Usage Page (Button)               */
34862306a36Sopenharmony_ci	0x19, 0x01,		/*    Usage Minimum (1)                 */
34962306a36Sopenharmony_ci	0x29, 0x08,		/*    Usage Maximum (8)                 */
35062306a36Sopenharmony_ci	0x15, 0x00,		/*    Logical Minimum (0)               */
35162306a36Sopenharmony_ci	0x25, 0x01,		/*    Logical Maximum (1)               */
35262306a36Sopenharmony_ci	0x95, 0x08,		/*    Report Count (8)                  */
35362306a36Sopenharmony_ci	0x75, 0x01,		/*    Report Size (1)                   */
35462306a36Sopenharmony_ci	0x81, 0x02,		/*    Input (Data,Var,Abs)              */
35562306a36Sopenharmony_ci	0x05, 0x01,		/*    Usage Page (Generic Desktop)      */
35662306a36Sopenharmony_ci	0x16, 0x01, 0xf8,	/*    Logical Minimum (-2047)           */
35762306a36Sopenharmony_ci	0x26, 0xff, 0x07,	/*    Logical Maximum (2047)            */
35862306a36Sopenharmony_ci	0x75, 0x0c,		/*    Report Size (12)                  */
35962306a36Sopenharmony_ci	0x95, 0x02,		/*    Report Count (2)                  */
36062306a36Sopenharmony_ci	0x09, 0x30,		/*    Usage (X)                         */
36162306a36Sopenharmony_ci	0x09, 0x31,		/*    Usage (Y)                         */
36262306a36Sopenharmony_ci	0x81, 0x06,		/*    Input (Data,Var,Rel)              */
36362306a36Sopenharmony_ci	0x15, 0x81,		/*    Logical Minimum (-127)            */
36462306a36Sopenharmony_ci	0x25, 0x7f,		/*    Logical Maximum (127)             */
36562306a36Sopenharmony_ci	0x75, 0x08,		/*    Report Size (8)                   */
36662306a36Sopenharmony_ci	0x95, 0x01,		/*    Report Count (1)                  */
36762306a36Sopenharmony_ci	0x09, 0x38,		/*    Usage (Wheel)                     */
36862306a36Sopenharmony_ci	0x81, 0x06,		/*    Input (Data,Var,Rel)              */
36962306a36Sopenharmony_ci	0x05, 0x0c,		/*    Usage Page (Consumer Devices)     */
37062306a36Sopenharmony_ci	0x0a, 0x38, 0x02,	/*    Usage (AC Pan)                    */
37162306a36Sopenharmony_ci	0x15, 0x81,		/*    Logical Minimum (-127)            */
37262306a36Sopenharmony_ci	0x25, 0x7f,		/*    Logical Maximum (127)             */
37362306a36Sopenharmony_ci	0x75, 0x08,		/*    Report Size (8)                   */
37462306a36Sopenharmony_ci	0x95, 0x01,		/*    Report Count (1)                  */
37562306a36Sopenharmony_ci	0x81, 0x06,		/*    Input (Data,Var,Rel)              */
37662306a36Sopenharmony_ci	0xc0,			/*   End Collection                     */
37762306a36Sopenharmony_ci	0xc0,			/*  End Collection                      */
37862306a36Sopenharmony_ci};
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ci/* Gaming Mouse descriptor (2) */
38162306a36Sopenharmony_cistatic const char mse_high_res_descriptor[] = {
38262306a36Sopenharmony_ci	0x05, 0x01,		/*  USAGE_PAGE (Generic Desktop)        */
38362306a36Sopenharmony_ci	0x09, 0x02,		/*  USAGE (Mouse)                       */
38462306a36Sopenharmony_ci	0xA1, 0x01,		/*  COLLECTION (Application)            */
38562306a36Sopenharmony_ci	0x85, 0x02,		/*    REPORT_ID = 2                     */
38662306a36Sopenharmony_ci	0x09, 0x01,		/*    USAGE (pointer)                   */
38762306a36Sopenharmony_ci	0xA1, 0x00,		/*    COLLECTION (physical)             */
38862306a36Sopenharmony_ci	0x05, 0x09,		/*      USAGE_PAGE (buttons)            */
38962306a36Sopenharmony_ci	0x19, 0x01,		/*      USAGE_MIN (1)                   */
39062306a36Sopenharmony_ci	0x29, 0x10,		/*      USAGE_MAX (16)                  */
39162306a36Sopenharmony_ci	0x15, 0x00,		/*      LOGICAL_MIN (0)                 */
39262306a36Sopenharmony_ci	0x25, 0x01,		/*      LOGICAL_MAX (1)                 */
39362306a36Sopenharmony_ci	0x95, 0x10,		/*      REPORT_COUNT (16)               */
39462306a36Sopenharmony_ci	0x75, 0x01,		/*      REPORT_SIZE (1)                 */
39562306a36Sopenharmony_ci	0x81, 0x02,		/*      INPUT (data var abs)            */
39662306a36Sopenharmony_ci	0x05, 0x01,		/*      USAGE_PAGE (generic desktop)    */
39762306a36Sopenharmony_ci	0x16, 0x01, 0x80,	/*      LOGICAL_MIN (-32767)            */
39862306a36Sopenharmony_ci	0x26, 0xFF, 0x7F,	/*      LOGICAL_MAX (32767)             */
39962306a36Sopenharmony_ci	0x75, 0x10,		/*      REPORT_SIZE (16)                */
40062306a36Sopenharmony_ci	0x95, 0x02,		/*      REPORT_COUNT (2)                */
40162306a36Sopenharmony_ci	0x09, 0x30,		/*      USAGE (X)                       */
40262306a36Sopenharmony_ci	0x09, 0x31,		/*      USAGE (Y)                       */
40362306a36Sopenharmony_ci	0x81, 0x06,		/*      INPUT                           */
40462306a36Sopenharmony_ci	0x15, 0x81,		/*      LOGICAL_MIN (-127)              */
40562306a36Sopenharmony_ci	0x25, 0x7F,		/*      LOGICAL_MAX (127)               */
40662306a36Sopenharmony_ci	0x75, 0x08,		/*      REPORT_SIZE (8)                 */
40762306a36Sopenharmony_ci	0x95, 0x01,		/*      REPORT_COUNT (1)                */
40862306a36Sopenharmony_ci	0x09, 0x38,		/*      USAGE (wheel)                   */
40962306a36Sopenharmony_ci	0x81, 0x06,		/*      INPUT                           */
41062306a36Sopenharmony_ci	0x05, 0x0C,		/*      USAGE_PAGE(consumer)            */
41162306a36Sopenharmony_ci	0x0A, 0x38, 0x02,	/*      USAGE(AC Pan)                   */
41262306a36Sopenharmony_ci	0x95, 0x01,		/*      REPORT_COUNT (1)                */
41362306a36Sopenharmony_ci	0x81, 0x06,		/*      INPUT                           */
41462306a36Sopenharmony_ci	0xC0,			/*    END_COLLECTION                    */
41562306a36Sopenharmony_ci	0xC0,			/*  END_COLLECTION                      */
41662306a36Sopenharmony_ci};
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_ci/* Consumer Control descriptor (3) */
41962306a36Sopenharmony_cistatic const char consumer_descriptor[] = {
42062306a36Sopenharmony_ci	0x05, 0x0C,		/* USAGE_PAGE (Consumer Devices)       */
42162306a36Sopenharmony_ci	0x09, 0x01,		/* USAGE (Consumer Control)            */
42262306a36Sopenharmony_ci	0xA1, 0x01,		/* COLLECTION (Application)            */
42362306a36Sopenharmony_ci	0x85, 0x03,		/* REPORT_ID = 3                       */
42462306a36Sopenharmony_ci	0x75, 0x10,		/* REPORT_SIZE (16)                    */
42562306a36Sopenharmony_ci	0x95, 0x02,		/* REPORT_COUNT (2)                    */
42662306a36Sopenharmony_ci	0x15, 0x01,		/* LOGICAL_MIN (1)                     */
42762306a36Sopenharmony_ci	0x26, 0xFF, 0x02,	/* LOGICAL_MAX (767)                   */
42862306a36Sopenharmony_ci	0x19, 0x01,		/* USAGE_MIN (1)                       */
42962306a36Sopenharmony_ci	0x2A, 0xFF, 0x02,	/* USAGE_MAX (767)                     */
43062306a36Sopenharmony_ci	0x81, 0x00,		/* INPUT (Data Ary Abs)                */
43162306a36Sopenharmony_ci	0xC0,			/* END_COLLECTION                      */
43262306a36Sopenharmony_ci};				/*                                     */
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_ci/* System control descriptor (4) */
43562306a36Sopenharmony_cistatic const char syscontrol_descriptor[] = {
43662306a36Sopenharmony_ci	0x05, 0x01,		/*   USAGE_PAGE (Generic Desktop)      */
43762306a36Sopenharmony_ci	0x09, 0x80,		/*   USAGE (System Control)            */
43862306a36Sopenharmony_ci	0xA1, 0x01,		/*   COLLECTION (Application)          */
43962306a36Sopenharmony_ci	0x85, 0x04,		/*   REPORT_ID = 4                     */
44062306a36Sopenharmony_ci	0x75, 0x02,		/*   REPORT_SIZE (2)                   */
44162306a36Sopenharmony_ci	0x95, 0x01,		/*   REPORT_COUNT (1)                  */
44262306a36Sopenharmony_ci	0x15, 0x01,		/*   LOGICAL_MIN (1)                   */
44362306a36Sopenharmony_ci	0x25, 0x03,		/*   LOGICAL_MAX (3)                   */
44462306a36Sopenharmony_ci	0x09, 0x82,		/*   USAGE (System Sleep)              */
44562306a36Sopenharmony_ci	0x09, 0x81,		/*   USAGE (System Power Down)         */
44662306a36Sopenharmony_ci	0x09, 0x83,		/*   USAGE (System Wake Up)            */
44762306a36Sopenharmony_ci	0x81, 0x60,		/*   INPUT (Data Ary Abs NPrf Null)    */
44862306a36Sopenharmony_ci	0x75, 0x06,		/*   REPORT_SIZE (6)                   */
44962306a36Sopenharmony_ci	0x81, 0x03,		/*   INPUT (Cnst Var Abs)              */
45062306a36Sopenharmony_ci	0xC0,			/*   END_COLLECTION                    */
45162306a36Sopenharmony_ci};
45262306a36Sopenharmony_ci
45362306a36Sopenharmony_ci/* Media descriptor (8) */
45462306a36Sopenharmony_cistatic const char media_descriptor[] = {
45562306a36Sopenharmony_ci	0x06, 0xbc, 0xff,	/* Usage Page 0xffbc                   */
45662306a36Sopenharmony_ci	0x09, 0x88,		/* Usage 0x0088                        */
45762306a36Sopenharmony_ci	0xa1, 0x01,		/* BeginCollection                     */
45862306a36Sopenharmony_ci	0x85, 0x08,		/*   Report ID 8                       */
45962306a36Sopenharmony_ci	0x19, 0x01,		/*   Usage Min 0x0001                  */
46062306a36Sopenharmony_ci	0x29, 0xff,		/*   Usage Max 0x00ff                  */
46162306a36Sopenharmony_ci	0x15, 0x01,		/*   Logical Min 1                     */
46262306a36Sopenharmony_ci	0x26, 0xff, 0x00,	/*   Logical Max 255                   */
46362306a36Sopenharmony_ci	0x75, 0x08,		/*   Report Size 8                     */
46462306a36Sopenharmony_ci	0x95, 0x01,		/*   Report Count 1                    */
46562306a36Sopenharmony_ci	0x81, 0x00,		/*   Input                             */
46662306a36Sopenharmony_ci	0xc0,			/* EndCollection                       */
46762306a36Sopenharmony_ci};				/*                                     */
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_ci/* HIDPP descriptor */
47062306a36Sopenharmony_cistatic const char hidpp_descriptor[] = {
47162306a36Sopenharmony_ci	0x06, 0x00, 0xff,	/* Usage Page (Vendor Defined Page 1)  */
47262306a36Sopenharmony_ci	0x09, 0x01,		/* Usage (Vendor Usage 1)              */
47362306a36Sopenharmony_ci	0xa1, 0x01,		/* Collection (Application)            */
47462306a36Sopenharmony_ci	0x85, 0x10,		/*   Report ID (16)                    */
47562306a36Sopenharmony_ci	0x75, 0x08,		/*   Report Size (8)                   */
47662306a36Sopenharmony_ci	0x95, 0x06,		/*   Report Count (6)                  */
47762306a36Sopenharmony_ci	0x15, 0x00,		/*   Logical Minimum (0)               */
47862306a36Sopenharmony_ci	0x26, 0xff, 0x00,	/*   Logical Maximum (255)             */
47962306a36Sopenharmony_ci	0x09, 0x01,		/*   Usage (Vendor Usage 1)            */
48062306a36Sopenharmony_ci	0x81, 0x00,		/*   Input (Data,Arr,Abs)              */
48162306a36Sopenharmony_ci	0x09, 0x01,		/*   Usage (Vendor Usage 1)            */
48262306a36Sopenharmony_ci	0x91, 0x00,		/*   Output (Data,Arr,Abs)             */
48362306a36Sopenharmony_ci	0xc0,			/* End Collection                      */
48462306a36Sopenharmony_ci	0x06, 0x00, 0xff,	/* Usage Page (Vendor Defined Page 1)  */
48562306a36Sopenharmony_ci	0x09, 0x02,		/* Usage (Vendor Usage 2)              */
48662306a36Sopenharmony_ci	0xa1, 0x01,		/* Collection (Application)            */
48762306a36Sopenharmony_ci	0x85, 0x11,		/*   Report ID (17)                    */
48862306a36Sopenharmony_ci	0x75, 0x08,		/*   Report Size (8)                   */
48962306a36Sopenharmony_ci	0x95, 0x13,		/*   Report Count (19)                 */
49062306a36Sopenharmony_ci	0x15, 0x00,		/*   Logical Minimum (0)               */
49162306a36Sopenharmony_ci	0x26, 0xff, 0x00,	/*   Logical Maximum (255)             */
49262306a36Sopenharmony_ci	0x09, 0x02,		/*   Usage (Vendor Usage 2)            */
49362306a36Sopenharmony_ci	0x81, 0x00,		/*   Input (Data,Arr,Abs)              */
49462306a36Sopenharmony_ci	0x09, 0x02,		/*   Usage (Vendor Usage 2)            */
49562306a36Sopenharmony_ci	0x91, 0x00,		/*   Output (Data,Arr,Abs)             */
49662306a36Sopenharmony_ci	0xc0,			/* End Collection                      */
49762306a36Sopenharmony_ci	0x06, 0x00, 0xff,	/* Usage Page (Vendor Defined Page 1)  */
49862306a36Sopenharmony_ci	0x09, 0x04,		/* Usage (Vendor Usage 0x04)           */
49962306a36Sopenharmony_ci	0xa1, 0x01,		/* Collection (Application)            */
50062306a36Sopenharmony_ci	0x85, 0x20,		/*   Report ID (32)                    */
50162306a36Sopenharmony_ci	0x75, 0x08,		/*   Report Size (8)                   */
50262306a36Sopenharmony_ci	0x95, 0x0e,		/*   Report Count (14)                 */
50362306a36Sopenharmony_ci	0x15, 0x00,		/*   Logical Minimum (0)               */
50462306a36Sopenharmony_ci	0x26, 0xff, 0x00,	/*   Logical Maximum (255)             */
50562306a36Sopenharmony_ci	0x09, 0x41,		/*   Usage (Vendor Usage 0x41)         */
50662306a36Sopenharmony_ci	0x81, 0x00,		/*   Input (Data,Arr,Abs)              */
50762306a36Sopenharmony_ci	0x09, 0x41,		/*   Usage (Vendor Usage 0x41)         */
50862306a36Sopenharmony_ci	0x91, 0x00,		/*   Output (Data,Arr,Abs)             */
50962306a36Sopenharmony_ci	0x85, 0x21,		/*   Report ID (33)                    */
51062306a36Sopenharmony_ci	0x95, 0x1f,		/*   Report Count (31)                 */
51162306a36Sopenharmony_ci	0x15, 0x00,		/*   Logical Minimum (0)               */
51262306a36Sopenharmony_ci	0x26, 0xff, 0x00,	/*   Logical Maximum (255)             */
51362306a36Sopenharmony_ci	0x09, 0x42,		/*   Usage (Vendor Usage 0x42)         */
51462306a36Sopenharmony_ci	0x81, 0x00,		/*   Input (Data,Arr,Abs)              */
51562306a36Sopenharmony_ci	0x09, 0x42,		/*   Usage (Vendor Usage 0x42)         */
51662306a36Sopenharmony_ci	0x91, 0x00,		/*   Output (Data,Arr,Abs)             */
51762306a36Sopenharmony_ci	0xc0,			/* End Collection                      */
51862306a36Sopenharmony_ci};
51962306a36Sopenharmony_ci
52062306a36Sopenharmony_ci/* Maximum size of all defined hid reports in bytes (including report id) */
52162306a36Sopenharmony_ci#define MAX_REPORT_SIZE 8
52262306a36Sopenharmony_ci
52362306a36Sopenharmony_ci/* Make sure all descriptors are present here */
52462306a36Sopenharmony_ci#define MAX_RDESC_SIZE				\
52562306a36Sopenharmony_ci	(sizeof(kbd_descriptor) +		\
52662306a36Sopenharmony_ci	 sizeof(mse_bluetooth_descriptor) +	\
52762306a36Sopenharmony_ci	 sizeof(mse5_bluetooth_descriptor) +	\
52862306a36Sopenharmony_ci	 sizeof(consumer_descriptor) +		\
52962306a36Sopenharmony_ci	 sizeof(syscontrol_descriptor) +	\
53062306a36Sopenharmony_ci	 sizeof(media_descriptor) +	\
53162306a36Sopenharmony_ci	 sizeof(hidpp_descriptor))
53262306a36Sopenharmony_ci
53362306a36Sopenharmony_ci/* Number of possible hid report types that can be created by this driver.
53462306a36Sopenharmony_ci *
53562306a36Sopenharmony_ci * Right now, RF report types have the same report types (or report id's)
53662306a36Sopenharmony_ci * than the hid report created from those RF reports. In the future
53762306a36Sopenharmony_ci * this doesnt have to be true.
53862306a36Sopenharmony_ci *
53962306a36Sopenharmony_ci * For instance, RF report type 0x01 which has a size of 8 bytes, corresponds
54062306a36Sopenharmony_ci * to hid report id 0x01, this is standard keyboard. Same thing applies to mice
54162306a36Sopenharmony_ci * reports and consumer control, etc. If a new RF report is created, it doesn't
54262306a36Sopenharmony_ci * has to have the same report id as its corresponding hid report, so an
54362306a36Sopenharmony_ci * translation may have to take place for future report types.
54462306a36Sopenharmony_ci */
54562306a36Sopenharmony_ci#define NUMBER_OF_HID_REPORTS 32
54662306a36Sopenharmony_cistatic const u8 hid_reportid_size_map[NUMBER_OF_HID_REPORTS] = {
54762306a36Sopenharmony_ci	[1] = 8,		/* Standard keyboard */
54862306a36Sopenharmony_ci	[2] = 8,		/* Standard mouse */
54962306a36Sopenharmony_ci	[3] = 5,		/* Consumer control */
55062306a36Sopenharmony_ci	[4] = 2,		/* System control */
55162306a36Sopenharmony_ci	[8] = 2,		/* Media Center */
55262306a36Sopenharmony_ci};
55362306a36Sopenharmony_ci
55462306a36Sopenharmony_ci
55562306a36Sopenharmony_ci#define LOGITECH_DJ_INTERFACE_NUMBER 0x02
55662306a36Sopenharmony_ci
55762306a36Sopenharmony_cistatic const struct hid_ll_driver logi_dj_ll_driver;
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_cistatic int logi_dj_recv_query_paired_devices(struct dj_receiver_dev *djrcv_dev);
56062306a36Sopenharmony_cistatic void delayedwork_callback(struct work_struct *work);
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_cistatic LIST_HEAD(dj_hdev_list);
56362306a36Sopenharmony_cistatic DEFINE_MUTEX(dj_hdev_list_lock);
56462306a36Sopenharmony_ci
56562306a36Sopenharmony_cistatic bool recvr_type_is_bluetooth(enum recvr_type type)
56662306a36Sopenharmony_ci{
56762306a36Sopenharmony_ci	return type == recvr_type_bluetooth || type == recvr_type_dinovo;
56862306a36Sopenharmony_ci}
56962306a36Sopenharmony_ci
57062306a36Sopenharmony_ci/*
57162306a36Sopenharmony_ci * dj/HID++ receivers are really a single logical entity, but for BIOS/Windows
57262306a36Sopenharmony_ci * compatibility they have multiple USB interfaces. On HID++ receivers we need
57362306a36Sopenharmony_ci * to listen for input reports on both interfaces. The functions below are used
57462306a36Sopenharmony_ci * to create a single struct dj_receiver_dev for all interfaces belonging to
57562306a36Sopenharmony_ci * a single USB-device / receiver.
57662306a36Sopenharmony_ci */
57762306a36Sopenharmony_cistatic struct dj_receiver_dev *dj_find_receiver_dev(struct hid_device *hdev,
57862306a36Sopenharmony_ci						    enum recvr_type type)
57962306a36Sopenharmony_ci{
58062306a36Sopenharmony_ci	struct dj_receiver_dev *djrcv_dev;
58162306a36Sopenharmony_ci	char sep;
58262306a36Sopenharmony_ci
58362306a36Sopenharmony_ci	/*
58462306a36Sopenharmony_ci	 * The bluetooth receiver contains a built-in hub and has separate
58562306a36Sopenharmony_ci	 * USB-devices for the keyboard and mouse interfaces.
58662306a36Sopenharmony_ci	 */
58762306a36Sopenharmony_ci	sep = recvr_type_is_bluetooth(type) ? '.' : '/';
58862306a36Sopenharmony_ci
58962306a36Sopenharmony_ci	/* Try to find an already-probed interface from the same device */
59062306a36Sopenharmony_ci	list_for_each_entry(djrcv_dev, &dj_hdev_list, list) {
59162306a36Sopenharmony_ci		if (djrcv_dev->mouse &&
59262306a36Sopenharmony_ci		    hid_compare_device_paths(hdev, djrcv_dev->mouse, sep)) {
59362306a36Sopenharmony_ci			kref_get(&djrcv_dev->kref);
59462306a36Sopenharmony_ci			return djrcv_dev;
59562306a36Sopenharmony_ci		}
59662306a36Sopenharmony_ci		if (djrcv_dev->keyboard &&
59762306a36Sopenharmony_ci		    hid_compare_device_paths(hdev, djrcv_dev->keyboard, sep)) {
59862306a36Sopenharmony_ci			kref_get(&djrcv_dev->kref);
59962306a36Sopenharmony_ci			return djrcv_dev;
60062306a36Sopenharmony_ci		}
60162306a36Sopenharmony_ci		if (djrcv_dev->hidpp &&
60262306a36Sopenharmony_ci		    hid_compare_device_paths(hdev, djrcv_dev->hidpp, sep)) {
60362306a36Sopenharmony_ci			kref_get(&djrcv_dev->kref);
60462306a36Sopenharmony_ci			return djrcv_dev;
60562306a36Sopenharmony_ci		}
60662306a36Sopenharmony_ci	}
60762306a36Sopenharmony_ci
60862306a36Sopenharmony_ci	return NULL;
60962306a36Sopenharmony_ci}
61062306a36Sopenharmony_ci
61162306a36Sopenharmony_cistatic void dj_release_receiver_dev(struct kref *kref)
61262306a36Sopenharmony_ci{
61362306a36Sopenharmony_ci	struct dj_receiver_dev *djrcv_dev = container_of(kref, struct dj_receiver_dev, kref);
61462306a36Sopenharmony_ci
61562306a36Sopenharmony_ci	list_del(&djrcv_dev->list);
61662306a36Sopenharmony_ci	kfifo_free(&djrcv_dev->notif_fifo);
61762306a36Sopenharmony_ci	kfree(djrcv_dev);
61862306a36Sopenharmony_ci}
61962306a36Sopenharmony_ci
62062306a36Sopenharmony_cistatic void dj_put_receiver_dev(struct hid_device *hdev)
62162306a36Sopenharmony_ci{
62262306a36Sopenharmony_ci	struct dj_receiver_dev *djrcv_dev = hid_get_drvdata(hdev);
62362306a36Sopenharmony_ci
62462306a36Sopenharmony_ci	mutex_lock(&dj_hdev_list_lock);
62562306a36Sopenharmony_ci
62662306a36Sopenharmony_ci	if (djrcv_dev->mouse == hdev)
62762306a36Sopenharmony_ci		djrcv_dev->mouse = NULL;
62862306a36Sopenharmony_ci	if (djrcv_dev->keyboard == hdev)
62962306a36Sopenharmony_ci		djrcv_dev->keyboard = NULL;
63062306a36Sopenharmony_ci	if (djrcv_dev->hidpp == hdev)
63162306a36Sopenharmony_ci		djrcv_dev->hidpp = NULL;
63262306a36Sopenharmony_ci
63362306a36Sopenharmony_ci	kref_put(&djrcv_dev->kref, dj_release_receiver_dev);
63462306a36Sopenharmony_ci
63562306a36Sopenharmony_ci	mutex_unlock(&dj_hdev_list_lock);
63662306a36Sopenharmony_ci}
63762306a36Sopenharmony_ci
63862306a36Sopenharmony_cistatic struct dj_receiver_dev *dj_get_receiver_dev(struct hid_device *hdev,
63962306a36Sopenharmony_ci						   enum recvr_type type,
64062306a36Sopenharmony_ci						   unsigned int application,
64162306a36Sopenharmony_ci						   bool is_hidpp)
64262306a36Sopenharmony_ci{
64362306a36Sopenharmony_ci	struct dj_receiver_dev *djrcv_dev;
64462306a36Sopenharmony_ci
64562306a36Sopenharmony_ci	mutex_lock(&dj_hdev_list_lock);
64662306a36Sopenharmony_ci
64762306a36Sopenharmony_ci	djrcv_dev = dj_find_receiver_dev(hdev, type);
64862306a36Sopenharmony_ci	if (!djrcv_dev) {
64962306a36Sopenharmony_ci		djrcv_dev = kzalloc(sizeof(*djrcv_dev), GFP_KERNEL);
65062306a36Sopenharmony_ci		if (!djrcv_dev)
65162306a36Sopenharmony_ci			goto out;
65262306a36Sopenharmony_ci
65362306a36Sopenharmony_ci		INIT_WORK(&djrcv_dev->work, delayedwork_callback);
65462306a36Sopenharmony_ci		spin_lock_init(&djrcv_dev->lock);
65562306a36Sopenharmony_ci		if (kfifo_alloc(&djrcv_dev->notif_fifo,
65662306a36Sopenharmony_ci			    DJ_MAX_NUMBER_NOTIFS * sizeof(struct dj_workitem),
65762306a36Sopenharmony_ci			    GFP_KERNEL)) {
65862306a36Sopenharmony_ci			kfree(djrcv_dev);
65962306a36Sopenharmony_ci			djrcv_dev = NULL;
66062306a36Sopenharmony_ci			goto out;
66162306a36Sopenharmony_ci		}
66262306a36Sopenharmony_ci		kref_init(&djrcv_dev->kref);
66362306a36Sopenharmony_ci		list_add_tail(&djrcv_dev->list, &dj_hdev_list);
66462306a36Sopenharmony_ci		djrcv_dev->last_query = jiffies;
66562306a36Sopenharmony_ci		djrcv_dev->type = type;
66662306a36Sopenharmony_ci	}
66762306a36Sopenharmony_ci
66862306a36Sopenharmony_ci	if (application == HID_GD_KEYBOARD)
66962306a36Sopenharmony_ci		djrcv_dev->keyboard = hdev;
67062306a36Sopenharmony_ci	if (application == HID_GD_MOUSE)
67162306a36Sopenharmony_ci		djrcv_dev->mouse = hdev;
67262306a36Sopenharmony_ci	if (is_hidpp)
67362306a36Sopenharmony_ci		djrcv_dev->hidpp = hdev;
67462306a36Sopenharmony_ci
67562306a36Sopenharmony_ci	hid_set_drvdata(hdev, djrcv_dev);
67662306a36Sopenharmony_ciout:
67762306a36Sopenharmony_ci	mutex_unlock(&dj_hdev_list_lock);
67862306a36Sopenharmony_ci	return djrcv_dev;
67962306a36Sopenharmony_ci}
68062306a36Sopenharmony_ci
68162306a36Sopenharmony_cistatic void logi_dj_recv_destroy_djhid_device(struct dj_receiver_dev *djrcv_dev,
68262306a36Sopenharmony_ci					      struct dj_workitem *workitem)
68362306a36Sopenharmony_ci{
68462306a36Sopenharmony_ci	/* Called in delayed work context */
68562306a36Sopenharmony_ci	struct dj_device *dj_dev;
68662306a36Sopenharmony_ci	unsigned long flags;
68762306a36Sopenharmony_ci
68862306a36Sopenharmony_ci	spin_lock_irqsave(&djrcv_dev->lock, flags);
68962306a36Sopenharmony_ci	dj_dev = djrcv_dev->paired_dj_devices[workitem->device_index];
69062306a36Sopenharmony_ci	djrcv_dev->paired_dj_devices[workitem->device_index] = NULL;
69162306a36Sopenharmony_ci	spin_unlock_irqrestore(&djrcv_dev->lock, flags);
69262306a36Sopenharmony_ci
69362306a36Sopenharmony_ci	if (dj_dev != NULL) {
69462306a36Sopenharmony_ci		hid_destroy_device(dj_dev->hdev);
69562306a36Sopenharmony_ci		kfree(dj_dev);
69662306a36Sopenharmony_ci	} else {
69762306a36Sopenharmony_ci		hid_err(djrcv_dev->hidpp, "%s: can't destroy a NULL device\n",
69862306a36Sopenharmony_ci			__func__);
69962306a36Sopenharmony_ci	}
70062306a36Sopenharmony_ci}
70162306a36Sopenharmony_ci
70262306a36Sopenharmony_cistatic void logi_dj_recv_add_djhid_device(struct dj_receiver_dev *djrcv_dev,
70362306a36Sopenharmony_ci					  struct dj_workitem *workitem)
70462306a36Sopenharmony_ci{
70562306a36Sopenharmony_ci	/* Called in delayed work context */
70662306a36Sopenharmony_ci	struct hid_device *djrcv_hdev = djrcv_dev->hidpp;
70762306a36Sopenharmony_ci	struct hid_device *dj_hiddev;
70862306a36Sopenharmony_ci	struct dj_device *dj_dev;
70962306a36Sopenharmony_ci	u8 device_index = workitem->device_index;
71062306a36Sopenharmony_ci	unsigned long flags;
71162306a36Sopenharmony_ci
71262306a36Sopenharmony_ci	/* Device index goes from 1 to 6, we need 3 bytes to store the
71362306a36Sopenharmony_ci	 * semicolon, the index, and a null terminator
71462306a36Sopenharmony_ci	 */
71562306a36Sopenharmony_ci	unsigned char tmpstr[3];
71662306a36Sopenharmony_ci
71762306a36Sopenharmony_ci	/* We are the only one ever adding a device, no need to lock */
71862306a36Sopenharmony_ci	if (djrcv_dev->paired_dj_devices[device_index]) {
71962306a36Sopenharmony_ci		/* The device is already known. No need to reallocate it. */
72062306a36Sopenharmony_ci		dbg_hid("%s: device is already known\n", __func__);
72162306a36Sopenharmony_ci		return;
72262306a36Sopenharmony_ci	}
72362306a36Sopenharmony_ci
72462306a36Sopenharmony_ci	dj_hiddev = hid_allocate_device();
72562306a36Sopenharmony_ci	if (IS_ERR(dj_hiddev)) {
72662306a36Sopenharmony_ci		hid_err(djrcv_hdev, "%s: hid_allocate_dev failed\n", __func__);
72762306a36Sopenharmony_ci		return;
72862306a36Sopenharmony_ci	}
72962306a36Sopenharmony_ci
73062306a36Sopenharmony_ci	dj_hiddev->ll_driver = &logi_dj_ll_driver;
73162306a36Sopenharmony_ci
73262306a36Sopenharmony_ci	dj_hiddev->dev.parent = &djrcv_hdev->dev;
73362306a36Sopenharmony_ci	dj_hiddev->bus = BUS_USB;
73462306a36Sopenharmony_ci	dj_hiddev->vendor = djrcv_hdev->vendor;
73562306a36Sopenharmony_ci	dj_hiddev->product = (workitem->quad_id_msb << 8) |
73662306a36Sopenharmony_ci			      workitem->quad_id_lsb;
73762306a36Sopenharmony_ci	if (workitem->device_type) {
73862306a36Sopenharmony_ci		const char *type_str = "Device";
73962306a36Sopenharmony_ci
74062306a36Sopenharmony_ci		switch (workitem->device_type) {
74162306a36Sopenharmony_ci		case 0x01: type_str = "Keyboard";	break;
74262306a36Sopenharmony_ci		case 0x02: type_str = "Mouse";		break;
74362306a36Sopenharmony_ci		case 0x03: type_str = "Numpad";		break;
74462306a36Sopenharmony_ci		case 0x04: type_str = "Presenter";	break;
74562306a36Sopenharmony_ci		case 0x07: type_str = "Remote Control";	break;
74662306a36Sopenharmony_ci		case 0x08: type_str = "Trackball";	break;
74762306a36Sopenharmony_ci		case 0x09: type_str = "Touchpad";	break;
74862306a36Sopenharmony_ci		}
74962306a36Sopenharmony_ci		snprintf(dj_hiddev->name, sizeof(dj_hiddev->name),
75062306a36Sopenharmony_ci			"Logitech Wireless %s PID:%04x",
75162306a36Sopenharmony_ci			type_str, dj_hiddev->product);
75262306a36Sopenharmony_ci	} else {
75362306a36Sopenharmony_ci		snprintf(dj_hiddev->name, sizeof(dj_hiddev->name),
75462306a36Sopenharmony_ci			"Logitech Wireless Device PID:%04x",
75562306a36Sopenharmony_ci			dj_hiddev->product);
75662306a36Sopenharmony_ci	}
75762306a36Sopenharmony_ci
75862306a36Sopenharmony_ci	if (djrcv_dev->type == recvr_type_27mhz)
75962306a36Sopenharmony_ci		dj_hiddev->group = HID_GROUP_LOGITECH_27MHZ_DEVICE;
76062306a36Sopenharmony_ci	else
76162306a36Sopenharmony_ci		dj_hiddev->group = HID_GROUP_LOGITECH_DJ_DEVICE;
76262306a36Sopenharmony_ci
76362306a36Sopenharmony_ci	memcpy(dj_hiddev->phys, djrcv_hdev->phys, sizeof(djrcv_hdev->phys));
76462306a36Sopenharmony_ci	snprintf(tmpstr, sizeof(tmpstr), ":%d", device_index);
76562306a36Sopenharmony_ci	strlcat(dj_hiddev->phys, tmpstr, sizeof(dj_hiddev->phys));
76662306a36Sopenharmony_ci
76762306a36Sopenharmony_ci	dj_dev = kzalloc(sizeof(struct dj_device), GFP_KERNEL);
76862306a36Sopenharmony_ci
76962306a36Sopenharmony_ci	if (!dj_dev) {
77062306a36Sopenharmony_ci		hid_err(djrcv_hdev, "%s: failed allocating dj_dev\n", __func__);
77162306a36Sopenharmony_ci		goto dj_device_allocate_fail;
77262306a36Sopenharmony_ci	}
77362306a36Sopenharmony_ci
77462306a36Sopenharmony_ci	dj_dev->reports_supported = workitem->reports_supported;
77562306a36Sopenharmony_ci	dj_dev->hdev = dj_hiddev;
77662306a36Sopenharmony_ci	dj_dev->dj_receiver_dev = djrcv_dev;
77762306a36Sopenharmony_ci	dj_dev->device_index = device_index;
77862306a36Sopenharmony_ci	dj_hiddev->driver_data = dj_dev;
77962306a36Sopenharmony_ci
78062306a36Sopenharmony_ci	spin_lock_irqsave(&djrcv_dev->lock, flags);
78162306a36Sopenharmony_ci	djrcv_dev->paired_dj_devices[device_index] = dj_dev;
78262306a36Sopenharmony_ci	spin_unlock_irqrestore(&djrcv_dev->lock, flags);
78362306a36Sopenharmony_ci
78462306a36Sopenharmony_ci	if (hid_add_device(dj_hiddev)) {
78562306a36Sopenharmony_ci		hid_err(djrcv_hdev, "%s: failed adding dj_device\n", __func__);
78662306a36Sopenharmony_ci		goto hid_add_device_fail;
78762306a36Sopenharmony_ci	}
78862306a36Sopenharmony_ci
78962306a36Sopenharmony_ci	return;
79062306a36Sopenharmony_ci
79162306a36Sopenharmony_cihid_add_device_fail:
79262306a36Sopenharmony_ci	spin_lock_irqsave(&djrcv_dev->lock, flags);
79362306a36Sopenharmony_ci	djrcv_dev->paired_dj_devices[device_index] = NULL;
79462306a36Sopenharmony_ci	spin_unlock_irqrestore(&djrcv_dev->lock, flags);
79562306a36Sopenharmony_ci	kfree(dj_dev);
79662306a36Sopenharmony_cidj_device_allocate_fail:
79762306a36Sopenharmony_ci	hid_destroy_device(dj_hiddev);
79862306a36Sopenharmony_ci}
79962306a36Sopenharmony_ci
80062306a36Sopenharmony_cistatic void delayedwork_callback(struct work_struct *work)
80162306a36Sopenharmony_ci{
80262306a36Sopenharmony_ci	struct dj_receiver_dev *djrcv_dev =
80362306a36Sopenharmony_ci		container_of(work, struct dj_receiver_dev, work);
80462306a36Sopenharmony_ci
80562306a36Sopenharmony_ci	struct dj_workitem workitem;
80662306a36Sopenharmony_ci	unsigned long flags;
80762306a36Sopenharmony_ci	int count;
80862306a36Sopenharmony_ci	int retval;
80962306a36Sopenharmony_ci
81062306a36Sopenharmony_ci	dbg_hid("%s\n", __func__);
81162306a36Sopenharmony_ci
81262306a36Sopenharmony_ci	spin_lock_irqsave(&djrcv_dev->lock, flags);
81362306a36Sopenharmony_ci
81462306a36Sopenharmony_ci	/*
81562306a36Sopenharmony_ci	 * Since we attach to multiple interfaces, we may get scheduled before
81662306a36Sopenharmony_ci	 * we are bound to the HID++ interface, catch this.
81762306a36Sopenharmony_ci	 */
81862306a36Sopenharmony_ci	if (!djrcv_dev->ready) {
81962306a36Sopenharmony_ci		pr_warn("%s: delayedwork queued before hidpp interface was enumerated\n",
82062306a36Sopenharmony_ci			__func__);
82162306a36Sopenharmony_ci		spin_unlock_irqrestore(&djrcv_dev->lock, flags);
82262306a36Sopenharmony_ci		return;
82362306a36Sopenharmony_ci	}
82462306a36Sopenharmony_ci
82562306a36Sopenharmony_ci	count = kfifo_out(&djrcv_dev->notif_fifo, &workitem, sizeof(workitem));
82662306a36Sopenharmony_ci
82762306a36Sopenharmony_ci	if (count != sizeof(workitem)) {
82862306a36Sopenharmony_ci		spin_unlock_irqrestore(&djrcv_dev->lock, flags);
82962306a36Sopenharmony_ci		return;
83062306a36Sopenharmony_ci	}
83162306a36Sopenharmony_ci
83262306a36Sopenharmony_ci	if (!kfifo_is_empty(&djrcv_dev->notif_fifo))
83362306a36Sopenharmony_ci		schedule_work(&djrcv_dev->work);
83462306a36Sopenharmony_ci
83562306a36Sopenharmony_ci	spin_unlock_irqrestore(&djrcv_dev->lock, flags);
83662306a36Sopenharmony_ci
83762306a36Sopenharmony_ci	switch (workitem.type) {
83862306a36Sopenharmony_ci	case WORKITEM_TYPE_PAIRED:
83962306a36Sopenharmony_ci		logi_dj_recv_add_djhid_device(djrcv_dev, &workitem);
84062306a36Sopenharmony_ci		break;
84162306a36Sopenharmony_ci	case WORKITEM_TYPE_UNPAIRED:
84262306a36Sopenharmony_ci		logi_dj_recv_destroy_djhid_device(djrcv_dev, &workitem);
84362306a36Sopenharmony_ci		break;
84462306a36Sopenharmony_ci	case WORKITEM_TYPE_UNKNOWN:
84562306a36Sopenharmony_ci		retval = logi_dj_recv_query_paired_devices(djrcv_dev);
84662306a36Sopenharmony_ci		if (retval) {
84762306a36Sopenharmony_ci			hid_err(djrcv_dev->hidpp, "%s: logi_dj_recv_query_paired_devices error: %d\n",
84862306a36Sopenharmony_ci				__func__, retval);
84962306a36Sopenharmony_ci		}
85062306a36Sopenharmony_ci		break;
85162306a36Sopenharmony_ci	case WORKITEM_TYPE_EMPTY:
85262306a36Sopenharmony_ci		dbg_hid("%s: device list is empty\n", __func__);
85362306a36Sopenharmony_ci		break;
85462306a36Sopenharmony_ci	}
85562306a36Sopenharmony_ci}
85662306a36Sopenharmony_ci
85762306a36Sopenharmony_ci/*
85862306a36Sopenharmony_ci * Sometimes we receive reports for which we do not have a paired dj_device
85962306a36Sopenharmony_ci * associated with the device_index or report-type to forward the report to.
86062306a36Sopenharmony_ci * This means that the original "device paired" notification corresponding
86162306a36Sopenharmony_ci * to the dj_device never arrived to this driver. Possible reasons for this are:
86262306a36Sopenharmony_ci * 1) hid-core discards all packets coming from a device during probe().
86362306a36Sopenharmony_ci * 2) if the receiver is plugged into a KVM switch then the pairing reports
86462306a36Sopenharmony_ci * are only forwarded to it if the focus is on this PC.
86562306a36Sopenharmony_ci * This function deals with this by re-asking the receiver for the list of
86662306a36Sopenharmony_ci * connected devices in the delayed work callback.
86762306a36Sopenharmony_ci * This function MUST be called with djrcv->lock held.
86862306a36Sopenharmony_ci */
86962306a36Sopenharmony_cistatic void logi_dj_recv_queue_unknown_work(struct dj_receiver_dev *djrcv_dev)
87062306a36Sopenharmony_ci{
87162306a36Sopenharmony_ci	struct dj_workitem workitem = { .type = WORKITEM_TYPE_UNKNOWN };
87262306a36Sopenharmony_ci
87362306a36Sopenharmony_ci	/* Rate limit queries done because of unhandled reports to 2/sec */
87462306a36Sopenharmony_ci	if (time_before(jiffies, djrcv_dev->last_query + HZ / 2))
87562306a36Sopenharmony_ci		return;
87662306a36Sopenharmony_ci
87762306a36Sopenharmony_ci	kfifo_in(&djrcv_dev->notif_fifo, &workitem, sizeof(workitem));
87862306a36Sopenharmony_ci	schedule_work(&djrcv_dev->work);
87962306a36Sopenharmony_ci}
88062306a36Sopenharmony_ci
88162306a36Sopenharmony_cistatic void logi_dj_recv_queue_notification(struct dj_receiver_dev *djrcv_dev,
88262306a36Sopenharmony_ci					   struct dj_report *dj_report)
88362306a36Sopenharmony_ci{
88462306a36Sopenharmony_ci	/* We are called from atomic context (tasklet && djrcv->lock held) */
88562306a36Sopenharmony_ci	struct dj_workitem workitem = {
88662306a36Sopenharmony_ci		.device_index = dj_report->device_index,
88762306a36Sopenharmony_ci	};
88862306a36Sopenharmony_ci
88962306a36Sopenharmony_ci	switch (dj_report->report_type) {
89062306a36Sopenharmony_ci	case REPORT_TYPE_NOTIF_DEVICE_PAIRED:
89162306a36Sopenharmony_ci		workitem.type = WORKITEM_TYPE_PAIRED;
89262306a36Sopenharmony_ci		if (dj_report->report_params[DEVICE_PAIRED_PARAM_SPFUNCTION] &
89362306a36Sopenharmony_ci		    SPFUNCTION_DEVICE_LIST_EMPTY) {
89462306a36Sopenharmony_ci			workitem.type = WORKITEM_TYPE_EMPTY;
89562306a36Sopenharmony_ci			break;
89662306a36Sopenharmony_ci		}
89762306a36Sopenharmony_ci		fallthrough;
89862306a36Sopenharmony_ci	case REPORT_TYPE_NOTIF_DEVICE_UNPAIRED:
89962306a36Sopenharmony_ci		workitem.quad_id_msb =
90062306a36Sopenharmony_ci			dj_report->report_params[DEVICE_PAIRED_PARAM_EQUAD_ID_MSB];
90162306a36Sopenharmony_ci		workitem.quad_id_lsb =
90262306a36Sopenharmony_ci			dj_report->report_params[DEVICE_PAIRED_PARAM_EQUAD_ID_LSB];
90362306a36Sopenharmony_ci		workitem.reports_supported = get_unaligned_le32(
90462306a36Sopenharmony_ci						dj_report->report_params +
90562306a36Sopenharmony_ci						DEVICE_PAIRED_RF_REPORT_TYPE);
90662306a36Sopenharmony_ci		workitem.reports_supported |= HIDPP;
90762306a36Sopenharmony_ci		if (dj_report->report_type == REPORT_TYPE_NOTIF_DEVICE_UNPAIRED)
90862306a36Sopenharmony_ci			workitem.type = WORKITEM_TYPE_UNPAIRED;
90962306a36Sopenharmony_ci		break;
91062306a36Sopenharmony_ci	default:
91162306a36Sopenharmony_ci		logi_dj_recv_queue_unknown_work(djrcv_dev);
91262306a36Sopenharmony_ci		return;
91362306a36Sopenharmony_ci	}
91462306a36Sopenharmony_ci
91562306a36Sopenharmony_ci	kfifo_in(&djrcv_dev->notif_fifo, &workitem, sizeof(workitem));
91662306a36Sopenharmony_ci	schedule_work(&djrcv_dev->work);
91762306a36Sopenharmony_ci}
91862306a36Sopenharmony_ci
91962306a36Sopenharmony_ci/*
92062306a36Sopenharmony_ci * Some quad/bluetooth keyboards have a builtin touchpad in this case we see
92162306a36Sopenharmony_ci * only 1 paired device with a device_type of REPORT_TYPE_KEYBOARD. For the
92262306a36Sopenharmony_ci * touchpad to work we must also forward mouse input reports to the dj_hiddev
92362306a36Sopenharmony_ci * created for the keyboard (instead of forwarding them to a second paired
92462306a36Sopenharmony_ci * device with a device_type of REPORT_TYPE_MOUSE as we normally would).
92562306a36Sopenharmony_ci *
92662306a36Sopenharmony_ci * On Dinovo receivers the keyboard's touchpad and an optional paired actual
92762306a36Sopenharmony_ci * mouse send separate input reports, INPUT(2) aka STD_MOUSE for the mouse
92862306a36Sopenharmony_ci * and INPUT(5) aka KBD_MOUSE for the keyboard's touchpad.
92962306a36Sopenharmony_ci *
93062306a36Sopenharmony_ci * On MX5x00 receivers (which can also be paired with a Dinovo keyboard)
93162306a36Sopenharmony_ci * INPUT(2) is used for both an optional paired actual mouse and for the
93262306a36Sopenharmony_ci * keyboard's touchpad.
93362306a36Sopenharmony_ci */
93462306a36Sopenharmony_cistatic const u16 kbd_builtin_touchpad_ids[] = {
93562306a36Sopenharmony_ci	0xb309, /* Dinovo Edge */
93662306a36Sopenharmony_ci	0xb30c, /* Dinovo Mini */
93762306a36Sopenharmony_ci};
93862306a36Sopenharmony_ci
93962306a36Sopenharmony_cistatic void logi_hidpp_dev_conn_notif_equad(struct hid_device *hdev,
94062306a36Sopenharmony_ci					    struct hidpp_event *hidpp_report,
94162306a36Sopenharmony_ci					    struct dj_workitem *workitem)
94262306a36Sopenharmony_ci{
94362306a36Sopenharmony_ci	struct dj_receiver_dev *djrcv_dev = hid_get_drvdata(hdev);
94462306a36Sopenharmony_ci	int i, id;
94562306a36Sopenharmony_ci
94662306a36Sopenharmony_ci	workitem->type = WORKITEM_TYPE_PAIRED;
94762306a36Sopenharmony_ci	workitem->device_type = hidpp_report->params[HIDPP_PARAM_DEVICE_INFO] &
94862306a36Sopenharmony_ci				HIDPP_DEVICE_TYPE_MASK;
94962306a36Sopenharmony_ci	workitem->quad_id_msb = hidpp_report->params[HIDPP_PARAM_EQUAD_MSB];
95062306a36Sopenharmony_ci	workitem->quad_id_lsb = hidpp_report->params[HIDPP_PARAM_EQUAD_LSB];
95162306a36Sopenharmony_ci	switch (workitem->device_type) {
95262306a36Sopenharmony_ci	case REPORT_TYPE_KEYBOARD:
95362306a36Sopenharmony_ci		workitem->reports_supported |= STD_KEYBOARD | MULTIMEDIA |
95462306a36Sopenharmony_ci					       POWER_KEYS | MEDIA_CENTER |
95562306a36Sopenharmony_ci					       HIDPP;
95662306a36Sopenharmony_ci		id = (workitem->quad_id_msb << 8) | workitem->quad_id_lsb;
95762306a36Sopenharmony_ci		for (i = 0; i < ARRAY_SIZE(kbd_builtin_touchpad_ids); i++) {
95862306a36Sopenharmony_ci			if (id == kbd_builtin_touchpad_ids[i]) {
95962306a36Sopenharmony_ci				if (djrcv_dev->type == recvr_type_dinovo)
96062306a36Sopenharmony_ci					workitem->reports_supported |= KBD_MOUSE;
96162306a36Sopenharmony_ci				else
96262306a36Sopenharmony_ci					workitem->reports_supported |= STD_MOUSE;
96362306a36Sopenharmony_ci				break;
96462306a36Sopenharmony_ci			}
96562306a36Sopenharmony_ci		}
96662306a36Sopenharmony_ci		break;
96762306a36Sopenharmony_ci	case REPORT_TYPE_MOUSE:
96862306a36Sopenharmony_ci		workitem->reports_supported |= STD_MOUSE | HIDPP;
96962306a36Sopenharmony_ci		if (djrcv_dev->type == recvr_type_mouse_only)
97062306a36Sopenharmony_ci			workitem->reports_supported |= MULTIMEDIA;
97162306a36Sopenharmony_ci		break;
97262306a36Sopenharmony_ci	}
97362306a36Sopenharmony_ci}
97462306a36Sopenharmony_ci
97562306a36Sopenharmony_cistatic void logi_hidpp_dev_conn_notif_27mhz(struct hid_device *hdev,
97662306a36Sopenharmony_ci					    struct hidpp_event *hidpp_report,
97762306a36Sopenharmony_ci					    struct dj_workitem *workitem)
97862306a36Sopenharmony_ci{
97962306a36Sopenharmony_ci	workitem->type = WORKITEM_TYPE_PAIRED;
98062306a36Sopenharmony_ci	workitem->quad_id_lsb = hidpp_report->params[HIDPP_PARAM_27MHZ_DEVID];
98162306a36Sopenharmony_ci	switch (hidpp_report->device_index) {
98262306a36Sopenharmony_ci	case 1: /* Index 1 is always a mouse */
98362306a36Sopenharmony_ci	case 2: /* Index 2 is always a mouse */
98462306a36Sopenharmony_ci		workitem->device_type = HIDPP_DEVICE_TYPE_MOUSE;
98562306a36Sopenharmony_ci		workitem->reports_supported |= STD_MOUSE | HIDPP;
98662306a36Sopenharmony_ci		break;
98762306a36Sopenharmony_ci	case 3: /* Index 3 is always the keyboard */
98862306a36Sopenharmony_ci		if (hidpp_report->params[HIDPP_PARAM_DEVICE_INFO] & HIDPP_27MHZ_SECURE_MASK) {
98962306a36Sopenharmony_ci			hid_info(hdev, "Keyboard connection is encrypted\n");
99062306a36Sopenharmony_ci		} else {
99162306a36Sopenharmony_ci			hid_warn(hdev, "Keyboard events are send over the air in plain-text / unencrypted\n");
99262306a36Sopenharmony_ci			hid_warn(hdev, "See: https://gitlab.freedesktop.org/jwrdegoede/logitech-27mhz-keyboard-encryption-setup/\n");
99362306a36Sopenharmony_ci		}
99462306a36Sopenharmony_ci		fallthrough;
99562306a36Sopenharmony_ci	case 4: /* Index 4 is used for an optional separate numpad */
99662306a36Sopenharmony_ci		workitem->device_type = HIDPP_DEVICE_TYPE_KEYBOARD;
99762306a36Sopenharmony_ci		workitem->reports_supported |= STD_KEYBOARD | MULTIMEDIA |
99862306a36Sopenharmony_ci					       POWER_KEYS | HIDPP;
99962306a36Sopenharmony_ci		break;
100062306a36Sopenharmony_ci	default:
100162306a36Sopenharmony_ci		hid_warn(hdev, "%s: unexpected device-index %d", __func__,
100262306a36Sopenharmony_ci			 hidpp_report->device_index);
100362306a36Sopenharmony_ci	}
100462306a36Sopenharmony_ci}
100562306a36Sopenharmony_ci
100662306a36Sopenharmony_cistatic void logi_hidpp_recv_queue_notif(struct hid_device *hdev,
100762306a36Sopenharmony_ci					struct hidpp_event *hidpp_report)
100862306a36Sopenharmony_ci{
100962306a36Sopenharmony_ci	/* We are called from atomic context (tasklet && djrcv->lock held) */
101062306a36Sopenharmony_ci	struct dj_receiver_dev *djrcv_dev = hid_get_drvdata(hdev);
101162306a36Sopenharmony_ci	const char *device_type = "UNKNOWN";
101262306a36Sopenharmony_ci	struct dj_workitem workitem = {
101362306a36Sopenharmony_ci		.type = WORKITEM_TYPE_EMPTY,
101462306a36Sopenharmony_ci		.device_index = hidpp_report->device_index,
101562306a36Sopenharmony_ci	};
101662306a36Sopenharmony_ci
101762306a36Sopenharmony_ci	switch (hidpp_report->params[HIDPP_PARAM_PROTO_TYPE]) {
101862306a36Sopenharmony_ci	case 0x01:
101962306a36Sopenharmony_ci		device_type = "Bluetooth";
102062306a36Sopenharmony_ci		/* Bluetooth connect packet contents is the same as (e)QUAD */
102162306a36Sopenharmony_ci		logi_hidpp_dev_conn_notif_equad(hdev, hidpp_report, &workitem);
102262306a36Sopenharmony_ci		if (!(hidpp_report->params[HIDPP_PARAM_DEVICE_INFO] &
102362306a36Sopenharmony_ci						HIDPP_MANUFACTURER_MASK)) {
102462306a36Sopenharmony_ci			hid_info(hdev, "Non Logitech device connected on slot %d\n",
102562306a36Sopenharmony_ci				 hidpp_report->device_index);
102662306a36Sopenharmony_ci			workitem.reports_supported &= ~HIDPP;
102762306a36Sopenharmony_ci		}
102862306a36Sopenharmony_ci		break;
102962306a36Sopenharmony_ci	case 0x02:
103062306a36Sopenharmony_ci		device_type = "27 Mhz";
103162306a36Sopenharmony_ci		logi_hidpp_dev_conn_notif_27mhz(hdev, hidpp_report, &workitem);
103262306a36Sopenharmony_ci		break;
103362306a36Sopenharmony_ci	case 0x03:
103462306a36Sopenharmony_ci		device_type = "QUAD or eQUAD";
103562306a36Sopenharmony_ci		logi_hidpp_dev_conn_notif_equad(hdev, hidpp_report, &workitem);
103662306a36Sopenharmony_ci		break;
103762306a36Sopenharmony_ci	case 0x04:
103862306a36Sopenharmony_ci		device_type = "eQUAD step 4 DJ";
103962306a36Sopenharmony_ci		logi_hidpp_dev_conn_notif_equad(hdev, hidpp_report, &workitem);
104062306a36Sopenharmony_ci		break;
104162306a36Sopenharmony_ci	case 0x05:
104262306a36Sopenharmony_ci		device_type = "DFU Lite";
104362306a36Sopenharmony_ci		break;
104462306a36Sopenharmony_ci	case 0x06:
104562306a36Sopenharmony_ci		device_type = "eQUAD step 4 Lite";
104662306a36Sopenharmony_ci		logi_hidpp_dev_conn_notif_equad(hdev, hidpp_report, &workitem);
104762306a36Sopenharmony_ci		break;
104862306a36Sopenharmony_ci	case 0x07:
104962306a36Sopenharmony_ci		device_type = "eQUAD step 4 Gaming";
105062306a36Sopenharmony_ci		logi_hidpp_dev_conn_notif_equad(hdev, hidpp_report, &workitem);
105162306a36Sopenharmony_ci		workitem.reports_supported |= STD_KEYBOARD;
105262306a36Sopenharmony_ci		break;
105362306a36Sopenharmony_ci	case 0x08:
105462306a36Sopenharmony_ci		device_type = "eQUAD step 4 for gamepads";
105562306a36Sopenharmony_ci		break;
105662306a36Sopenharmony_ci	case 0x0a:
105762306a36Sopenharmony_ci		device_type = "eQUAD nano Lite";
105862306a36Sopenharmony_ci		logi_hidpp_dev_conn_notif_equad(hdev, hidpp_report, &workitem);
105962306a36Sopenharmony_ci		break;
106062306a36Sopenharmony_ci	case 0x0c:
106162306a36Sopenharmony_ci		device_type = "eQUAD Lightspeed 1";
106262306a36Sopenharmony_ci		logi_hidpp_dev_conn_notif_equad(hdev, hidpp_report, &workitem);
106362306a36Sopenharmony_ci		workitem.reports_supported |= STD_KEYBOARD;
106462306a36Sopenharmony_ci		break;
106562306a36Sopenharmony_ci	case 0x0d:
106662306a36Sopenharmony_ci		device_type = "eQUAD Lightspeed 1.1";
106762306a36Sopenharmony_ci		logi_hidpp_dev_conn_notif_equad(hdev, hidpp_report, &workitem);
106862306a36Sopenharmony_ci		workitem.reports_supported |= STD_KEYBOARD;
106962306a36Sopenharmony_ci		break;
107062306a36Sopenharmony_ci	case 0x0f:
107162306a36Sopenharmony_ci	case 0x11:
107262306a36Sopenharmony_ci		device_type = "eQUAD Lightspeed 1.2";
107362306a36Sopenharmony_ci		logi_hidpp_dev_conn_notif_equad(hdev, hidpp_report, &workitem);
107462306a36Sopenharmony_ci		workitem.reports_supported |= STD_KEYBOARD;
107562306a36Sopenharmony_ci		break;
107662306a36Sopenharmony_ci	}
107762306a36Sopenharmony_ci
107862306a36Sopenharmony_ci	/* custom receiver device (eg. powerplay) */
107962306a36Sopenharmony_ci	if (hidpp_report->device_index == 7) {
108062306a36Sopenharmony_ci		workitem.reports_supported |= HIDPP;
108162306a36Sopenharmony_ci	}
108262306a36Sopenharmony_ci
108362306a36Sopenharmony_ci	if (workitem.type == WORKITEM_TYPE_EMPTY) {
108462306a36Sopenharmony_ci		hid_warn(hdev,
108562306a36Sopenharmony_ci			 "unusable device of type %s (0x%02x) connected on slot %d",
108662306a36Sopenharmony_ci			 device_type,
108762306a36Sopenharmony_ci			 hidpp_report->params[HIDPP_PARAM_PROTO_TYPE],
108862306a36Sopenharmony_ci			 hidpp_report->device_index);
108962306a36Sopenharmony_ci		return;
109062306a36Sopenharmony_ci	}
109162306a36Sopenharmony_ci
109262306a36Sopenharmony_ci	hid_info(hdev, "device of type %s (0x%02x) connected on slot %d",
109362306a36Sopenharmony_ci		 device_type, hidpp_report->params[HIDPP_PARAM_PROTO_TYPE],
109462306a36Sopenharmony_ci		 hidpp_report->device_index);
109562306a36Sopenharmony_ci
109662306a36Sopenharmony_ci	kfifo_in(&djrcv_dev->notif_fifo, &workitem, sizeof(workitem));
109762306a36Sopenharmony_ci	schedule_work(&djrcv_dev->work);
109862306a36Sopenharmony_ci}
109962306a36Sopenharmony_ci
110062306a36Sopenharmony_cistatic void logi_dj_recv_forward_null_report(struct dj_receiver_dev *djrcv_dev,
110162306a36Sopenharmony_ci					     struct dj_report *dj_report)
110262306a36Sopenharmony_ci{
110362306a36Sopenharmony_ci	/* We are called from atomic context (tasklet && djrcv->lock held) */
110462306a36Sopenharmony_ci	unsigned int i;
110562306a36Sopenharmony_ci	u8 reportbuffer[MAX_REPORT_SIZE];
110662306a36Sopenharmony_ci	struct dj_device *djdev;
110762306a36Sopenharmony_ci
110862306a36Sopenharmony_ci	djdev = djrcv_dev->paired_dj_devices[dj_report->device_index];
110962306a36Sopenharmony_ci
111062306a36Sopenharmony_ci	memset(reportbuffer, 0, sizeof(reportbuffer));
111162306a36Sopenharmony_ci
111262306a36Sopenharmony_ci	for (i = 0; i < NUMBER_OF_HID_REPORTS; i++) {
111362306a36Sopenharmony_ci		if (djdev->reports_supported & (1 << i)) {
111462306a36Sopenharmony_ci			reportbuffer[0] = i;
111562306a36Sopenharmony_ci			if (hid_input_report(djdev->hdev,
111662306a36Sopenharmony_ci					     HID_INPUT_REPORT,
111762306a36Sopenharmony_ci					     reportbuffer,
111862306a36Sopenharmony_ci					     hid_reportid_size_map[i], 1)) {
111962306a36Sopenharmony_ci				dbg_hid("hid_input_report error sending null "
112062306a36Sopenharmony_ci					"report\n");
112162306a36Sopenharmony_ci			}
112262306a36Sopenharmony_ci		}
112362306a36Sopenharmony_ci	}
112462306a36Sopenharmony_ci}
112562306a36Sopenharmony_ci
112662306a36Sopenharmony_cistatic void logi_dj_recv_forward_dj(struct dj_receiver_dev *djrcv_dev,
112762306a36Sopenharmony_ci				    struct dj_report *dj_report)
112862306a36Sopenharmony_ci{
112962306a36Sopenharmony_ci	/* We are called from atomic context (tasklet && djrcv->lock held) */
113062306a36Sopenharmony_ci	struct dj_device *dj_device;
113162306a36Sopenharmony_ci
113262306a36Sopenharmony_ci	dj_device = djrcv_dev->paired_dj_devices[dj_report->device_index];
113362306a36Sopenharmony_ci
113462306a36Sopenharmony_ci	if ((dj_report->report_type > ARRAY_SIZE(hid_reportid_size_map) - 1) ||
113562306a36Sopenharmony_ci	    (hid_reportid_size_map[dj_report->report_type] == 0)) {
113662306a36Sopenharmony_ci		dbg_hid("invalid report type:%x\n", dj_report->report_type);
113762306a36Sopenharmony_ci		return;
113862306a36Sopenharmony_ci	}
113962306a36Sopenharmony_ci
114062306a36Sopenharmony_ci	if (hid_input_report(dj_device->hdev,
114162306a36Sopenharmony_ci			HID_INPUT_REPORT, &dj_report->report_type,
114262306a36Sopenharmony_ci			hid_reportid_size_map[dj_report->report_type], 1)) {
114362306a36Sopenharmony_ci		dbg_hid("hid_input_report error\n");
114462306a36Sopenharmony_ci	}
114562306a36Sopenharmony_ci}
114662306a36Sopenharmony_ci
114762306a36Sopenharmony_cistatic void logi_dj_recv_forward_report(struct dj_device *dj_dev, u8 *data,
114862306a36Sopenharmony_ci					int size)
114962306a36Sopenharmony_ci{
115062306a36Sopenharmony_ci	/* We are called from atomic context (tasklet && djrcv->lock held) */
115162306a36Sopenharmony_ci	if (hid_input_report(dj_dev->hdev, HID_INPUT_REPORT, data, size, 1))
115262306a36Sopenharmony_ci		dbg_hid("hid_input_report error\n");
115362306a36Sopenharmony_ci}
115462306a36Sopenharmony_ci
115562306a36Sopenharmony_cistatic void logi_dj_recv_forward_input_report(struct hid_device *hdev,
115662306a36Sopenharmony_ci					      u8 *data, int size)
115762306a36Sopenharmony_ci{
115862306a36Sopenharmony_ci	struct dj_receiver_dev *djrcv_dev = hid_get_drvdata(hdev);
115962306a36Sopenharmony_ci	struct dj_device *dj_dev;
116062306a36Sopenharmony_ci	unsigned long flags;
116162306a36Sopenharmony_ci	u8 report = data[0];
116262306a36Sopenharmony_ci	int i;
116362306a36Sopenharmony_ci
116462306a36Sopenharmony_ci	if (report > REPORT_TYPE_RFREPORT_LAST) {
116562306a36Sopenharmony_ci		hid_err(hdev, "Unexpected input report number %d\n", report);
116662306a36Sopenharmony_ci		return;
116762306a36Sopenharmony_ci	}
116862306a36Sopenharmony_ci
116962306a36Sopenharmony_ci	spin_lock_irqsave(&djrcv_dev->lock, flags);
117062306a36Sopenharmony_ci	for (i = 0; i < (DJ_MAX_PAIRED_DEVICES + DJ_DEVICE_INDEX_MIN); i++) {
117162306a36Sopenharmony_ci		dj_dev = djrcv_dev->paired_dj_devices[i];
117262306a36Sopenharmony_ci		if (dj_dev && (dj_dev->reports_supported & BIT(report))) {
117362306a36Sopenharmony_ci			logi_dj_recv_forward_report(dj_dev, data, size);
117462306a36Sopenharmony_ci			spin_unlock_irqrestore(&djrcv_dev->lock, flags);
117562306a36Sopenharmony_ci			return;
117662306a36Sopenharmony_ci		}
117762306a36Sopenharmony_ci	}
117862306a36Sopenharmony_ci
117962306a36Sopenharmony_ci	logi_dj_recv_queue_unknown_work(djrcv_dev);
118062306a36Sopenharmony_ci	spin_unlock_irqrestore(&djrcv_dev->lock, flags);
118162306a36Sopenharmony_ci
118262306a36Sopenharmony_ci	dbg_hid("No dj-devs handling input report number %d\n", report);
118362306a36Sopenharmony_ci}
118462306a36Sopenharmony_ci
118562306a36Sopenharmony_cistatic int logi_dj_recv_send_report(struct dj_receiver_dev *djrcv_dev,
118662306a36Sopenharmony_ci				    struct dj_report *dj_report)
118762306a36Sopenharmony_ci{
118862306a36Sopenharmony_ci	struct hid_device *hdev = djrcv_dev->hidpp;
118962306a36Sopenharmony_ci	struct hid_report *report;
119062306a36Sopenharmony_ci	struct hid_report_enum *output_report_enum;
119162306a36Sopenharmony_ci	u8 *data = (u8 *)(&dj_report->device_index);
119262306a36Sopenharmony_ci	unsigned int i;
119362306a36Sopenharmony_ci
119462306a36Sopenharmony_ci	output_report_enum = &hdev->report_enum[HID_OUTPUT_REPORT];
119562306a36Sopenharmony_ci	report = output_report_enum->report_id_hash[REPORT_ID_DJ_SHORT];
119662306a36Sopenharmony_ci
119762306a36Sopenharmony_ci	if (!report) {
119862306a36Sopenharmony_ci		hid_err(hdev, "%s: unable to find dj report\n", __func__);
119962306a36Sopenharmony_ci		return -ENODEV;
120062306a36Sopenharmony_ci	}
120162306a36Sopenharmony_ci
120262306a36Sopenharmony_ci	for (i = 0; i < DJREPORT_SHORT_LENGTH - 1; i++)
120362306a36Sopenharmony_ci		report->field[0]->value[i] = data[i];
120462306a36Sopenharmony_ci
120562306a36Sopenharmony_ci	hid_hw_request(hdev, report, HID_REQ_SET_REPORT);
120662306a36Sopenharmony_ci
120762306a36Sopenharmony_ci	return 0;
120862306a36Sopenharmony_ci}
120962306a36Sopenharmony_ci
121062306a36Sopenharmony_cistatic int logi_dj_recv_query_hidpp_devices(struct dj_receiver_dev *djrcv_dev)
121162306a36Sopenharmony_ci{
121262306a36Sopenharmony_ci	static const u8 template[] = {
121362306a36Sopenharmony_ci		REPORT_ID_HIDPP_SHORT,
121462306a36Sopenharmony_ci		HIDPP_RECEIVER_INDEX,
121562306a36Sopenharmony_ci		HIDPP_SET_REGISTER,
121662306a36Sopenharmony_ci		HIDPP_REG_CONNECTION_STATE,
121762306a36Sopenharmony_ci		HIDPP_FAKE_DEVICE_ARRIVAL,
121862306a36Sopenharmony_ci		0x00, 0x00
121962306a36Sopenharmony_ci	};
122062306a36Sopenharmony_ci	u8 *hidpp_report;
122162306a36Sopenharmony_ci	int retval;
122262306a36Sopenharmony_ci
122362306a36Sopenharmony_ci	hidpp_report = kmemdup(template, sizeof(template), GFP_KERNEL);
122462306a36Sopenharmony_ci	if (!hidpp_report)
122562306a36Sopenharmony_ci		return -ENOMEM;
122662306a36Sopenharmony_ci
122762306a36Sopenharmony_ci	retval = hid_hw_raw_request(djrcv_dev->hidpp,
122862306a36Sopenharmony_ci				    REPORT_ID_HIDPP_SHORT,
122962306a36Sopenharmony_ci				    hidpp_report, sizeof(template),
123062306a36Sopenharmony_ci				    HID_OUTPUT_REPORT,
123162306a36Sopenharmony_ci				    HID_REQ_SET_REPORT);
123262306a36Sopenharmony_ci
123362306a36Sopenharmony_ci	kfree(hidpp_report);
123462306a36Sopenharmony_ci	return (retval < 0) ? retval : 0;
123562306a36Sopenharmony_ci}
123662306a36Sopenharmony_ci
123762306a36Sopenharmony_cistatic int logi_dj_recv_query_paired_devices(struct dj_receiver_dev *djrcv_dev)
123862306a36Sopenharmony_ci{
123962306a36Sopenharmony_ci	struct dj_report *dj_report;
124062306a36Sopenharmony_ci	int retval;
124162306a36Sopenharmony_ci
124262306a36Sopenharmony_ci	djrcv_dev->last_query = jiffies;
124362306a36Sopenharmony_ci
124462306a36Sopenharmony_ci	if (djrcv_dev->type != recvr_type_dj)
124562306a36Sopenharmony_ci		return logi_dj_recv_query_hidpp_devices(djrcv_dev);
124662306a36Sopenharmony_ci
124762306a36Sopenharmony_ci	dj_report = kzalloc(sizeof(struct dj_report), GFP_KERNEL);
124862306a36Sopenharmony_ci	if (!dj_report)
124962306a36Sopenharmony_ci		return -ENOMEM;
125062306a36Sopenharmony_ci	dj_report->report_id = REPORT_ID_DJ_SHORT;
125162306a36Sopenharmony_ci	dj_report->device_index = HIDPP_RECEIVER_INDEX;
125262306a36Sopenharmony_ci	dj_report->report_type = REPORT_TYPE_CMD_GET_PAIRED_DEVICES;
125362306a36Sopenharmony_ci	retval = logi_dj_recv_send_report(djrcv_dev, dj_report);
125462306a36Sopenharmony_ci	kfree(dj_report);
125562306a36Sopenharmony_ci	return retval;
125662306a36Sopenharmony_ci}
125762306a36Sopenharmony_ci
125862306a36Sopenharmony_ci
125962306a36Sopenharmony_cistatic int logi_dj_recv_switch_to_dj_mode(struct dj_receiver_dev *djrcv_dev,
126062306a36Sopenharmony_ci					  unsigned timeout)
126162306a36Sopenharmony_ci{
126262306a36Sopenharmony_ci	struct hid_device *hdev = djrcv_dev->hidpp;
126362306a36Sopenharmony_ci	struct dj_report *dj_report;
126462306a36Sopenharmony_ci	u8 *buf;
126562306a36Sopenharmony_ci	int retval = 0;
126662306a36Sopenharmony_ci
126762306a36Sopenharmony_ci	dj_report = kzalloc(sizeof(struct dj_report), GFP_KERNEL);
126862306a36Sopenharmony_ci	if (!dj_report)
126962306a36Sopenharmony_ci		return -ENOMEM;
127062306a36Sopenharmony_ci
127162306a36Sopenharmony_ci	if (djrcv_dev->type == recvr_type_dj) {
127262306a36Sopenharmony_ci		dj_report->report_id = REPORT_ID_DJ_SHORT;
127362306a36Sopenharmony_ci		dj_report->device_index = HIDPP_RECEIVER_INDEX;
127462306a36Sopenharmony_ci		dj_report->report_type = REPORT_TYPE_CMD_SWITCH;
127562306a36Sopenharmony_ci		dj_report->report_params[CMD_SWITCH_PARAM_DEVBITFIELD] = 0x3F;
127662306a36Sopenharmony_ci		dj_report->report_params[CMD_SWITCH_PARAM_TIMEOUT_SECONDS] =
127762306a36Sopenharmony_ci								(u8)timeout;
127862306a36Sopenharmony_ci
127962306a36Sopenharmony_ci		retval = logi_dj_recv_send_report(djrcv_dev, dj_report);
128062306a36Sopenharmony_ci
128162306a36Sopenharmony_ci		/*
128262306a36Sopenharmony_ci		 * Ugly sleep to work around a USB 3.0 bug when the receiver is
128362306a36Sopenharmony_ci		 * still processing the "switch-to-dj" command while we send an
128462306a36Sopenharmony_ci		 * other command.
128562306a36Sopenharmony_ci		 * 50 msec should gives enough time to the receiver to be ready.
128662306a36Sopenharmony_ci		 */
128762306a36Sopenharmony_ci		msleep(50);
128862306a36Sopenharmony_ci
128962306a36Sopenharmony_ci		if (retval)
129062306a36Sopenharmony_ci			return retval;
129162306a36Sopenharmony_ci	}
129262306a36Sopenharmony_ci
129362306a36Sopenharmony_ci	/*
129462306a36Sopenharmony_ci	 * Magical bits to set up hidpp notifications when the dj devices
129562306a36Sopenharmony_ci	 * are connected/disconnected.
129662306a36Sopenharmony_ci	 *
129762306a36Sopenharmony_ci	 * We can reuse dj_report because HIDPP_REPORT_SHORT_LENGTH is smaller
129862306a36Sopenharmony_ci	 * than DJREPORT_SHORT_LENGTH.
129962306a36Sopenharmony_ci	 */
130062306a36Sopenharmony_ci	buf = (u8 *)dj_report;
130162306a36Sopenharmony_ci
130262306a36Sopenharmony_ci	memset(buf, 0, HIDPP_REPORT_SHORT_LENGTH);
130362306a36Sopenharmony_ci
130462306a36Sopenharmony_ci	buf[0] = REPORT_ID_HIDPP_SHORT;
130562306a36Sopenharmony_ci	buf[1] = HIDPP_RECEIVER_INDEX;
130662306a36Sopenharmony_ci	buf[2] = 0x80;
130762306a36Sopenharmony_ci	buf[3] = 0x00;
130862306a36Sopenharmony_ci	buf[4] = 0x00;
130962306a36Sopenharmony_ci	buf[5] = 0x09;
131062306a36Sopenharmony_ci	buf[6] = 0x00;
131162306a36Sopenharmony_ci
131262306a36Sopenharmony_ci	retval = hid_hw_raw_request(hdev, REPORT_ID_HIDPP_SHORT, buf,
131362306a36Sopenharmony_ci			HIDPP_REPORT_SHORT_LENGTH, HID_OUTPUT_REPORT,
131462306a36Sopenharmony_ci			HID_REQ_SET_REPORT);
131562306a36Sopenharmony_ci
131662306a36Sopenharmony_ci	kfree(dj_report);
131762306a36Sopenharmony_ci	return retval;
131862306a36Sopenharmony_ci}
131962306a36Sopenharmony_ci
132062306a36Sopenharmony_ci
132162306a36Sopenharmony_cistatic int logi_dj_ll_open(struct hid_device *hid)
132262306a36Sopenharmony_ci{
132362306a36Sopenharmony_ci	dbg_hid("%s: %s\n", __func__, hid->phys);
132462306a36Sopenharmony_ci	return 0;
132562306a36Sopenharmony_ci
132662306a36Sopenharmony_ci}
132762306a36Sopenharmony_ci
132862306a36Sopenharmony_cistatic void logi_dj_ll_close(struct hid_device *hid)
132962306a36Sopenharmony_ci{
133062306a36Sopenharmony_ci	dbg_hid("%s: %s\n", __func__, hid->phys);
133162306a36Sopenharmony_ci}
133262306a36Sopenharmony_ci
133362306a36Sopenharmony_ci/*
133462306a36Sopenharmony_ci * Register 0xB5 is "pairing information". It is solely intended for the
133562306a36Sopenharmony_ci * receiver, so do not overwrite the device index.
133662306a36Sopenharmony_ci */
133762306a36Sopenharmony_cistatic u8 unifying_pairing_query[]  = { REPORT_ID_HIDPP_SHORT,
133862306a36Sopenharmony_ci					HIDPP_RECEIVER_INDEX,
133962306a36Sopenharmony_ci					HIDPP_GET_LONG_REGISTER,
134062306a36Sopenharmony_ci					HIDPP_REG_PAIRING_INFORMATION };
134162306a36Sopenharmony_cistatic u8 unifying_pairing_answer[] = { REPORT_ID_HIDPP_LONG,
134262306a36Sopenharmony_ci					HIDPP_RECEIVER_INDEX,
134362306a36Sopenharmony_ci					HIDPP_GET_LONG_REGISTER,
134462306a36Sopenharmony_ci					HIDPP_REG_PAIRING_INFORMATION };
134562306a36Sopenharmony_ci
134662306a36Sopenharmony_cistatic int logi_dj_ll_raw_request(struct hid_device *hid,
134762306a36Sopenharmony_ci				  unsigned char reportnum, __u8 *buf,
134862306a36Sopenharmony_ci				  size_t count, unsigned char report_type,
134962306a36Sopenharmony_ci				  int reqtype)
135062306a36Sopenharmony_ci{
135162306a36Sopenharmony_ci	struct dj_device *djdev = hid->driver_data;
135262306a36Sopenharmony_ci	struct dj_receiver_dev *djrcv_dev = djdev->dj_receiver_dev;
135362306a36Sopenharmony_ci	u8 *out_buf;
135462306a36Sopenharmony_ci	int ret;
135562306a36Sopenharmony_ci
135662306a36Sopenharmony_ci	if ((buf[0] == REPORT_ID_HIDPP_SHORT) ||
135762306a36Sopenharmony_ci	    (buf[0] == REPORT_ID_HIDPP_LONG) ||
135862306a36Sopenharmony_ci	    (buf[0] == REPORT_ID_HIDPP_VERY_LONG)) {
135962306a36Sopenharmony_ci		if (count < 2)
136062306a36Sopenharmony_ci			return -EINVAL;
136162306a36Sopenharmony_ci
136262306a36Sopenharmony_ci		/* special case where we should not overwrite
136362306a36Sopenharmony_ci		 * the device_index */
136462306a36Sopenharmony_ci		if (count == 7 && !memcmp(buf, unifying_pairing_query,
136562306a36Sopenharmony_ci					  sizeof(unifying_pairing_query)))
136662306a36Sopenharmony_ci			buf[4] = (buf[4] & 0xf0) | (djdev->device_index - 1);
136762306a36Sopenharmony_ci		else
136862306a36Sopenharmony_ci			buf[1] = djdev->device_index;
136962306a36Sopenharmony_ci		return hid_hw_raw_request(djrcv_dev->hidpp, reportnum, buf,
137062306a36Sopenharmony_ci				count, report_type, reqtype);
137162306a36Sopenharmony_ci	}
137262306a36Sopenharmony_ci
137362306a36Sopenharmony_ci	if (buf[0] != REPORT_TYPE_LEDS)
137462306a36Sopenharmony_ci		return -EINVAL;
137562306a36Sopenharmony_ci
137662306a36Sopenharmony_ci	if (djrcv_dev->type != recvr_type_dj && count >= 2) {
137762306a36Sopenharmony_ci		if (!djrcv_dev->keyboard) {
137862306a36Sopenharmony_ci			hid_warn(hid, "Received REPORT_TYPE_LEDS request before the keyboard interface was enumerated\n");
137962306a36Sopenharmony_ci			return 0;
138062306a36Sopenharmony_ci		}
138162306a36Sopenharmony_ci		/* usbhid overrides the report ID and ignores the first byte */
138262306a36Sopenharmony_ci		return hid_hw_raw_request(djrcv_dev->keyboard, 0, buf, count,
138362306a36Sopenharmony_ci					  report_type, reqtype);
138462306a36Sopenharmony_ci	}
138562306a36Sopenharmony_ci
138662306a36Sopenharmony_ci	out_buf = kzalloc(DJREPORT_SHORT_LENGTH, GFP_ATOMIC);
138762306a36Sopenharmony_ci	if (!out_buf)
138862306a36Sopenharmony_ci		return -ENOMEM;
138962306a36Sopenharmony_ci
139062306a36Sopenharmony_ci	if (count > DJREPORT_SHORT_LENGTH - 2)
139162306a36Sopenharmony_ci		count = DJREPORT_SHORT_LENGTH - 2;
139262306a36Sopenharmony_ci
139362306a36Sopenharmony_ci	out_buf[0] = REPORT_ID_DJ_SHORT;
139462306a36Sopenharmony_ci	out_buf[1] = djdev->device_index;
139562306a36Sopenharmony_ci	memcpy(out_buf + 2, buf, count);
139662306a36Sopenharmony_ci
139762306a36Sopenharmony_ci	ret = hid_hw_raw_request(djrcv_dev->hidpp, out_buf[0], out_buf,
139862306a36Sopenharmony_ci		DJREPORT_SHORT_LENGTH, report_type, reqtype);
139962306a36Sopenharmony_ci
140062306a36Sopenharmony_ci	kfree(out_buf);
140162306a36Sopenharmony_ci	return ret;
140262306a36Sopenharmony_ci}
140362306a36Sopenharmony_ci
140462306a36Sopenharmony_cistatic void rdcat(char *rdesc, unsigned int *rsize, const char *data, unsigned int size)
140562306a36Sopenharmony_ci{
140662306a36Sopenharmony_ci	memcpy(rdesc + *rsize, data, size);
140762306a36Sopenharmony_ci	*rsize += size;
140862306a36Sopenharmony_ci}
140962306a36Sopenharmony_ci
141062306a36Sopenharmony_cistatic int logi_dj_ll_parse(struct hid_device *hid)
141162306a36Sopenharmony_ci{
141262306a36Sopenharmony_ci	struct dj_device *djdev = hid->driver_data;
141362306a36Sopenharmony_ci	unsigned int rsize = 0;
141462306a36Sopenharmony_ci	char *rdesc;
141562306a36Sopenharmony_ci	int retval;
141662306a36Sopenharmony_ci
141762306a36Sopenharmony_ci	dbg_hid("%s\n", __func__);
141862306a36Sopenharmony_ci
141962306a36Sopenharmony_ci	djdev->hdev->version = 0x0111;
142062306a36Sopenharmony_ci	djdev->hdev->country = 0x00;
142162306a36Sopenharmony_ci
142262306a36Sopenharmony_ci	rdesc = kmalloc(MAX_RDESC_SIZE, GFP_KERNEL);
142362306a36Sopenharmony_ci	if (!rdesc)
142462306a36Sopenharmony_ci		return -ENOMEM;
142562306a36Sopenharmony_ci
142662306a36Sopenharmony_ci	if (djdev->reports_supported & STD_KEYBOARD) {
142762306a36Sopenharmony_ci		dbg_hid("%s: sending a kbd descriptor, reports_supported: %llx\n",
142862306a36Sopenharmony_ci			__func__, djdev->reports_supported);
142962306a36Sopenharmony_ci		rdcat(rdesc, &rsize, kbd_descriptor, sizeof(kbd_descriptor));
143062306a36Sopenharmony_ci	}
143162306a36Sopenharmony_ci
143262306a36Sopenharmony_ci	if (djdev->reports_supported & STD_MOUSE) {
143362306a36Sopenharmony_ci		dbg_hid("%s: sending a mouse descriptor, reports_supported: %llx\n",
143462306a36Sopenharmony_ci			__func__, djdev->reports_supported);
143562306a36Sopenharmony_ci		if (djdev->dj_receiver_dev->type == recvr_type_gaming_hidpp ||
143662306a36Sopenharmony_ci		    djdev->dj_receiver_dev->type == recvr_type_mouse_only)
143762306a36Sopenharmony_ci			rdcat(rdesc, &rsize, mse_high_res_descriptor,
143862306a36Sopenharmony_ci			      sizeof(mse_high_res_descriptor));
143962306a36Sopenharmony_ci		else if (djdev->dj_receiver_dev->type == recvr_type_27mhz)
144062306a36Sopenharmony_ci			rdcat(rdesc, &rsize, mse_27mhz_descriptor,
144162306a36Sopenharmony_ci			      sizeof(mse_27mhz_descriptor));
144262306a36Sopenharmony_ci		else if (recvr_type_is_bluetooth(djdev->dj_receiver_dev->type))
144362306a36Sopenharmony_ci			rdcat(rdesc, &rsize, mse_bluetooth_descriptor,
144462306a36Sopenharmony_ci			      sizeof(mse_bluetooth_descriptor));
144562306a36Sopenharmony_ci		else
144662306a36Sopenharmony_ci			rdcat(rdesc, &rsize, mse_descriptor,
144762306a36Sopenharmony_ci			      sizeof(mse_descriptor));
144862306a36Sopenharmony_ci	}
144962306a36Sopenharmony_ci
145062306a36Sopenharmony_ci	if (djdev->reports_supported & KBD_MOUSE) {
145162306a36Sopenharmony_ci		dbg_hid("%s: sending a kbd-mouse descriptor, reports_supported: %llx\n",
145262306a36Sopenharmony_ci			__func__, djdev->reports_supported);
145362306a36Sopenharmony_ci		rdcat(rdesc, &rsize, mse5_bluetooth_descriptor,
145462306a36Sopenharmony_ci		      sizeof(mse5_bluetooth_descriptor));
145562306a36Sopenharmony_ci	}
145662306a36Sopenharmony_ci
145762306a36Sopenharmony_ci	if (djdev->reports_supported & MULTIMEDIA) {
145862306a36Sopenharmony_ci		dbg_hid("%s: sending a multimedia report descriptor: %llx\n",
145962306a36Sopenharmony_ci			__func__, djdev->reports_supported);
146062306a36Sopenharmony_ci		rdcat(rdesc, &rsize, consumer_descriptor, sizeof(consumer_descriptor));
146162306a36Sopenharmony_ci	}
146262306a36Sopenharmony_ci
146362306a36Sopenharmony_ci	if (djdev->reports_supported & POWER_KEYS) {
146462306a36Sopenharmony_ci		dbg_hid("%s: sending a power keys report descriptor: %llx\n",
146562306a36Sopenharmony_ci			__func__, djdev->reports_supported);
146662306a36Sopenharmony_ci		rdcat(rdesc, &rsize, syscontrol_descriptor, sizeof(syscontrol_descriptor));
146762306a36Sopenharmony_ci	}
146862306a36Sopenharmony_ci
146962306a36Sopenharmony_ci	if (djdev->reports_supported & MEDIA_CENTER) {
147062306a36Sopenharmony_ci		dbg_hid("%s: sending a media center report descriptor: %llx\n",
147162306a36Sopenharmony_ci			__func__, djdev->reports_supported);
147262306a36Sopenharmony_ci		rdcat(rdesc, &rsize, media_descriptor, sizeof(media_descriptor));
147362306a36Sopenharmony_ci	}
147462306a36Sopenharmony_ci
147562306a36Sopenharmony_ci	if (djdev->reports_supported & KBD_LEDS) {
147662306a36Sopenharmony_ci		dbg_hid("%s: need to send kbd leds report descriptor: %llx\n",
147762306a36Sopenharmony_ci			__func__, djdev->reports_supported);
147862306a36Sopenharmony_ci	}
147962306a36Sopenharmony_ci
148062306a36Sopenharmony_ci	if (djdev->reports_supported & HIDPP) {
148162306a36Sopenharmony_ci		dbg_hid("%s: sending a HID++ descriptor, reports_supported: %llx\n",
148262306a36Sopenharmony_ci			__func__, djdev->reports_supported);
148362306a36Sopenharmony_ci		rdcat(rdesc, &rsize, hidpp_descriptor,
148462306a36Sopenharmony_ci		      sizeof(hidpp_descriptor));
148562306a36Sopenharmony_ci	}
148662306a36Sopenharmony_ci
148762306a36Sopenharmony_ci	retval = hid_parse_report(hid, rdesc, rsize);
148862306a36Sopenharmony_ci	kfree(rdesc);
148962306a36Sopenharmony_ci
149062306a36Sopenharmony_ci	return retval;
149162306a36Sopenharmony_ci}
149262306a36Sopenharmony_ci
149362306a36Sopenharmony_cistatic int logi_dj_ll_start(struct hid_device *hid)
149462306a36Sopenharmony_ci{
149562306a36Sopenharmony_ci	dbg_hid("%s\n", __func__);
149662306a36Sopenharmony_ci	return 0;
149762306a36Sopenharmony_ci}
149862306a36Sopenharmony_ci
149962306a36Sopenharmony_cistatic void logi_dj_ll_stop(struct hid_device *hid)
150062306a36Sopenharmony_ci{
150162306a36Sopenharmony_ci	dbg_hid("%s\n", __func__);
150262306a36Sopenharmony_ci}
150362306a36Sopenharmony_ci
150462306a36Sopenharmony_cistatic bool logi_dj_ll_may_wakeup(struct hid_device *hid)
150562306a36Sopenharmony_ci{
150662306a36Sopenharmony_ci	struct dj_device *djdev = hid->driver_data;
150762306a36Sopenharmony_ci	struct dj_receiver_dev *djrcv_dev = djdev->dj_receiver_dev;
150862306a36Sopenharmony_ci
150962306a36Sopenharmony_ci	return hid_hw_may_wakeup(djrcv_dev->hidpp);
151062306a36Sopenharmony_ci}
151162306a36Sopenharmony_ci
151262306a36Sopenharmony_cistatic const struct hid_ll_driver logi_dj_ll_driver = {
151362306a36Sopenharmony_ci	.parse = logi_dj_ll_parse,
151462306a36Sopenharmony_ci	.start = logi_dj_ll_start,
151562306a36Sopenharmony_ci	.stop = logi_dj_ll_stop,
151662306a36Sopenharmony_ci	.open = logi_dj_ll_open,
151762306a36Sopenharmony_ci	.close = logi_dj_ll_close,
151862306a36Sopenharmony_ci	.raw_request = logi_dj_ll_raw_request,
151962306a36Sopenharmony_ci	.may_wakeup = logi_dj_ll_may_wakeup,
152062306a36Sopenharmony_ci};
152162306a36Sopenharmony_ci
152262306a36Sopenharmony_cistatic int logi_dj_dj_event(struct hid_device *hdev,
152362306a36Sopenharmony_ci			     struct hid_report *report, u8 *data,
152462306a36Sopenharmony_ci			     int size)
152562306a36Sopenharmony_ci{
152662306a36Sopenharmony_ci	struct dj_receiver_dev *djrcv_dev = hid_get_drvdata(hdev);
152762306a36Sopenharmony_ci	struct dj_report *dj_report = (struct dj_report *) data;
152862306a36Sopenharmony_ci	unsigned long flags;
152962306a36Sopenharmony_ci
153062306a36Sopenharmony_ci	/*
153162306a36Sopenharmony_ci	 * Here we receive all data coming from iface 2, there are 3 cases:
153262306a36Sopenharmony_ci	 *
153362306a36Sopenharmony_ci	 * 1) Data is intended for this driver i. e. data contains arrival,
153462306a36Sopenharmony_ci	 * departure, etc notifications, in which case we queue them for delayed
153562306a36Sopenharmony_ci	 * processing by the work queue. We return 1 to hid-core as no further
153662306a36Sopenharmony_ci	 * processing is required from it.
153762306a36Sopenharmony_ci	 *
153862306a36Sopenharmony_ci	 * 2) Data informs a connection change, if the change means rf link
153962306a36Sopenharmony_ci	 * loss, then we must send a null report to the upper layer to discard
154062306a36Sopenharmony_ci	 * potentially pressed keys that may be repeated forever by the input
154162306a36Sopenharmony_ci	 * layer. Return 1 to hid-core as no further processing is required.
154262306a36Sopenharmony_ci	 *
154362306a36Sopenharmony_ci	 * 3) Data is an actual input event from a paired DJ device in which
154462306a36Sopenharmony_ci	 * case we forward it to the correct hid device (via hid_input_report()
154562306a36Sopenharmony_ci	 * ) and return 1 so hid-core does not anything else with it.
154662306a36Sopenharmony_ci	 */
154762306a36Sopenharmony_ci
154862306a36Sopenharmony_ci	if ((dj_report->device_index < DJ_DEVICE_INDEX_MIN) ||
154962306a36Sopenharmony_ci	    (dj_report->device_index > DJ_DEVICE_INDEX_MAX)) {
155062306a36Sopenharmony_ci		/*
155162306a36Sopenharmony_ci		 * Device index is wrong, bail out.
155262306a36Sopenharmony_ci		 * This driver can ignore safely the receiver notifications,
155362306a36Sopenharmony_ci		 * so ignore those reports too.
155462306a36Sopenharmony_ci		 */
155562306a36Sopenharmony_ci		if (dj_report->device_index != DJ_RECEIVER_INDEX)
155662306a36Sopenharmony_ci			hid_err(hdev, "%s: invalid device index:%d\n",
155762306a36Sopenharmony_ci				__func__, dj_report->device_index);
155862306a36Sopenharmony_ci		return false;
155962306a36Sopenharmony_ci	}
156062306a36Sopenharmony_ci
156162306a36Sopenharmony_ci	spin_lock_irqsave(&djrcv_dev->lock, flags);
156262306a36Sopenharmony_ci
156362306a36Sopenharmony_ci	if (!djrcv_dev->paired_dj_devices[dj_report->device_index]) {
156462306a36Sopenharmony_ci		/* received an event for an unknown device, bail out */
156562306a36Sopenharmony_ci		logi_dj_recv_queue_notification(djrcv_dev, dj_report);
156662306a36Sopenharmony_ci		goto out;
156762306a36Sopenharmony_ci	}
156862306a36Sopenharmony_ci
156962306a36Sopenharmony_ci	switch (dj_report->report_type) {
157062306a36Sopenharmony_ci	case REPORT_TYPE_NOTIF_DEVICE_PAIRED:
157162306a36Sopenharmony_ci		/* pairing notifications are handled above the switch */
157262306a36Sopenharmony_ci		break;
157362306a36Sopenharmony_ci	case REPORT_TYPE_NOTIF_DEVICE_UNPAIRED:
157462306a36Sopenharmony_ci		logi_dj_recv_queue_notification(djrcv_dev, dj_report);
157562306a36Sopenharmony_ci		break;
157662306a36Sopenharmony_ci	case REPORT_TYPE_NOTIF_CONNECTION_STATUS:
157762306a36Sopenharmony_ci		if (dj_report->report_params[CONNECTION_STATUS_PARAM_STATUS] ==
157862306a36Sopenharmony_ci		    STATUS_LINKLOSS) {
157962306a36Sopenharmony_ci			logi_dj_recv_forward_null_report(djrcv_dev, dj_report);
158062306a36Sopenharmony_ci		}
158162306a36Sopenharmony_ci		break;
158262306a36Sopenharmony_ci	default:
158362306a36Sopenharmony_ci		logi_dj_recv_forward_dj(djrcv_dev, dj_report);
158462306a36Sopenharmony_ci	}
158562306a36Sopenharmony_ci
158662306a36Sopenharmony_ciout:
158762306a36Sopenharmony_ci	spin_unlock_irqrestore(&djrcv_dev->lock, flags);
158862306a36Sopenharmony_ci
158962306a36Sopenharmony_ci	return true;
159062306a36Sopenharmony_ci}
159162306a36Sopenharmony_ci
159262306a36Sopenharmony_cistatic int logi_dj_hidpp_event(struct hid_device *hdev,
159362306a36Sopenharmony_ci			     struct hid_report *report, u8 *data,
159462306a36Sopenharmony_ci			     int size)
159562306a36Sopenharmony_ci{
159662306a36Sopenharmony_ci	struct dj_receiver_dev *djrcv_dev = hid_get_drvdata(hdev);
159762306a36Sopenharmony_ci	struct hidpp_event *hidpp_report = (struct hidpp_event *) data;
159862306a36Sopenharmony_ci	struct dj_device *dj_dev;
159962306a36Sopenharmony_ci	unsigned long flags;
160062306a36Sopenharmony_ci	u8 device_index = hidpp_report->device_index;
160162306a36Sopenharmony_ci
160262306a36Sopenharmony_ci	if (device_index == HIDPP_RECEIVER_INDEX) {
160362306a36Sopenharmony_ci		/* special case were the device wants to know its unifying
160462306a36Sopenharmony_ci		 * name */
160562306a36Sopenharmony_ci		if (size == HIDPP_REPORT_LONG_LENGTH &&
160662306a36Sopenharmony_ci		    !memcmp(data, unifying_pairing_answer,
160762306a36Sopenharmony_ci			    sizeof(unifying_pairing_answer)))
160862306a36Sopenharmony_ci			device_index = (data[4] & 0x0F) + 1;
160962306a36Sopenharmony_ci		else
161062306a36Sopenharmony_ci			return false;
161162306a36Sopenharmony_ci	}
161262306a36Sopenharmony_ci
161362306a36Sopenharmony_ci	/*
161462306a36Sopenharmony_ci	 * Data is from the HID++ collection, in this case, we forward the
161562306a36Sopenharmony_ci	 * data to the corresponding child dj device and return 0 to hid-core
161662306a36Sopenharmony_ci	 * so he data also goes to the hidraw device of the receiver. This
161762306a36Sopenharmony_ci	 * allows a user space application to implement the full HID++ routing
161862306a36Sopenharmony_ci	 * via the receiver.
161962306a36Sopenharmony_ci	 */
162062306a36Sopenharmony_ci
162162306a36Sopenharmony_ci	if ((device_index < DJ_DEVICE_INDEX_MIN) ||
162262306a36Sopenharmony_ci	    (device_index > DJ_DEVICE_INDEX_MAX)) {
162362306a36Sopenharmony_ci		/*
162462306a36Sopenharmony_ci		 * Device index is wrong, bail out.
162562306a36Sopenharmony_ci		 * This driver can ignore safely the receiver notifications,
162662306a36Sopenharmony_ci		 * so ignore those reports too.
162762306a36Sopenharmony_ci		 */
162862306a36Sopenharmony_ci		hid_err(hdev, "%s: invalid device index:%d\n", __func__,
162962306a36Sopenharmony_ci			hidpp_report->device_index);
163062306a36Sopenharmony_ci		return false;
163162306a36Sopenharmony_ci	}
163262306a36Sopenharmony_ci
163362306a36Sopenharmony_ci	spin_lock_irqsave(&djrcv_dev->lock, flags);
163462306a36Sopenharmony_ci
163562306a36Sopenharmony_ci	dj_dev = djrcv_dev->paired_dj_devices[device_index];
163662306a36Sopenharmony_ci
163762306a36Sopenharmony_ci	/*
163862306a36Sopenharmony_ci	 * With 27 MHz receivers, we do not get an explicit unpair event,
163962306a36Sopenharmony_ci	 * remove the old device if the user has paired a *different* device.
164062306a36Sopenharmony_ci	 */
164162306a36Sopenharmony_ci	if (djrcv_dev->type == recvr_type_27mhz && dj_dev &&
164262306a36Sopenharmony_ci	    hidpp_report->sub_id == REPORT_TYPE_NOTIF_DEVICE_CONNECTED &&
164362306a36Sopenharmony_ci	    hidpp_report->params[HIDPP_PARAM_PROTO_TYPE] == 0x02 &&
164462306a36Sopenharmony_ci	    hidpp_report->params[HIDPP_PARAM_27MHZ_DEVID] !=
164562306a36Sopenharmony_ci						dj_dev->hdev->product) {
164662306a36Sopenharmony_ci		struct dj_workitem workitem = {
164762306a36Sopenharmony_ci			.device_index = hidpp_report->device_index,
164862306a36Sopenharmony_ci			.type = WORKITEM_TYPE_UNPAIRED,
164962306a36Sopenharmony_ci		};
165062306a36Sopenharmony_ci		kfifo_in(&djrcv_dev->notif_fifo, &workitem, sizeof(workitem));
165162306a36Sopenharmony_ci		/* logi_hidpp_recv_queue_notif will queue the work */
165262306a36Sopenharmony_ci		dj_dev = NULL;
165362306a36Sopenharmony_ci	}
165462306a36Sopenharmony_ci
165562306a36Sopenharmony_ci	if (dj_dev) {
165662306a36Sopenharmony_ci		logi_dj_recv_forward_report(dj_dev, data, size);
165762306a36Sopenharmony_ci	} else {
165862306a36Sopenharmony_ci		if (hidpp_report->sub_id == REPORT_TYPE_NOTIF_DEVICE_CONNECTED)
165962306a36Sopenharmony_ci			logi_hidpp_recv_queue_notif(hdev, hidpp_report);
166062306a36Sopenharmony_ci		else
166162306a36Sopenharmony_ci			logi_dj_recv_queue_unknown_work(djrcv_dev);
166262306a36Sopenharmony_ci	}
166362306a36Sopenharmony_ci
166462306a36Sopenharmony_ci	spin_unlock_irqrestore(&djrcv_dev->lock, flags);
166562306a36Sopenharmony_ci
166662306a36Sopenharmony_ci	return false;
166762306a36Sopenharmony_ci}
166862306a36Sopenharmony_ci
166962306a36Sopenharmony_cistatic int logi_dj_raw_event(struct hid_device *hdev,
167062306a36Sopenharmony_ci			     struct hid_report *report, u8 *data,
167162306a36Sopenharmony_ci			     int size)
167262306a36Sopenharmony_ci{
167362306a36Sopenharmony_ci	struct dj_receiver_dev *djrcv_dev = hid_get_drvdata(hdev);
167462306a36Sopenharmony_ci	dbg_hid("%s, size:%d\n", __func__, size);
167562306a36Sopenharmony_ci
167662306a36Sopenharmony_ci	if (!djrcv_dev)
167762306a36Sopenharmony_ci		return 0;
167862306a36Sopenharmony_ci
167962306a36Sopenharmony_ci	if (!hdev->report_enum[HID_INPUT_REPORT].numbered) {
168062306a36Sopenharmony_ci
168162306a36Sopenharmony_ci		if (djrcv_dev->unnumbered_application == HID_GD_KEYBOARD) {
168262306a36Sopenharmony_ci			/*
168362306a36Sopenharmony_ci			 * For the keyboard, we can reuse the same report by
168462306a36Sopenharmony_ci			 * using the second byte which is constant in the USB
168562306a36Sopenharmony_ci			 * HID report descriptor.
168662306a36Sopenharmony_ci			 */
168762306a36Sopenharmony_ci			data[1] = data[0];
168862306a36Sopenharmony_ci			data[0] = REPORT_TYPE_KEYBOARD;
168962306a36Sopenharmony_ci
169062306a36Sopenharmony_ci			logi_dj_recv_forward_input_report(hdev, data, size);
169162306a36Sopenharmony_ci
169262306a36Sopenharmony_ci			/* restore previous state */
169362306a36Sopenharmony_ci			data[0] = data[1];
169462306a36Sopenharmony_ci			data[1] = 0;
169562306a36Sopenharmony_ci		}
169662306a36Sopenharmony_ci		/*
169762306a36Sopenharmony_ci		 * Mouse-only receivers send unnumbered mouse data. The 27 MHz
169862306a36Sopenharmony_ci		 * receiver uses 6 byte packets, the nano receiver 8 bytes.
169962306a36Sopenharmony_ci		 */
170062306a36Sopenharmony_ci		if (djrcv_dev->unnumbered_application == HID_GD_MOUSE &&
170162306a36Sopenharmony_ci		    size <= 8) {
170262306a36Sopenharmony_ci			u8 mouse_report[9];
170362306a36Sopenharmony_ci
170462306a36Sopenharmony_ci			/* Prepend report id */
170562306a36Sopenharmony_ci			mouse_report[0] = REPORT_TYPE_MOUSE;
170662306a36Sopenharmony_ci			memcpy(mouse_report + 1, data, size);
170762306a36Sopenharmony_ci			logi_dj_recv_forward_input_report(hdev, mouse_report,
170862306a36Sopenharmony_ci							  size + 1);
170962306a36Sopenharmony_ci		}
171062306a36Sopenharmony_ci
171162306a36Sopenharmony_ci		return false;
171262306a36Sopenharmony_ci	}
171362306a36Sopenharmony_ci
171462306a36Sopenharmony_ci	switch (data[0]) {
171562306a36Sopenharmony_ci	case REPORT_ID_DJ_SHORT:
171662306a36Sopenharmony_ci		if (size != DJREPORT_SHORT_LENGTH) {
171762306a36Sopenharmony_ci			hid_err(hdev, "Short DJ report bad size (%d)", size);
171862306a36Sopenharmony_ci			return false;
171962306a36Sopenharmony_ci		}
172062306a36Sopenharmony_ci		return logi_dj_dj_event(hdev, report, data, size);
172162306a36Sopenharmony_ci	case REPORT_ID_DJ_LONG:
172262306a36Sopenharmony_ci		if (size != DJREPORT_LONG_LENGTH) {
172362306a36Sopenharmony_ci			hid_err(hdev, "Long DJ report bad size (%d)", size);
172462306a36Sopenharmony_ci			return false;
172562306a36Sopenharmony_ci		}
172662306a36Sopenharmony_ci		return logi_dj_dj_event(hdev, report, data, size);
172762306a36Sopenharmony_ci	case REPORT_ID_HIDPP_SHORT:
172862306a36Sopenharmony_ci		if (size != HIDPP_REPORT_SHORT_LENGTH) {
172962306a36Sopenharmony_ci			hid_err(hdev, "Short HID++ report bad size (%d)", size);
173062306a36Sopenharmony_ci			return false;
173162306a36Sopenharmony_ci		}
173262306a36Sopenharmony_ci		return logi_dj_hidpp_event(hdev, report, data, size);
173362306a36Sopenharmony_ci	case REPORT_ID_HIDPP_LONG:
173462306a36Sopenharmony_ci		if (size != HIDPP_REPORT_LONG_LENGTH) {
173562306a36Sopenharmony_ci			hid_err(hdev, "Long HID++ report bad size (%d)", size);
173662306a36Sopenharmony_ci			return false;
173762306a36Sopenharmony_ci		}
173862306a36Sopenharmony_ci		return logi_dj_hidpp_event(hdev, report, data, size);
173962306a36Sopenharmony_ci	}
174062306a36Sopenharmony_ci
174162306a36Sopenharmony_ci	logi_dj_recv_forward_input_report(hdev, data, size);
174262306a36Sopenharmony_ci
174362306a36Sopenharmony_ci	return false;
174462306a36Sopenharmony_ci}
174562306a36Sopenharmony_ci
174662306a36Sopenharmony_cistatic int logi_dj_probe(struct hid_device *hdev,
174762306a36Sopenharmony_ci			 const struct hid_device_id *id)
174862306a36Sopenharmony_ci{
174962306a36Sopenharmony_ci	struct hid_report_enum *rep_enum;
175062306a36Sopenharmony_ci	struct hid_report *rep;
175162306a36Sopenharmony_ci	struct dj_receiver_dev *djrcv_dev;
175262306a36Sopenharmony_ci	struct usb_interface *intf;
175362306a36Sopenharmony_ci	unsigned int no_dj_interfaces = 0;
175462306a36Sopenharmony_ci	bool has_hidpp = false;
175562306a36Sopenharmony_ci	unsigned long flags;
175662306a36Sopenharmony_ci	int retval;
175762306a36Sopenharmony_ci
175862306a36Sopenharmony_ci	/*
175962306a36Sopenharmony_ci	 * Call to usbhid to fetch the HID descriptors of the current
176062306a36Sopenharmony_ci	 * interface subsequently call to the hid/hid-core to parse the
176162306a36Sopenharmony_ci	 * fetched descriptors.
176262306a36Sopenharmony_ci	 */
176362306a36Sopenharmony_ci	retval = hid_parse(hdev);
176462306a36Sopenharmony_ci	if (retval) {
176562306a36Sopenharmony_ci		hid_err(hdev, "%s: parse failed\n", __func__);
176662306a36Sopenharmony_ci		return retval;
176762306a36Sopenharmony_ci	}
176862306a36Sopenharmony_ci
176962306a36Sopenharmony_ci	/*
177062306a36Sopenharmony_ci	 * Some KVMs add an extra interface for e.g. mouse emulation. If we
177162306a36Sopenharmony_ci	 * treat these as logitech-dj interfaces then this causes input events
177262306a36Sopenharmony_ci	 * reported through this extra interface to not be reported correctly.
177362306a36Sopenharmony_ci	 * To avoid this, we treat these as generic-hid devices.
177462306a36Sopenharmony_ci	 */
177562306a36Sopenharmony_ci	switch (id->driver_data) {
177662306a36Sopenharmony_ci	case recvr_type_dj:		no_dj_interfaces = 3; break;
177762306a36Sopenharmony_ci	case recvr_type_hidpp:		no_dj_interfaces = 2; break;
177862306a36Sopenharmony_ci	case recvr_type_gaming_hidpp:	no_dj_interfaces = 3; break;
177962306a36Sopenharmony_ci	case recvr_type_mouse_only:	no_dj_interfaces = 2; break;
178062306a36Sopenharmony_ci	case recvr_type_27mhz:		no_dj_interfaces = 2; break;
178162306a36Sopenharmony_ci	case recvr_type_bluetooth:	no_dj_interfaces = 2; break;
178262306a36Sopenharmony_ci	case recvr_type_dinovo:		no_dj_interfaces = 2; break;
178362306a36Sopenharmony_ci	}
178462306a36Sopenharmony_ci	if (hid_is_usb(hdev)) {
178562306a36Sopenharmony_ci		intf = to_usb_interface(hdev->dev.parent);
178662306a36Sopenharmony_ci		if (intf && intf->altsetting->desc.bInterfaceNumber >=
178762306a36Sopenharmony_ci							no_dj_interfaces) {
178862306a36Sopenharmony_ci			hdev->quirks |= HID_QUIRK_INPUT_PER_APP;
178962306a36Sopenharmony_ci			return hid_hw_start(hdev, HID_CONNECT_DEFAULT);
179062306a36Sopenharmony_ci		}
179162306a36Sopenharmony_ci	}
179262306a36Sopenharmony_ci
179362306a36Sopenharmony_ci	rep_enum = &hdev->report_enum[HID_INPUT_REPORT];
179462306a36Sopenharmony_ci
179562306a36Sopenharmony_ci	/* no input reports, bail out */
179662306a36Sopenharmony_ci	if (list_empty(&rep_enum->report_list))
179762306a36Sopenharmony_ci		return -ENODEV;
179862306a36Sopenharmony_ci
179962306a36Sopenharmony_ci	/*
180062306a36Sopenharmony_ci	 * Check for the HID++ application.
180162306a36Sopenharmony_ci	 * Note: we should theoretically check for HID++ and DJ
180262306a36Sopenharmony_ci	 * collections, but this will do.
180362306a36Sopenharmony_ci	 */
180462306a36Sopenharmony_ci	list_for_each_entry(rep, &rep_enum->report_list, list) {
180562306a36Sopenharmony_ci		if (rep->application == 0xff000001)
180662306a36Sopenharmony_ci			has_hidpp = true;
180762306a36Sopenharmony_ci	}
180862306a36Sopenharmony_ci
180962306a36Sopenharmony_ci	/*
181062306a36Sopenharmony_ci	 * Ignore interfaces without DJ/HID++ collection, they will not carry
181162306a36Sopenharmony_ci	 * any data, dont create any hid_device for them.
181262306a36Sopenharmony_ci	 */
181362306a36Sopenharmony_ci	if (!has_hidpp && id->driver_data == recvr_type_dj)
181462306a36Sopenharmony_ci		return -ENODEV;
181562306a36Sopenharmony_ci
181662306a36Sopenharmony_ci	/* get the current application attached to the node */
181762306a36Sopenharmony_ci	rep = list_first_entry(&rep_enum->report_list, struct hid_report, list);
181862306a36Sopenharmony_ci	djrcv_dev = dj_get_receiver_dev(hdev, id->driver_data,
181962306a36Sopenharmony_ci					rep->application, has_hidpp);
182062306a36Sopenharmony_ci	if (!djrcv_dev) {
182162306a36Sopenharmony_ci		hid_err(hdev, "%s: dj_get_receiver_dev failed\n", __func__);
182262306a36Sopenharmony_ci		return -ENOMEM;
182362306a36Sopenharmony_ci	}
182462306a36Sopenharmony_ci
182562306a36Sopenharmony_ci	if (!rep_enum->numbered)
182662306a36Sopenharmony_ci		djrcv_dev->unnumbered_application = rep->application;
182762306a36Sopenharmony_ci
182862306a36Sopenharmony_ci	/* Starts the usb device and connects to upper interfaces hiddev and
182962306a36Sopenharmony_ci	 * hidraw */
183062306a36Sopenharmony_ci	retval = hid_hw_start(hdev, HID_CONNECT_HIDRAW|HID_CONNECT_HIDDEV);
183162306a36Sopenharmony_ci	if (retval) {
183262306a36Sopenharmony_ci		hid_err(hdev, "%s: hid_hw_start returned error\n", __func__);
183362306a36Sopenharmony_ci		goto hid_hw_start_fail;
183462306a36Sopenharmony_ci	}
183562306a36Sopenharmony_ci
183662306a36Sopenharmony_ci	if (has_hidpp) {
183762306a36Sopenharmony_ci		retval = logi_dj_recv_switch_to_dj_mode(djrcv_dev, 0);
183862306a36Sopenharmony_ci		if (retval < 0) {
183962306a36Sopenharmony_ci			hid_err(hdev, "%s: logi_dj_recv_switch_to_dj_mode returned error:%d\n",
184062306a36Sopenharmony_ci				__func__, retval);
184162306a36Sopenharmony_ci			goto switch_to_dj_mode_fail;
184262306a36Sopenharmony_ci		}
184362306a36Sopenharmony_ci	}
184462306a36Sopenharmony_ci
184562306a36Sopenharmony_ci	/* This is enabling the polling urb on the IN endpoint */
184662306a36Sopenharmony_ci	retval = hid_hw_open(hdev);
184762306a36Sopenharmony_ci	if (retval < 0) {
184862306a36Sopenharmony_ci		hid_err(hdev, "%s: hid_hw_open returned error:%d\n",
184962306a36Sopenharmony_ci			__func__, retval);
185062306a36Sopenharmony_ci		goto llopen_failed;
185162306a36Sopenharmony_ci	}
185262306a36Sopenharmony_ci
185362306a36Sopenharmony_ci	/* Allow incoming packets to arrive: */
185462306a36Sopenharmony_ci	hid_device_io_start(hdev);
185562306a36Sopenharmony_ci
185662306a36Sopenharmony_ci	if (has_hidpp) {
185762306a36Sopenharmony_ci		spin_lock_irqsave(&djrcv_dev->lock, flags);
185862306a36Sopenharmony_ci		djrcv_dev->ready = true;
185962306a36Sopenharmony_ci		spin_unlock_irqrestore(&djrcv_dev->lock, flags);
186062306a36Sopenharmony_ci		retval = logi_dj_recv_query_paired_devices(djrcv_dev);
186162306a36Sopenharmony_ci		if (retval < 0) {
186262306a36Sopenharmony_ci			hid_err(hdev, "%s: logi_dj_recv_query_paired_devices error:%d\n",
186362306a36Sopenharmony_ci				__func__, retval);
186462306a36Sopenharmony_ci			/*
186562306a36Sopenharmony_ci			 * This can happen with a KVM, let the probe succeed,
186662306a36Sopenharmony_ci			 * logi_dj_recv_queue_unknown_work will retry later.
186762306a36Sopenharmony_ci			 */
186862306a36Sopenharmony_ci		}
186962306a36Sopenharmony_ci	}
187062306a36Sopenharmony_ci
187162306a36Sopenharmony_ci	return 0;
187262306a36Sopenharmony_ci
187362306a36Sopenharmony_cillopen_failed:
187462306a36Sopenharmony_ciswitch_to_dj_mode_fail:
187562306a36Sopenharmony_ci	hid_hw_stop(hdev);
187662306a36Sopenharmony_ci
187762306a36Sopenharmony_cihid_hw_start_fail:
187862306a36Sopenharmony_ci	dj_put_receiver_dev(hdev);
187962306a36Sopenharmony_ci	return retval;
188062306a36Sopenharmony_ci}
188162306a36Sopenharmony_ci
188262306a36Sopenharmony_ci#ifdef CONFIG_PM
188362306a36Sopenharmony_cistatic int logi_dj_reset_resume(struct hid_device *hdev)
188462306a36Sopenharmony_ci{
188562306a36Sopenharmony_ci	int retval;
188662306a36Sopenharmony_ci	struct dj_receiver_dev *djrcv_dev = hid_get_drvdata(hdev);
188762306a36Sopenharmony_ci
188862306a36Sopenharmony_ci	if (!djrcv_dev || djrcv_dev->hidpp != hdev)
188962306a36Sopenharmony_ci		return 0;
189062306a36Sopenharmony_ci
189162306a36Sopenharmony_ci	retval = logi_dj_recv_switch_to_dj_mode(djrcv_dev, 0);
189262306a36Sopenharmony_ci	if (retval < 0) {
189362306a36Sopenharmony_ci		hid_err(hdev, "%s: logi_dj_recv_switch_to_dj_mode returned error:%d\n",
189462306a36Sopenharmony_ci			__func__, retval);
189562306a36Sopenharmony_ci	}
189662306a36Sopenharmony_ci
189762306a36Sopenharmony_ci	return 0;
189862306a36Sopenharmony_ci}
189962306a36Sopenharmony_ci#endif
190062306a36Sopenharmony_ci
190162306a36Sopenharmony_cistatic void logi_dj_remove(struct hid_device *hdev)
190262306a36Sopenharmony_ci{
190362306a36Sopenharmony_ci	struct dj_receiver_dev *djrcv_dev = hid_get_drvdata(hdev);
190462306a36Sopenharmony_ci	struct dj_device *dj_dev;
190562306a36Sopenharmony_ci	unsigned long flags;
190662306a36Sopenharmony_ci	int i;
190762306a36Sopenharmony_ci
190862306a36Sopenharmony_ci	dbg_hid("%s\n", __func__);
190962306a36Sopenharmony_ci
191062306a36Sopenharmony_ci	if (!djrcv_dev)
191162306a36Sopenharmony_ci		return hid_hw_stop(hdev);
191262306a36Sopenharmony_ci
191362306a36Sopenharmony_ci	/*
191462306a36Sopenharmony_ci	 * This ensures that if the work gets requeued from another
191562306a36Sopenharmony_ci	 * interface of the same receiver it will be a no-op.
191662306a36Sopenharmony_ci	 */
191762306a36Sopenharmony_ci	spin_lock_irqsave(&djrcv_dev->lock, flags);
191862306a36Sopenharmony_ci	djrcv_dev->ready = false;
191962306a36Sopenharmony_ci	spin_unlock_irqrestore(&djrcv_dev->lock, flags);
192062306a36Sopenharmony_ci
192162306a36Sopenharmony_ci	cancel_work_sync(&djrcv_dev->work);
192262306a36Sopenharmony_ci
192362306a36Sopenharmony_ci	hid_hw_close(hdev);
192462306a36Sopenharmony_ci	hid_hw_stop(hdev);
192562306a36Sopenharmony_ci
192662306a36Sopenharmony_ci	/*
192762306a36Sopenharmony_ci	 * For proper operation we need access to all interfaces, so we destroy
192862306a36Sopenharmony_ci	 * the paired devices when we're unbound from any interface.
192962306a36Sopenharmony_ci	 *
193062306a36Sopenharmony_ci	 * Note we may still be bound to other interfaces, sharing the same
193162306a36Sopenharmony_ci	 * djrcv_dev, so we need locking here.
193262306a36Sopenharmony_ci	 */
193362306a36Sopenharmony_ci	for (i = 0; i < (DJ_MAX_PAIRED_DEVICES + DJ_DEVICE_INDEX_MIN); i++) {
193462306a36Sopenharmony_ci		spin_lock_irqsave(&djrcv_dev->lock, flags);
193562306a36Sopenharmony_ci		dj_dev = djrcv_dev->paired_dj_devices[i];
193662306a36Sopenharmony_ci		djrcv_dev->paired_dj_devices[i] = NULL;
193762306a36Sopenharmony_ci		spin_unlock_irqrestore(&djrcv_dev->lock, flags);
193862306a36Sopenharmony_ci		if (dj_dev != NULL) {
193962306a36Sopenharmony_ci			hid_destroy_device(dj_dev->hdev);
194062306a36Sopenharmony_ci			kfree(dj_dev);
194162306a36Sopenharmony_ci		}
194262306a36Sopenharmony_ci	}
194362306a36Sopenharmony_ci
194462306a36Sopenharmony_ci	dj_put_receiver_dev(hdev);
194562306a36Sopenharmony_ci}
194662306a36Sopenharmony_ci
194762306a36Sopenharmony_cistatic const struct hid_device_id logi_dj_receivers[] = {
194862306a36Sopenharmony_ci	{ /* Logitech unifying receiver (0xc52b) */
194962306a36Sopenharmony_ci	  HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH,
195062306a36Sopenharmony_ci		USB_DEVICE_ID_LOGITECH_UNIFYING_RECEIVER),
195162306a36Sopenharmony_ci	 .driver_data = recvr_type_dj},
195262306a36Sopenharmony_ci	{ /* Logitech unifying receiver (0xc532) */
195362306a36Sopenharmony_ci	  HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH,
195462306a36Sopenharmony_ci		USB_DEVICE_ID_LOGITECH_UNIFYING_RECEIVER_2),
195562306a36Sopenharmony_ci	 .driver_data = recvr_type_dj},
195662306a36Sopenharmony_ci
195762306a36Sopenharmony_ci	{ /* Logitech Nano mouse only receiver (0xc52f) */
195862306a36Sopenharmony_ci	  HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH,
195962306a36Sopenharmony_ci			 USB_DEVICE_ID_LOGITECH_NANO_RECEIVER),
196062306a36Sopenharmony_ci	 .driver_data = recvr_type_mouse_only},
196162306a36Sopenharmony_ci	{ /* Logitech Nano (non DJ) receiver (0xc534) */
196262306a36Sopenharmony_ci	  HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH,
196362306a36Sopenharmony_ci			 USB_DEVICE_ID_LOGITECH_NANO_RECEIVER_2),
196462306a36Sopenharmony_ci	 .driver_data = recvr_type_hidpp},
196562306a36Sopenharmony_ci
196662306a36Sopenharmony_ci	{ /* Logitech G700(s) receiver (0xc531) */
196762306a36Sopenharmony_ci	  HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH,
196862306a36Sopenharmony_ci			 USB_DEVICE_ID_LOGITECH_G700_RECEIVER),
196962306a36Sopenharmony_ci	 .driver_data = recvr_type_gaming_hidpp},
197062306a36Sopenharmony_ci	{ /* Logitech G602 receiver (0xc537) */
197162306a36Sopenharmony_ci	  HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH,
197262306a36Sopenharmony_ci		0xc537),
197362306a36Sopenharmony_ci	 .driver_data = recvr_type_gaming_hidpp},
197462306a36Sopenharmony_ci	{ /* Logitech lightspeed receiver (0xc539) */
197562306a36Sopenharmony_ci	  HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH,
197662306a36Sopenharmony_ci		USB_DEVICE_ID_LOGITECH_NANO_RECEIVER_LIGHTSPEED_1),
197762306a36Sopenharmony_ci	 .driver_data = recvr_type_gaming_hidpp},
197862306a36Sopenharmony_ci	{ /* Logitech powerplay receiver (0xc53a) */
197962306a36Sopenharmony_ci	  HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH,
198062306a36Sopenharmony_ci		USB_DEVICE_ID_LOGITECH_NANO_RECEIVER_POWERPLAY),
198162306a36Sopenharmony_ci	 .driver_data = recvr_type_gaming_hidpp},
198262306a36Sopenharmony_ci	{ /* Logitech lightspeed receiver (0xc53f) */
198362306a36Sopenharmony_ci	  HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH,
198462306a36Sopenharmony_ci		USB_DEVICE_ID_LOGITECH_NANO_RECEIVER_LIGHTSPEED_1_1),
198562306a36Sopenharmony_ci	 .driver_data = recvr_type_gaming_hidpp},
198662306a36Sopenharmony_ci
198762306a36Sopenharmony_ci	{ /* Logitech 27 MHz HID++ 1.0 receiver (0xc513) */
198862306a36Sopenharmony_ci	  HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_MX3000_RECEIVER),
198962306a36Sopenharmony_ci	 .driver_data = recvr_type_27mhz},
199062306a36Sopenharmony_ci	{ /* Logitech 27 MHz HID++ 1.0 receiver (0xc517) */
199162306a36Sopenharmony_ci	  HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH,
199262306a36Sopenharmony_ci		USB_DEVICE_ID_S510_RECEIVER_2),
199362306a36Sopenharmony_ci	 .driver_data = recvr_type_27mhz},
199462306a36Sopenharmony_ci	{ /* Logitech 27 MHz HID++ 1.0 mouse-only receiver (0xc51b) */
199562306a36Sopenharmony_ci	  HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH,
199662306a36Sopenharmony_ci		USB_DEVICE_ID_LOGITECH_27MHZ_MOUSE_RECEIVER),
199762306a36Sopenharmony_ci	 .driver_data = recvr_type_27mhz},
199862306a36Sopenharmony_ci
199962306a36Sopenharmony_ci	{ /* Logitech MX5000 HID++ / bluetooth receiver keyboard intf. (0xc70e) */
200062306a36Sopenharmony_ci	  HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH,
200162306a36Sopenharmony_ci		USB_DEVICE_ID_MX5000_RECEIVER_KBD_DEV),
200262306a36Sopenharmony_ci	 .driver_data = recvr_type_bluetooth},
200362306a36Sopenharmony_ci	{ /* Logitech MX5000 HID++ / bluetooth receiver mouse intf. (0xc70a) */
200462306a36Sopenharmony_ci	  HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH,
200562306a36Sopenharmony_ci		USB_DEVICE_ID_MX5000_RECEIVER_MOUSE_DEV),
200662306a36Sopenharmony_ci	 .driver_data = recvr_type_bluetooth},
200762306a36Sopenharmony_ci	{ /* Logitech MX5500 HID++ / bluetooth receiver keyboard intf. (0xc71b) */
200862306a36Sopenharmony_ci	  HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH,
200962306a36Sopenharmony_ci		USB_DEVICE_ID_MX5500_RECEIVER_KBD_DEV),
201062306a36Sopenharmony_ci	 .driver_data = recvr_type_bluetooth},
201162306a36Sopenharmony_ci	{ /* Logitech MX5500 HID++ / bluetooth receiver mouse intf. (0xc71c) */
201262306a36Sopenharmony_ci	  HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH,
201362306a36Sopenharmony_ci		USB_DEVICE_ID_MX5500_RECEIVER_MOUSE_DEV),
201462306a36Sopenharmony_ci	 .driver_data = recvr_type_bluetooth},
201562306a36Sopenharmony_ci
201662306a36Sopenharmony_ci	{ /* Logitech Dinovo Edge HID++ / bluetooth receiver keyboard intf. (0xc713) */
201762306a36Sopenharmony_ci	  HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH,
201862306a36Sopenharmony_ci		USB_DEVICE_ID_DINOVO_EDGE_RECEIVER_KBD_DEV),
201962306a36Sopenharmony_ci	 .driver_data = recvr_type_dinovo},
202062306a36Sopenharmony_ci	{ /* Logitech Dinovo Edge HID++ / bluetooth receiver mouse intf. (0xc714) */
202162306a36Sopenharmony_ci	  HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH,
202262306a36Sopenharmony_ci		USB_DEVICE_ID_DINOVO_EDGE_RECEIVER_MOUSE_DEV),
202362306a36Sopenharmony_ci	 .driver_data = recvr_type_dinovo},
202462306a36Sopenharmony_ci	{ /* Logitech DiNovo Mini HID++ / bluetooth receiver mouse intf. (0xc71e) */
202562306a36Sopenharmony_ci	  HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH,
202662306a36Sopenharmony_ci		USB_DEVICE_ID_DINOVO_MINI_RECEIVER_KBD_DEV),
202762306a36Sopenharmony_ci	 .driver_data = recvr_type_dinovo},
202862306a36Sopenharmony_ci	{ /* Logitech DiNovo Mini HID++ / bluetooth receiver keyboard intf. (0xc71f) */
202962306a36Sopenharmony_ci	  HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH,
203062306a36Sopenharmony_ci		USB_DEVICE_ID_DINOVO_MINI_RECEIVER_MOUSE_DEV),
203162306a36Sopenharmony_ci	 .driver_data = recvr_type_dinovo},
203262306a36Sopenharmony_ci	{}
203362306a36Sopenharmony_ci};
203462306a36Sopenharmony_ci
203562306a36Sopenharmony_ciMODULE_DEVICE_TABLE(hid, logi_dj_receivers);
203662306a36Sopenharmony_ci
203762306a36Sopenharmony_cistatic struct hid_driver logi_djreceiver_driver = {
203862306a36Sopenharmony_ci	.name = "logitech-djreceiver",
203962306a36Sopenharmony_ci	.id_table = logi_dj_receivers,
204062306a36Sopenharmony_ci	.probe = logi_dj_probe,
204162306a36Sopenharmony_ci	.remove = logi_dj_remove,
204262306a36Sopenharmony_ci	.raw_event = logi_dj_raw_event,
204362306a36Sopenharmony_ci#ifdef CONFIG_PM
204462306a36Sopenharmony_ci	.reset_resume = logi_dj_reset_resume,
204562306a36Sopenharmony_ci#endif
204662306a36Sopenharmony_ci};
204762306a36Sopenharmony_ci
204862306a36Sopenharmony_cimodule_hid_driver(logi_djreceiver_driver);
204962306a36Sopenharmony_ci
205062306a36Sopenharmony_ciMODULE_LICENSE("GPL");
205162306a36Sopenharmony_ciMODULE_AUTHOR("Logitech");
205262306a36Sopenharmony_ciMODULE_AUTHOR("Nestor Lopez Casado");
205362306a36Sopenharmony_ciMODULE_AUTHOR("nlopezcasad@logitech.com");
2054