xref: /linux/drivers/hid/hid-a4tech.c (revision 1c703b53e5bfb5c2205c30f0fb157ce271fd42fb)
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