source: mainline/uspace/srv/hid/isdv4_tablet/isdv4.c@ 192565b

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

Rename wacomdump to isdv4_tablet and register with unique service name.

  • Property mode set to 100644
File size: 11.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 <stdlib.h>
32#include <mem.h>
33
34#include "isdv4.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
53/* packet_consumer_fn(uint8_t *packet, size_t size, isdv4_state_t *state,
54 void *data)
55 return true if reading of packets should continue */
56typedef bool (*packet_consumer_fn)(uint8_t *, size_t, isdv4_state_t *);
57
58static void isdv4_event_init(isdv4_event_t *event)
59{
60 memset(event, 0, sizeof(isdv4_event_t));
61}
62
63/**
64 * Parse event packet and emit events
65 * @return true if reading of packets should continue
66 */
67static bool parse_event(uint8_t *packet, size_t size, isdv4_state_t *state)
68{
69 if (size < 1)
70 return false;
71
72 bool control_packet = ((packet[0] & CONTROL_PACKET) > 0);
73 if (control_packet)
74 return true;
75
76 /* This is an event initiated by the device */
77 isdv4_event_t event;
78 isdv4_event_init(&event);
79
80 if (packet[0] & TOUCH_EVENT) {
81 if (size != 5)
82 return true;
83
84 /* This is a touch event */
85 bool finger1 = (packet[0] & FINGER1) > 0;
86 event.x = ((packet[1] & 127) << 7) | (packet[2] & 127);
87 event.y = ((packet[3] & 127) << 7) | (packet[4] & 127);
88 event.source = TOUCH;
89
90 if (!state->stylus_in_proximity) {
91 if (!finger1 && state->finger1_pressed) {
92 state->finger1_pressed = false;
93
94 event.type = RELEASE;
95 event.button = 1;
96 state->emit_event_fn(&event);
97 }
98 else if (finger1 && !state->finger1_pressed) {
99 state->finger1_pressed = true;
100
101 event.type = PRESS;
102 event.button = 1;
103 state->emit_event_fn(&event);
104 }
105 else {
106 event.type = MOVE;
107 event.button = 1;
108 state->emit_event_fn(&event);
109 }
110 }
111 }
112 else {
113 if (size != 9)
114 return true;
115
116 /* This is a stylus event */
117 bool tip = packet[0] & TIP;
118 bool button1 = packet[0] & BUTTON1;
119 bool button2 = packet[0] & BUTTON2;
120 bool proximity = packet[0] & PROXIMITY;
121 event.x = ((packet[1] & 127) << 7) | (packet[2] & 124) | ((packet[6] >> 5) & 3);
122 event.y = ((packet[3] & 127) << 7) | (packet[4] & 124) | ((packet[6] >> 3) & 3);
123 event.pressure = (packet[5] & 127) | ((packet[6] & 7) << 7);
124
125 if (proximity && !state->stylus_in_proximity) {
126 /* Stylus came into proximity */
127 state->stylus_in_proximity = true;
128 state->stylus_is_eraser = !tip && button2;
129 event.source = (state->stylus_is_eraser ? STYLUS_ERASER : STYLUS_TIP);
130 event.type = PROXIMITY_IN;
131 state->emit_event_fn(&event);
132 }
133 else if (!proximity && state->stylus_in_proximity) {
134 /* Stylus came out of proximity */
135 state->stylus_in_proximity = false;
136 event.source = (state->stylus_is_eraser ? STYLUS_ERASER : STYLUS_TIP);
137 event.type = PROXIMITY_OUT;
138 state->emit_event_fn(&event);
139 }
140 else {
141 /* Proximity state didn't change, but we need to check if it is still eraser */
142 if (state->stylus_is_eraser && !button2) {
143 event.type = PROXIMITY_OUT;
144 event.source = STYLUS_ERASER;
145 state->emit_event_fn(&event);
146 event.type = PROXIMITY_IN;
147 event.source = STYLUS_TIP;
148 state->emit_event_fn(&event);
149 state->stylus_is_eraser = false;
150 }
151 else if (!state->stylus_is_eraser && !tip && button2) {
152 event.type = PROXIMITY_OUT;
153 event.source = STYLUS_TIP;
154 state->emit_event_fn(&event);
155 event.type = PROXIMITY_IN;
156 event.source = STYLUS_ERASER;
157 state->emit_event_fn(&event);
158 state->stylus_is_eraser = true;
159 }
160 }
161
162 if (!state->stylus_is_eraser) {
163 if (tip && !state->tip_pressed) {
164 state->tip_pressed = true;
165 event.type = PRESS;
166 event.source = STYLUS_TIP;
167 event.button = 1;
168 state->emit_event_fn(&event);
169 }
170 else if (!tip && state->tip_pressed) {
171 state->tip_pressed = false;
172 event.type = RELEASE;
173 event.source = STYLUS_TIP;
174 event.button = 1;
175 state->emit_event_fn(&event);
176 }
177 if (button1 && !state->button1_pressed) {
178 state->button1_pressed = true;
179 event.type = PRESS;
180 event.source = STYLUS_TIP;
181 event.button = 2;
182 state->emit_event_fn(&event);
183 }
184 else if (!button1 && state->button1_pressed) {
185 state->button1_pressed = false;
186 event.type = RELEASE;
187 event.source = STYLUS_TIP;
188 event.button = 2;
189 state->emit_event_fn(&event);
190 }
191 if (button2 && !state->button2_pressed) {
192 state->button2_pressed = true;
193 event.type = PRESS;
194 event.source = STYLUS_TIP;
195 event.button = 3;
196 state->emit_event_fn(&event);
197 }
198 else if (!button2 && state->button2_pressed) {
199 state->button2_pressed = false;
200 event.type = RELEASE;
201 event.source = STYLUS_TIP;
202 event.button = 3;
203 state->emit_event_fn(&event);
204 }
205 event.type = MOVE;
206 event.source = STYLUS_TIP;
207 event.button = 0;
208 state->emit_event_fn(&event);
209 }
210 else {
211 if (tip && !state->tip_pressed) {
212 state->tip_pressed = true;
213 event.type = PRESS;
214 event.source = STYLUS_ERASER;
215 event.button = 1;
216 state->emit_event_fn(&event);
217 }
218 else if (!tip && state->tip_pressed) {
219 state->tip_pressed = false;
220 event.type = RELEASE;
221 event.source = STYLUS_ERASER;
222 event.button = 1;
223 state->emit_event_fn(&event);
224 }
225 event.type = MOVE;
226 event.source = STYLUS_ERASER;
227 event.button = 0;
228 state->emit_event_fn(&event);
229 }
230 }
231
232 return true;
233}
234
235static bool parse_response_stylus(uint8_t *packet, size_t size,
236 isdv4_state_t *state)
237{
238 if (size < 1)
239 return false;
240
241 bool control_packet = ((packet[0] & CONTROL_PACKET) > 0);
242 if (!control_packet)
243 return true;
244
245 if (size != 11)
246 return false;
247
248 state->stylus_max_x = ((packet[1] & 127) << 7) | (packet[2] & 124) |
249 ((packet[6] >> 5) & 3);
250 state->stylus_max_y = ((packet[3] & 127) << 7) | (packet[4] & 124) |
251 ((packet[6] >> 3) & 3);
252 state->stylus_max_pressure = (packet[5] & 63) | ((packet[6] & 7) << 7);
253 state->stylus_max_xtilt = packet[8] & 127;
254 state->stylus_max_ytilt = packet[7] & 127;
255 state->stylus_tilt_supported = (state->stylus_max_xtilt &&
256 state->stylus_max_ytilt);
257
258 return false;
259}
260
261static bool parse_response_touch(uint8_t *packet, size_t size,
262 isdv4_state_t *state)
263{
264 if (size < 1)
265 return false;
266
267 bool control_packet = ((packet[0] & CONTROL_PACKET) > 0);
268 if (!control_packet)
269 return true;
270
271 if (size != 11)
272 return false;
273
274 state->touch_type = (packet[0] & 63);
275
276 unsigned int touch_resolution = packet[1] & 127;
277 state->touch_max_x = ((packet[2] >> 5) & 3) | ((packet[3] & 127) << 7) |
278 (packet[4] & 124);
279 state->touch_max_y = ((packet[2] >> 3) & 3) | ((packet[5] & 127) << 7) |
280 (packet[6] & 124);
281
282 if (touch_resolution == 0)
283 touch_resolution = 10;
284
285 if (state->touch_max_x == 0 || state->touch_max_y == 0) {
286 state->touch_max_x = (1 << touch_resolution);
287 state->touch_max_y = (1 << touch_resolution);
288 }
289
290 return false;
291}
292
293static int read_packets(isdv4_state_t *state, packet_consumer_fn consumer)
294{
295 bool reading = true;
296 while (reading) {
297 ssize_t read = char_dev_read(state->sess, state->buf + state->buf_end,
298 state->buf_size - state->buf_end);
299 if (read < 0)
300 return EIO;
301 state->buf_end += read;
302
303 size_t i = 0;
304
305 /* Skip data until a start of packet is found */
306 while (i < state->buf_end && (state->buf[i] & START_OF_PACKET) == 0) i++;
307
308 size_t start = i;
309 size_t end = i;
310 size_t processed_end = i;
311
312 /* Process packets one by one */
313 while (reading && i < state->buf_end) {
314 /* Determine the packet length */
315 size_t packet_remaining;
316 if (state->buf[i] & CONTROL_PACKET) {
317 packet_remaining = 11;
318 }
319 else if (state->buf[i] & TOUCH_EVENT) {
320 packet_remaining = 5;
321 }
322 else {
323 packet_remaining = 9;
324 }
325
326 /* Find the end of the packet */
327 i++; /* We need to skip the first byte with START_OF_PACKET set */
328 packet_remaining--;
329 while (packet_remaining > 0 && i < state->buf_end &&
330 (state->buf[i] & START_OF_PACKET) == 0) {
331 i++;
332 packet_remaining--;
333 }
334 end = i;
335
336 /* If we have whole packet, process it */
337 if (end > start && packet_remaining == 0) {
338 reading = consumer(state->buf + start, end - start, state);
339 start = end;
340 processed_end = end;
341 }
342 }
343
344 if (processed_end == 0 && state->buf_end == state->buf_size) {
345 /* Packet too large, throw it away */
346 state->buf_end = 0;
347 }
348
349 /* Shift the buffer contents to the left */
350 size_t unprocessed_len = state->buf_end - processed_end;
351 memcpy(state->buf, state->buf + processed_end, unprocessed_len);
352 state->buf_end = unprocessed_len;
353 }
354 return EOK;
355}
356static bool write_command(async_sess_t *sess, uint8_t command)
357{
358 return char_dev_write(sess, &command, 1) == 1;
359}
360
361int isdv4_init(isdv4_state_t *state, async_sess_t *sess,
362 isdv4_event_fn event_fn)
363{
364 memset(state, 0, sizeof(isdv4_state_t));
365 state->sess = sess;
366 state->buf = malloc(BUF_SIZE);
367 if (state->buf == NULL)
368 return ENOMEM;
369 state->buf_size = BUF_SIZE;
370 state->emit_event_fn = event_fn;
371 return EOK;
372}
373
374int isdv4_init_tablet(isdv4_state_t *state)
375{
376 if (!write_command(state->sess, CMD_STOP))
377 return EIO;
378
379 usleep(250000); /* 250 ms */
380
381 // FIXME: Read all possible garbage before sending commands
382 if (!write_command(state->sess, CMD_QUERY_STYLUS))
383 return EIO;
384
385 int rc = read_packets(state, parse_response_stylus);
386 if (rc != EOK)
387 return rc;
388
389 if (!write_command(state->sess, CMD_QUERY_TOUCH))
390 return EIO;
391
392 rc = read_packets(state, parse_response_touch);
393 if (rc != EOK)
394 return rc;
395
396 if (!write_command(state->sess, CMD_START))
397 return EIO;
398
399 return EOK;
400}
401
402int isdv4_read_events(isdv4_state_t *state)
403{
404 return read_packets(state, parse_event);
405}
406
407void isdv4_fini(isdv4_state_t *state)
408{
409 free(state->buf);
410}
Note: See TracBrowser for help on using the repository browser.