1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * HID driver for some cypress "special" devices 4 * 5 * Copyright (c) 1999 Andreas Gal 6 * Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz> 7 * Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc 8 * Copyright (c) 2006-2007 Jiri Kosina 9 * Copyright (c) 2008 Jiri Slaby 10 */ 11 12 /* 13 */ 14 15 #include <linux/device.h> 16 #include <linux/hid.h> 17 #include <linux/input.h> 18 #include <linux/module.h> 19 20 #include "hid-ids.h" 21 22 #define CP_RDESC_SWAPPED_MIN_MAX 0x01 23 #define CP_2WHEEL_MOUSE_HACK 0x02 24 #define CP_2WHEEL_MOUSE_HACK_ON 0x04 25 26 #define VA_INVAL_LOGICAL_BOUNDARY 0x08 27 28 struct cp_device { 29 unsigned long quirks; 30 }; 31 32 /* 33 * Some USB barcode readers from cypress have usage min and usage max in 34 * the wrong order 35 */ 36 static __u8 *cp_rdesc_fixup(struct hid_device *hdev, __u8 *rdesc, 37 unsigned int *rsize) 38 { 39 unsigned int i; 40 41 if (*rsize < 4) 42 return rdesc; 43 44 for (i = 0; i < *rsize - 4; i++) 45 if (rdesc[i] == 0x29 && rdesc[i + 2] == 0x19) { 46 rdesc[i] = 0x19; 47 rdesc[i + 2] = 0x29; 48 swap(rdesc[i + 3], rdesc[i + 1]); 49 } 50 return rdesc; 51 } 52 53 static __u8 *va_logical_boundary_fixup(struct hid_device *hdev, __u8 *rdesc, 54 unsigned int *rsize) 55 { 56 /* 57 * Varmilo VA104M (with VID Cypress and device ID 07B1) incorrectly 58 * reports Logical Minimum of its Consumer Control device as 572 59 * (0x02 0x3c). Fix this by setting its Logical Minimum to zero. 60 */ 61 if (*rsize == 25 && 62 rdesc[0] == 0x05 && rdesc[1] == 0x0c && 63 rdesc[2] == 0x09 && rdesc[3] == 0x01 && 64 rdesc[6] == 0x19 && rdesc[7] == 0x00 && 65 rdesc[11] == 0x16 && rdesc[12] == 0x3c && rdesc[13] == 0x02) { 66 hid_info(hdev, 67 "fixing up varmilo VA104M consumer control report descriptor\n"); 68 rdesc[12] = 0x00; 69 rdesc[13] = 0x00; 70 } 71 return rdesc; 72 } 73 74 static const __u8 *cp_report_fixup(struct hid_device *hdev, __u8 *rdesc, 75 unsigned int *rsize) 76 { 77 const struct cp_device *cp_device = hid_get_drvdata(hdev); 78 unsigned long quirks = cp_device->quirks; 79 80 if (quirks & CP_RDESC_SWAPPED_MIN_MAX) 81 rdesc = cp_rdesc_fixup(hdev, rdesc, rsize); 82 if (quirks & VA_INVAL_LOGICAL_BOUNDARY) 83 rdesc = va_logical_boundary_fixup(hdev, rdesc, rsize); 84 85 return rdesc; 86 } 87 88 static int cp_input_mapped(struct hid_device *hdev, struct hid_input *hi, 89 struct hid_field *field, struct hid_usage *usage, 90 unsigned long **bit, int *max) 91 { 92 const struct cp_device *cp_device = hid_get_drvdata(hdev); 93 unsigned long quirks = cp_device->quirks; 94 95 if (!(quirks & CP_2WHEEL_MOUSE_HACK)) 96 return 0; 97 98 if (usage->type == EV_REL && usage->code == REL_WHEEL) 99 set_bit(REL_HWHEEL, *bit); 100 if (usage->hid == 0x00090005) 101 return -1; 102 103 return 0; 104 } 105 106 static int cp_event(struct hid_device *hdev, struct hid_field *field, 107 struct hid_usage *usage, __s32 value) 108 { 109 struct cp_device *cp_device = hid_get_drvdata(hdev); 110 111 if (!(hdev->claimed & HID_CLAIMED_INPUT) || !field->hidinput || 112 !usage->type || !(cp_device->quirks & CP_2WHEEL_MOUSE_HACK)) 113 return 0; 114 115 if (usage->hid == 0x00090005) { 116 if (value) 117 cp_device->quirks |= CP_2WHEEL_MOUSE_HACK_ON; 118 else 119 cp_device->quirks &= ~CP_2WHEEL_MOUSE_HACK_ON; 120 return 1; 121 } 122 123 if (usage->code == REL_WHEEL && (cp_device->quirks & CP_2WHEEL_MOUSE_HACK_ON)) { 124 struct input_dev *input = field->hidinput->input; 125 126 input_event(input, usage->type, REL_HWHEEL, value); 127 return 1; 128 } 129 130 return 0; 131 } 132 133 static int cp_probe(struct hid_device *hdev, const struct hid_device_id *id) 134 { 135 int ret; 136 struct cp_device *cp_device; 137 138 cp_device = devm_kzalloc(&hdev->dev, sizeof(*cp_device), GFP_KERNEL); 139 140 if (!cp_device) 141 return -ENOMEM; 142 143 cp_device->quirks = id->driver_data; 144 145 hid_set_drvdata(hdev, cp_device); 146 147 ret = hid_parse(hdev); 148 if (ret) { 149 hid_err(hdev, "parse failed\n"); 150 goto err_free; 151 } 152 153 ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT); 154 if (ret) { 155 hid_err(hdev, "hw start failed\n"); 156 goto err_free; 157 } 158 159 return 0; 160 err_free: 161 return ret; 162 } 163 164 static const struct hid_device_id cp_devices[] = { 165 { HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_BARCODE_1), 166 .driver_data = CP_RDESC_SWAPPED_MIN_MAX }, 167 { HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_BARCODE_2), 168 .driver_data = CP_RDESC_SWAPPED_MIN_MAX }, 169 { HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_BARCODE_3), 170 .driver_data = CP_RDESC_SWAPPED_MIN_MAX }, 171 { HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_BARCODE_4), 172 .driver_data = CP_RDESC_SWAPPED_MIN_MAX }, 173 { HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_MOUSE), 174 .driver_data = CP_2WHEEL_MOUSE_HACK }, 175 { HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_VARMILO_VA104M_07B1), 176 .driver_data = VA_INVAL_LOGICAL_BOUNDARY }, 177 { } 178 }; 179 MODULE_DEVICE_TABLE(hid, cp_devices); 180 181 static struct hid_driver cp_driver = { 182 .name = "cypress", 183 .id_table = cp_devices, 184 .report_fixup = cp_report_fixup, 185 .input_mapped = cp_input_mapped, 186 .event = cp_event, 187 .probe = cp_probe, 188 }; 189 module_hid_driver(cp_driver); 190 191 MODULE_DESCRIPTION("HID driver for some cypress \"special\" devices"); 192 MODULE_LICENSE("GPL"); 193