source: mainline/uspace/drv/hid/xtkbd/xtkbd.c@ 4122410

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 4122410 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: 10.1 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
30/** @addtogroup drvkbd
31 * @{
32 */
33/** @file
34 * @brief XT keyboard driver
35 */
36
37#include <errno.h>
38#include <ddf/log.h>
39#include <io/keycode.h>
40#include <io/chardev.h>
41#include <io/console.h>
42#include <ipc/kbdev.h>
43#include <abi/ipc/methods.h>
44#include "xtkbd.h"
45
46/** Scancode set 1 table. */
47static const unsigned int scanmap_simple[] = {
48 [0x29] = KC_BACKTICK,
49
50 [0x02] = KC_1,
51 [0x03] = KC_2,
52 [0x04] = KC_3,
53 [0x05] = KC_4,
54 [0x06] = KC_5,
55 [0x07] = KC_6,
56 [0x08] = KC_7,
57 [0x09] = KC_8,
58 [0x0a] = KC_9,
59 [0x0b] = KC_0,
60
61 [0x0c] = KC_MINUS,
62 [0x0d] = KC_EQUALS,
63 [0x0e] = KC_BACKSPACE,
64
65 [0x0f] = KC_TAB,
66
67 [0x10] = KC_Q,
68 [0x11] = KC_W,
69 [0x12] = KC_E,
70 [0x13] = KC_R,
71 [0x14] = KC_T,
72 [0x15] = KC_Y,
73 [0x16] = KC_U,
74 [0x17] = KC_I,
75 [0x18] = KC_O,
76 [0x19] = KC_P,
77
78 [0x1a] = KC_LBRACKET,
79 [0x1b] = KC_RBRACKET,
80
81 [0x3a] = KC_CAPS_LOCK,
82
83 [0x1e] = KC_A,
84 [0x1f] = KC_S,
85 [0x20] = KC_D,
86 [0x21] = KC_F,
87 [0x22] = KC_G,
88 [0x23] = KC_H,
89 [0x24] = KC_J,
90 [0x25] = KC_K,
91 [0x26] = KC_L,
92
93 [0x27] = KC_SEMICOLON,
94 [0x28] = KC_QUOTE,
95 [0x2b] = KC_BACKSLASH,
96
97 [0x2a] = KC_LSHIFT,
98
99 [0x2c] = KC_Z,
100 [0x2d] = KC_X,
101 [0x2e] = KC_C,
102 [0x2f] = KC_V,
103 [0x30] = KC_B,
104 [0x31] = KC_N,
105 [0x32] = KC_M,
106
107 [0x33] = KC_COMMA,
108 [0x34] = KC_PERIOD,
109 [0x35] = KC_SLASH,
110
111 [0x36] = KC_RSHIFT,
112
113 [0x1d] = KC_LCTRL,
114 [0x38] = KC_LALT,
115 [0x39] = KC_SPACE,
116
117 [0x01] = KC_ESCAPE,
118
119 [0x3b] = KC_F1,
120 [0x3c] = KC_F2,
121 [0x3d] = KC_F3,
122 [0x3e] = KC_F4,
123 [0x3f] = KC_F5,
124 [0x40] = KC_F6,
125 [0x41] = KC_F7,
126
127 [0x42] = KC_F8,
128 [0x43] = KC_F9,
129 [0x44] = KC_F10,
130
131 [0x57] = KC_F11,
132 [0x58] = KC_F12,
133
134 [0x46] = KC_SCROLL_LOCK,
135
136 [0x1c] = KC_ENTER,
137
138 [0x45] = KC_NUM_LOCK,
139 [0x37] = KC_NTIMES,
140 [0x4a] = KC_NMINUS,
141 [0x4e] = KC_NPLUS,
142 [0x47] = KC_N7,
143 [0x48] = KC_N8,
144 [0x49] = KC_N9,
145 [0x4b] = KC_N4,
146 [0x4c] = KC_N5,
147 [0x4d] = KC_N6,
148 [0x4f] = KC_N1,
149 [0x50] = KC_N2,
150 [0x51] = KC_N3,
151 [0x52] = KC_N0,
152 [0x53] = KC_NPERIOD
153};
154
155#define KBD_ACK 0xfa
156#define KBD_RESEND 0xfe
157#define KBD_SCANCODE_SET_EXTENDED 0xe0
158#define KBD_SCANCODE_SET_EXTENDED_SPECIAL 0xe1
159
160/** Scancode set 1 extended codes table */
161static const unsigned int scanmap_e0[] = {
162 [0x38] = KC_RALT,
163 [0x1d] = KC_RCTRL,
164
165 [0x37] = KC_SYSREQ,
166
167 [0x52] = KC_INSERT,
168 [0x47] = KC_HOME,
169 [0x49] = KC_PAGE_UP,
170
171 [0x53] = KC_DELETE,
172 [0x4f] = KC_END,
173 [0x51] = KC_PAGE_DOWN,
174
175 [0x48] = KC_UP,
176 [0x4b] = KC_LEFT,
177 [0x50] = KC_DOWN,
178 [0x4d] = KC_RIGHT,
179
180 [0x35] = KC_NSLASH,
181 [0x1c] = KC_NENTER
182};
183
184#define KBD_CMD_SET_LEDS 0xed
185
186enum led_indicators {
187 LI_SCROLL = 0x01,
188 LI_NUM = 0x02,
189 LI_CAPS = 0x04
190};
191
192static void push_event(async_sess_t *sess, kbd_event_type_t type,
193 unsigned int key)
194{
195 async_exch_t *exch = async_exchange_begin(sess);
196 async_msg_4(exch, KBDEV_EVENT, type, key, 0, 0);
197 async_exchange_end(exch);
198}
199
200/** Get data and parse scancodes.
201 *
202 * @param arg Pointer to xt_kbd_t structure.
203 *
204 * @return EIO on error.
205 *
206 */
207static errno_t polling(void *arg)
208{
209 xt_kbd_t *kbd = arg;
210 size_t nread;
211 errno_t rc;
212
213 while (true) {
214 const unsigned int *map = scanmap_simple;
215 size_t map_size = sizeof(scanmap_simple) / sizeof(unsigned int);
216
217 uint8_t code = 0;
218 rc = chardev_read(kbd->chardev, &code, 1, &nread);
219 if (rc != EOK)
220 return EIO;
221
222 /* Ignore AT command reply */
223 if ((code == KBD_ACK) || (code == KBD_RESEND))
224 continue;
225
226 /* Extended set */
227 if (code == KBD_SCANCODE_SET_EXTENDED) {
228 map = scanmap_e0;
229 map_size = sizeof(scanmap_e0) / sizeof(unsigned int);
230
231 rc = chardev_read(kbd->chardev, &code, 1, &nread);
232 if (rc != EOK)
233 return EIO;
234
235 /* Handle really special keys */
236
237 if (code == 0x2a) { /* Print Screen */
238 rc = chardev_read(kbd->chardev, &code, 1, &nread);
239 if (rc != EOK)
240 return EIO;
241
242 if (code != 0xe0)
243 continue;
244
245 rc = chardev_read(kbd->chardev, &code, 1, &nread);
246 if (rc != EOK)
247 return EIO;
248
249 if (code == 0x37)
250 push_event(kbd->client_sess, KEY_PRESS, KC_PRTSCR);
251
252 continue;
253 }
254
255 if (code == 0x46) { /* Break */
256 rc = chardev_read(kbd->chardev, &code, 1, &nread);
257 if (rc != EOK)
258 return EIO;
259
260 if (code != 0xe0)
261 continue;
262
263 rc = chardev_read(kbd->chardev, &code, 1, &nread);
264 if (rc != EOK)
265 return EIO;
266
267 if (code == 0xc6)
268 push_event(kbd->client_sess, KEY_PRESS, KC_BREAK);
269
270 continue;
271 }
272 }
273
274 /* Extended special set */
275 if (code == KBD_SCANCODE_SET_EXTENDED_SPECIAL) {
276 rc = chardev_read(kbd->chardev, &code, 1, &nread);
277 if (rc != EOK)
278 return EIO;
279
280 if (code != 0x1d)
281 continue;
282
283 rc = chardev_read(kbd->chardev, &code, 1, &nread);
284 if (rc != EOK)
285 return EIO;
286
287 if (code != 0x45)
288 continue;
289
290 rc = chardev_read(kbd->chardev, &code, 1, &nread);
291 if (rc != EOK)
292 return EIO;
293
294 if (code != 0xe1)
295 continue;
296
297 rc = chardev_read(kbd->chardev, &code, 1, &nread);
298 if (rc != EOK)
299 return EIO;
300
301 if (code != 0x9d)
302 continue;
303
304 rc = chardev_read(kbd->chardev, &code, 1, &nread);
305 if (rc != EOK)
306 return EIO;
307
308 if (code == 0xc5)
309 push_event(kbd->client_sess, KEY_PRESS, KC_PAUSE);
310
311 continue;
312 }
313
314 /* Bit 7 indicates press/release */
315 const kbd_event_type_t type =
316 (code & 0x80) ? KEY_RELEASE : KEY_PRESS;
317 code &= ~0x80;
318
319 const unsigned int key = (code < map_size) ? map[code] : 0;
320
321 if (key != 0)
322 push_event(kbd->client_sess, type, key);
323 else
324 ddf_msg(LVL_WARN, "Unknown scancode: %hhx", code);
325 }
326}
327
328/** Default handler for IPC methods not handled by DDF.
329 *
330 * @param fun Device function handling the call.
331 * @param icall Call data.
332 *
333 */
334static void default_connection_handler(ddf_fun_t *fun, ipc_call_t *icall)
335{
336 const sysarg_t method = IPC_GET_IMETHOD(*icall);
337 xt_kbd_t *kbd = ddf_dev_data_get(ddf_fun_get_dev(fun));
338 unsigned mods;
339 async_sess_t *sess;
340
341 switch (method) {
342 case KBDEV_SET_IND:
343 /*
344 * XT keyboards do not support setting mods,
345 * assume AT keyboard with Scan Code Set 1.
346 */
347 mods = IPC_GET_ARG1(*icall);
348 const uint8_t status = 0 |
349 ((mods & KM_CAPS_LOCK) ? LI_CAPS : 0) |
350 ((mods & KM_NUM_LOCK) ? LI_NUM : 0) |
351 ((mods & KM_SCROLL_LOCK) ? LI_SCROLL : 0);
352 uint8_t cmds[] = { KBD_CMD_SET_LEDS, status };
353
354 size_t nwr;
355 errno_t rc = chardev_write(kbd->chardev, &cmds[0], 1, &nwr);
356 if (rc != EOK) {
357 async_answer_0(icall, rc);
358 break;
359 }
360
361 rc = chardev_write(kbd->chardev, &cmds[1], 1, &nwr);
362 async_answer_0(icall, rc);
363 break;
364 case IPC_M_CONNECT_TO_ME:
365 /*
366 * This might be ugly but async_callback_receive_start makes no
367 * difference for incorrect call and malloc failure.
368 */
369 sess = async_callback_receive_start(EXCHANGE_SERIALIZE, icall);
370
371 /* Probably ENOMEM error, try again. */
372 if (sess == NULL) {
373 ddf_msg(LVL_WARN,
374 "Failed creating callback session");
375 async_answer_0(icall, EAGAIN);
376 break;
377 }
378
379 if (kbd->client_sess == NULL) {
380 kbd->client_sess = sess;
381 ddf_msg(LVL_DEBUG, "Set client session");
382 async_answer_0(icall, EOK);
383 } else {
384 ddf_msg(LVL_ERROR, "Client session already set");
385 async_answer_0(icall, ELIMIT);
386 }
387
388 break;
389 default:
390 ddf_msg(LVL_ERROR, "Unknown method: %d.", (int)method);
391 async_answer_0(icall, EINVAL);
392 break;
393 }
394}
395
396/** Keyboard function ops. */
397static ddf_dev_ops_t kbd_ops = {
398 .default_handler = default_connection_handler
399};
400
401/** Initialize keyboard driver structure.
402 *
403 * @param kbd Keyboard driver structure to initialize.
404 * @param dev DDF device structure.
405 *
406 * Connects to parent, creates keyboard function, starts polling fibril.
407 *
408 */
409errno_t xt_kbd_init(xt_kbd_t *kbd, ddf_dev_t *dev)
410{
411 async_sess_t *parent_sess;
412 bool bound = false;
413 errno_t rc;
414
415 kbd->client_sess = NULL;
416
417 parent_sess = ddf_dev_parent_sess_get(dev);
418 if (parent_sess == NULL) {
419 ddf_msg(LVL_ERROR, "Failed creating parent session.");
420 rc = EIO;
421 goto error;
422 }
423
424 rc = chardev_open(parent_sess, &kbd->chardev);
425 if (rc != EOK) {
426 ddf_msg(LVL_ERROR, "Failed opening character device.");
427 goto error;
428 }
429
430 kbd->kbd_fun = ddf_fun_create(dev, fun_exposed, "kbd");
431 if (kbd->kbd_fun == NULL) {
432 ddf_msg(LVL_ERROR, "Failed creating function 'kbd'.");
433 rc = ENOMEM;
434 goto error;
435 }
436
437 ddf_fun_set_ops(kbd->kbd_fun, &kbd_ops);
438
439 rc = ddf_fun_bind(kbd->kbd_fun);
440 if (rc != EOK) {
441 ddf_msg(LVL_ERROR, "Failed binding function 'kbd'.");
442 goto error;
443 }
444
445 rc = ddf_fun_add_to_category(kbd->kbd_fun, "keyboard");
446 if (rc != EOK) {
447 ddf_msg(LVL_ERROR, "Failed adding function 'kbd' to category "
448 "'keyboard'.");
449 goto error;
450 }
451
452 kbd->polling_fibril = fibril_create(polling, kbd);
453 if (kbd->polling_fibril == 0) {
454 ddf_msg(LVL_ERROR, "Failed creating polling fibril.");
455 rc = ENOMEM;
456 goto error;
457 }
458
459 fibril_add_ready(kbd->polling_fibril);
460 return EOK;
461error:
462 if (bound)
463 ddf_fun_unbind(kbd->kbd_fun);
464 if (kbd->kbd_fun != NULL)
465 ddf_fun_destroy(kbd->kbd_fun);
466 chardev_close(kbd->chardev);
467 return rc;
468}
Note: See TracBrowser for help on using the repository browser.