18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * HID driver for some cypress "special" devices 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 1999 Andreas Gal 68c2ecf20Sopenharmony_ci * Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz> 78c2ecf20Sopenharmony_ci * Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc 88c2ecf20Sopenharmony_ci * Copyright (c) 2006-2007 Jiri Kosina 98c2ecf20Sopenharmony_ci * Copyright (c) 2008 Jiri Slaby 108c2ecf20Sopenharmony_ci */ 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci/* 138c2ecf20Sopenharmony_ci */ 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci#include <linux/device.h> 168c2ecf20Sopenharmony_ci#include <linux/hid.h> 178c2ecf20Sopenharmony_ci#include <linux/input.h> 188c2ecf20Sopenharmony_ci#include <linux/module.h> 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#include "hid-ids.h" 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#define CP_RDESC_SWAPPED_MIN_MAX 0x01 238c2ecf20Sopenharmony_ci#define CP_2WHEEL_MOUSE_HACK 0x02 248c2ecf20Sopenharmony_ci#define CP_2WHEEL_MOUSE_HACK_ON 0x04 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci#define VA_INVAL_LOGICAL_BOUNDARY 0x08 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci/* 298c2ecf20Sopenharmony_ci * Some USB barcode readers from cypress have usage min and usage max in 308c2ecf20Sopenharmony_ci * the wrong order 318c2ecf20Sopenharmony_ci */ 328c2ecf20Sopenharmony_cistatic __u8 *cp_rdesc_fixup(struct hid_device *hdev, __u8 *rdesc, 338c2ecf20Sopenharmony_ci unsigned int *rsize) 348c2ecf20Sopenharmony_ci{ 358c2ecf20Sopenharmony_ci unsigned int i; 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci if (*rsize < 4) 388c2ecf20Sopenharmony_ci return rdesc; 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci for (i = 0; i < *rsize - 4; i++) 418c2ecf20Sopenharmony_ci if (rdesc[i] == 0x29 && rdesc[i + 2] == 0x19) { 428c2ecf20Sopenharmony_ci rdesc[i] = 0x19; 438c2ecf20Sopenharmony_ci rdesc[i + 2] = 0x29; 448c2ecf20Sopenharmony_ci swap(rdesc[i + 3], rdesc[i + 1]); 458c2ecf20Sopenharmony_ci } 468c2ecf20Sopenharmony_ci return rdesc; 478c2ecf20Sopenharmony_ci} 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_cistatic __u8 *va_logical_boundary_fixup(struct hid_device *hdev, __u8 *rdesc, 508c2ecf20Sopenharmony_ci unsigned int *rsize) 518c2ecf20Sopenharmony_ci{ 528c2ecf20Sopenharmony_ci /* 538c2ecf20Sopenharmony_ci * Varmilo VA104M (with VID Cypress and device ID 07B1) incorrectly 548c2ecf20Sopenharmony_ci * reports Logical Minimum of its Consumer Control device as 572 558c2ecf20Sopenharmony_ci * (0x02 0x3c). Fix this by setting its Logical Minimum to zero. 568c2ecf20Sopenharmony_ci */ 578c2ecf20Sopenharmony_ci if (*rsize == 25 && 588c2ecf20Sopenharmony_ci rdesc[0] == 0x05 && rdesc[1] == 0x0c && 598c2ecf20Sopenharmony_ci rdesc[2] == 0x09 && rdesc[3] == 0x01 && 608c2ecf20Sopenharmony_ci rdesc[6] == 0x19 && rdesc[7] == 0x00 && 618c2ecf20Sopenharmony_ci rdesc[11] == 0x16 && rdesc[12] == 0x3c && rdesc[13] == 0x02) { 628c2ecf20Sopenharmony_ci hid_info(hdev, 638c2ecf20Sopenharmony_ci "fixing up varmilo VA104M consumer control report descriptor\n"); 648c2ecf20Sopenharmony_ci rdesc[12] = 0x00; 658c2ecf20Sopenharmony_ci rdesc[13] = 0x00; 668c2ecf20Sopenharmony_ci } 678c2ecf20Sopenharmony_ci return rdesc; 688c2ecf20Sopenharmony_ci} 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_cistatic __u8 *cp_report_fixup(struct hid_device *hdev, __u8 *rdesc, 718c2ecf20Sopenharmony_ci unsigned int *rsize) 728c2ecf20Sopenharmony_ci{ 738c2ecf20Sopenharmony_ci unsigned long quirks = (unsigned long)hid_get_drvdata(hdev); 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci if (quirks & CP_RDESC_SWAPPED_MIN_MAX) 768c2ecf20Sopenharmony_ci rdesc = cp_rdesc_fixup(hdev, rdesc, rsize); 778c2ecf20Sopenharmony_ci if (quirks & VA_INVAL_LOGICAL_BOUNDARY) 788c2ecf20Sopenharmony_ci rdesc = va_logical_boundary_fixup(hdev, rdesc, rsize); 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci return rdesc; 818c2ecf20Sopenharmony_ci} 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_cistatic int cp_input_mapped(struct hid_device *hdev, struct hid_input *hi, 848c2ecf20Sopenharmony_ci struct hid_field *field, struct hid_usage *usage, 858c2ecf20Sopenharmony_ci unsigned long **bit, int *max) 868c2ecf20Sopenharmony_ci{ 878c2ecf20Sopenharmony_ci unsigned long quirks = (unsigned long)hid_get_drvdata(hdev); 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci if (!(quirks & CP_2WHEEL_MOUSE_HACK)) 908c2ecf20Sopenharmony_ci return 0; 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci if (usage->type == EV_REL && usage->code == REL_WHEEL) 938c2ecf20Sopenharmony_ci set_bit(REL_HWHEEL, *bit); 948c2ecf20Sopenharmony_ci if (usage->hid == 0x00090005) 958c2ecf20Sopenharmony_ci return -1; 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci return 0; 988c2ecf20Sopenharmony_ci} 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_cistatic int cp_event(struct hid_device *hdev, struct hid_field *field, 1018c2ecf20Sopenharmony_ci struct hid_usage *usage, __s32 value) 1028c2ecf20Sopenharmony_ci{ 1038c2ecf20Sopenharmony_ci unsigned long quirks = (unsigned long)hid_get_drvdata(hdev); 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci if (!(hdev->claimed & HID_CLAIMED_INPUT) || !field->hidinput || 1068c2ecf20Sopenharmony_ci !usage->type || !(quirks & CP_2WHEEL_MOUSE_HACK)) 1078c2ecf20Sopenharmony_ci return 0; 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci if (usage->hid == 0x00090005) { 1108c2ecf20Sopenharmony_ci if (value) 1118c2ecf20Sopenharmony_ci quirks |= CP_2WHEEL_MOUSE_HACK_ON; 1128c2ecf20Sopenharmony_ci else 1138c2ecf20Sopenharmony_ci quirks &= ~CP_2WHEEL_MOUSE_HACK_ON; 1148c2ecf20Sopenharmony_ci hid_set_drvdata(hdev, (void *)quirks); 1158c2ecf20Sopenharmony_ci return 1; 1168c2ecf20Sopenharmony_ci } 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci if (usage->code == REL_WHEEL && (quirks & CP_2WHEEL_MOUSE_HACK_ON)) { 1198c2ecf20Sopenharmony_ci struct input_dev *input = field->hidinput->input; 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci input_event(input, usage->type, REL_HWHEEL, value); 1228c2ecf20Sopenharmony_ci return 1; 1238c2ecf20Sopenharmony_ci } 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci return 0; 1268c2ecf20Sopenharmony_ci} 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_cistatic int cp_probe(struct hid_device *hdev, const struct hid_device_id *id) 1298c2ecf20Sopenharmony_ci{ 1308c2ecf20Sopenharmony_ci unsigned long quirks = id->driver_data; 1318c2ecf20Sopenharmony_ci int ret; 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci hid_set_drvdata(hdev, (void *)quirks); 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci ret = hid_parse(hdev); 1368c2ecf20Sopenharmony_ci if (ret) { 1378c2ecf20Sopenharmony_ci hid_err(hdev, "parse failed\n"); 1388c2ecf20Sopenharmony_ci goto err_free; 1398c2ecf20Sopenharmony_ci } 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT); 1428c2ecf20Sopenharmony_ci if (ret) { 1438c2ecf20Sopenharmony_ci hid_err(hdev, "hw start failed\n"); 1448c2ecf20Sopenharmony_ci goto err_free; 1458c2ecf20Sopenharmony_ci } 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci return 0; 1488c2ecf20Sopenharmony_cierr_free: 1498c2ecf20Sopenharmony_ci return ret; 1508c2ecf20Sopenharmony_ci} 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_cistatic const struct hid_device_id cp_devices[] = { 1538c2ecf20Sopenharmony_ci { HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_BARCODE_1), 1548c2ecf20Sopenharmony_ci .driver_data = CP_RDESC_SWAPPED_MIN_MAX }, 1558c2ecf20Sopenharmony_ci { HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_BARCODE_2), 1568c2ecf20Sopenharmony_ci .driver_data = CP_RDESC_SWAPPED_MIN_MAX }, 1578c2ecf20Sopenharmony_ci { HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_BARCODE_3), 1588c2ecf20Sopenharmony_ci .driver_data = CP_RDESC_SWAPPED_MIN_MAX }, 1598c2ecf20Sopenharmony_ci { HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_BARCODE_4), 1608c2ecf20Sopenharmony_ci .driver_data = CP_RDESC_SWAPPED_MIN_MAX }, 1618c2ecf20Sopenharmony_ci { HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_MOUSE), 1628c2ecf20Sopenharmony_ci .driver_data = CP_2WHEEL_MOUSE_HACK }, 1638c2ecf20Sopenharmony_ci { HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_VARMILO_VA104M_07B1), 1648c2ecf20Sopenharmony_ci .driver_data = VA_INVAL_LOGICAL_BOUNDARY }, 1658c2ecf20Sopenharmony_ci { } 1668c2ecf20Sopenharmony_ci}; 1678c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(hid, cp_devices); 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_cistatic struct hid_driver cp_driver = { 1708c2ecf20Sopenharmony_ci .name = "cypress", 1718c2ecf20Sopenharmony_ci .id_table = cp_devices, 1728c2ecf20Sopenharmony_ci .report_fixup = cp_report_fixup, 1738c2ecf20Sopenharmony_ci .input_mapped = cp_input_mapped, 1748c2ecf20Sopenharmony_ci .event = cp_event, 1758c2ecf20Sopenharmony_ci .probe = cp_probe, 1768c2ecf20Sopenharmony_ci}; 1778c2ecf20Sopenharmony_cimodule_hid_driver(cp_driver); 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 180