10f2213208f8da51bcb665309e3468f000489c04fJiri Slaby/*
20f2213208f8da51bcb665309e3468f000489c04fJiri Slaby *  HID driver for some cypress "special" devices
30f2213208f8da51bcb665309e3468f000489c04fJiri Slaby *
40f2213208f8da51bcb665309e3468f000489c04fJiri Slaby *  Copyright (c) 1999 Andreas Gal
50f2213208f8da51bcb665309e3468f000489c04fJiri Slaby *  Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
60f2213208f8da51bcb665309e3468f000489c04fJiri Slaby *  Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
70f2213208f8da51bcb665309e3468f000489c04fJiri Slaby *  Copyright (c) 2006-2007 Jiri Kosina
80f2213208f8da51bcb665309e3468f000489c04fJiri Slaby *  Copyright (c) 2007 Paul Walmsley
90f2213208f8da51bcb665309e3468f000489c04fJiri Slaby *  Copyright (c) 2008 Jiri Slaby
100f2213208f8da51bcb665309e3468f000489c04fJiri Slaby */
110f2213208f8da51bcb665309e3468f000489c04fJiri Slaby
120f2213208f8da51bcb665309e3468f000489c04fJiri Slaby/*
130f2213208f8da51bcb665309e3468f000489c04fJiri Slaby * This program is free software; you can redistribute it and/or modify it
140f2213208f8da51bcb665309e3468f000489c04fJiri Slaby * under the terms of the GNU General Public License as published by the Free
150f2213208f8da51bcb665309e3468f000489c04fJiri Slaby * Software Foundation; either version 2 of the License, or (at your option)
160f2213208f8da51bcb665309e3468f000489c04fJiri Slaby * any later version.
170f2213208f8da51bcb665309e3468f000489c04fJiri Slaby */
180f2213208f8da51bcb665309e3468f000489c04fJiri Slaby
190f2213208f8da51bcb665309e3468f000489c04fJiri Slaby#include <linux/device.h>
200f2213208f8da51bcb665309e3468f000489c04fJiri Slaby#include <linux/hid.h>
210f2213208f8da51bcb665309e3468f000489c04fJiri Slaby#include <linux/input.h>
220f2213208f8da51bcb665309e3468f000489c04fJiri Slaby#include <linux/module.h>
230f2213208f8da51bcb665309e3468f000489c04fJiri Slaby
240f2213208f8da51bcb665309e3468f000489c04fJiri Slaby#include "hid-ids.h"
250f2213208f8da51bcb665309e3468f000489c04fJiri Slaby
260f2213208f8da51bcb665309e3468f000489c04fJiri Slaby#define CP_RDESC_SWAPPED_MIN_MAX	0x01
270f2213208f8da51bcb665309e3468f000489c04fJiri Slaby#define CP_2WHEEL_MOUSE_HACK		0x02
280f2213208f8da51bcb665309e3468f000489c04fJiri Slaby#define CP_2WHEEL_MOUSE_HACK_ON		0x04
290f2213208f8da51bcb665309e3468f000489c04fJiri Slaby
300f2213208f8da51bcb665309e3468f000489c04fJiri Slaby/*
310f2213208f8da51bcb665309e3468f000489c04fJiri Slaby * Some USB barcode readers from cypress have usage min and usage max in
320f2213208f8da51bcb665309e3468f000489c04fJiri Slaby * the wrong order
330f2213208f8da51bcb665309e3468f000489c04fJiri Slaby */
3473e4008ddddc84d5f2499c17012b340a0dae153eNikolai Kondrashovstatic __u8 *cp_report_fixup(struct hid_device *hdev, __u8 *rdesc,
3573e4008ddddc84d5f2499c17012b340a0dae153eNikolai Kondrashov		unsigned int *rsize)
360f2213208f8da51bcb665309e3468f000489c04fJiri Slaby{
370f2213208f8da51bcb665309e3468f000489c04fJiri Slaby	unsigned long quirks = (unsigned long)hid_get_drvdata(hdev);
380f2213208f8da51bcb665309e3468f000489c04fJiri Slaby	unsigned int i;
390f2213208f8da51bcb665309e3468f000489c04fJiri Slaby
400f2213208f8da51bcb665309e3468f000489c04fJiri Slaby	if (!(quirks & CP_RDESC_SWAPPED_MIN_MAX))
4173e4008ddddc84d5f2499c17012b340a0dae153eNikolai Kondrashov		return rdesc;
420f2213208f8da51bcb665309e3468f000489c04fJiri Slaby
4373e4008ddddc84d5f2499c17012b340a0dae153eNikolai Kondrashov	for (i = 0; i < *rsize - 4; i++)
440f2213208f8da51bcb665309e3468f000489c04fJiri Slaby		if (rdesc[i] == 0x29 && rdesc[i + 2] == 0x19) {
450f2213208f8da51bcb665309e3468f000489c04fJiri Slaby			__u8 tmp;
460f2213208f8da51bcb665309e3468f000489c04fJiri Slaby
470f2213208f8da51bcb665309e3468f000489c04fJiri Slaby			rdesc[i] = 0x19;
480f2213208f8da51bcb665309e3468f000489c04fJiri Slaby			rdesc[i + 2] = 0x29;
490f2213208f8da51bcb665309e3468f000489c04fJiri Slaby			tmp = rdesc[i + 3];
500f2213208f8da51bcb665309e3468f000489c04fJiri Slaby			rdesc[i + 3] = rdesc[i + 1];
510f2213208f8da51bcb665309e3468f000489c04fJiri Slaby			rdesc[i + 1] = tmp;
520f2213208f8da51bcb665309e3468f000489c04fJiri Slaby		}
5373e4008ddddc84d5f2499c17012b340a0dae153eNikolai Kondrashov	return rdesc;
540f2213208f8da51bcb665309e3468f000489c04fJiri Slaby}
550f2213208f8da51bcb665309e3468f000489c04fJiri Slaby
560f2213208f8da51bcb665309e3468f000489c04fJiri Slabystatic int cp_input_mapped(struct hid_device *hdev, struct hid_input *hi,
570f2213208f8da51bcb665309e3468f000489c04fJiri Slaby		struct hid_field *field, struct hid_usage *usage,
580f2213208f8da51bcb665309e3468f000489c04fJiri Slaby		unsigned long **bit, int *max)
590f2213208f8da51bcb665309e3468f000489c04fJiri Slaby{
600f2213208f8da51bcb665309e3468f000489c04fJiri Slaby	unsigned long quirks = (unsigned long)hid_get_drvdata(hdev);
610f2213208f8da51bcb665309e3468f000489c04fJiri Slaby
620f2213208f8da51bcb665309e3468f000489c04fJiri Slaby	if (!(quirks & CP_2WHEEL_MOUSE_HACK))
630f2213208f8da51bcb665309e3468f000489c04fJiri Slaby		return 0;
640f2213208f8da51bcb665309e3468f000489c04fJiri Slaby
650f2213208f8da51bcb665309e3468f000489c04fJiri Slaby	if (usage->type == EV_REL && usage->code == REL_WHEEL)
660f2213208f8da51bcb665309e3468f000489c04fJiri Slaby		set_bit(REL_HWHEEL, *bit);
670f2213208f8da51bcb665309e3468f000489c04fJiri Slaby	if (usage->hid == 0x00090005)
680f2213208f8da51bcb665309e3468f000489c04fJiri Slaby		return -1;
690f2213208f8da51bcb665309e3468f000489c04fJiri Slaby
700f2213208f8da51bcb665309e3468f000489c04fJiri Slaby	return 0;
710f2213208f8da51bcb665309e3468f000489c04fJiri Slaby}
720f2213208f8da51bcb665309e3468f000489c04fJiri Slaby
730f2213208f8da51bcb665309e3468f000489c04fJiri Slabystatic int cp_event(struct hid_device *hdev, struct hid_field *field,
740f2213208f8da51bcb665309e3468f000489c04fJiri Slaby		struct hid_usage *usage, __s32 value)
750f2213208f8da51bcb665309e3468f000489c04fJiri Slaby{
760f2213208f8da51bcb665309e3468f000489c04fJiri Slaby	unsigned long quirks = (unsigned long)hid_get_drvdata(hdev);
770f2213208f8da51bcb665309e3468f000489c04fJiri Slaby
780f2213208f8da51bcb665309e3468f000489c04fJiri Slaby	if (!(hdev->claimed & HID_CLAIMED_INPUT) || !field->hidinput ||
790f2213208f8da51bcb665309e3468f000489c04fJiri Slaby			!usage->type || !(quirks & CP_2WHEEL_MOUSE_HACK))
800f2213208f8da51bcb665309e3468f000489c04fJiri Slaby		return 0;
810f2213208f8da51bcb665309e3468f000489c04fJiri Slaby
820f2213208f8da51bcb665309e3468f000489c04fJiri Slaby	if (usage->hid == 0x00090005) {
830f2213208f8da51bcb665309e3468f000489c04fJiri Slaby		if (value)
840f2213208f8da51bcb665309e3468f000489c04fJiri Slaby			quirks |=  CP_2WHEEL_MOUSE_HACK_ON;
850f2213208f8da51bcb665309e3468f000489c04fJiri Slaby		else
860f2213208f8da51bcb665309e3468f000489c04fJiri Slaby			quirks &= ~CP_2WHEEL_MOUSE_HACK_ON;
870f2213208f8da51bcb665309e3468f000489c04fJiri Slaby		hid_set_drvdata(hdev, (void *)quirks);
880f2213208f8da51bcb665309e3468f000489c04fJiri Slaby		return 1;
890f2213208f8da51bcb665309e3468f000489c04fJiri Slaby	}
900f2213208f8da51bcb665309e3468f000489c04fJiri Slaby
910f2213208f8da51bcb665309e3468f000489c04fJiri Slaby	if (usage->code == REL_WHEEL && (quirks & CP_2WHEEL_MOUSE_HACK_ON)) {
920f2213208f8da51bcb665309e3468f000489c04fJiri Slaby		struct input_dev *input = field->hidinput->input;
930f2213208f8da51bcb665309e3468f000489c04fJiri Slaby
940f2213208f8da51bcb665309e3468f000489c04fJiri Slaby		input_event(input, usage->type, REL_HWHEEL, value);
950f2213208f8da51bcb665309e3468f000489c04fJiri Slaby		return 1;
960f2213208f8da51bcb665309e3468f000489c04fJiri Slaby	}
970f2213208f8da51bcb665309e3468f000489c04fJiri Slaby
980f2213208f8da51bcb665309e3468f000489c04fJiri Slaby	return 0;
990f2213208f8da51bcb665309e3468f000489c04fJiri Slaby}
1000f2213208f8da51bcb665309e3468f000489c04fJiri Slaby
1010f2213208f8da51bcb665309e3468f000489c04fJiri Slabystatic int cp_probe(struct hid_device *hdev, const struct hid_device_id *id)
1020f2213208f8da51bcb665309e3468f000489c04fJiri Slaby{
1030f2213208f8da51bcb665309e3468f000489c04fJiri Slaby	unsigned long quirks = id->driver_data;
1040f2213208f8da51bcb665309e3468f000489c04fJiri Slaby	int ret;
1050f2213208f8da51bcb665309e3468f000489c04fJiri Slaby
1060f2213208f8da51bcb665309e3468f000489c04fJiri Slaby	hid_set_drvdata(hdev, (void *)quirks);
1070f2213208f8da51bcb665309e3468f000489c04fJiri Slaby
1080f2213208f8da51bcb665309e3468f000489c04fJiri Slaby	ret = hid_parse(hdev);
1090f2213208f8da51bcb665309e3468f000489c04fJiri Slaby	if (ret) {
1104291ee305e9bb0699504a66f0e2b7aefcf0512a5Joe Perches		hid_err(hdev, "parse failed\n");
1110f2213208f8da51bcb665309e3468f000489c04fJiri Slaby		goto err_free;
1120f2213208f8da51bcb665309e3468f000489c04fJiri Slaby	}
1130f2213208f8da51bcb665309e3468f000489c04fJiri Slaby
11493c10132a7ac160df3175b53f7ee857625412165Jiri Slaby	ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
1150f2213208f8da51bcb665309e3468f000489c04fJiri Slaby	if (ret) {
1164291ee305e9bb0699504a66f0e2b7aefcf0512a5Joe Perches		hid_err(hdev, "hw start failed\n");
1170f2213208f8da51bcb665309e3468f000489c04fJiri Slaby		goto err_free;
1180f2213208f8da51bcb665309e3468f000489c04fJiri Slaby	}
1190f2213208f8da51bcb665309e3468f000489c04fJiri Slaby
1200f2213208f8da51bcb665309e3468f000489c04fJiri Slaby	return 0;
1210f2213208f8da51bcb665309e3468f000489c04fJiri Slabyerr_free:
1220f2213208f8da51bcb665309e3468f000489c04fJiri Slaby	return ret;
1230f2213208f8da51bcb665309e3468f000489c04fJiri Slaby}
1240f2213208f8da51bcb665309e3468f000489c04fJiri Slaby
1250f2213208f8da51bcb665309e3468f000489c04fJiri Slabystatic const struct hid_device_id cp_devices[] = {
1260f2213208f8da51bcb665309e3468f000489c04fJiri Slaby	{ HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_BARCODE_1),
1270f2213208f8da51bcb665309e3468f000489c04fJiri Slaby		.driver_data = CP_RDESC_SWAPPED_MIN_MAX },
1280f2213208f8da51bcb665309e3468f000489c04fJiri Slaby	{ HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_BARCODE_2),
1290f2213208f8da51bcb665309e3468f000489c04fJiri Slaby		.driver_data = CP_RDESC_SWAPPED_MIN_MAX },
130e8d0eab4d9eda9f5e97852f780f020bfb134f9f0Jiri Kosina	{ HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_BARCODE_3),
131e8d0eab4d9eda9f5e97852f780f020bfb134f9f0Jiri Kosina		.driver_data = CP_RDESC_SWAPPED_MIN_MAX },
1320f2213208f8da51bcb665309e3468f000489c04fJiri Slaby	{ HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_MOUSE),
1330f2213208f8da51bcb665309e3468f000489c04fJiri Slaby		.driver_data = CP_2WHEEL_MOUSE_HACK },
1340f2213208f8da51bcb665309e3468f000489c04fJiri Slaby	{ }
1350f2213208f8da51bcb665309e3468f000489c04fJiri Slaby};
1360f2213208f8da51bcb665309e3468f000489c04fJiri SlabyMODULE_DEVICE_TABLE(hid, cp_devices);
1370f2213208f8da51bcb665309e3468f000489c04fJiri Slaby
1380f2213208f8da51bcb665309e3468f000489c04fJiri Slabystatic struct hid_driver cp_driver = {
1390f2213208f8da51bcb665309e3468f000489c04fJiri Slaby	.name = "cypress",
1400f2213208f8da51bcb665309e3468f000489c04fJiri Slaby	.id_table = cp_devices,
1410f2213208f8da51bcb665309e3468f000489c04fJiri Slaby	.report_fixup = cp_report_fixup,
1420f2213208f8da51bcb665309e3468f000489c04fJiri Slaby	.input_mapped = cp_input_mapped,
1430f2213208f8da51bcb665309e3468f000489c04fJiri Slaby	.event = cp_event,
1440f2213208f8da51bcb665309e3468f000489c04fJiri Slaby	.probe = cp_probe,
1450f2213208f8da51bcb665309e3468f000489c04fJiri Slaby};
1460f2213208f8da51bcb665309e3468f000489c04fJiri Slaby
147a24f423bdf253ccee369adc6c5451b40a0716fbbPeter Huewestatic int __init cp_init(void)
1480f2213208f8da51bcb665309e3468f000489c04fJiri Slaby{
1490f2213208f8da51bcb665309e3468f000489c04fJiri Slaby	return hid_register_driver(&cp_driver);
1500f2213208f8da51bcb665309e3468f000489c04fJiri Slaby}
1510f2213208f8da51bcb665309e3468f000489c04fJiri Slaby
152a24f423bdf253ccee369adc6c5451b40a0716fbbPeter Huewestatic void __exit cp_exit(void)
1530f2213208f8da51bcb665309e3468f000489c04fJiri Slaby{
1540f2213208f8da51bcb665309e3468f000489c04fJiri Slaby	hid_unregister_driver(&cp_driver);
1550f2213208f8da51bcb665309e3468f000489c04fJiri Slaby}
1560f2213208f8da51bcb665309e3468f000489c04fJiri Slaby
1570f2213208f8da51bcb665309e3468f000489c04fJiri Slabymodule_init(cp_init);
1580f2213208f8da51bcb665309e3468f000489c04fJiri Slabymodule_exit(cp_exit);
1590f2213208f8da51bcb665309e3468f000489c04fJiri SlabyMODULE_LICENSE("GPL");
160