xref: /openbmc/linux/drivers/hid/hid-cypress.c (revision cdd38c5f1ce4398ec58fec95904b75824daab7b5)
12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
20f221320SJiri Slaby /*
30f221320SJiri Slaby  *  HID driver for some cypress "special" devices
40f221320SJiri Slaby  *
50f221320SJiri Slaby  *  Copyright (c) 1999 Andreas Gal
60f221320SJiri Slaby  *  Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
70f221320SJiri Slaby  *  Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
80f221320SJiri Slaby  *  Copyright (c) 2006-2007 Jiri Kosina
90f221320SJiri Slaby  *  Copyright (c) 2008 Jiri Slaby
100f221320SJiri Slaby  */
110f221320SJiri Slaby 
120f221320SJiri Slaby /*
130f221320SJiri Slaby  */
140f221320SJiri Slaby 
150f221320SJiri Slaby #include <linux/device.h>
160f221320SJiri Slaby #include <linux/hid.h>
170f221320SJiri Slaby #include <linux/input.h>
180f221320SJiri Slaby #include <linux/module.h>
190f221320SJiri Slaby 
200f221320SJiri Slaby #include "hid-ids.h"
210f221320SJiri Slaby 
220f221320SJiri Slaby #define CP_RDESC_SWAPPED_MIN_MAX	0x01
230f221320SJiri Slaby #define CP_2WHEEL_MOUSE_HACK		0x02
240f221320SJiri Slaby #define CP_2WHEEL_MOUSE_HACK_ON		0x04
250f221320SJiri Slaby 
26*652f3d00SFrank Yang #define VA_INVAL_LOGICAL_BOUNDARY	0x08
27*652f3d00SFrank Yang 
280f221320SJiri Slaby /*
290f221320SJiri Slaby  * Some USB barcode readers from cypress have usage min and usage max in
300f221320SJiri Slaby  * the wrong order
310f221320SJiri Slaby  */
cp_rdesc_fixup(struct hid_device * hdev,__u8 * rdesc,unsigned int * rsize)32*652f3d00SFrank Yang static __u8 *cp_rdesc_fixup(struct hid_device *hdev, __u8 *rdesc,
3373e4008dSNikolai Kondrashov 		unsigned int *rsize)
340f221320SJiri Slaby {
350f221320SJiri Slaby 	unsigned int i;
360f221320SJiri Slaby 
371ebb7114SGreg Kroah-Hartman 	if (*rsize < 4)
381ebb7114SGreg Kroah-Hartman 		return rdesc;
391ebb7114SGreg Kroah-Hartman 
4073e4008dSNikolai Kondrashov 	for (i = 0; i < *rsize - 4; i++)
410f221320SJiri Slaby 		if (rdesc[i] == 0x29 && rdesc[i + 2] == 0x19) {
420f221320SJiri Slaby 			rdesc[i] = 0x19;
430f221320SJiri Slaby 			rdesc[i + 2] = 0x29;
4474a3e0c7SFabian Frederick 			swap(rdesc[i + 3], rdesc[i + 1]);
450f221320SJiri Slaby 		}
4673e4008dSNikolai Kondrashov 	return rdesc;
470f221320SJiri Slaby }
480f221320SJiri Slaby 
va_logical_boundary_fixup(struct hid_device * hdev,__u8 * rdesc,unsigned int * rsize)49*652f3d00SFrank Yang static __u8 *va_logical_boundary_fixup(struct hid_device *hdev, __u8 *rdesc,
50*652f3d00SFrank Yang 		unsigned int *rsize)
51*652f3d00SFrank Yang {
52*652f3d00SFrank Yang 	/*
53*652f3d00SFrank Yang 	 * Varmilo VA104M (with VID Cypress and device ID 07B1) incorrectly
54*652f3d00SFrank Yang 	 * reports Logical Minimum of its Consumer Control device as 572
55*652f3d00SFrank Yang 	 * (0x02 0x3c). Fix this by setting its Logical Minimum to zero.
56*652f3d00SFrank Yang 	 */
57*652f3d00SFrank Yang 	if (*rsize == 25 &&
58*652f3d00SFrank Yang 			rdesc[0] == 0x05 && rdesc[1] == 0x0c &&
59*652f3d00SFrank Yang 			rdesc[2] == 0x09 && rdesc[3] == 0x01 &&
60*652f3d00SFrank Yang 			rdesc[6] == 0x19 && rdesc[7] == 0x00 &&
61*652f3d00SFrank Yang 			rdesc[11] == 0x16 && rdesc[12] == 0x3c && rdesc[13] == 0x02) {
62*652f3d00SFrank Yang 		hid_info(hdev,
63*652f3d00SFrank Yang 			 "fixing up varmilo VA104M consumer control report descriptor\n");
64*652f3d00SFrank Yang 		rdesc[12] = 0x00;
65*652f3d00SFrank Yang 		rdesc[13] = 0x00;
66*652f3d00SFrank Yang 	}
67*652f3d00SFrank Yang 	return rdesc;
68*652f3d00SFrank Yang }
69*652f3d00SFrank Yang 
cp_report_fixup(struct hid_device * hdev,__u8 * rdesc,unsigned int * rsize)70*652f3d00SFrank Yang static __u8 *cp_report_fixup(struct hid_device *hdev, __u8 *rdesc,
71*652f3d00SFrank Yang 		unsigned int *rsize)
72*652f3d00SFrank Yang {
73*652f3d00SFrank Yang 	unsigned long quirks = (unsigned long)hid_get_drvdata(hdev);
74*652f3d00SFrank Yang 
75*652f3d00SFrank Yang 	if (quirks & CP_RDESC_SWAPPED_MIN_MAX)
76*652f3d00SFrank Yang 		rdesc = cp_rdesc_fixup(hdev, rdesc, rsize);
77*652f3d00SFrank Yang 	if (quirks & VA_INVAL_LOGICAL_BOUNDARY)
78*652f3d00SFrank Yang 		rdesc = va_logical_boundary_fixup(hdev, rdesc, rsize);
79*652f3d00SFrank Yang 
80*652f3d00SFrank Yang 	return rdesc;
81*652f3d00SFrank Yang }
82*652f3d00SFrank Yang 
cp_input_mapped(struct hid_device * hdev,struct hid_input * hi,struct hid_field * field,struct hid_usage * usage,unsigned long ** bit,int * max)830f221320SJiri Slaby static int cp_input_mapped(struct hid_device *hdev, struct hid_input *hi,
840f221320SJiri Slaby 		struct hid_field *field, struct hid_usage *usage,
850f221320SJiri Slaby 		unsigned long **bit, int *max)
860f221320SJiri Slaby {
870f221320SJiri Slaby 	unsigned long quirks = (unsigned long)hid_get_drvdata(hdev);
880f221320SJiri Slaby 
890f221320SJiri Slaby 	if (!(quirks & CP_2WHEEL_MOUSE_HACK))
900f221320SJiri Slaby 		return 0;
910f221320SJiri Slaby 
920f221320SJiri Slaby 	if (usage->type == EV_REL && usage->code == REL_WHEEL)
930f221320SJiri Slaby 		set_bit(REL_HWHEEL, *bit);
940f221320SJiri Slaby 	if (usage->hid == 0x00090005)
950f221320SJiri Slaby 		return -1;
960f221320SJiri Slaby 
970f221320SJiri Slaby 	return 0;
980f221320SJiri Slaby }
990f221320SJiri Slaby 
cp_event(struct hid_device * hdev,struct hid_field * field,struct hid_usage * usage,__s32 value)1000f221320SJiri Slaby static int cp_event(struct hid_device *hdev, struct hid_field *field,
1010f221320SJiri Slaby 		struct hid_usage *usage, __s32 value)
1020f221320SJiri Slaby {
1030f221320SJiri Slaby 	unsigned long quirks = (unsigned long)hid_get_drvdata(hdev);
1040f221320SJiri Slaby 
1050f221320SJiri Slaby 	if (!(hdev->claimed & HID_CLAIMED_INPUT) || !field->hidinput ||
1060f221320SJiri Slaby 			!usage->type || !(quirks & CP_2WHEEL_MOUSE_HACK))
1070f221320SJiri Slaby 		return 0;
1080f221320SJiri Slaby 
1090f221320SJiri Slaby 	if (usage->hid == 0x00090005) {
1100f221320SJiri Slaby 		if (value)
1110f221320SJiri Slaby 			quirks |=  CP_2WHEEL_MOUSE_HACK_ON;
1120f221320SJiri Slaby 		else
1130f221320SJiri Slaby 			quirks &= ~CP_2WHEEL_MOUSE_HACK_ON;
1140f221320SJiri Slaby 		hid_set_drvdata(hdev, (void *)quirks);
1150f221320SJiri Slaby 		return 1;
1160f221320SJiri Slaby 	}
1170f221320SJiri Slaby 
1180f221320SJiri Slaby 	if (usage->code == REL_WHEEL && (quirks & CP_2WHEEL_MOUSE_HACK_ON)) {
1190f221320SJiri Slaby 		struct input_dev *input = field->hidinput->input;
1200f221320SJiri Slaby 
1210f221320SJiri Slaby 		input_event(input, usage->type, REL_HWHEEL, value);
1220f221320SJiri Slaby 		return 1;
1230f221320SJiri Slaby 	}
1240f221320SJiri Slaby 
1250f221320SJiri Slaby 	return 0;
1260f221320SJiri Slaby }
1270f221320SJiri Slaby 
cp_probe(struct hid_device * hdev,const struct hid_device_id * id)1280f221320SJiri Slaby static int cp_probe(struct hid_device *hdev, const struct hid_device_id *id)
1290f221320SJiri Slaby {
1300f221320SJiri Slaby 	unsigned long quirks = id->driver_data;
1310f221320SJiri Slaby 	int ret;
1320f221320SJiri Slaby 
1330f221320SJiri Slaby 	hid_set_drvdata(hdev, (void *)quirks);
1340f221320SJiri Slaby 
1350f221320SJiri Slaby 	ret = hid_parse(hdev);
1360f221320SJiri Slaby 	if (ret) {
1374291ee30SJoe Perches 		hid_err(hdev, "parse failed\n");
1380f221320SJiri Slaby 		goto err_free;
1390f221320SJiri Slaby 	}
1400f221320SJiri Slaby 
14193c10132SJiri Slaby 	ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
1420f221320SJiri Slaby 	if (ret) {
1434291ee30SJoe Perches 		hid_err(hdev, "hw start failed\n");
1440f221320SJiri Slaby 		goto err_free;
1450f221320SJiri Slaby 	}
1460f221320SJiri Slaby 
1470f221320SJiri Slaby 	return 0;
1480f221320SJiri Slaby err_free:
1490f221320SJiri Slaby 	return ret;
1500f221320SJiri Slaby }
1510f221320SJiri Slaby 
1520f221320SJiri Slaby static const struct hid_device_id cp_devices[] = {
1530f221320SJiri Slaby 	{ HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_BARCODE_1),
1540f221320SJiri Slaby 		.driver_data = CP_RDESC_SWAPPED_MIN_MAX },
1550f221320SJiri Slaby 	{ HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_BARCODE_2),
1560f221320SJiri Slaby 		.driver_data = CP_RDESC_SWAPPED_MIN_MAX },
157e8d0eab4SJiri Kosina 	{ HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_BARCODE_3),
158e8d0eab4SJiri Kosina 		.driver_data = CP_RDESC_SWAPPED_MIN_MAX },
15976c9d8feSLionel Vaux 	{ HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_BARCODE_4),
16076c9d8feSLionel Vaux 		.driver_data = CP_RDESC_SWAPPED_MIN_MAX },
1610f221320SJiri Slaby 	{ HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_MOUSE),
1620f221320SJiri Slaby 		.driver_data = CP_2WHEEL_MOUSE_HACK },
163*652f3d00SFrank Yang 	{ HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_VARMILO_VA104M_07B1),
164*652f3d00SFrank Yang 		.driver_data = VA_INVAL_LOGICAL_BOUNDARY },
1650f221320SJiri Slaby 	{ }
1660f221320SJiri Slaby };
1670f221320SJiri Slaby MODULE_DEVICE_TABLE(hid, cp_devices);
1680f221320SJiri Slaby 
1690f221320SJiri Slaby static struct hid_driver cp_driver = {
1700f221320SJiri Slaby 	.name = "cypress",
1710f221320SJiri Slaby 	.id_table = cp_devices,
1720f221320SJiri Slaby 	.report_fixup = cp_report_fixup,
1730f221320SJiri Slaby 	.input_mapped = cp_input_mapped,
1740f221320SJiri Slaby 	.event = cp_event,
1750f221320SJiri Slaby 	.probe = cp_probe,
1760f221320SJiri Slaby };
177f425458eSH Hartley Sweeten module_hid_driver(cp_driver);
1780f221320SJiri Slaby 
1790f221320SJiri Slaby MODULE_LICENSE("GPL");
180