162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * HID driver for some cypress "special" devices 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (c) 1999 Andreas Gal 662306a36Sopenharmony_ci * Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz> 762306a36Sopenharmony_ci * Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc 862306a36Sopenharmony_ci * Copyright (c) 2006-2007 Jiri Kosina 962306a36Sopenharmony_ci * Copyright (c) 2008 Jiri Slaby 1062306a36Sopenharmony_ci */ 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci/* 1362306a36Sopenharmony_ci */ 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#include <linux/device.h> 1662306a36Sopenharmony_ci#include <linux/hid.h> 1762306a36Sopenharmony_ci#include <linux/input.h> 1862306a36Sopenharmony_ci#include <linux/module.h> 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#include "hid-ids.h" 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#define CP_RDESC_SWAPPED_MIN_MAX 0x01 2362306a36Sopenharmony_ci#define CP_2WHEEL_MOUSE_HACK 0x02 2462306a36Sopenharmony_ci#define CP_2WHEEL_MOUSE_HACK_ON 0x04 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#define VA_INVAL_LOGICAL_BOUNDARY 0x08 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci/* 2962306a36Sopenharmony_ci * Some USB barcode readers from cypress have usage min and usage max in 3062306a36Sopenharmony_ci * the wrong order 3162306a36Sopenharmony_ci */ 3262306a36Sopenharmony_cistatic __u8 *cp_rdesc_fixup(struct hid_device *hdev, __u8 *rdesc, 3362306a36Sopenharmony_ci unsigned int *rsize) 3462306a36Sopenharmony_ci{ 3562306a36Sopenharmony_ci unsigned int i; 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci if (*rsize < 4) 3862306a36Sopenharmony_ci return rdesc; 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci for (i = 0; i < *rsize - 4; i++) 4162306a36Sopenharmony_ci if (rdesc[i] == 0x29 && rdesc[i + 2] == 0x19) { 4262306a36Sopenharmony_ci rdesc[i] = 0x19; 4362306a36Sopenharmony_ci rdesc[i + 2] = 0x29; 4462306a36Sopenharmony_ci swap(rdesc[i + 3], rdesc[i + 1]); 4562306a36Sopenharmony_ci } 4662306a36Sopenharmony_ci return rdesc; 4762306a36Sopenharmony_ci} 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_cistatic __u8 *va_logical_boundary_fixup(struct hid_device *hdev, __u8 *rdesc, 5062306a36Sopenharmony_ci unsigned int *rsize) 5162306a36Sopenharmony_ci{ 5262306a36Sopenharmony_ci /* 5362306a36Sopenharmony_ci * Varmilo VA104M (with VID Cypress and device ID 07B1) incorrectly 5462306a36Sopenharmony_ci * reports Logical Minimum of its Consumer Control device as 572 5562306a36Sopenharmony_ci * (0x02 0x3c). Fix this by setting its Logical Minimum to zero. 5662306a36Sopenharmony_ci */ 5762306a36Sopenharmony_ci if (*rsize == 25 && 5862306a36Sopenharmony_ci rdesc[0] == 0x05 && rdesc[1] == 0x0c && 5962306a36Sopenharmony_ci rdesc[2] == 0x09 && rdesc[3] == 0x01 && 6062306a36Sopenharmony_ci rdesc[6] == 0x19 && rdesc[7] == 0x00 && 6162306a36Sopenharmony_ci rdesc[11] == 0x16 && rdesc[12] == 0x3c && rdesc[13] == 0x02) { 6262306a36Sopenharmony_ci hid_info(hdev, 6362306a36Sopenharmony_ci "fixing up varmilo VA104M consumer control report descriptor\n"); 6462306a36Sopenharmony_ci rdesc[12] = 0x00; 6562306a36Sopenharmony_ci rdesc[13] = 0x00; 6662306a36Sopenharmony_ci } 6762306a36Sopenharmony_ci return rdesc; 6862306a36Sopenharmony_ci} 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_cistatic __u8 *cp_report_fixup(struct hid_device *hdev, __u8 *rdesc, 7162306a36Sopenharmony_ci unsigned int *rsize) 7262306a36Sopenharmony_ci{ 7362306a36Sopenharmony_ci unsigned long quirks = (unsigned long)hid_get_drvdata(hdev); 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci if (quirks & CP_RDESC_SWAPPED_MIN_MAX) 7662306a36Sopenharmony_ci rdesc = cp_rdesc_fixup(hdev, rdesc, rsize); 7762306a36Sopenharmony_ci if (quirks & VA_INVAL_LOGICAL_BOUNDARY) 7862306a36Sopenharmony_ci rdesc = va_logical_boundary_fixup(hdev, rdesc, rsize); 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci return rdesc; 8162306a36Sopenharmony_ci} 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_cistatic int cp_input_mapped(struct hid_device *hdev, struct hid_input *hi, 8462306a36Sopenharmony_ci struct hid_field *field, struct hid_usage *usage, 8562306a36Sopenharmony_ci unsigned long **bit, int *max) 8662306a36Sopenharmony_ci{ 8762306a36Sopenharmony_ci unsigned long quirks = (unsigned long)hid_get_drvdata(hdev); 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci if (!(quirks & CP_2WHEEL_MOUSE_HACK)) 9062306a36Sopenharmony_ci return 0; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci if (usage->type == EV_REL && usage->code == REL_WHEEL) 9362306a36Sopenharmony_ci set_bit(REL_HWHEEL, *bit); 9462306a36Sopenharmony_ci if (usage->hid == 0x00090005) 9562306a36Sopenharmony_ci return -1; 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci return 0; 9862306a36Sopenharmony_ci} 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_cistatic int cp_event(struct hid_device *hdev, struct hid_field *field, 10162306a36Sopenharmony_ci struct hid_usage *usage, __s32 value) 10262306a36Sopenharmony_ci{ 10362306a36Sopenharmony_ci unsigned long quirks = (unsigned long)hid_get_drvdata(hdev); 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci if (!(hdev->claimed & HID_CLAIMED_INPUT) || !field->hidinput || 10662306a36Sopenharmony_ci !usage->type || !(quirks & CP_2WHEEL_MOUSE_HACK)) 10762306a36Sopenharmony_ci return 0; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci if (usage->hid == 0x00090005) { 11062306a36Sopenharmony_ci if (value) 11162306a36Sopenharmony_ci quirks |= CP_2WHEEL_MOUSE_HACK_ON; 11262306a36Sopenharmony_ci else 11362306a36Sopenharmony_ci quirks &= ~CP_2WHEEL_MOUSE_HACK_ON; 11462306a36Sopenharmony_ci hid_set_drvdata(hdev, (void *)quirks); 11562306a36Sopenharmony_ci return 1; 11662306a36Sopenharmony_ci } 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci if (usage->code == REL_WHEEL && (quirks & CP_2WHEEL_MOUSE_HACK_ON)) { 11962306a36Sopenharmony_ci struct input_dev *input = field->hidinput->input; 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci input_event(input, usage->type, REL_HWHEEL, value); 12262306a36Sopenharmony_ci return 1; 12362306a36Sopenharmony_ci } 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci return 0; 12662306a36Sopenharmony_ci} 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_cistatic int cp_probe(struct hid_device *hdev, const struct hid_device_id *id) 12962306a36Sopenharmony_ci{ 13062306a36Sopenharmony_ci unsigned long quirks = id->driver_data; 13162306a36Sopenharmony_ci int ret; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci hid_set_drvdata(hdev, (void *)quirks); 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci ret = hid_parse(hdev); 13662306a36Sopenharmony_ci if (ret) { 13762306a36Sopenharmony_ci hid_err(hdev, "parse failed\n"); 13862306a36Sopenharmony_ci goto err_free; 13962306a36Sopenharmony_ci } 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT); 14262306a36Sopenharmony_ci if (ret) { 14362306a36Sopenharmony_ci hid_err(hdev, "hw start failed\n"); 14462306a36Sopenharmony_ci goto err_free; 14562306a36Sopenharmony_ci } 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci return 0; 14862306a36Sopenharmony_cierr_free: 14962306a36Sopenharmony_ci return ret; 15062306a36Sopenharmony_ci} 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_cistatic const struct hid_device_id cp_devices[] = { 15362306a36Sopenharmony_ci { HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_BARCODE_1), 15462306a36Sopenharmony_ci .driver_data = CP_RDESC_SWAPPED_MIN_MAX }, 15562306a36Sopenharmony_ci { HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_BARCODE_2), 15662306a36Sopenharmony_ci .driver_data = CP_RDESC_SWAPPED_MIN_MAX }, 15762306a36Sopenharmony_ci { HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_BARCODE_3), 15862306a36Sopenharmony_ci .driver_data = CP_RDESC_SWAPPED_MIN_MAX }, 15962306a36Sopenharmony_ci { HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_BARCODE_4), 16062306a36Sopenharmony_ci .driver_data = CP_RDESC_SWAPPED_MIN_MAX }, 16162306a36Sopenharmony_ci { HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_MOUSE), 16262306a36Sopenharmony_ci .driver_data = CP_2WHEEL_MOUSE_HACK }, 16362306a36Sopenharmony_ci { HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_VARMILO_VA104M_07B1), 16462306a36Sopenharmony_ci .driver_data = VA_INVAL_LOGICAL_BOUNDARY }, 16562306a36Sopenharmony_ci { } 16662306a36Sopenharmony_ci}; 16762306a36Sopenharmony_ciMODULE_DEVICE_TABLE(hid, cp_devices); 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_cistatic struct hid_driver cp_driver = { 17062306a36Sopenharmony_ci .name = "cypress", 17162306a36Sopenharmony_ci .id_table = cp_devices, 17262306a36Sopenharmony_ci .report_fixup = cp_report_fixup, 17362306a36Sopenharmony_ci .input_mapped = cp_input_mapped, 17462306a36Sopenharmony_ci .event = cp_event, 17562306a36Sopenharmony_ci .probe = cp_probe, 17662306a36Sopenharmony_ci}; 17762306a36Sopenharmony_cimodule_hid_driver(cp_driver); 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 180