source: mainline/uspace/app/wacomdump/wacomdump.c@ 4c73361

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

Update the wacom dump application to work with blocking read.

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