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