Commit | Line | Data |
---|---|---|
fc9c6924 | 1 | // SPDX-License-Identifier: GPL-2.0-only |
d23efc19 JS |
2 | /* |
3 | * HID driver for ELO usb touchscreen 4000/4500 | |
4 | * | |
5 | * Copyright (c) 2013 Jiri Slaby | |
6 | * | |
7 | * Data parsing taken from elousb driver by Vojtech Pavlik. | |
d23efc19 JS |
8 | */ |
9 | ||
10 | #include <linux/hid.h> | |
11 | #include <linux/input.h> | |
12 | #include <linux/module.h> | |
d8e5aec8 JS |
13 | #include <linux/usb.h> |
14 | #include <linux/workqueue.h> | |
d23efc19 JS |
15 | |
16 | #include "hid-ids.h" | |
17 | ||
d8e5aec8 JS |
18 | #define ELO_PERIODIC_READ_INTERVAL HZ |
19 | #define ELO_SMARTSET_CMD_TIMEOUT 2000 /* msec */ | |
20 | ||
21 | /* Elo SmartSet commands */ | |
22 | #define ELO_FLUSH_SMARTSET_RESPONSES 0x02 /* Flush all pending smartset responses */ | |
23 | #define ELO_SEND_SMARTSET_COMMAND 0x05 /* Send a smartset command */ | |
24 | #define ELO_GET_SMARTSET_RESPONSE 0x06 /* Get a smartset response */ | |
25 | #define ELO_DIAG 0x64 /* Diagnostics command */ | |
26 | #define ELO_SMARTSET_PACKET_SIZE 8 | |
27 | ||
28 | struct elo_priv { | |
29 | struct usb_device *usbdev; | |
30 | struct delayed_work work; | |
31 | unsigned char buffer[ELO_SMARTSET_PACKET_SIZE]; | |
32 | }; | |
33 | ||
34 | static struct workqueue_struct *wq; | |
35 | static bool use_fw_quirk = true; | |
36 | module_param(use_fw_quirk, bool, S_IRUGO); | |
37 | MODULE_PARM_DESC(use_fw_quirk, "Do periodic pokes for broken M firmwares (default = true)"); | |
38 | ||
9154301a | 39 | static int elo_input_configured(struct hid_device *hdev, |
d23efc19 JS |
40 | struct hid_input *hidinput) |
41 | { | |
42 | struct input_dev *input = hidinput->input; | |
43 | ||
9abd04af JK |
44 | /* |
45 | * ELO devices have one Button usage in GenDesk field, which makes | |
46 | * hid-input map it to BTN_LEFT; that confuses userspace, which then | |
47 | * considers the device to be a mouse/touchpad instead of touchscreen. | |
48 | */ | |
49 | clear_bit(BTN_LEFT, input->keybit); | |
d23efc19 JS |
50 | set_bit(BTN_TOUCH, input->keybit); |
51 | set_bit(ABS_PRESSURE, input->absbit); | |
52 | input_set_abs_params(input, ABS_PRESSURE, 0, 256, 0, 0); | |
9154301a DT |
53 | |
54 | return 0; | |
d23efc19 JS |
55 | } |
56 | ||
57 | static void elo_process_data(struct input_dev *input, const u8 *data, int size) | |
58 | { | |
59 | int press; | |
60 | ||
61 | input_report_abs(input, ABS_X, (data[3] << 8) | data[2]); | |
62 | input_report_abs(input, ABS_Y, (data[5] << 8) | data[4]); | |
63 | ||
64 | press = 0; | |
65 | if (data[1] & 0x80) | |
66 | press = (data[7] << 8) | data[6]; | |
67 | input_report_abs(input, ABS_PRESSURE, press); | |
68 | ||
69 | if (data[1] & 0x03) { | |
70 | input_report_key(input, BTN_TOUCH, 1); | |
71 | input_sync(input); | |
72 | } | |
73 | ||
74 | if (data[1] & 0x04) | |
75 | input_report_key(input, BTN_TOUCH, 0); | |
76 | ||
77 | input_sync(input); | |
78 | } | |
79 | ||
80 | static int elo_raw_event(struct hid_device *hdev, struct hid_report *report, | |
81 | u8 *data, int size) | |
82 | { | |
83 | struct hid_input *hidinput; | |
84 | ||
85 | if (!(hdev->claimed & HID_CLAIMED_INPUT) || list_empty(&hdev->inputs)) | |
86 | return 0; | |
87 | ||
88 | hidinput = list_first_entry(&hdev->inputs, struct hid_input, list); | |
89 | ||
90 | switch (report->id) { | |
91 | case 0: | |
92 | if (data[0] == 'T') { /* Mandatory ELO packet marker */ | |
93 | elo_process_data(hidinput->input, data, size); | |
94 | return 1; | |
95 | } | |
96 | break; | |
97 | default: /* unknown report */ | |
98 | /* Unknown report type; pass upstream */ | |
99 | hid_info(hdev, "unknown report type %d\n", report->id); | |
100 | break; | |
101 | } | |
102 | ||
103 | return 0; | |
104 | } | |
105 | ||
d8e5aec8 JS |
106 | static int elo_smartset_send_get(struct usb_device *dev, u8 command, |
107 | void *data) | |
108 | { | |
109 | unsigned int pipe; | |
110 | u8 dir; | |
111 | ||
112 | if (command == ELO_SEND_SMARTSET_COMMAND) { | |
113 | pipe = usb_sndctrlpipe(dev, 0); | |
114 | dir = USB_DIR_OUT; | |
115 | } else if (command == ELO_GET_SMARTSET_RESPONSE) { | |
116 | pipe = usb_rcvctrlpipe(dev, 0); | |
117 | dir = USB_DIR_IN; | |
118 | } else | |
119 | return -EINVAL; | |
120 | ||
121 | return usb_control_msg(dev, pipe, command, | |
122 | dir | USB_TYPE_VENDOR | USB_RECIP_DEVICE, | |
123 | 0, 0, data, ELO_SMARTSET_PACKET_SIZE, | |
124 | ELO_SMARTSET_CMD_TIMEOUT); | |
125 | } | |
126 | ||
127 | static int elo_flush_smartset_responses(struct usb_device *dev) | |
128 | { | |
129 | return usb_control_msg(dev, usb_sndctrlpipe(dev, 0), | |
130 | ELO_FLUSH_SMARTSET_RESPONSES, | |
131 | USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, | |
132 | 0, 0, NULL, 0, USB_CTRL_SET_TIMEOUT); | |
133 | } | |
134 | ||
135 | static void elo_work(struct work_struct *work) | |
136 | { | |
137 | struct elo_priv *priv = container_of(work, struct elo_priv, work.work); | |
138 | struct usb_device *dev = priv->usbdev; | |
139 | unsigned char *buffer = priv->buffer; | |
140 | int ret; | |
141 | ||
142 | ret = elo_flush_smartset_responses(dev); | |
143 | if (ret < 0) { | |
144 | dev_err(&dev->dev, "initial FLUSH_SMARTSET_RESPONSES failed, error %d\n", | |
145 | ret); | |
146 | goto fail; | |
147 | } | |
148 | ||
149 | /* send Diagnostics command */ | |
150 | *buffer = ELO_DIAG; | |
151 | ret = elo_smartset_send_get(dev, ELO_SEND_SMARTSET_COMMAND, buffer); | |
152 | if (ret < 0) { | |
153 | dev_err(&dev->dev, "send Diagnostics Command failed, error %d\n", | |
154 | ret); | |
155 | goto fail; | |
156 | } | |
157 | ||
158 | /* get the result */ | |
159 | ret = elo_smartset_send_get(dev, ELO_GET_SMARTSET_RESPONSE, buffer); | |
160 | if (ret < 0) { | |
161 | dev_err(&dev->dev, "get Diagnostics Command response failed, error %d\n", | |
162 | ret); | |
163 | goto fail; | |
164 | } | |
165 | ||
166 | /* read the ack */ | |
167 | if (*buffer != 'A') { | |
168 | ret = elo_smartset_send_get(dev, ELO_GET_SMARTSET_RESPONSE, | |
169 | buffer); | |
170 | if (ret < 0) { | |
171 | dev_err(&dev->dev, "get acknowledge response failed, error %d\n", | |
172 | ret); | |
173 | goto fail; | |
174 | } | |
175 | } | |
176 | ||
177 | fail: | |
178 | ret = elo_flush_smartset_responses(dev); | |
179 | if (ret < 0) | |
180 | dev_err(&dev->dev, "final FLUSH_SMARTSET_RESPONSES failed, error %d\n", | |
181 | ret); | |
182 | queue_delayed_work(wq, &priv->work, ELO_PERIODIC_READ_INTERVAL); | |
183 | } | |
184 | ||
185 | /* | |
186 | * Not all Elo devices need the periodic HID descriptor reads. | |
187 | * Only firmware version M needs this. | |
188 | */ | |
189 | static bool elo_broken_firmware(struct usb_device *dev) | |
190 | { | |
403cfb53 ON |
191 | struct usb_device *hub = dev->parent; |
192 | struct usb_device *child = NULL; | |
193 | u16 fw_lvl = le16_to_cpu(dev->descriptor.bcdDevice); | |
194 | u16 child_vid, child_pid; | |
195 | int i; | |
196 | ||
197 | if (!use_fw_quirk) | |
198 | return false; | |
199 | if (fw_lvl != 0x10d) | |
200 | return false; | |
201 | ||
202 | /* iterate sibling devices of the touch controller */ | |
203 | usb_hub_for_each_child(hub, i, child) { | |
204 | child_vid = le16_to_cpu(child->descriptor.idVendor); | |
205 | child_pid = le16_to_cpu(child->descriptor.idProduct); | |
206 | ||
207 | /* | |
208 | * If one of the devices below is present attached as a sibling of | |
209 | * the touch controller then this is a newer IBM 4820 monitor that | |
210 | * does not need the IBM-requested workaround if fw level is | |
211 | * 0x010d - aka 'M'. | |
212 | * No other HW can have this combination. | |
213 | */ | |
214 | if (child_vid==0x04b3) { | |
215 | switch (child_pid) { | |
216 | case 0x4676: /* 4820 21x Video */ | |
217 | case 0x4677: /* 4820 51x Video */ | |
218 | case 0x4678: /* 4820 2Lx Video */ | |
219 | case 0x4679: /* 4820 5Lx Video */ | |
220 | return false; | |
221 | } | |
222 | } | |
223 | } | |
224 | return true; | |
d8e5aec8 JS |
225 | } |
226 | ||
d23efc19 JS |
227 | static int elo_probe(struct hid_device *hdev, const struct hid_device_id *id) |
228 | { | |
d8e5aec8 | 229 | struct elo_priv *priv; |
d23efc19 JS |
230 | int ret; |
231 | ||
d8e5aec8 JS |
232 | priv = kzalloc(sizeof(*priv), GFP_KERNEL); |
233 | if (!priv) | |
234 | return -ENOMEM; | |
235 | ||
236 | INIT_DELAYED_WORK(&priv->work, elo_work); | |
237 | priv->usbdev = interface_to_usbdev(to_usb_interface(hdev->dev.parent)); | |
238 | ||
239 | hid_set_drvdata(hdev, priv); | |
240 | ||
d23efc19 JS |
241 | ret = hid_parse(hdev); |
242 | if (ret) { | |
243 | hid_err(hdev, "parse failed\n"); | |
244 | goto err_free; | |
245 | } | |
246 | ||
247 | ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT); | |
248 | if (ret) { | |
249 | hid_err(hdev, "hw start failed\n"); | |
250 | goto err_free; | |
251 | } | |
252 | ||
d8e5aec8 JS |
253 | if (elo_broken_firmware(priv->usbdev)) { |
254 | hid_info(hdev, "broken firmware found, installing workaround\n"); | |
255 | queue_delayed_work(wq, &priv->work, ELO_PERIODIC_READ_INTERVAL); | |
256 | } | |
257 | ||
d23efc19 JS |
258 | return 0; |
259 | err_free: | |
d8e5aec8 | 260 | kfree(priv); |
d23efc19 JS |
261 | return ret; |
262 | } | |
263 | ||
264 | static void elo_remove(struct hid_device *hdev) | |
265 | { | |
d8e5aec8 JS |
266 | struct elo_priv *priv = hid_get_drvdata(hdev); |
267 | ||
d23efc19 | 268 | hid_hw_stop(hdev); |
ed596a4a | 269 | cancel_delayed_work_sync(&priv->work); |
d8e5aec8 | 270 | kfree(priv); |
d23efc19 JS |
271 | } |
272 | ||
273 | static const struct hid_device_id elo_devices[] = { | |
274 | { HID_USB_DEVICE(USB_VENDOR_ID_ELO, 0x0009), }, | |
275 | { HID_USB_DEVICE(USB_VENDOR_ID_ELO, 0x0030), }, | |
276 | { } | |
277 | }; | |
278 | MODULE_DEVICE_TABLE(hid, elo_devices); | |
279 | ||
280 | static struct hid_driver elo_driver = { | |
281 | .name = "elo", | |
282 | .id_table = elo_devices, | |
283 | .probe = elo_probe, | |
284 | .remove = elo_remove, | |
285 | .raw_event = elo_raw_event, | |
286 | .input_configured = elo_input_configured, | |
287 | }; | |
288 | ||
289 | static int __init elo_driver_init(void) | |
290 | { | |
d8e5aec8 JS |
291 | int ret; |
292 | ||
293 | wq = create_singlethread_workqueue("elousb"); | |
294 | if (!wq) | |
295 | return -ENOMEM; | |
296 | ||
297 | ret = hid_register_driver(&elo_driver); | |
298 | if (ret) | |
299 | destroy_workqueue(wq); | |
300 | ||
301 | return ret; | |
d23efc19 JS |
302 | } |
303 | module_init(elo_driver_init); | |
304 | ||
305 | static void __exit elo_driver_exit(void) | |
306 | { | |
307 | hid_unregister_driver(&elo_driver); | |
d8e5aec8 | 308 | destroy_workqueue(wq); |
d23efc19 JS |
309 | } |
310 | module_exit(elo_driver_exit); | |
311 | ||
312 | MODULE_AUTHOR("Jiri Slaby <jslaby@suse.cz>"); | |
313 | MODULE_LICENSE("GPL"); |