Branch data Line data Source code
1 : : // SPDX-License-Identifier: GPL-2.0-or-later
2 : : /*
3 : : * HID driver for some samsung "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 : : * Copyright (c) 2010 Don Prince <dhprince.devel@yahoo.co.uk>
11 : : *
12 : : * This driver supports several HID devices:
13 : : *
14 : : * [0419:0001] Samsung IrDA remote controller (reports as Cypress USB Mouse).
15 : : * various hid report fixups for different variants.
16 : : *
17 : : * [0419:0600] Creative Desktop Wireless 6000 keyboard/mouse combo
18 : : * several key mappings used from the consumer usage page
19 : : * deviate from the USB HUT 1.12 standard.
20 : : */
21 : :
22 : : /*
23 : : */
24 : :
25 : : #include <linux/device.h>
26 : : #include <linux/usb.h>
27 : : #include <linux/hid.h>
28 : : #include <linux/module.h>
29 : :
30 : : #include "hid-ids.h"
31 : :
32 : : /*
33 : : * There are several variants for 0419:0001:
34 : : *
35 : : * 1. 184 byte report descriptor
36 : : * Vendor specific report #4 has a size of 48 bit,
37 : : * and therefore is not accepted when inspecting the descriptors.
38 : : * As a workaround we reinterpret the report as:
39 : : * Variable type, count 6, size 8 bit, log. maximum 255
40 : : * The burden to reconstruct the data is moved into user space.
41 : : *
42 : : * 2. 203 byte report descriptor
43 : : * Report #4 has an array field with logical range 0..18 instead of 1..15.
44 : : *
45 : : * 3. 135 byte report descriptor
46 : : * Report #4 has an array field with logical range 0..17 instead of 1..14.
47 : : *
48 : : * 4. 171 byte report descriptor
49 : : * Report #3 has an array field with logical range 0..1 instead of 1..3.
50 : : */
51 : 0 : static inline void samsung_irda_dev_trace(struct hid_device *hdev,
52 : : unsigned int rsize)
53 : : {
54 : 0 : hid_info(hdev, "fixing up Samsung IrDA %d byte report descriptor\n",
55 : : rsize);
56 : 0 : }
57 : :
58 : : static __u8 *samsung_irda_report_fixup(struct hid_device *hdev, __u8 *rdesc,
59 : : unsigned int *rsize)
60 : : {
61 : : if (*rsize == 184 && rdesc[175] == 0x25 && rdesc[176] == 0x40 &&
62 : : rdesc[177] == 0x75 && rdesc[178] == 0x30 &&
63 : : rdesc[179] == 0x95 && rdesc[180] == 0x01 &&
64 : : rdesc[182] == 0x40) {
65 : : samsung_irda_dev_trace(hdev, 184);
66 : : rdesc[176] = 0xff;
67 : : rdesc[178] = 0x08;
68 : : rdesc[180] = 0x06;
69 : : rdesc[182] = 0x42;
70 : : } else
71 : : if (*rsize == 203 && rdesc[192] == 0x15 && rdesc[193] == 0x0 &&
72 : : rdesc[194] == 0x25 && rdesc[195] == 0x12) {
73 : : samsung_irda_dev_trace(hdev, 203);
74 : : rdesc[193] = 0x1;
75 : : rdesc[195] = 0xf;
76 : : } else
77 : : if (*rsize == 135 && rdesc[124] == 0x15 && rdesc[125] == 0x0 &&
78 : : rdesc[126] == 0x25 && rdesc[127] == 0x11) {
79 : : samsung_irda_dev_trace(hdev, 135);
80 : : rdesc[125] = 0x1;
81 : : rdesc[127] = 0xe;
82 : : } else
83 : : if (*rsize == 171 && rdesc[160] == 0x15 && rdesc[161] == 0x0 &&
84 : : rdesc[162] == 0x25 && rdesc[163] == 0x01) {
85 : : samsung_irda_dev_trace(hdev, 171);
86 : : rdesc[161] = 0x1;
87 : : rdesc[163] = 0x3;
88 : : }
89 : : return rdesc;
90 : : }
91 : :
92 : : #define samsung_kbd_mouse_map_key_clear(c) \
93 : : hid_map_usage_clear(hi, usage, bit, max, EV_KEY, (c))
94 : :
95 : : static int samsung_kbd_mouse_input_mapping(struct hid_device *hdev,
96 : : struct hid_input *hi, struct hid_field *field, struct hid_usage *usage,
97 : : unsigned long **bit, int *max)
98 : : {
99 : : struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
100 : : unsigned short ifnum = intf->cur_altsetting->desc.bInterfaceNumber;
101 : :
102 : : if (1 != ifnum || HID_UP_CONSUMER != (usage->hid & HID_USAGE_PAGE))
103 : : return 0;
104 : :
105 : : dbg_hid("samsung wireless keyboard/mouse input mapping event [0x%x]\n",
106 : : usage->hid & HID_USAGE);
107 : :
108 : : switch (usage->hid & HID_USAGE) {
109 : : /* report 2 */
110 : : case 0x183: samsung_kbd_mouse_map_key_clear(KEY_MEDIA); break;
111 : : case 0x195: samsung_kbd_mouse_map_key_clear(KEY_EMAIL); break;
112 : : case 0x196: samsung_kbd_mouse_map_key_clear(KEY_CALC); break;
113 : : case 0x197: samsung_kbd_mouse_map_key_clear(KEY_COMPUTER); break;
114 : : case 0x22b: samsung_kbd_mouse_map_key_clear(KEY_SEARCH); break;
115 : : case 0x22c: samsung_kbd_mouse_map_key_clear(KEY_WWW); break;
116 : : case 0x22d: samsung_kbd_mouse_map_key_clear(KEY_BACK); break;
117 : : case 0x22e: samsung_kbd_mouse_map_key_clear(KEY_FORWARD); break;
118 : : case 0x22f: samsung_kbd_mouse_map_key_clear(KEY_FAVORITES); break;
119 : : case 0x230: samsung_kbd_mouse_map_key_clear(KEY_REFRESH); break;
120 : : case 0x231: samsung_kbd_mouse_map_key_clear(KEY_STOP); break;
121 : : default:
122 : : return 0;
123 : : }
124 : :
125 : : return 1;
126 : : }
127 : :
128 : 0 : static __u8 *samsung_report_fixup(struct hid_device *hdev, __u8 *rdesc,
129 : : unsigned int *rsize)
130 : : {
131 [ # # ]: 0 : if (USB_DEVICE_ID_SAMSUNG_IR_REMOTE == hdev->product)
132 : 0 : rdesc = samsung_irda_report_fixup(hdev, rdesc, rsize);
133 : 0 : return rdesc;
134 : : }
135 : :
136 : 0 : static int samsung_input_mapping(struct hid_device *hdev, struct hid_input *hi,
137 : : struct hid_field *field, struct hid_usage *usage,
138 : : unsigned long **bit, int *max)
139 : : {
140 : 0 : int ret = 0;
141 : :
142 [ # # ]: 0 : if (USB_DEVICE_ID_SAMSUNG_WIRELESS_KBD_MOUSE == hdev->product)
143 : 0 : ret = samsung_kbd_mouse_input_mapping(hdev,
144 : : hi, field, usage, bit, max);
145 : :
146 : 0 : return ret;
147 : : }
148 : :
149 : 0 : static int samsung_probe(struct hid_device *hdev,
150 : : const struct hid_device_id *id)
151 : : {
152 : 0 : int ret;
153 : 0 : unsigned int cmask = HID_CONNECT_DEFAULT;
154 : :
155 : 0 : ret = hid_parse(hdev);
156 [ # # ]: 0 : if (ret) {
157 : 0 : hid_err(hdev, "parse failed\n");
158 : 0 : goto err_free;
159 : : }
160 : :
161 [ # # ]: 0 : if (USB_DEVICE_ID_SAMSUNG_IR_REMOTE == hdev->product) {
162 [ # # ]: 0 : if (hdev->rsize == 184) {
163 : : /* disable hidinput, force hiddev */
164 : 0 : cmask = (cmask & ~HID_CONNECT_HIDINPUT) |
165 : : HID_CONNECT_HIDDEV_FORCE;
166 : : }
167 : : }
168 : :
169 : 0 : ret = hid_hw_start(hdev, cmask);
170 [ # # ]: 0 : if (ret) {
171 : 0 : hid_err(hdev, "hw start failed\n");
172 : 0 : goto err_free;
173 : : }
174 : :
175 : : return 0;
176 : : err_free:
177 : : return ret;
178 : : }
179 : :
180 : : static const struct hid_device_id samsung_devices[] = {
181 : : { HID_USB_DEVICE(USB_VENDOR_ID_SAMSUNG, USB_DEVICE_ID_SAMSUNG_IR_REMOTE) },
182 : : { HID_USB_DEVICE(USB_VENDOR_ID_SAMSUNG, USB_DEVICE_ID_SAMSUNG_WIRELESS_KBD_MOUSE) },
183 : : { }
184 : : };
185 : : MODULE_DEVICE_TABLE(hid, samsung_devices);
186 : :
187 : : static struct hid_driver samsung_driver = {
188 : : .name = "samsung",
189 : : .id_table = samsung_devices,
190 : : .report_fixup = samsung_report_fixup,
191 : : .input_mapping = samsung_input_mapping,
192 : : .probe = samsung_probe,
193 : : };
194 : 13 : module_hid_driver(samsung_driver);
195 : :
196 : : MODULE_LICENSE("GPL");
|