18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *  HID driver for ELECOM devices:
48c2ecf20Sopenharmony_ci *  - BM084 Bluetooth Mouse
58c2ecf20Sopenharmony_ci *  - EX-G Trackballs (M-XT3DRBK, M-XT3URBK, M-XT4DRBK)
68c2ecf20Sopenharmony_ci *  - DEFT Trackballs (M-DT1DRBK, M-DT1URBK, M-DT2DRBK, M-DT2URBK)
78c2ecf20Sopenharmony_ci *  - HUGE Trackballs (M-HT1DRBK, M-HT1URBK)
88c2ecf20Sopenharmony_ci *
98c2ecf20Sopenharmony_ci *  Copyright (c) 2010 Richard Nauber <Richard.Nauber@gmail.com>
108c2ecf20Sopenharmony_ci *  Copyright (c) 2016 Yuxuan Shui <yshuiv7@gmail.com>
118c2ecf20Sopenharmony_ci *  Copyright (c) 2017 Diego Elio Pettenò <flameeyes@flameeyes.eu>
128c2ecf20Sopenharmony_ci *  Copyright (c) 2017 Alex Manoussakis <amanou@gnu.org>
138c2ecf20Sopenharmony_ci *  Copyright (c) 2017 Tomasz Kramkowski <tk@the-tk.com>
148c2ecf20Sopenharmony_ci */
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci/*
178c2ecf20Sopenharmony_ci */
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci#include <linux/device.h>
208c2ecf20Sopenharmony_ci#include <linux/hid.h>
218c2ecf20Sopenharmony_ci#include <linux/module.h>
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci#include "hid-ids.h"
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci/*
268c2ecf20Sopenharmony_ci * Certain ELECOM mice misreport their button count meaning that they only work
278c2ecf20Sopenharmony_ci * correctly with the ELECOM mouse assistant software which is unavailable for
288c2ecf20Sopenharmony_ci * Linux. A four extra INPUT reports and a FEATURE report are described by the
298c2ecf20Sopenharmony_ci * report descriptor but it does not appear that these enable software to
308c2ecf20Sopenharmony_ci * control what the extra buttons map to. The only simple and straightforward
318c2ecf20Sopenharmony_ci * solution seems to involve fixing up the report descriptor.
328c2ecf20Sopenharmony_ci *
338c2ecf20Sopenharmony_ci * Report descriptor format:
348c2ecf20Sopenharmony_ci * Positions 13, 15, 21 and 31 store the button bit count, button usage minimum,
358c2ecf20Sopenharmony_ci * button usage maximum and padding bit count respectively.
368c2ecf20Sopenharmony_ci */
378c2ecf20Sopenharmony_ci#define MOUSE_BUTTONS_MAX 8
388c2ecf20Sopenharmony_cistatic void mouse_button_fixup(struct hid_device *hdev,
398c2ecf20Sopenharmony_ci			       __u8 *rdesc, unsigned int rsize,
408c2ecf20Sopenharmony_ci			       int nbuttons)
418c2ecf20Sopenharmony_ci{
428c2ecf20Sopenharmony_ci	if (rsize < 32 || rdesc[12] != 0x95 ||
438c2ecf20Sopenharmony_ci	    rdesc[14] != 0x75 || rdesc[15] != 0x01 ||
448c2ecf20Sopenharmony_ci	    rdesc[20] != 0x29 || rdesc[30] != 0x75)
458c2ecf20Sopenharmony_ci		return;
468c2ecf20Sopenharmony_ci	hid_info(hdev, "Fixing up Elecom mouse button count\n");
478c2ecf20Sopenharmony_ci	nbuttons = clamp(nbuttons, 0, MOUSE_BUTTONS_MAX);
488c2ecf20Sopenharmony_ci	rdesc[13] = nbuttons;
498c2ecf20Sopenharmony_ci	rdesc[21] = nbuttons;
508c2ecf20Sopenharmony_ci	rdesc[31] = MOUSE_BUTTONS_MAX - nbuttons;
518c2ecf20Sopenharmony_ci}
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_cistatic __u8 *elecom_report_fixup(struct hid_device *hdev, __u8 *rdesc,
548c2ecf20Sopenharmony_ci		unsigned int *rsize)
558c2ecf20Sopenharmony_ci{
568c2ecf20Sopenharmony_ci	switch (hdev->product) {
578c2ecf20Sopenharmony_ci	case USB_DEVICE_ID_ELECOM_BM084:
588c2ecf20Sopenharmony_ci		/* The BM084 Bluetooth mouse includes a non-existing horizontal
598c2ecf20Sopenharmony_ci		 * wheel in the HID descriptor. */
608c2ecf20Sopenharmony_ci		if (*rsize >= 48 && rdesc[46] == 0x05 && rdesc[47] == 0x0c) {
618c2ecf20Sopenharmony_ci			hid_info(hdev, "Fixing up Elecom BM084 report descriptor\n");
628c2ecf20Sopenharmony_ci			rdesc[47] = 0x00;
638c2ecf20Sopenharmony_ci		}
648c2ecf20Sopenharmony_ci		break;
658c2ecf20Sopenharmony_ci	case USB_DEVICE_ID_ELECOM_M_XT3URBK:
668c2ecf20Sopenharmony_ci	case USB_DEVICE_ID_ELECOM_M_XT3DRBK:
678c2ecf20Sopenharmony_ci	case USB_DEVICE_ID_ELECOM_M_XT4DRBK:
688c2ecf20Sopenharmony_ci		mouse_button_fixup(hdev, rdesc, *rsize, 6);
698c2ecf20Sopenharmony_ci		break;
708c2ecf20Sopenharmony_ci	case USB_DEVICE_ID_ELECOM_M_DT1URBK:
718c2ecf20Sopenharmony_ci	case USB_DEVICE_ID_ELECOM_M_DT1DRBK:
728c2ecf20Sopenharmony_ci	case USB_DEVICE_ID_ELECOM_M_HT1URBK:
738c2ecf20Sopenharmony_ci	case USB_DEVICE_ID_ELECOM_M_HT1DRBK:
748c2ecf20Sopenharmony_ci		mouse_button_fixup(hdev, rdesc, *rsize, 8);
758c2ecf20Sopenharmony_ci		break;
768c2ecf20Sopenharmony_ci	}
778c2ecf20Sopenharmony_ci	return rdesc;
788c2ecf20Sopenharmony_ci}
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_cistatic const struct hid_device_id elecom_devices[] = {
818c2ecf20Sopenharmony_ci	{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_BM084) },
828c2ecf20Sopenharmony_ci	{ HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_XT3URBK) },
838c2ecf20Sopenharmony_ci	{ HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_XT3DRBK) },
848c2ecf20Sopenharmony_ci	{ HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_XT4DRBK) },
858c2ecf20Sopenharmony_ci	{ HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_DT1URBK) },
868c2ecf20Sopenharmony_ci	{ HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_DT1DRBK) },
878c2ecf20Sopenharmony_ci	{ HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_HT1URBK) },
888c2ecf20Sopenharmony_ci	{ HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_HT1DRBK) },
898c2ecf20Sopenharmony_ci	{ }
908c2ecf20Sopenharmony_ci};
918c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(hid, elecom_devices);
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_cistatic struct hid_driver elecom_driver = {
948c2ecf20Sopenharmony_ci	.name = "elecom",
958c2ecf20Sopenharmony_ci	.id_table = elecom_devices,
968c2ecf20Sopenharmony_ci	.report_fixup = elecom_report_fixup
978c2ecf20Sopenharmony_ci};
988c2ecf20Sopenharmony_cimodule_hid_driver(elecom_driver);
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
101