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