source: mainline/uspace/drv/hid/ps2mouse/ps2mouse.c@ 1dcba91

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 1dcba91 was 984a9ba, checked in by Martin Decky <martin@…>, 7 years ago

do not expose the call capability handler from the async framework

Keep the call capability handler encapsulated within the async framework
and do not expose it explicitly via its API. Use the pointer to
ipc_call_t as the sole object identifying an IPC call in the code that
uses the async framework.

This plugs a major leak in the abstraction and also simplifies both the
async framework (slightly) and all IPC servers.

  • Property mode set to 100644
File size: 12.2 KB
Line 
1/*
2 * Copyright (c) 2011 Jan Vesely
3 * Copyright (c) 2017 Jiri Svoboda
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * - Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * - Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * - The name of the author may not be used to endorse or promote products
16 * derived from this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29/** @addtogroup drvmouse
30 * @{
31 */
32/** @file
33 * @brief PS/2 mouse driver.
34 */
35
36#include <stdbool.h>
37#include <errno.h>
38#include <str_error.h>
39#include <ddf/log.h>
40#include <io/keycode.h>
41#include <io/chardev.h>
42#include <io/console.h>
43#include <ipc/mouseev.h>
44#include <abi/ipc/methods.h>
45
46#include "ps2mouse.h"
47
48#define PS2_MOUSE_GET_DEVICE_ID 0xf2
49#define PS2_MOUSE_SET_SAMPLE_RATE 0xf3
50#define PS2_MOUSE_ENABLE_DATA_REPORT 0xf4
51#define PS2_MOUSE_ACK 0xfa
52
53#define PS2_BUFSIZE 3
54#define INTELLIMOUSE_BUFSIZE 4
55
56#define Z_SIGN (1 << 3) /* 4th byte */
57#define X_SIGN (1 << 4) /* 1st byte */
58#define Y_SIGN (1 << 5) /* 1st byte */
59#define X_OVERFLOW (1 << 6) /* 1st byte */
60#define Y_OVERFLOW (1 << 7) /* 1st byte */
61
62#define BUTTON_LEFT 0
63#define BUTTON_RIGHT 1
64#define BUTTON_MIDDLE 2
65#define PS2_BUTTON_COUNT 3
66
67#define INTELLIMOUSE_ALWAYS_ZERO (0xc0)
68#define INTELLIMOUSE_BUTTON_4 (1 << 4) /* 4th byte */
69#define INTELLIMOUSE_BUTTON_5 (1 << 5) /* 4th byte */
70#define INTELLIMOUSE_BUTTON_COUNT 5
71
72#define PS2_BUTTON_MASK(button) (1 << button)
73
74#define MOUSE_READ_BYTE_TEST(mouse, value_) \
75do { \
76 uint8_t value = (value_); \
77 uint8_t data = 0; \
78 size_t nread; \
79 const errno_t rc = chardev_read((mouse)->chardev, &data, 1, &nread); \
80 if (rc != EOK) { \
81 ddf_msg(LVL_ERROR, "Failed reading byte: %s", str_error_name(rc));\
82 return rc; \
83 } \
84 if (data != value) { \
85 ddf_msg(LVL_DEBUG, "Failed testing byte: got %hhx vs. %hhx)", \
86 data, value); \
87 return EIO; \
88 } \
89} while (0)
90
91#define MOUSE_WRITE_BYTE(mouse, value_) \
92do { \
93 uint8_t value = (value_); \
94 uint8_t data = (value); \
95 size_t nwr; \
96 const errno_t rc = chardev_write((mouse)->chardev, &data, 1, &nwr); \
97 if (rc != EOK) { \
98 ddf_msg(LVL_ERROR, "Failed writing byte: %s", str_error_name(rc)); \
99 return rc; \
100 } \
101} while (0)
102
103static errno_t polling_ps2(void *);
104static errno_t polling_intellimouse(void *);
105static errno_t probe_intellimouse(ps2_mouse_t *, bool);
106static void default_connection_handler(ddf_fun_t *, ipc_call_t *);
107
108/** ps/2 mouse driver ops. */
109static ddf_dev_ops_t mouse_ops = {
110 .default_handler = default_connection_handler
111};
112
113/** Initialize mouse driver structure.
114 *
115 * Connects to parent, creates keyboard function, starts polling fibril.
116 *
117 * @param kbd Mouse driver structure to initialize.
118 * @param dev DDF device structure.
119 *
120 * @return EOK on success or non-zero error code
121 */
122errno_t ps2_mouse_init(ps2_mouse_t *mouse, ddf_dev_t *dev)
123{
124 async_sess_t *parent_sess;
125 bool bound = false;
126 errno_t rc;
127
128 mouse->client_sess = NULL;
129
130 parent_sess = ddf_dev_parent_sess_get(dev);
131 if (parent_sess == NULL) {
132 ddf_msg(LVL_ERROR, "Failed getting parent session.");
133 rc = ENOMEM;
134 goto error;
135 }
136
137 rc = chardev_open(parent_sess, &mouse->chardev);
138 if (rc != EOK) {
139 ddf_msg(LVL_ERROR, "Failed opening character device.");
140 goto error;
141 }
142
143 mouse->mouse_fun = ddf_fun_create(dev, fun_exposed, "mouse");
144 if (mouse->mouse_fun == NULL) {
145 ddf_msg(LVL_ERROR, "Error creating mouse function.");
146 rc = ENOMEM;
147 goto error;
148 }
149
150 ddf_fun_set_ops(mouse->mouse_fun, &mouse_ops);
151
152 rc = ddf_fun_bind(mouse->mouse_fun);
153 if (rc != EOK) {
154 ddf_msg(LVL_ERROR, "Failed binding mouse function.");
155 goto error;
156 }
157
158 bound = true;
159
160 rc = ddf_fun_add_to_category(mouse->mouse_fun, "mouse");
161 if (rc != EOK) {
162 ddf_msg(LVL_ERROR, "Failed adding mouse function to category.");
163 goto error;
164 }
165
166 /* Probe IntelliMouse extensions. */
167 errno_t (*polling_f)(void *) = polling_ps2;
168 if (probe_intellimouse(mouse, false) == EOK) {
169 ddf_msg(LVL_NOTE, "Enabled IntelliMouse extensions");
170 polling_f = polling_intellimouse;
171 if (probe_intellimouse(mouse, true) == EOK)
172 ddf_msg(LVL_NOTE, "Enabled 4th and 5th button.");
173 }
174
175 /* Enable mouse data reporting. */
176 uint8_t report = PS2_MOUSE_ENABLE_DATA_REPORT;
177 size_t nwr;
178 rc = chardev_write(mouse->chardev, &report, 1, &nwr);
179 if (rc != EOK) {
180 ddf_msg(LVL_ERROR, "Failed to enable data reporting.");
181 rc = EIO;
182 goto error;
183 }
184
185 size_t nread;
186 rc = chardev_read(mouse->chardev, &report, 1, &nread);
187 if (rc != EOK || report != PS2_MOUSE_ACK) {
188 ddf_msg(LVL_ERROR, "Failed to confirm data reporting: %hhx.",
189 report);
190 rc = EIO;
191 goto error;
192 }
193
194 mouse->polling_fibril = fibril_create(polling_f, mouse);
195 if (mouse->polling_fibril == 0) {
196 rc = ENOMEM;
197 goto error;
198 }
199
200 fibril_add_ready(mouse->polling_fibril);
201 return EOK;
202error:
203 if (bound)
204 ddf_fun_unbind(mouse->mouse_fun);
205 if (mouse->mouse_fun != NULL) {
206 ddf_fun_destroy(mouse->mouse_fun);
207 mouse->mouse_fun = NULL;
208 }
209
210 chardev_close(mouse->chardev);
211 mouse->chardev = NULL;
212 return rc;
213}
214
215/** Read fixed-size mouse packet.
216 *
217 * Continue reading until entire packet is received.
218 *
219 * @param mouse Mouse device
220 * @param pbuf Buffer for storing packet
221 * @param psize Packet size
222 *
223 * @return EOK on success or non-zero error code
224 */
225static errno_t ps2_mouse_read_packet(ps2_mouse_t *mouse, void *pbuf, size_t psize)
226{
227 errno_t rc;
228 size_t pos;
229 size_t nread;
230
231 pos = 0;
232 while (pos < psize) {
233 rc = chardev_read(mouse->chardev, pbuf + pos, psize - pos,
234 &nread);
235 if (rc != EOK) {
236 ddf_msg(LVL_WARN, "Error reading packet.");
237 return rc;
238 }
239
240 pos += nread;
241 }
242
243 return EOK;
244}
245
246/** Get data and parse ps2 protocol packets.
247 * @param arg Pointer to ps2_mouse_t structure.
248 * @return Never.
249 */
250errno_t polling_ps2(void *arg)
251{
252 ps2_mouse_t *mouse = (ps2_mouse_t *) arg;
253 errno_t rc;
254
255 bool buttons[PS2_BUTTON_COUNT] = { };
256 while (true) {
257 uint8_t packet[PS2_BUFSIZE] = { };
258 rc = ps2_mouse_read_packet(mouse, packet, PS2_BUFSIZE);
259 if (rc != EOK)
260 continue;
261
262 ddf_msg(LVL_DEBUG2, "Got packet: %hhx:%hhx:%hhx.",
263 packet[0], packet[1], packet[2]);
264
265 async_exch_t *exch =
266 async_exchange_begin(mouse->client_sess);
267 if (!exch) {
268 ddf_msg(LVL_ERROR,
269 "Failed creating exchange.");
270 continue;
271 }
272
273 /* Buttons */
274 for (unsigned i = 0; i < PS2_BUTTON_COUNT; ++i) {
275 const bool status = (packet[0] & PS2_BUTTON_MASK(i));
276 if (buttons[i] != status) {
277 buttons[i] = status;
278 async_msg_2(exch, MOUSEEV_BUTTON_EVENT, i + 1,
279 buttons[i]);
280 }
281 }
282
283 /* Movement */
284 const int16_t move_x =
285 ((packet[0] & X_SIGN) ? 0xff00 : 0) | packet[1];
286 const int16_t move_y =
287 (((packet[0] & Y_SIGN) ? 0xff00 : 0) | packet[2]);
288 //TODO: Consider overflow bit
289 if (move_x != 0 || move_y != 0) {
290 async_msg_2(exch, MOUSEEV_MOVE_EVENT, move_x, -move_y);
291 }
292 async_exchange_end(exch);
293 }
294
295 return 0;
296}
297
298/** Get data and parse ps2 protocol with IntelliMouse extension packets.
299 * @param arg Pointer to ps2_mouse_t structure.
300 * @return Never.
301 */
302static errno_t polling_intellimouse(void *arg)
303{
304 ps2_mouse_t *mouse = (ps2_mouse_t *) arg;
305 errno_t rc;
306
307 bool buttons[INTELLIMOUSE_BUTTON_COUNT] = { };
308 while (true) {
309 uint8_t packet[INTELLIMOUSE_BUFSIZE] = { };
310 rc = ps2_mouse_read_packet(mouse, packet, INTELLIMOUSE_BUFSIZE);
311 if (rc != EOK)
312 continue;
313
314 ddf_msg(LVL_DEBUG2, "Got packet: %hhx:%hhx:%hhx:%hhx.",
315 packet[0], packet[1], packet[2], packet[3]);
316
317 async_exch_t *exch =
318 async_exchange_begin(mouse->client_sess);
319 if (!exch) {
320 ddf_msg(LVL_ERROR,
321 "Failed creating exchange.");
322 continue;
323 }
324
325 /* Buttons */
326 /*
327 * NOTE: Parsing 4th and 5th button works even if this extension
328 * is not supported and whole 4th byte should be interpreted
329 * as Z-axis movement. the upper 4 bits are just a sign
330 * extension then. + sign is interpreted as "button up"
331 * (i.e no change since that is the default) and - sign fails
332 * the "imb" condition. Thus 4th and 5th buttons are never
333 * down on wheel only extension.
334 */
335 const bool imb = (packet[3] & INTELLIMOUSE_ALWAYS_ZERO) == 0;
336 const bool status[] = {
337 [0] = packet[0] & PS2_BUTTON_MASK(0),
338 [1] = packet[0] & PS2_BUTTON_MASK(1),
339 [2] = packet[0] & PS2_BUTTON_MASK(2),
340 [3] = (packet[3] & INTELLIMOUSE_BUTTON_4) && imb,
341 [4] = (packet[3] & INTELLIMOUSE_BUTTON_5) && imb,
342 };
343 for (unsigned i = 0; i < INTELLIMOUSE_BUTTON_COUNT; ++i) {
344 if (buttons[i] != status[i]) {
345 buttons[i] = status[i];
346 async_msg_2(exch, MOUSEEV_BUTTON_EVENT, i + 1,
347 buttons[i]);
348 }
349 }
350
351 /* Movement */
352 const int16_t move_x =
353 ((packet[0] & X_SIGN) ? 0xff00 : 0) | packet[1];
354 const int16_t move_y =
355 (((packet[0] & Y_SIGN) ? 0xff00 : 0) | packet[2]);
356 const int8_t move_z =
357 (((packet[3] & Z_SIGN) ? 0xf0 : 0) | (packet[3] & 0xf));
358 ddf_msg(LVL_DEBUG2, "Parsed moves: %d:%d:%hhd", move_x, move_y,
359 move_z);
360 //TODO: Consider overflow bit
361 if (move_x != 0 || move_y != 0 || move_z != 0) {
362 async_msg_3(exch, MOUSEEV_MOVE_EVENT,
363 move_x, -move_y, -move_z);
364 }
365 async_exchange_end(exch);
366 }
367
368 return 0;
369}
370
371/** Send magic sequence to initialize IntelliMouse extensions.
372 * @param exch IPC exchange to the parent device.
373 * @param buttons True selects magic sequence for 4th and 5th button,
374 * false selects wheel support magic sequence.
375 * See http://www.computer-engineering.org/ps2mouse/ for details.
376 */
377static errno_t probe_intellimouse(ps2_mouse_t *mouse, bool buttons)
378{
379 MOUSE_WRITE_BYTE(mouse, PS2_MOUSE_SET_SAMPLE_RATE);
380 MOUSE_READ_BYTE_TEST(mouse, PS2_MOUSE_ACK);
381 MOUSE_WRITE_BYTE(mouse, 200);
382 MOUSE_READ_BYTE_TEST(mouse, PS2_MOUSE_ACK);
383
384 MOUSE_WRITE_BYTE(mouse, PS2_MOUSE_SET_SAMPLE_RATE);
385 MOUSE_READ_BYTE_TEST(mouse, PS2_MOUSE_ACK);
386 MOUSE_WRITE_BYTE(mouse, buttons ? 200 : 100);
387 MOUSE_READ_BYTE_TEST(mouse, PS2_MOUSE_ACK);
388
389 MOUSE_WRITE_BYTE(mouse, PS2_MOUSE_SET_SAMPLE_RATE);
390 MOUSE_READ_BYTE_TEST(mouse, PS2_MOUSE_ACK);
391 MOUSE_WRITE_BYTE(mouse, 80);
392 MOUSE_READ_BYTE_TEST(mouse, PS2_MOUSE_ACK);
393
394 MOUSE_WRITE_BYTE(mouse, PS2_MOUSE_GET_DEVICE_ID);
395 MOUSE_READ_BYTE_TEST(mouse, PS2_MOUSE_ACK);
396 MOUSE_READ_BYTE_TEST(mouse, buttons ? 4 : 3);
397
398 return EOK;
399}
400
401/** Default handler for IPC methods not handled by DDF.
402 *
403 * @param fun Device function handling the call.
404 * @param icall Call data.
405 *
406 */
407void default_connection_handler(ddf_fun_t *fun, ipc_call_t *icall)
408{
409 const sysarg_t method = IPC_GET_IMETHOD(*icall);
410 ps2_mouse_t *mouse = ddf_dev_data_get(ddf_fun_get_dev(fun));
411 async_sess_t *sess;
412
413 switch (method) {
414 case IPC_M_CONNECT_TO_ME:
415 /*
416 * This might be ugly but async_callback_receive_start makes no
417 * difference for incorrect call and malloc failure.
418 */
419 sess = async_callback_receive_start(EXCHANGE_SERIALIZE, icall);
420 /* Probably ENOMEM error, try again. */
421 if (sess == NULL) {
422 ddf_msg(LVL_WARN,
423 "Failed creating client callback session");
424 async_answer_0(icall, EAGAIN);
425 break;
426 }
427 if (mouse->client_sess == NULL) {
428 mouse->client_sess = sess;
429 ddf_msg(LVL_DEBUG, "Set client session");
430 async_answer_0(icall, EOK);
431 } else {
432 ddf_msg(LVL_ERROR, "Client session already set");
433 async_answer_0(icall, ELIMIT);
434 }
435 break;
436 default:
437 ddf_msg(LVL_ERROR, "Unknown method: %d.", (int)method);
438 async_answer_0(icall, EINVAL);
439 break;
440 }
441}
Note: See TracBrowser for help on using the repository browser.