Commit | Line | Data |
---|---|---|
2874c5fd | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
fcfacfd3 JS |
2 | /* |
3 | * HID driver for some chicony "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) 2007 Paul Walmsley | |
10 | * Copyright (c) 2008 Jiri Slaby | |
11 | */ | |
12 | ||
13 | /* | |
fcfacfd3 JS |
14 | */ |
15 | ||
16 | #include <linux/device.h> | |
17 | #include <linux/input.h> | |
18 | #include <linux/hid.h> | |
19 | #include <linux/module.h> | |
9a1d78a3 | 20 | #include <linux/usb.h> |
fcfacfd3 JS |
21 | |
22 | #include "hid-ids.h" | |
23 | ||
859b510b JHP |
24 | #define CH_WIRELESS_CTL_REPORT_ID 0x11 |
25 | ||
26 | static int ch_report_wireless(struct hid_report *report, u8 *data, int size) | |
27 | { | |
28 | struct hid_device *hdev = report->device; | |
29 | struct input_dev *input; | |
30 | ||
31 | if (report->id != CH_WIRELESS_CTL_REPORT_ID || report->maxfield != 1) | |
32 | return 0; | |
33 | ||
34 | input = report->field[0]->hidinput->input; | |
35 | if (!input) { | |
36 | hid_warn(hdev, "can't find wireless radio control's input"); | |
37 | return 0; | |
38 | } | |
39 | ||
40 | input_report_key(input, KEY_RFKILL, 1); | |
41 | input_sync(input); | |
42 | input_report_key(input, KEY_RFKILL, 0); | |
43 | input_sync(input); | |
44 | ||
45 | return 1; | |
46 | } | |
47 | ||
48 | static int ch_raw_event(struct hid_device *hdev, | |
49 | struct hid_report *report, u8 *data, int size) | |
50 | { | |
51 | if (report->application == HID_GD_WIRELESS_RADIO_CTLS) | |
52 | return ch_report_wireless(report, data, size); | |
53 | ||
54 | return 0; | |
55 | } | |
56 | ||
fcfacfd3 JS |
57 | #define ch_map_key_clear(c) hid_map_usage_clear(hi, usage, bit, max, \ |
58 | EV_KEY, (c)) | |
59 | static int ch_input_mapping(struct hid_device *hdev, struct hid_input *hi, | |
60 | struct hid_field *field, struct hid_usage *usage, | |
61 | unsigned long **bit, int *max) | |
62 | { | |
63 | if ((usage->hid & HID_USAGE_PAGE) != HID_UP_MSVENDOR) | |
64 | return 0; | |
65 | ||
66 | set_bit(EV_REP, hi->input->evbit); | |
67 | switch (usage->hid & HID_USAGE) { | |
68 | case 0xff01: ch_map_key_clear(BTN_1); break; | |
69 | case 0xff02: ch_map_key_clear(BTN_2); break; | |
70 | case 0xff03: ch_map_key_clear(BTN_3); break; | |
71 | case 0xff04: ch_map_key_clear(BTN_4); break; | |
72 | case 0xff05: ch_map_key_clear(BTN_5); break; | |
73 | case 0xff06: ch_map_key_clear(BTN_6); break; | |
74 | case 0xff07: ch_map_key_clear(BTN_7); break; | |
75 | case 0xff08: ch_map_key_clear(BTN_8); break; | |
76 | case 0xff09: ch_map_key_clear(BTN_9); break; | |
77 | case 0xff0a: ch_map_key_clear(BTN_A); break; | |
78 | case 0xff0b: ch_map_key_clear(BTN_B); break; | |
3596bb92 KYL |
79 | case 0x00f1: ch_map_key_clear(KEY_WLAN); break; |
80 | case 0x00f2: ch_map_key_clear(KEY_BRIGHTNESSDOWN); break; | |
81 | case 0x00f3: ch_map_key_clear(KEY_BRIGHTNESSUP); break; | |
82 | case 0x00f4: ch_map_key_clear(KEY_DISPLAY_OFF); break; | |
6c30d5a5 KYL |
83 | case 0x00f7: ch_map_key_clear(KEY_CAMERA); break; |
84 | case 0x00f8: ch_map_key_clear(KEY_PROG1); break; | |
fcfacfd3 JS |
85 | default: |
86 | return 0; | |
87 | } | |
88 | return 1; | |
89 | } | |
90 | ||
9a1d78a3 НК |
91 | static __u8 *ch_switch12_report_fixup(struct hid_device *hdev, __u8 *rdesc, |
92 | unsigned int *rsize) | |
93 | { | |
94 | struct usb_interface *intf = to_usb_interface(hdev->dev.parent); | |
95 | ||
96 | if (intf->cur_altsetting->desc.bInterfaceNumber == 1) { | |
97 | /* Change usage maximum and logical maximum from 0x7fff to | |
98 | * 0x2fff, so they don't exceed HID_MAX_USAGES */ | |
99 | switch (hdev->product) { | |
100 | case USB_DEVICE_ID_CHICONY_ACER_SWITCH12: | |
101 | if (*rsize >= 128 && rdesc[64] == 0xff && rdesc[65] == 0x7f | |
102 | && rdesc[69] == 0xff && rdesc[70] == 0x7f) { | |
103 | hid_info(hdev, "Fixing up report descriptor\n"); | |
104 | rdesc[65] = rdesc[70] = 0x2f; | |
105 | } | |
106 | break; | |
107 | } | |
108 | ||
109 | } | |
110 | return rdesc; | |
111 | } | |
112 | ||
859b510b JHP |
113 | static int ch_probe(struct hid_device *hdev, const struct hid_device_id *id) |
114 | { | |
115 | int ret; | |
116 | ||
117 | hdev->quirks |= HID_QUIRK_INPUT_PER_APP; | |
118 | ret = hid_parse(hdev); | |
119 | if (ret) { | |
120 | hid_err(hdev, "Chicony hid parse failed: %d\n", ret); | |
121 | return ret; | |
122 | } | |
123 | ||
124 | ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT); | |
125 | if (ret) { | |
126 | hid_err(hdev, "Chicony hw start failed: %d\n", ret); | |
127 | return ret; | |
128 | } | |
129 | ||
130 | return 0; | |
131 | } | |
9a1d78a3 | 132 | |
fcfacfd3 JS |
133 | static const struct hid_device_id ch_devices[] = { |
134 | { HID_USB_DEVICE(USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_CHICONY_TACTICAL_PAD) }, | |
3596bb92 | 135 | { HID_USB_DEVICE(USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_CHICONY_WIRELESS2) }, |
859b510b | 136 | { HID_USB_DEVICE(USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_CHICONY_WIRELESS3) }, |
9a1d78a3 | 137 | { HID_USB_DEVICE(USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_CHICONY_ACER_SWITCH12) }, |
fcfacfd3 JS |
138 | { } |
139 | }; | |
140 | MODULE_DEVICE_TABLE(hid, ch_devices); | |
141 | ||
142 | static struct hid_driver ch_driver = { | |
143 | .name = "chicony", | |
144 | .id_table = ch_devices, | |
9a1d78a3 | 145 | .report_fixup = ch_switch12_report_fixup, |
fcfacfd3 | 146 | .input_mapping = ch_input_mapping, |
859b510b JHP |
147 | .probe = ch_probe, |
148 | .raw_event = ch_raw_event, | |
fcfacfd3 | 149 | }; |
f425458e | 150 | module_hid_driver(ch_driver); |
fcfacfd3 | 151 | |
fcfacfd3 | 152 | MODULE_LICENSE("GPL"); |