source: mainline/uspace/app/wacomdump/wacomdump.c@ 6e09bee

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 6e09bee was 6e09bee, checked in by Martin Sucha <sucha14@…>, 13 years ago

Interpret wacom events in more depth

  • Property mode set to 100644
File size: 15.4 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 <device/char_dev.h>
30#include <errno.h>
31#include <ipc/serial_ctl.h>
32#include <loc.h>
33#include <stdio.h>
34
35#define BUF_SIZE 64
36
37#define START_OF_PACKET 128
38#define CONTROL_PACKET 64
39#define FINGER1 1
40#define FINGER2 2
41#define TIP 1
42#define BUTTON1 2
43#define BUTTON2 4
44#define PROXIMITY 32
45
46#define CMD_START '1'
47#define CMD_STOP '0'
48#define CMD_QUERY_STYLUS '*'
49#define CMD_QUERY_TOUCH '%'
50
51typedef struct isdv4_event isdv4_event_t;
52
53typedef void (*isdv4_event_fn)(const isdv4_event_t *);
54
55typedef struct {
56 /* Stylus information */
57 unsigned int stylus_max_x;
58 unsigned int stylus_max_y;
59 unsigned int stylus_max_pressure;
60 unsigned int stylus_max_xtilt;
61 unsigned int stylus_max_ytilt;
62 bool stylus_tilt_supported;
63
64 /* Touch information */
65 unsigned int touch_type;
66 unsigned int touch_max_x;
67 unsigned int touch_max_y;
68
69 /* Event state */
70 bool stylus_in_proximity;
71 bool stylus_is_eraser;
72 bool button1_pressed;
73 bool button2_pressed;
74
75 /* Session to the serial device */
76 async_sess_t *sess;
77
78 /* Receive buffer state */
79 uint8_t *buf;
80 size_t buf_size;
81 size_t buf_end;
82
83 /* Callbacks */
84 isdv4_event_fn emit_event_fn;
85} isdv4_state_t;
86
87typedef enum {
88 UNKNOWN, PRESS, RELEASE, PROXIMITY_IN, PROXIMITY_OUT, MOVE
89} isdv4_event_type_t;
90
91typedef enum {
92 STYLUS_TIP, STYLUS_ERASER, TOUCH
93} isdv4_source_type_t;
94
95typedef struct isdv4_event {
96 isdv4_event_type_t type;
97 isdv4_source_type_t source;
98 unsigned int x;
99 unsigned int y;
100 unsigned int pressure;
101 unsigned int button;
102} isdv4_event_t;
103
104static void isdv4_event_init(isdv4_event_t *event)
105{
106 memset(event, 0, sizeof(isdv4_event_t));
107}
108
109/* packet_consumer_fn(uint8_t *packet, size_t size, isdv4_state_t *state)
110 return true if reading of packets should continue */
111typedef bool (*packet_consumer_fn)(uint8_t *, size_t, isdv4_state_t *);
112
113static void syntax_print(void)
114{
115 fprintf(stderr, "Usage: wacomdump [--baud=<baud>] [device_service]\n");
116}
117
118static void print_event(const isdv4_event_t *event)
119{
120 const char *type = NULL;
121 switch (event->type) {
122 case PRESS:
123 type = "PRESS";
124 break;
125 case RELEASE:
126 type = "RELEASE";
127 break;
128 case PROXIMITY_IN:
129 type = "PROXIMITY IN";
130 break;
131 case PROXIMITY_OUT:
132 type = "PROXIMITY OUT";
133 break;
134 case MOVE:
135 type = "MOVE";
136 return;
137 case UNKNOWN:
138 type = "UNKNOWN";
139 break;
140 }
141
142 const char *source = NULL;
143 switch (event->source) {
144 case STYLUS_TIP:
145 source = "stylus tip";
146 break;
147 case STYLUS_ERASER:
148 source = "stylus eraser";
149 break;
150 case TOUCH:
151 source = "touch";
152 break;
153 }
154
155 const char *buttons = "none";
156 switch (event->button) {
157 case 1:
158 buttons = "button1";
159 break;
160 case 2:
161 buttons = "button2";
162 break;
163 case 3:
164 buttons = "both";
165 break;
166 }
167
168 printf("%s %s %u %u %u %s\n", type, source, event->x, event->y,
169 event->pressure, buttons);
170}
171
172static bool parse_event(uint8_t *packet, size_t size, isdv4_state_t *state)
173{
174 if (size < 1) {
175 printf("Invalid packet size\n");
176 return false;
177 }
178 bool control_packet = ((packet[0] & CONTROL_PACKET) > 0);
179 if (control_packet) {
180 printf("This is not an event packet\n");
181 return true;
182 }
183
184 /* This is an event initiated by the device */
185 isdv4_event_t event;
186 isdv4_event_init(&event);
187
188/* size_t dbg_ctr;*/
189/* printf("Packet: ");*/
190/* for (dbg_ctr = 0; dbg_ctr < size; dbg_ctr++) {*/
191/* printf("%02hhx ", packet[dbg_ctr]);*/
192/* }*/
193/* printf("\n");*/
194
195 if (size == 5 || size == 7) {
196 /* This is a touch event */
197 bool finger1 = (packet[0] & FINGER1) > 0;
198 event.x = ((packet[1] & 127) << 7) | (packet[2] & 127);
199 event.y = ((packet[3] & 127) << 7) | (packet[4] & 127);
200 event.source = TOUCH;
201 printf("TOUCH finger finger1=%d x=%u y=%u\n", finger1, event.x, event.y);
202
203 if (!state->stylus_in_proximity) {
204 if (!finger1 && state->button1_pressed) {
205 state->button1_pressed = false;
206
207 event.type = RELEASE;
208 event.button = 1;
209 state->emit_event_fn(&event);
210 }
211 else if (finger1 && !state->button1_pressed) {
212 state->button1_pressed = true;
213
214 event.type = PRESS;
215 event.button = 1;
216 state->emit_event_fn(&event);
217 }
218 else {
219 event.type = MOVE;
220 event.button = 1;
221 state->emit_event_fn(&event);
222 }
223 }
224 }
225 else if (size == 9) {
226 /* This is a stylus event */
227 bool tip = packet[0] & TIP;
228 bool button1 = packet[0] & BUTTON1;
229 bool button2 = packet[0] & BUTTON2;
230 bool proximity = packet[0] & PROXIMITY;
231 event.x = ((packet[1] & 127) << 7) | (packet[2] & 124) | ((packet[6] >> 5) & 3);
232 event.y = ((packet[3] & 127) << 7) | (packet[4] & 124) | ((packet[6] >> 3) & 3);
233 event.pressure = (packet[5] & 127) | ((packet[6] & 7) << 7);
234
235 bool eraser = !tip && button2;
236 printf("STYLUS tip=%d button1=%d button2=%d proximity=%d x=%u y=%u p=%u\n",
237 tip, button1, button2, proximity, event.x, event.y, event.pressure);
238
239 if (proximity && !state->stylus_in_proximity) {
240 /* Stylus came into proximity */
241 state->stylus_in_proximity = true;
242 state->stylus_is_eraser = eraser;
243 event.source = (state->stylus_is_eraser ? STYLUS_ERASER : STYLUS_TIP);
244 event.type = PROXIMITY_IN;
245 state->emit_event_fn(&event);
246 }
247 else if (!proximity && state->stylus_in_proximity) {
248 /* Stylus came out of proximity */
249 state->stylus_in_proximity = false;
250 event.source = (state->stylus_is_eraser ? STYLUS_ERASER : STYLUS_TIP);
251 event.type = PROXIMITY_OUT;
252 state->emit_event_fn(&event);
253 }
254 else {
255 /* Proximity state didn't change, but we need to check if it is still eraser */
256 if (eraser != state->stylus_is_eraser) {
257 event.type = PROXIMITY_OUT;
258 event.source = eraser ? STYLUS_TIP : STYLUS_ERASER;
259 state->emit_event_fn(&event);
260 event.type = PROXIMITY_IN;
261 event.source = eraser ? STYLUS_ERASER : STYLUS_TIP;
262 state->emit_event_fn(&event);
263 state->stylus_is_eraser = eraser;
264 }
265 }
266
267 if (!state->stylus_is_eraser) {
268 if (button1 && !state->button1_pressed) {
269 state->button1_pressed = true;
270 event.type = PRESS;
271 event.source = STYLUS_TIP;
272 event.button = 1;
273 state->emit_event_fn(&event);
274 }
275 else if (!button1 && state->button1_pressed) {
276 state->button1_pressed = false;
277 event.type = RELEASE;
278 event.source = STYLUS_TIP;
279 event.button = 1;
280 state->emit_event_fn(&event);
281 }
282 if (button2 && !state->button2_pressed) {
283 state->button2_pressed = true;
284 event.type = PRESS;
285 event.source = STYLUS_TIP;
286 event.button = 2;
287 state->emit_event_fn(&event);
288 }
289 else if (!button2 && state->button2_pressed) {
290 state->button2_pressed = false;
291 event.type = RELEASE;
292 event.source = STYLUS_TIP;
293 event.button = 2;
294 state->emit_event_fn(&event);
295 }
296 event.type = MOVE;
297 event.source = STYLUS_TIP;
298 event.button = (button1 ? 1 : 0) | (button2 ? 2 : 0);
299 state->emit_event_fn(&event);
300 }
301 else {
302 if (button1 && !state->button1_pressed) {
303 state->button1_pressed = true;
304 event.type = PRESS;
305 event.source = STYLUS_ERASER;
306 event.button = 1;
307 state->emit_event_fn(&event);
308 }
309 else if (!button1 && state->button1_pressed) {
310 state->button1_pressed = false;
311 event.type = RELEASE;
312 event.source = STYLUS_ERASER;
313 event.button = 1;
314 state->emit_event_fn(&event);
315 }
316 event.type = MOVE;
317 event.source = STYLUS_ERASER;
318 event.button = (button1 ? 1 : 0);
319 state->emit_event_fn(&event);
320 }
321 //int xtilt = (packet[8] & 127);
322 //int ytilt = (packet[7] & 127);
323 }
324
325 return true;
326}
327
328static bool parse_response_stylus(uint8_t *packet, size_t size,
329 isdv4_state_t *state)
330{
331 if (size != 11) {
332 fprintf(stderr, "Unexpected length of stylus response packet\n");
333 return false;
334 }
335 bool control_packet = ((packet[0] & CONTROL_PACKET) > 0);
336 if (!control_packet) {
337 fprintf(stderr, "This is not a control packet\n");
338 return false;
339 }
340
341 unsigned int data_id = (packet[0] & 63);
342 unsigned int version = ((packet[9] & 127) << 7) | (packet[10] & 127);
343
344 unsigned int max_x = ((packet[1] & 127) << 7) | (packet[2] & 124) |
345 ((packet[6] >> 5) & 3);
346 unsigned int max_y = ((packet[3] & 127) << 7) | (packet[4] & 124) |
347 ((packet[6] >> 3) & 3);
348 unsigned int max_pressure = (packet[5] & 63) | ((packet[6] & 7) << 7);
349 unsigned int max_xtilt = packet[8] & 127;
350 unsigned int max_ytilt = packet[7] & 127;
351
352 printf("Stylus info: data_id=%u version=%u max_x=%u max_y=%u max_pressure=%u "
353 "max_xtilt=%u max_ytilt=%u\n", data_id, version, max_x, max_y,
354 max_pressure, max_xtilt, max_ytilt);
355
356 return false;
357}
358
359static const char *touch_type(unsigned int data_id)
360{
361 switch (data_id) {
362 case 0:
363 return "resistive+stylus";
364 case 1:
365 return "capacitive+stylus";
366 case 2:
367 return "resistive";
368 case 3:
369 case 4:
370 return "capacitive";
371 case 5:
372 return "penabled";
373 }
374 return "unknown";
375}
376
377static bool parse_response_touch(uint8_t *packet, size_t size,
378 isdv4_state_t *state)
379{
380 if (size != 11) {
381 fprintf(stderr, "Unexpected length of touch response packet\n");
382 return false;
383 }
384 bool control_packet = ((packet[0] & CONTROL_PACKET) > 0);
385 if (!control_packet) {
386 fprintf(stderr, "This is not a control packet\n");
387 return false;
388 }
389
390 state->touch_type = (packet[0] & 63);
391 unsigned int version = ((packet[9] & 127) << 7) | (packet[10] & 127);
392
393 unsigned int touch_resolution = packet[1] & 127;
394 state->touch_max_x = ((packet[2] >> 5) & 3) | ((packet[3] & 127) << 7) |
395 (packet[4] & 124);
396 state->touch_max_y = ((packet[2] >> 3) & 3) | ((packet[5] & 127) << 7) |
397 (packet[6] & 124);
398
399 if (touch_resolution == 0)
400 touch_resolution = 10;
401
402 if (state->touch_max_x == 0 || state->touch_max_y == 0) {
403 state->touch_max_x = (1 << touch_resolution);
404 state->touch_max_y = (1 << touch_resolution);
405 }
406
407 printf("Touch info: data_id=%u (%s) version=%u max_x=%u "
408 "max_y=%u\n", state->touch_type, touch_type(state->touch_type), version,
409 state->touch_max_x, state->touch_max_y);
410 return false;
411}
412
413static void read_packets(isdv4_state_t *state, packet_consumer_fn consumer)
414{
415 bool reading = true;
416 bool silence = true;
417 while (reading) {
418 ssize_t read = char_dev_read(state->sess, state->buf + state->buf_end,
419 state->buf_size - state->buf_end);
420 if (read < 0) {
421 fprintf(stderr, "Failed reading from serial device\n");
422 return;
423 }
424 state->buf_end += read;
425
426 if (!silence && read == 0) {
427 silence = true;
428 usleep(100000); /* 100 ms */
429 continue;
430 }
431 else if (read > 0) {
432 silence = false;
433 }
434
435 size_t i = 0;
436
437 /* Skip data until a start of packet is found */
438 while (i < state->buf_end && (state->buf[i] & START_OF_PACKET) == 0) i++;
439
440 size_t start = i;
441 size_t end = i;
442 size_t processed_end = i;
443
444 /* Process packets one by one */
445 while (reading && i < state->buf_end) {
446 /* Find a start of next packet */
447 i++; /* We need to skip the first byte with START_OF_PACKET set */
448 while (i < state->buf_end && (state->buf[i] & START_OF_PACKET) == 0) i++;
449 end = i;
450
451 /* If we have whole packet, process it */
452 if (end > start && (end != state->buf_end || read == 0)) {
453 reading = consumer(state->buf + start, end - start, state);
454 start = end;
455 processed_end = end;
456 }
457 }
458
459 if (processed_end == 0 && state->buf_end == BUF_SIZE) {
460 fprintf(stderr, "Buffer overflow detected, discarding contents\n");
461 state->buf_end = 0;
462 }
463
464 /* Shift the buffer contents to the left */
465 size_t unprocessed_len = state->buf_end - processed_end;
466 memcpy(state->buf, state->buf + processed_end, unprocessed_len);
467 state->buf_end = unprocessed_len;
468 }
469}
470static bool write_command(async_sess_t *sess, uint8_t command)
471{
472 return char_dev_write(sess, &command, 1) == 1;
473}
474
475static void isdv4_init(isdv4_state_t *state, async_sess_t *sess,
476 uint8_t *buf, size_t buf_size, isdv4_event_fn event_fn)
477{
478 memset(state, 0, sizeof(isdv4_state_t));
479 state->sess = sess;
480 state->buf = buf;
481 state->buf_size = buf_size;
482 state->emit_event_fn = event_fn;
483}
484
485static void isdv4_init_tablet(isdv4_state_t *state)
486{
487 write_command(state->sess, CMD_STOP);
488 usleep(250000); /* 250 ms */
489 while (char_dev_read(state->sess, state->buf, state->buf_size) > 0);
490 write_command(state->sess, CMD_QUERY_STYLUS);
491 read_packets(state, parse_response_stylus);
492 write_command(state->sess, CMD_QUERY_TOUCH);
493 read_packets(state, parse_response_touch);
494 write_command(state->sess, CMD_START);
495}
496
497int main(int argc, char **argv)
498{
499 sysarg_t baud = 38400;
500 service_id_t svc_id;
501
502 int arg = 1;
503 int rc;
504
505 if (argc > arg && str_test_prefix(argv[arg], "--baud=")) {
506 size_t arg_offset = str_lsize(argv[arg], 7);
507 char* arg_str = argv[arg] + arg_offset;
508 if (str_length(arg_str) == 0) {
509 fprintf(stderr, "--baud requires an argument\n");
510 syntax_print();
511 return 1;
512 }
513 char *endptr;
514 baud = strtol(arg_str, &endptr, 10);
515 if (*endptr != '\0') {
516 fprintf(stderr, "Invalid value for baud\n");
517 syntax_print();
518 return 1;
519 }
520 arg++;
521 }
522
523 if (argc > arg) {
524 rc = loc_service_get_id(argv[arg], &svc_id, 0);
525 if (rc != EOK) {
526 fprintf(stderr, "Cannot find device service %s\n",
527 argv[arg]);
528 return 1;
529 }
530 arg++;
531 }
532 else {
533 category_id_t serial_cat_id;
534
535 rc = loc_category_get_id("serial", &serial_cat_id, 0);
536 if (rc != EOK) {
537 fprintf(stderr, "Failed getting id of category "
538 "'serial'\n");
539 return 1;
540 }
541
542 service_id_t *svc_ids;
543 size_t svc_count;
544
545 rc = loc_category_get_svcs(serial_cat_id, &svc_ids, &svc_count); if (rc != EOK) {
546 fprintf(stderr, "Failed getting list of services\n");
547 return 1;
548 }
549
550 if (svc_count == 0) {
551 fprintf(stderr, "No service in category 'serial'\n");
552 free(svc_ids);
553 return 1;
554 }
555
556 svc_id = svc_ids[0];
557 free(svc_ids);
558 }
559
560 if (argc > arg) {
561 fprintf(stderr, "Too many arguments\n");
562 syntax_print();
563 return 1;
564 }
565
566
567 async_sess_t *sess = loc_service_connect(EXCHANGE_SERIALIZE, svc_id,
568 IPC_FLAG_BLOCKING);
569 if (!sess) {
570 fprintf(stderr, "Failed connecting to service\n");
571 }
572
573 async_exch_t *exch = async_exchange_begin(sess);
574 rc = async_req_4_0(exch, SERIAL_SET_COM_PROPS, baud,
575 SERIAL_NO_PARITY, 8, 1);
576 async_exchange_end(exch);
577
578 if (rc != EOK) {
579 fprintf(stderr, "Failed setting serial properties\n");
580 return 2;
581 }
582
583 uint8_t buf[BUF_SIZE];
584
585 isdv4_state_t state;
586 isdv4_init(&state, sess, buf, BUF_SIZE, print_event);
587 isdv4_init_tablet(&state);
588
589 read_packets(&state, parse_event);
590
591 return 0;
592}
Note: See TracBrowser for help on using the repository browser.