12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later 214a21cd4SJiri Slaby /* 314a21cd4SJiri Slaby * HID driver for some a4tech "special" devices 414a21cd4SJiri Slaby * 514a21cd4SJiri Slaby * Copyright (c) 1999 Andreas Gal 614a21cd4SJiri Slaby * Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz> 714a21cd4SJiri Slaby * Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc 814a21cd4SJiri Slaby * Copyright (c) 2006-2007 Jiri Kosina 914a21cd4SJiri Slaby * Copyright (c) 2008 Jiri Slaby 1014a21cd4SJiri Slaby */ 1114a21cd4SJiri Slaby 1214a21cd4SJiri Slaby /* 1314a21cd4SJiri Slaby */ 1414a21cd4SJiri Slaby 1514a21cd4SJiri Slaby #include <linux/device.h> 1614a21cd4SJiri Slaby #include <linux/input.h> 1714a21cd4SJiri Slaby #include <linux/hid.h> 1814a21cd4SJiri Slaby #include <linux/module.h> 195a0e3ad6STejun Heo #include <linux/slab.h> 2014a21cd4SJiri Slaby 2114a21cd4SJiri Slaby #include "hid-ids.h" 2214a21cd4SJiri Slaby 2314a21cd4SJiri Slaby #define A4_2WHEEL_MOUSE_HACK_7 0x01 2414a21cd4SJiri Slaby #define A4_2WHEEL_MOUSE_HACK_B8 0x02 2514a21cd4SJiri Slaby 26*1c703b53SNicolas Saenz Julienne #define A4_WHEEL_ORIENTATION (HID_UP_GENDESK | 0x000000b8) 27*1c703b53SNicolas Saenz Julienne 2814a21cd4SJiri Slaby struct a4tech_sc { 2914a21cd4SJiri Slaby unsigned long quirks; 3014a21cd4SJiri Slaby unsigned int hw_wheel; 3114a21cd4SJiri Slaby __s32 delayed_value; 3214a21cd4SJiri Slaby }; 3314a21cd4SJiri Slaby 34*1c703b53SNicolas Saenz Julienne static int a4_input_mapping(struct hid_device *hdev, struct hid_input *hi, 35*1c703b53SNicolas Saenz Julienne struct hid_field *field, struct hid_usage *usage, 36*1c703b53SNicolas Saenz Julienne unsigned long **bit, int *max) 37*1c703b53SNicolas Saenz Julienne { 38*1c703b53SNicolas Saenz Julienne struct a4tech_sc *a4 = hid_get_drvdata(hdev); 39*1c703b53SNicolas Saenz Julienne 40*1c703b53SNicolas Saenz Julienne if (a4->quirks & A4_2WHEEL_MOUSE_HACK_B8 && 41*1c703b53SNicolas Saenz Julienne usage->hid == A4_WHEEL_ORIENTATION) { 42*1c703b53SNicolas Saenz Julienne /* 43*1c703b53SNicolas Saenz Julienne * We do not want to have this usage mapped to anything as it's 44*1c703b53SNicolas Saenz Julienne * nonstandard and doesn't really behave like an HID report. 45*1c703b53SNicolas Saenz Julienne * It's only selecting the orientation (vertical/horizontal) of 46*1c703b53SNicolas Saenz Julienne * the previous mouse wheel report. The input_events will be 47*1c703b53SNicolas Saenz Julienne * generated once both reports are recorded in a4_event(). 48*1c703b53SNicolas Saenz Julienne */ 49*1c703b53SNicolas Saenz Julienne return -1; 50*1c703b53SNicolas Saenz Julienne } 51*1c703b53SNicolas Saenz Julienne 52*1c703b53SNicolas Saenz Julienne return 0; 53*1c703b53SNicolas Saenz Julienne 54*1c703b53SNicolas Saenz Julienne } 55*1c703b53SNicolas Saenz Julienne 5614a21cd4SJiri Slaby static int a4_input_mapped(struct hid_device *hdev, struct hid_input *hi, 5714a21cd4SJiri Slaby struct hid_field *field, struct hid_usage *usage, 5814a21cd4SJiri Slaby unsigned long **bit, int *max) 5914a21cd4SJiri Slaby { 6014a21cd4SJiri Slaby struct a4tech_sc *a4 = hid_get_drvdata(hdev); 6114a21cd4SJiri Slaby 62abf82e8fSBłażej Szczygieł if (usage->type == EV_REL && usage->code == REL_WHEEL_HI_RES) { 6314a21cd4SJiri Slaby set_bit(REL_HWHEEL, *bit); 64abf82e8fSBłażej Szczygieł set_bit(REL_HWHEEL_HI_RES, *bit); 65abf82e8fSBłażej Szczygieł } 6614a21cd4SJiri Slaby 6714a21cd4SJiri Slaby if ((a4->quirks & A4_2WHEEL_MOUSE_HACK_7) && usage->hid == 0x00090007) 6814a21cd4SJiri Slaby return -1; 6914a21cd4SJiri Slaby 7014a21cd4SJiri Slaby return 0; 7114a21cd4SJiri Slaby } 7214a21cd4SJiri Slaby 7314a21cd4SJiri Slaby static int a4_event(struct hid_device *hdev, struct hid_field *field, 7414a21cd4SJiri Slaby struct hid_usage *usage, __s32 value) 7514a21cd4SJiri Slaby { 7614a21cd4SJiri Slaby struct a4tech_sc *a4 = hid_get_drvdata(hdev); 7714a21cd4SJiri Slaby struct input_dev *input; 7814a21cd4SJiri Slaby 79*1c703b53SNicolas Saenz Julienne if (!(hdev->claimed & HID_CLAIMED_INPUT) || !field->hidinput) 8014a21cd4SJiri Slaby return 0; 8114a21cd4SJiri Slaby 8214a21cd4SJiri Slaby input = field->hidinput->input; 8314a21cd4SJiri Slaby 8414a21cd4SJiri Slaby if (a4->quirks & A4_2WHEEL_MOUSE_HACK_B8) { 85abf82e8fSBłażej Szczygieł if (usage->type == EV_REL && usage->code == REL_WHEEL_HI_RES) { 8614a21cd4SJiri Slaby a4->delayed_value = value; 8714a21cd4SJiri Slaby return 1; 8814a21cd4SJiri Slaby } 8914a21cd4SJiri Slaby 90*1c703b53SNicolas Saenz Julienne if (usage->hid == A4_WHEEL_ORIENTATION) { 9114a21cd4SJiri Slaby input_event(input, EV_REL, value ? REL_HWHEEL : 9214a21cd4SJiri Slaby REL_WHEEL, a4->delayed_value); 93abf82e8fSBłażej Szczygieł input_event(input, EV_REL, value ? REL_HWHEEL_HI_RES : 94abf82e8fSBłażej Szczygieł REL_WHEEL_HI_RES, a4->delayed_value * 120); 9514a21cd4SJiri Slaby return 1; 9614a21cd4SJiri Slaby } 9714a21cd4SJiri Slaby } 9814a21cd4SJiri Slaby 9914a21cd4SJiri Slaby if ((a4->quirks & A4_2WHEEL_MOUSE_HACK_7) && usage->hid == 0x00090007) { 10014a21cd4SJiri Slaby a4->hw_wheel = !!value; 10114a21cd4SJiri Slaby return 1; 10214a21cd4SJiri Slaby } 10314a21cd4SJiri Slaby 104abf82e8fSBłażej Szczygieł if (usage->code == REL_WHEEL_HI_RES && a4->hw_wheel) { 10514a21cd4SJiri Slaby input_event(input, usage->type, REL_HWHEEL, value); 106abf82e8fSBłażej Szczygieł input_event(input, usage->type, REL_HWHEEL_HI_RES, value * 120); 10714a21cd4SJiri Slaby return 1; 10814a21cd4SJiri Slaby } 10914a21cd4SJiri Slaby 11014a21cd4SJiri Slaby return 0; 11114a21cd4SJiri Slaby } 11214a21cd4SJiri Slaby 11314a21cd4SJiri Slaby static int a4_probe(struct hid_device *hdev, const struct hid_device_id *id) 11414a21cd4SJiri Slaby { 11514a21cd4SJiri Slaby struct a4tech_sc *a4; 11614a21cd4SJiri Slaby int ret; 11714a21cd4SJiri Slaby 118abf832bfSBenjamin Tissoires a4 = devm_kzalloc(&hdev->dev, sizeof(*a4), GFP_KERNEL); 11914a21cd4SJiri Slaby if (a4 == NULL) { 1204291ee30SJoe Perches hid_err(hdev, "can't alloc device descriptor\n"); 121abf832bfSBenjamin Tissoires return -ENOMEM; 12214a21cd4SJiri Slaby } 12314a21cd4SJiri Slaby 12414a21cd4SJiri Slaby a4->quirks = id->driver_data; 12514a21cd4SJiri Slaby 12614a21cd4SJiri Slaby hid_set_drvdata(hdev, a4); 12714a21cd4SJiri Slaby 12814a21cd4SJiri Slaby ret = hid_parse(hdev); 12914a21cd4SJiri Slaby if (ret) { 1304291ee30SJoe Perches hid_err(hdev, "parse failed\n"); 131abf832bfSBenjamin Tissoires return ret; 13214a21cd4SJiri Slaby } 13314a21cd4SJiri Slaby 13493c10132SJiri Slaby ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT); 13514a21cd4SJiri Slaby if (ret) { 1364291ee30SJoe Perches hid_err(hdev, "hw start failed\n"); 13714a21cd4SJiri Slaby return ret; 13814a21cd4SJiri Slaby } 13914a21cd4SJiri Slaby 140abf832bfSBenjamin Tissoires return 0; 14114a21cd4SJiri Slaby } 14214a21cd4SJiri Slaby 14314a21cd4SJiri Slaby static const struct hid_device_id a4_devices[] = { 14414a21cd4SJiri Slaby { HID_USB_DEVICE(USB_VENDOR_ID_A4TECH, USB_DEVICE_ID_A4TECH_WCP32PU), 14514a21cd4SJiri Slaby .driver_data = A4_2WHEEL_MOUSE_HACK_7 }, 14614a21cd4SJiri Slaby { HID_USB_DEVICE(USB_VENDOR_ID_A4TECH, USB_DEVICE_ID_A4TECH_X5_005D), 14714a21cd4SJiri Slaby .driver_data = A4_2WHEEL_MOUSE_HACK_B8 }, 148b7e1b203SLech Perczak { HID_USB_DEVICE(USB_VENDOR_ID_A4TECH, USB_DEVICE_ID_A4TECH_RP_649), 149b7e1b203SLech Perczak .driver_data = A4_2WHEEL_MOUSE_HACK_B8 }, 15014a21cd4SJiri Slaby { } 15114a21cd4SJiri Slaby }; 15214a21cd4SJiri Slaby MODULE_DEVICE_TABLE(hid, a4_devices); 15314a21cd4SJiri Slaby 15414a21cd4SJiri Slaby static struct hid_driver a4_driver = { 15514a21cd4SJiri Slaby .name = "a4tech", 15614a21cd4SJiri Slaby .id_table = a4_devices, 157*1c703b53SNicolas Saenz Julienne .input_mapping = a4_input_mapping, 15814a21cd4SJiri Slaby .input_mapped = a4_input_mapped, 15914a21cd4SJiri Slaby .event = a4_event, 16014a21cd4SJiri Slaby .probe = a4_probe, 16114a21cd4SJiri Slaby }; 162f425458eSH Hartley Sweeten module_hid_driver(a4_driver); 16314a21cd4SJiri Slaby 16414a21cd4SJiri Slaby MODULE_LICENSE("GPL"); 165