hid-elecom.c (4406B)
1// SPDX-License-Identifier: GPL-2.0-or-later 2/* 3 * HID driver for ELECOM devices: 4 * - BM084 Bluetooth Mouse 5 * - EX-G Trackballs (M-XT3DRBK, M-XT3URBK, M-XT4DRBK) 6 * - DEFT Trackballs (M-DT1DRBK, M-DT1URBK, M-DT2DRBK, M-DT2URBK) 7 * - HUGE Trackballs (M-HT1DRBK, M-HT1URBK) 8 * 9 * Copyright (c) 2010 Richard Nauber <Richard.Nauber@gmail.com> 10 * Copyright (c) 2016 Yuxuan Shui <yshuiv7@gmail.com> 11 * Copyright (c) 2017 Diego Elio Pettenò <flameeyes@flameeyes.eu> 12 * Copyright (c) 2017 Alex Manoussakis <amanou@gnu.org> 13 * Copyright (c) 2017 Tomasz Kramkowski <tk@the-tk.com> 14 * Copyright (c) 2020 YOSHIOKA Takuma <lo48576@hard-wi.red> 15 */ 16 17/* 18 */ 19 20#include <linux/device.h> 21#include <linux/hid.h> 22#include <linux/module.h> 23 24#include "hid-ids.h" 25 26/* 27 * Certain ELECOM mice misreport their button count meaning that they only work 28 * correctly with the ELECOM mouse assistant software which is unavailable for 29 * Linux. A four extra INPUT reports and a FEATURE report are described by the 30 * report descriptor but it does not appear that these enable software to 31 * control what the extra buttons map to. The only simple and straightforward 32 * solution seems to involve fixing up the report descriptor. 33 */ 34#define MOUSE_BUTTONS_MAX 8 35static void mouse_button_fixup(struct hid_device *hdev, 36 __u8 *rdesc, unsigned int rsize, 37 unsigned int button_bit_count, 38 unsigned int padding_bit, 39 unsigned int button_report_size, 40 unsigned int button_usage_maximum, 41 int nbuttons) 42{ 43 if (rsize < 32 || rdesc[button_bit_count] != 0x95 || 44 rdesc[button_report_size] != 0x75 || 45 rdesc[button_report_size + 1] != 0x01 || 46 rdesc[button_usage_maximum] != 0x29 || rdesc[padding_bit] != 0x75) 47 return; 48 hid_info(hdev, "Fixing up Elecom mouse button count\n"); 49 nbuttons = clamp(nbuttons, 0, MOUSE_BUTTONS_MAX); 50 rdesc[button_bit_count + 1] = nbuttons; 51 rdesc[button_usage_maximum + 1] = nbuttons; 52 rdesc[padding_bit + 1] = MOUSE_BUTTONS_MAX - nbuttons; 53} 54 55static __u8 *elecom_report_fixup(struct hid_device *hdev, __u8 *rdesc, 56 unsigned int *rsize) 57{ 58 switch (hdev->product) { 59 case USB_DEVICE_ID_ELECOM_BM084: 60 /* The BM084 Bluetooth mouse includes a non-existing horizontal 61 * wheel in the HID descriptor. */ 62 if (*rsize >= 48 && rdesc[46] == 0x05 && rdesc[47] == 0x0c) { 63 hid_info(hdev, "Fixing up Elecom BM084 report descriptor\n"); 64 rdesc[47] = 0x00; 65 } 66 break; 67 case USB_DEVICE_ID_ELECOM_M_XGL20DLBK: 68 /* 69 * Report descriptor format: 70 * 20: button bit count 71 * 28: padding bit count 72 * 22: button report size 73 * 14: button usage maximum 74 */ 75 mouse_button_fixup(hdev, rdesc, *rsize, 20, 28, 22, 14, 8); 76 break; 77 case USB_DEVICE_ID_ELECOM_M_XT3URBK: 78 case USB_DEVICE_ID_ELECOM_M_XT3DRBK: 79 case USB_DEVICE_ID_ELECOM_M_XT4DRBK: 80 /* 81 * Report descriptor format: 82 * 12: button bit count 83 * 30: padding bit count 84 * 14: button report size 85 * 20: button usage maximum 86 */ 87 mouse_button_fixup(hdev, rdesc, *rsize, 12, 30, 14, 20, 6); 88 break; 89 case USB_DEVICE_ID_ELECOM_M_DT1URBK: 90 case USB_DEVICE_ID_ELECOM_M_DT1DRBK: 91 case USB_DEVICE_ID_ELECOM_M_HT1URBK: 92 case USB_DEVICE_ID_ELECOM_M_HT1DRBK: 93 /* 94 * Report descriptor format: 95 * 12: button bit count 96 * 30: padding bit count 97 * 14: button report size 98 * 20: button usage maximum 99 */ 100 mouse_button_fixup(hdev, rdesc, *rsize, 12, 30, 14, 20, 8); 101 break; 102 } 103 return rdesc; 104} 105 106static const struct hid_device_id elecom_devices[] = { 107 { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_BM084) }, 108 { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_XGL20DLBK) }, 109 { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_XT3URBK) }, 110 { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_XT3DRBK) }, 111 { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_XT4DRBK) }, 112 { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_DT1URBK) }, 113 { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_DT1DRBK) }, 114 { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_HT1URBK) }, 115 { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_HT1DRBK) }, 116 { } 117}; 118MODULE_DEVICE_TABLE(hid, elecom_devices); 119 120static struct hid_driver elecom_driver = { 121 .name = "elecom", 122 .id_table = elecom_devices, 123 .report_fixup = elecom_report_fixup 124}; 125module_hid_driver(elecom_driver); 126 127MODULE_LICENSE("GPL");