1319349Sjkim// SPDX-License-Identifier: GPL-2.0-or-later
2234949Sbapt/*
3234949Sbapt *  HID driver for ELECOM devices:
4319349Sjkim *  - BM084 Bluetooth Mouse
5234949Sbapt *  - EX-G Trackballs (M-XT3DRBK, M-XT3URBK, M-XT4DRBK)
6234949Sbapt *  - DEFT Trackballs (M-DT1DRBK, M-DT1URBK, M-DT2DRBK, M-DT2URBK)
7234949Sbapt *  - HUGE Trackballs (M-HT1DRBK, M-HT1URBK)
8234949Sbapt *
9234949Sbapt *  Copyright (c) 2010 Richard Nauber <Richard.Nauber@gmail.com>
10234949Sbapt *  Copyright (c) 2016 Yuxuan Shui <yshuiv7@gmail.com>
11234949Sbapt *  Copyright (c) 2017 Diego Elio Petten�� <flameeyes@flameeyes.eu>
12234949Sbapt *  Copyright (c) 2017 Alex Manoussakis <amanou@gnu.org>
13234949Sbapt *  Copyright (c) 2017 Tomasz Kramkowski <tk@the-tk.com>
14234949Sbapt *  Copyright (c) 2020 YOSHIOKA Takuma <lo48576@hard-wi.red>
15234949Sbapt *  Copyright (c) 2022 Takahiro Fujii <fujii@xaxxi.net>
16234949Sbapt */
17234949Sbapt
18234949Sbapt/*
19234949Sbapt */
20234949Sbapt
21234949Sbapt#include <linux/device.h>
22234949Sbapt#include <linux/hid.h>
23234949Sbapt#include <linux/module.h>
24234949Sbapt
25234949Sbapt#include "hid-ids.h"
26234949Sbapt
27234949Sbapt/*
28234949Sbapt * Certain ELECOM mice misreport their button count meaning that they only work
29234949Sbapt * correctly with the ELECOM mouse assistant software which is unavailable for
30234949Sbapt * Linux. A four extra INPUT reports and a FEATURE report are described by the
31268811Sbapt * report descriptor but it does not appear that these enable software to
32251143Sbapt * control what the extra buttons map to. The only simple and straightforward
33251143Sbapt * solution seems to involve fixing up the report descriptor.
34251143Sbapt */
35251143Sbapt#define MOUSE_BUTTONS_MAX 8
36251143Sbaptstatic void mouse_button_fixup(struct hid_device *hdev,
37251143Sbapt			       __u8 *rdesc, unsigned int rsize,
38251143Sbapt			       unsigned int button_bit_count,
39251143Sbapt			       unsigned int padding_bit,
40268811Sbapt			       unsigned int button_report_size,
41251143Sbapt			       unsigned int button_usage_maximum,
42251143Sbapt			       int nbuttons)
43251143Sbapt{
44251143Sbapt	if (rsize < 32 || rdesc[button_bit_count] != 0x95 ||
45251143Sbapt	    rdesc[button_report_size] != 0x75 ||
46251143Sbapt	    rdesc[button_report_size + 1] != 0x01 ||
47251143Sbapt	    rdesc[button_usage_maximum] != 0x29 || rdesc[padding_bit] != 0x75)
48251143Sbapt		return;
49251143Sbapt	hid_info(hdev, "Fixing up Elecom mouse button count\n");
50251143Sbapt	nbuttons = clamp(nbuttons, 0, MOUSE_BUTTONS_MAX);
51251143Sbapt	rdesc[button_bit_count + 1] = nbuttons;
52251143Sbapt	rdesc[button_usage_maximum + 1] = nbuttons;
53251143Sbapt	rdesc[padding_bit + 1] = MOUSE_BUTTONS_MAX - nbuttons;
54251143Sbapt}
55251143Sbapt
56251143Sbaptstatic __u8 *elecom_report_fixup(struct hid_device *hdev, __u8 *rdesc,
57296240Sjkim		unsigned int *rsize)
58234949Sbapt{
59234949Sbapt	switch (hdev->product) {
60234949Sbapt	case USB_DEVICE_ID_ELECOM_BM084:
61234949Sbapt		/* The BM084 Bluetooth mouse includes a non-existing horizontal
62234949Sbapt		 * wheel in the HID descriptor. */
63234949Sbapt		if (*rsize >= 48 && rdesc[46] == 0x05 && rdesc[47] == 0x0c) {
64234949Sbapt			hid_info(hdev, "Fixing up Elecom BM084 report descriptor\n");
65234949Sbapt			rdesc[47] = 0x00;
66234949Sbapt		}
67234949Sbapt		break;
68234949Sbapt	case USB_DEVICE_ID_ELECOM_M_XGL20DLBK:
69234949Sbapt		/*
70234949Sbapt		 * Report descriptor format:
71234949Sbapt		 * 20: button bit count
72234949Sbapt		 * 28: padding bit count
73234949Sbapt		 * 22: button report size
74234949Sbapt		 * 14: button usage maximum
75296240Sjkim		 */
76296240Sjkim		mouse_button_fixup(hdev, rdesc, *rsize, 20, 28, 22, 14, 8);
77296240Sjkim		break;
78234949Sbapt	case USB_DEVICE_ID_ELECOM_M_XT3URBK:
79296240Sjkim	case USB_DEVICE_ID_ELECOM_M_XT3DRBK:
80234949Sbapt	case USB_DEVICE_ID_ELECOM_M_XT4DRBK:
81234949Sbapt		/*
82272655Sbapt		 * Report descriptor format:
83234949Sbapt		 * 12: button bit count
84234949Sbapt		 * 30: padding bit count
85234949Sbapt		 * 14: button report size
86234949Sbapt		 * 20: button usage maximum
87234949Sbapt		 */
88234949Sbapt		mouse_button_fixup(hdev, rdesc, *rsize, 12, 30, 14, 20, 6);
89234949Sbapt		break;
90234949Sbapt	case USB_DEVICE_ID_ELECOM_M_DT1URBK:
91234949Sbapt	case USB_DEVICE_ID_ELECOM_M_DT1DRBK:
92234949Sbapt	case USB_DEVICE_ID_ELECOM_M_HT1URBK:
93234949Sbapt	case USB_DEVICE_ID_ELECOM_M_HT1DRBK_010D:
94234949Sbapt		/*
95234949Sbapt		 * Report descriptor format:
96296240Sjkim		 * 12: button bit count
97234949Sbapt		 * 30: padding bit count
98296240Sjkim		 * 14: button report size
99296240Sjkim		 * 20: button usage maximum
100296240Sjkim		 */
101234949Sbapt		mouse_button_fixup(hdev, rdesc, *rsize, 12, 30, 14, 20, 8);
102234949Sbapt		break;
103234949Sbapt	case USB_DEVICE_ID_ELECOM_M_HT1DRBK_011C:
104234949Sbapt		/*
105234949Sbapt		 * Report descriptor format:
106234949Sbapt		 * 22: button bit count
107234949Sbapt		 * 30: padding bit count
108234949Sbapt		 * 24: button report size
109296240Sjkim		 * 16: button usage maximum
110234949Sbapt		 */
111234949Sbapt		mouse_button_fixup(hdev, rdesc, *rsize, 22, 30, 24, 16, 8);
112234949Sbapt		break;
113234949Sbapt	}
114296240Sjkim	return rdesc;
115234949Sbapt}
116234949Sbapt
117234949Sbaptstatic const struct hid_device_id elecom_devices[] = {
118234949Sbapt	{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_BM084) },
119272655Sbapt	{ HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_XGL20DLBK) },
120234949Sbapt	{ HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_XT3URBK) },
121234949Sbapt	{ HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_XT3DRBK) },
122234949Sbapt	{ HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_XT4DRBK) },
123234949Sbapt	{ HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_DT1URBK) },
124234949Sbapt	{ HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_DT1DRBK) },
125234949Sbapt	{ HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_HT1URBK) },
126234949Sbapt	{ HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_HT1DRBK_010D) },
127234949Sbapt	{ HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_HT1DRBK_011C) },
128234949Sbapt	{ }
129234949Sbapt};
130234949SbaptMODULE_DEVICE_TABLE(hid, elecom_devices);
131234949Sbapt
132234949Sbaptstatic struct hid_driver elecom_driver = {
133234949Sbapt	.name = "elecom",
134234949Sbapt	.id_table = elecom_devices,
135234949Sbapt	.report_fixup = elecom_report_fixup
136234949Sbapt};
137234949Sbaptmodule_hid_driver(elecom_driver);
138234949Sbapt
139234949SbaptMODULE_LICENSE("GPL");
140234949Sbapt