162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * HID driver for ELECOM devices: 462306a36Sopenharmony_ci * - BM084 Bluetooth Mouse 562306a36Sopenharmony_ci * - EX-G Trackballs (M-XT3DRBK, M-XT3URBK, M-XT4DRBK) 662306a36Sopenharmony_ci * - DEFT Trackballs (M-DT1DRBK, M-DT1URBK, M-DT2DRBK, M-DT2URBK) 762306a36Sopenharmony_ci * - HUGE Trackballs (M-HT1DRBK, M-HT1URBK) 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * Copyright (c) 2010 Richard Nauber <Richard.Nauber@gmail.com> 1062306a36Sopenharmony_ci * Copyright (c) 2016 Yuxuan Shui <yshuiv7@gmail.com> 1162306a36Sopenharmony_ci * Copyright (c) 2017 Diego Elio Pettenò <flameeyes@flameeyes.eu> 1262306a36Sopenharmony_ci * Copyright (c) 2017 Alex Manoussakis <amanou@gnu.org> 1362306a36Sopenharmony_ci * Copyright (c) 2017 Tomasz Kramkowski <tk@the-tk.com> 1462306a36Sopenharmony_ci * Copyright (c) 2020 YOSHIOKA Takuma <lo48576@hard-wi.red> 1562306a36Sopenharmony_ci * Copyright (c) 2022 Takahiro Fujii <fujii@xaxxi.net> 1662306a36Sopenharmony_ci */ 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci/* 1962306a36Sopenharmony_ci */ 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#include <linux/device.h> 2262306a36Sopenharmony_ci#include <linux/hid.h> 2362306a36Sopenharmony_ci#include <linux/module.h> 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci#include "hid-ids.h" 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci/* 2862306a36Sopenharmony_ci * Certain ELECOM mice misreport their button count meaning that they only work 2962306a36Sopenharmony_ci * correctly with the ELECOM mouse assistant software which is unavailable for 3062306a36Sopenharmony_ci * Linux. A four extra INPUT reports and a FEATURE report are described by the 3162306a36Sopenharmony_ci * report descriptor but it does not appear that these enable software to 3262306a36Sopenharmony_ci * control what the extra buttons map to. The only simple and straightforward 3362306a36Sopenharmony_ci * solution seems to involve fixing up the report descriptor. 3462306a36Sopenharmony_ci */ 3562306a36Sopenharmony_ci#define MOUSE_BUTTONS_MAX 8 3662306a36Sopenharmony_cistatic void mouse_button_fixup(struct hid_device *hdev, 3762306a36Sopenharmony_ci __u8 *rdesc, unsigned int rsize, 3862306a36Sopenharmony_ci unsigned int button_bit_count, 3962306a36Sopenharmony_ci unsigned int padding_bit, 4062306a36Sopenharmony_ci unsigned int button_report_size, 4162306a36Sopenharmony_ci unsigned int button_usage_maximum, 4262306a36Sopenharmony_ci int nbuttons) 4362306a36Sopenharmony_ci{ 4462306a36Sopenharmony_ci if (rsize < 32 || rdesc[button_bit_count] != 0x95 || 4562306a36Sopenharmony_ci rdesc[button_report_size] != 0x75 || 4662306a36Sopenharmony_ci rdesc[button_report_size + 1] != 0x01 || 4762306a36Sopenharmony_ci rdesc[button_usage_maximum] != 0x29 || rdesc[padding_bit] != 0x75) 4862306a36Sopenharmony_ci return; 4962306a36Sopenharmony_ci hid_info(hdev, "Fixing up Elecom mouse button count\n"); 5062306a36Sopenharmony_ci nbuttons = clamp(nbuttons, 0, MOUSE_BUTTONS_MAX); 5162306a36Sopenharmony_ci rdesc[button_bit_count + 1] = nbuttons; 5262306a36Sopenharmony_ci rdesc[button_usage_maximum + 1] = nbuttons; 5362306a36Sopenharmony_ci rdesc[padding_bit + 1] = MOUSE_BUTTONS_MAX - nbuttons; 5462306a36Sopenharmony_ci} 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_cistatic __u8 *elecom_report_fixup(struct hid_device *hdev, __u8 *rdesc, 5762306a36Sopenharmony_ci unsigned int *rsize) 5862306a36Sopenharmony_ci{ 5962306a36Sopenharmony_ci switch (hdev->product) { 6062306a36Sopenharmony_ci case USB_DEVICE_ID_ELECOM_BM084: 6162306a36Sopenharmony_ci /* The BM084 Bluetooth mouse includes a non-existing horizontal 6262306a36Sopenharmony_ci * wheel in the HID descriptor. */ 6362306a36Sopenharmony_ci if (*rsize >= 48 && rdesc[46] == 0x05 && rdesc[47] == 0x0c) { 6462306a36Sopenharmony_ci hid_info(hdev, "Fixing up Elecom BM084 report descriptor\n"); 6562306a36Sopenharmony_ci rdesc[47] = 0x00; 6662306a36Sopenharmony_ci } 6762306a36Sopenharmony_ci break; 6862306a36Sopenharmony_ci case USB_DEVICE_ID_ELECOM_M_XGL20DLBK: 6962306a36Sopenharmony_ci /* 7062306a36Sopenharmony_ci * Report descriptor format: 7162306a36Sopenharmony_ci * 20: button bit count 7262306a36Sopenharmony_ci * 28: padding bit count 7362306a36Sopenharmony_ci * 22: button report size 7462306a36Sopenharmony_ci * 14: button usage maximum 7562306a36Sopenharmony_ci */ 7662306a36Sopenharmony_ci mouse_button_fixup(hdev, rdesc, *rsize, 20, 28, 22, 14, 8); 7762306a36Sopenharmony_ci break; 7862306a36Sopenharmony_ci case USB_DEVICE_ID_ELECOM_M_XT3URBK: 7962306a36Sopenharmony_ci case USB_DEVICE_ID_ELECOM_M_XT3DRBK: 8062306a36Sopenharmony_ci case USB_DEVICE_ID_ELECOM_M_XT4DRBK: 8162306a36Sopenharmony_ci /* 8262306a36Sopenharmony_ci * Report descriptor format: 8362306a36Sopenharmony_ci * 12: button bit count 8462306a36Sopenharmony_ci * 30: padding bit count 8562306a36Sopenharmony_ci * 14: button report size 8662306a36Sopenharmony_ci * 20: button usage maximum 8762306a36Sopenharmony_ci */ 8862306a36Sopenharmony_ci mouse_button_fixup(hdev, rdesc, *rsize, 12, 30, 14, 20, 6); 8962306a36Sopenharmony_ci break; 9062306a36Sopenharmony_ci case USB_DEVICE_ID_ELECOM_M_DT1URBK: 9162306a36Sopenharmony_ci case USB_DEVICE_ID_ELECOM_M_DT1DRBK: 9262306a36Sopenharmony_ci case USB_DEVICE_ID_ELECOM_M_HT1URBK: 9362306a36Sopenharmony_ci case USB_DEVICE_ID_ELECOM_M_HT1DRBK_010D: 9462306a36Sopenharmony_ci /* 9562306a36Sopenharmony_ci * Report descriptor format: 9662306a36Sopenharmony_ci * 12: button bit count 9762306a36Sopenharmony_ci * 30: padding bit count 9862306a36Sopenharmony_ci * 14: button report size 9962306a36Sopenharmony_ci * 20: button usage maximum 10062306a36Sopenharmony_ci */ 10162306a36Sopenharmony_ci mouse_button_fixup(hdev, rdesc, *rsize, 12, 30, 14, 20, 8); 10262306a36Sopenharmony_ci break; 10362306a36Sopenharmony_ci case USB_DEVICE_ID_ELECOM_M_HT1DRBK_011C: 10462306a36Sopenharmony_ci /* 10562306a36Sopenharmony_ci * Report descriptor format: 10662306a36Sopenharmony_ci * 22: button bit count 10762306a36Sopenharmony_ci * 30: padding bit count 10862306a36Sopenharmony_ci * 24: button report size 10962306a36Sopenharmony_ci * 16: button usage maximum 11062306a36Sopenharmony_ci */ 11162306a36Sopenharmony_ci mouse_button_fixup(hdev, rdesc, *rsize, 22, 30, 24, 16, 8); 11262306a36Sopenharmony_ci break; 11362306a36Sopenharmony_ci } 11462306a36Sopenharmony_ci return rdesc; 11562306a36Sopenharmony_ci} 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_cistatic const struct hid_device_id elecom_devices[] = { 11862306a36Sopenharmony_ci { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_BM084) }, 11962306a36Sopenharmony_ci { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_XGL20DLBK) }, 12062306a36Sopenharmony_ci { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_XT3URBK) }, 12162306a36Sopenharmony_ci { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_XT3DRBK) }, 12262306a36Sopenharmony_ci { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_XT4DRBK) }, 12362306a36Sopenharmony_ci { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_DT1URBK) }, 12462306a36Sopenharmony_ci { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_DT1DRBK) }, 12562306a36Sopenharmony_ci { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_HT1URBK) }, 12662306a36Sopenharmony_ci { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_HT1DRBK_010D) }, 12762306a36Sopenharmony_ci { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_HT1DRBK_011C) }, 12862306a36Sopenharmony_ci { } 12962306a36Sopenharmony_ci}; 13062306a36Sopenharmony_ciMODULE_DEVICE_TABLE(hid, elecom_devices); 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_cistatic struct hid_driver elecom_driver = { 13362306a36Sopenharmony_ci .name = "elecom", 13462306a36Sopenharmony_ci .id_table = elecom_devices, 13562306a36Sopenharmony_ci .report_fixup = elecom_report_fixup 13662306a36Sopenharmony_ci}; 13762306a36Sopenharmony_cimodule_hid_driver(elecom_driver); 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 140