162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * HID driver for Holtek gaming mice 462306a36Sopenharmony_ci * Copyright (c) 2013 Christian Ohm 562306a36Sopenharmony_ci * Heavily inspired by various other HID drivers that adjust the report 662306a36Sopenharmony_ci * descriptor. 762306a36Sopenharmony_ci*/ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci/* 1062306a36Sopenharmony_ci */ 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include <linux/hid.h> 1362306a36Sopenharmony_ci#include <linux/module.h> 1462306a36Sopenharmony_ci#include <linux/usb.h> 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#include "hid-ids.h" 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci/* 1962306a36Sopenharmony_ci * The report descriptor of some Holtek based gaming mice specifies an 2062306a36Sopenharmony_ci * excessively large number of consumer usages (2^15), which is more than 2162306a36Sopenharmony_ci * HID_MAX_USAGES. This prevents proper parsing of the report descriptor. 2262306a36Sopenharmony_ci * 2362306a36Sopenharmony_ci * This driver fixes the report descriptor for: 2462306a36Sopenharmony_ci * - USB ID 04d9:a067, sold as Sharkoon Drakonia and Perixx MX-2000 2562306a36Sopenharmony_ci * - USB ID 04d9:a04a, sold as Tracer Sniper TRM-503, NOVA Gaming Slider X200 2662306a36Sopenharmony_ci * and Zalman ZM-GM1 2762306a36Sopenharmony_ci * - USB ID 04d9:a081, sold as SHARKOON DarkGlider Gaming mouse 2862306a36Sopenharmony_ci * - USB ID 04d9:a072, sold as LEETGION Hellion Gaming Mouse 2962306a36Sopenharmony_ci * - USB ID 04d9:a0c2, sold as ETEKCITY Scroll T-140 Gaming Mouse 3062306a36Sopenharmony_ci */ 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_cistatic __u8 *holtek_mouse_report_fixup(struct hid_device *hdev, __u8 *rdesc, 3362306a36Sopenharmony_ci unsigned int *rsize) 3462306a36Sopenharmony_ci{ 3562306a36Sopenharmony_ci struct usb_interface *intf = to_usb_interface(hdev->dev.parent); 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci if (intf->cur_altsetting->desc.bInterfaceNumber == 1) { 3862306a36Sopenharmony_ci /* Change usage maximum and logical maximum from 0x7fff to 3962306a36Sopenharmony_ci * 0x2fff, so they don't exceed HID_MAX_USAGES */ 4062306a36Sopenharmony_ci switch (hdev->product) { 4162306a36Sopenharmony_ci case USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A067: 4262306a36Sopenharmony_ci case USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A072: 4362306a36Sopenharmony_ci case USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A0C2: 4462306a36Sopenharmony_ci if (*rsize >= 122 && rdesc[115] == 0xff && rdesc[116] == 0x7f 4562306a36Sopenharmony_ci && rdesc[120] == 0xff && rdesc[121] == 0x7f) { 4662306a36Sopenharmony_ci hid_info(hdev, "Fixing up report descriptor\n"); 4762306a36Sopenharmony_ci rdesc[116] = rdesc[121] = 0x2f; 4862306a36Sopenharmony_ci } 4962306a36Sopenharmony_ci break; 5062306a36Sopenharmony_ci case USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A04A: 5162306a36Sopenharmony_ci case USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A070: 5262306a36Sopenharmony_ci case USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A081: 5362306a36Sopenharmony_ci if (*rsize >= 113 && rdesc[106] == 0xff && rdesc[107] == 0x7f 5462306a36Sopenharmony_ci && rdesc[111] == 0xff && rdesc[112] == 0x7f) { 5562306a36Sopenharmony_ci hid_info(hdev, "Fixing up report descriptor\n"); 5662306a36Sopenharmony_ci rdesc[107] = rdesc[112] = 0x2f; 5762306a36Sopenharmony_ci } 5862306a36Sopenharmony_ci break; 5962306a36Sopenharmony_ci } 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci } 6262306a36Sopenharmony_ci return rdesc; 6362306a36Sopenharmony_ci} 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_cistatic int holtek_mouse_probe(struct hid_device *hdev, 6662306a36Sopenharmony_ci const struct hid_device_id *id) 6762306a36Sopenharmony_ci{ 6862306a36Sopenharmony_ci int ret; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci if (!hid_is_usb(hdev)) 7162306a36Sopenharmony_ci return -EINVAL; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci ret = hid_parse(hdev); 7462306a36Sopenharmony_ci if (ret) { 7562306a36Sopenharmony_ci hid_err(hdev, "hid parse failed: %d\n", ret); 7662306a36Sopenharmony_ci return ret; 7762306a36Sopenharmony_ci } 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT); 8062306a36Sopenharmony_ci if (ret) { 8162306a36Sopenharmony_ci hid_err(hdev, "hw start failed: %d\n", ret); 8262306a36Sopenharmony_ci return ret; 8362306a36Sopenharmony_ci } 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci return 0; 8662306a36Sopenharmony_ci} 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_cistatic const struct hid_device_id holtek_mouse_devices[] = { 8962306a36Sopenharmony_ci { HID_USB_DEVICE(USB_VENDOR_ID_HOLTEK_ALT, 9062306a36Sopenharmony_ci USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A067) }, 9162306a36Sopenharmony_ci { HID_USB_DEVICE(USB_VENDOR_ID_HOLTEK_ALT, 9262306a36Sopenharmony_ci USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A070) }, 9362306a36Sopenharmony_ci { HID_USB_DEVICE(USB_VENDOR_ID_HOLTEK_ALT, 9462306a36Sopenharmony_ci USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A04A) }, 9562306a36Sopenharmony_ci { HID_USB_DEVICE(USB_VENDOR_ID_HOLTEK_ALT, 9662306a36Sopenharmony_ci USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A072) }, 9762306a36Sopenharmony_ci { HID_USB_DEVICE(USB_VENDOR_ID_HOLTEK_ALT, 9862306a36Sopenharmony_ci USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A081) }, 9962306a36Sopenharmony_ci { HID_USB_DEVICE(USB_VENDOR_ID_HOLTEK_ALT, 10062306a36Sopenharmony_ci USB_DEVICE_ID_HOLTEK_ALT_MOUSE_A0C2) }, 10162306a36Sopenharmony_ci { } 10262306a36Sopenharmony_ci}; 10362306a36Sopenharmony_ciMODULE_DEVICE_TABLE(hid, holtek_mouse_devices); 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_cistatic struct hid_driver holtek_mouse_driver = { 10662306a36Sopenharmony_ci .name = "holtek_mouse", 10762306a36Sopenharmony_ci .id_table = holtek_mouse_devices, 10862306a36Sopenharmony_ci .report_fixup = holtek_mouse_report_fixup, 10962306a36Sopenharmony_ci .probe = holtek_mouse_probe, 11062306a36Sopenharmony_ci}; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_cimodule_hid_driver(holtek_mouse_driver); 11362306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 114