source: mainline/uspace/srv/hid/isdv4_tablet/isdv4.c@ 3be9d10

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 3be9d10 was a35b458, checked in by Jiří Zárevúcky <zarevucky.jiri@…>, 7 years ago

style: Remove trailing whitespace on _all_ lines, including empty ones, for particular file types.

Command used: tools/srepl '\s\+$' '' -- *.c *.h *.py *.sh *.s *.S *.ag

Currently, whitespace on empty lines is very inconsistent.
There are two basic choices: Either remove the whitespace, or keep empty lines
indented to the level of surrounding code. The former is AFAICT more common,
and also much easier to do automatically.

Alternatively, we could write script for automatic indentation, and use that
instead. However, if such a script exists, it's possible to use the indented
style locally, by having the editor apply relevant conversions on load/save,
without affecting remote repository. IMO, it makes more sense to adopt
the simpler rule.

  • Property mode set to 100644
File size: 11.3 KB
Line 
1/*
2 * Copyright (c) 2012 Martin Sucha
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29#include <async.h>
30#include <errno.h>
31#include <io/chardev.h>
32#include <mem.h>
33#include <stdbool.h>
34#include <stdint.h>
35#include <stdlib.h>
36
37#include "isdv4.h"
38
39#define BUF_SIZE 64
40
41#define START_OF_PACKET 128
42#define CONTROL_PACKET 64
43#define TOUCH_EVENT 16
44#define FINGER1 1
45#define FINGER2 2
46#define TIP 1
47#define BUTTON1 2
48#define BUTTON2 4
49#define PROXIMITY 32
50
51#define CMD_START '1'
52#define CMD_STOP '0'
53#define CMD_QUERY_STYLUS '*'
54#define CMD_QUERY_TOUCH '%'
55
56/* packet_consumer_fn(uint8_t *packet, size_t size, isdv4_state_t *state,
57 void *data)
58 return true if reading of packets should continue */
59typedef bool (*packet_consumer_fn)(uint8_t *, size_t, isdv4_state_t *);
60
61static void isdv4_event_init(isdv4_event_t *event)
62{
63 memset(event, 0, sizeof(isdv4_event_t));
64}
65
66/**
67 * Parse event packet and emit events
68 * @return true if reading of packets should continue
69 */
70static bool parse_event(uint8_t *packet, size_t size, isdv4_state_t *state)
71{
72 if (size < 1)
73 return false;
74
75 bool control_packet = ((packet[0] & CONTROL_PACKET) > 0);
76 if (control_packet)
77 return true;
78
79 /* This is an event initiated by the device */
80 isdv4_event_t event;
81 isdv4_event_init(&event);
82
83 if (packet[0] & TOUCH_EVENT) {
84 if (size != 5)
85 return true;
86
87 /* This is a touch event */
88 bool finger1 = (packet[0] & FINGER1) > 0;
89 event.x = ((packet[1] & 127) << 7) | (packet[2] & 127);
90 event.y = ((packet[3] & 127) << 7) | (packet[4] & 127);
91 event.source = TOUCH;
92
93 if (!state->stylus_in_proximity) {
94 if (!finger1 && state->finger1_pressed) {
95 state->finger1_pressed = false;
96
97 event.type = RELEASE;
98 event.button = 1;
99 state->emit_event_fn(&event);
100 }
101 else if (finger1 && !state->finger1_pressed) {
102 state->finger1_pressed = true;
103
104 event.type = PRESS;
105 event.button = 1;
106 state->emit_event_fn(&event);
107 }
108 else {
109 event.type = MOVE;
110 event.button = 1;
111 state->emit_event_fn(&event);
112 }
113 }
114 }
115 else {
116 if (size != 9)
117 return true;
118
119 /* This is a stylus event */
120 bool tip = packet[0] & TIP;
121 bool button1 = packet[0] & BUTTON1;
122 bool button2 = packet[0] & BUTTON2;
123 bool proximity = packet[0] & PROXIMITY;
124 event.x = ((packet[1] & 127) << 7) | (packet[2] & 124) | ((packet[6] >> 5) & 3);
125 event.y = ((packet[3] & 127) << 7) | (packet[4] & 124) | ((packet[6] >> 3) & 3);
126 event.pressure = (packet[5] & 127) | ((packet[6] & 7) << 7);
127
128 if (proximity && !state->stylus_in_proximity) {
129 /* Stylus came into proximity */
130 state->stylus_in_proximity = true;
131 state->stylus_is_eraser = !tip && button2;
132 event.source = (state->stylus_is_eraser ? STYLUS_ERASER : STYLUS_TIP);
133 event.type = PROXIMITY_IN;
134 state->emit_event_fn(&event);
135 }
136 else if (!proximity && state->stylus_in_proximity) {
137 /* Stylus came out of proximity */
138 state->stylus_in_proximity = false;
139 event.source = (state->stylus_is_eraser ? STYLUS_ERASER : STYLUS_TIP);
140 event.type = PROXIMITY_OUT;
141 state->emit_event_fn(&event);
142 }
143 else {
144 /* Proximity state didn't change, but we need to check if it is still eraser */
145 if (state->stylus_is_eraser && !button2) {
146 event.type = PROXIMITY_OUT;
147 event.source = STYLUS_ERASER;
148 state->emit_event_fn(&event);
149 event.type = PROXIMITY_IN;
150 event.source = STYLUS_TIP;
151 state->emit_event_fn(&event);
152 state->stylus_is_eraser = false;
153 }
154 else if (!state->stylus_is_eraser && !tip && button2) {
155 event.type = PROXIMITY_OUT;
156 event.source = STYLUS_TIP;
157 state->emit_event_fn(&event);
158 event.type = PROXIMITY_IN;
159 event.source = STYLUS_ERASER;
160 state->emit_event_fn(&event);
161 state->stylus_is_eraser = true;
162 }
163 }
164
165 if (!state->stylus_is_eraser) {
166 if (tip && !state->tip_pressed) {
167 state->tip_pressed = true;
168 event.type = PRESS;
169 event.source = STYLUS_TIP;
170 event.button = 1;
171 state->emit_event_fn(&event);
172 }
173 else if (!tip && state->tip_pressed) {
174 state->tip_pressed = false;
175 event.type = RELEASE;
176 event.source = STYLUS_TIP;
177 event.button = 1;
178 state->emit_event_fn(&event);
179 }
180 if (button1 && !state->button1_pressed) {
181 state->button1_pressed = true;
182 event.type = PRESS;
183 event.source = STYLUS_TIP;
184 event.button = 2;
185 state->emit_event_fn(&event);
186 }
187 else if (!button1 && state->button1_pressed) {
188 state->button1_pressed = false;
189 event.type = RELEASE;
190 event.source = STYLUS_TIP;
191 event.button = 2;
192 state->emit_event_fn(&event);
193 }
194 if (button2 && !state->button2_pressed) {
195 state->button2_pressed = true;
196 event.type = PRESS;
197 event.source = STYLUS_TIP;
198 event.button = 3;
199 state->emit_event_fn(&event);
200 }
201 else if (!button2 && state->button2_pressed) {
202 state->button2_pressed = false;
203 event.type = RELEASE;
204 event.source = STYLUS_TIP;
205 event.button = 3;
206 state->emit_event_fn(&event);
207 }
208 event.type = MOVE;
209 event.source = STYLUS_TIP;
210 event.button = 0;
211 state->emit_event_fn(&event);
212 }
213 else {
214 if (tip && !state->tip_pressed) {
215 state->tip_pressed = true;
216 event.type = PRESS;
217 event.source = STYLUS_ERASER;
218 event.button = 1;
219 state->emit_event_fn(&event);
220 }
221 else if (!tip && state->tip_pressed) {
222 state->tip_pressed = false;
223 event.type = RELEASE;
224 event.source = STYLUS_ERASER;
225 event.button = 1;
226 state->emit_event_fn(&event);
227 }
228 event.type = MOVE;
229 event.source = STYLUS_ERASER;
230 event.button = 0;
231 state->emit_event_fn(&event);
232 }
233 }
234
235 return true;
236}
237
238static bool parse_response_stylus(uint8_t *packet, size_t size,
239 isdv4_state_t *state)
240{
241 if (size < 1)
242 return false;
243
244 bool control_packet = ((packet[0] & CONTROL_PACKET) > 0);
245 if (!control_packet)
246 return true;
247
248 if (size != 11)
249 return false;
250
251 state->stylus_max_x = ((packet[1] & 127) << 7) | (packet[2] & 124) |
252 ((packet[6] >> 5) & 3);
253 state->stylus_max_y = ((packet[3] & 127) << 7) | (packet[4] & 124) |
254 ((packet[6] >> 3) & 3);
255 state->stylus_max_pressure = (packet[5] & 63) | ((packet[6] & 7) << 7);
256 state->stylus_max_xtilt = packet[8] & 127;
257 state->stylus_max_ytilt = packet[7] & 127;
258 state->stylus_tilt_supported = (state->stylus_max_xtilt &&
259 state->stylus_max_ytilt);
260
261 return false;
262}
263
264static bool parse_response_touch(uint8_t *packet, size_t size,
265 isdv4_state_t *state)
266{
267 if (size < 1)
268 return false;
269
270 bool control_packet = ((packet[0] & CONTROL_PACKET) > 0);
271 if (!control_packet)
272 return true;
273
274 if (size != 11)
275 return false;
276
277 state->touch_type = (packet[0] & 63);
278
279 unsigned int touch_resolution = packet[1] & 127;
280 state->touch_max_x = ((packet[2] >> 5) & 3) | ((packet[3] & 127) << 7) |
281 (packet[4] & 124);
282 state->touch_max_y = ((packet[2] >> 3) & 3) | ((packet[5] & 127) << 7) |
283 (packet[6] & 124);
284
285 if (touch_resolution == 0)
286 touch_resolution = 10;
287
288 if (state->touch_max_x == 0 || state->touch_max_y == 0) {
289 state->touch_max_x = (1 << touch_resolution);
290 state->touch_max_y = (1 << touch_resolution);
291 }
292
293 return false;
294}
295
296static errno_t read_packets(isdv4_state_t *state, packet_consumer_fn consumer)
297{
298 bool reading = true;
299 while (reading) {
300 size_t nread;
301 errno_t rc;
302
303 rc = chardev_read(state->chardev, state->buf + state->buf_end,
304 state->buf_size - state->buf_end, &nread);
305 if (rc != EOK && nread == 0)
306 return EIO;
307 state->buf_end += nread;
308
309 size_t i = 0;
310
311 /* Skip data until a start of packet is found */
312 while (i < state->buf_end && (state->buf[i] & START_OF_PACKET) == 0) i++;
313
314 size_t start = i;
315 size_t end = i;
316 size_t processed_end = i;
317
318 /* Process packets one by one */
319 while (reading && i < state->buf_end) {
320 /* Determine the packet length */
321 size_t packet_remaining;
322 if (state->buf[i] & CONTROL_PACKET) {
323 packet_remaining = 11;
324 }
325 else if (state->buf[i] & TOUCH_EVENT) {
326 packet_remaining = 5;
327 }
328 else {
329 packet_remaining = 9;
330 }
331
332 /* Find the end of the packet */
333 i++; /* We need to skip the first byte with START_OF_PACKET set */
334 packet_remaining--;
335 while (packet_remaining > 0 && i < state->buf_end &&
336 (state->buf[i] & START_OF_PACKET) == 0) {
337 i++;
338 packet_remaining--;
339 }
340 end = i;
341
342 /* If we have whole packet, process it */
343 if (end > start && packet_remaining == 0) {
344 reading = consumer(state->buf + start, end - start, state);
345 start = end;
346 processed_end = end;
347 }
348 }
349
350 if (processed_end == 0 && state->buf_end == state->buf_size) {
351 /* Packet too large, throw it away */
352 state->buf_end = 0;
353 }
354
355 /* Shift the buffer contents to the left */
356 size_t unprocessed_len = state->buf_end - processed_end;
357 memcpy(state->buf, state->buf + processed_end, unprocessed_len);
358 state->buf_end = unprocessed_len;
359 }
360 return EOK;
361}
362
363static bool write_command(chardev_t *chardev, uint8_t command)
364{
365 errno_t rc;
366 size_t nwr;
367
368 rc = chardev_write(chardev, &command, 1, &nwr);
369 return rc == EOK;
370}
371
372errno_t isdv4_init(isdv4_state_t *state, async_sess_t *sess,
373 isdv4_event_fn event_fn)
374{
375 chardev_t *chardev;
376 errno_t rc;
377
378 rc = chardev_open(sess, &chardev);
379 if (rc != EOK)
380 return rc;
381
382 memset(state, 0, sizeof(isdv4_state_t));
383
384 state->sess = sess;
385 state->chardev = chardev;
386
387 state->buf = malloc(BUF_SIZE);
388 if (state->buf == NULL) {
389 chardev_close(chardev);
390 return ENOMEM;
391 }
392
393 state->buf_size = BUF_SIZE;
394 state->emit_event_fn = event_fn;
395 return EOK;
396}
397
398errno_t isdv4_init_tablet(isdv4_state_t *state)
399{
400 if (!write_command(state->chardev, CMD_STOP))
401 return EIO;
402
403 async_usleep(250000); /* 250 ms */
404
405 // FIXME: Read all possible garbage before sending commands
406 if (!write_command(state->chardev, CMD_QUERY_STYLUS))
407 return EIO;
408
409 errno_t rc = read_packets(state, parse_response_stylus);
410 if (rc != EOK)
411 return rc;
412
413 if (!write_command(state->chardev, CMD_QUERY_TOUCH))
414 return EIO;
415
416 rc = read_packets(state, parse_response_touch);
417 if (rc != EOK)
418 return rc;
419
420 if (!write_command(state->chardev, CMD_START))
421 return EIO;
422
423 return EOK;
424}
425
426errno_t isdv4_read_events(isdv4_state_t *state)
427{
428 return read_packets(state, parse_event);
429}
430
431void isdv4_fini(isdv4_state_t *state)
432{
433 free(state->buf);
434}
Note: See TracBrowser for help on using the repository browser.