source: mainline/uspace/drv/hid/atkbd/atkbd.c@ e8c4c59

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since e8c4c59 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: 9.0 KB
Line 
1/*
2 * Copyright (c) 2017 Jiri Svoboda
3 * Copyright (c) 2011 Jan Vesely
4 * Copyright (c) 2009 Vineeth Pillai
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 *
11 * - Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * - Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * - The name of the author may not be used to endorse or promote products
17 * derived from this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
20 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
23 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
24 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31/** @addtogroup drvkbd
32 * @{
33 */
34/** @file
35 * @brief AT keyboard driver
36 */
37
38#include <errno.h>
39#include <ddf/log.h>
40#include <io/keycode.h>
41#include <io/chardev.h>
42#include <io/console.h>
43#include <ipc/kbdev.h>
44#include <abi/ipc/methods.h>
45#include "atkbd.h"
46
47#define AT_CAPS_SCAN_CODE 0x58
48#define AT_NUM_SCAN_CODE 0x77
49#define AT_SCROLL_SCAN_CODE 0x7E
50
51/* Set 2 scan codes (AT keyboard) */
52static const unsigned int scanmap_simple[] = {
53 [0x0e] = KC_BACKTICK,
54
55 [0x16] = KC_1,
56 [0x1e] = KC_2,
57 [0x26] = KC_3,
58 [0x25] = KC_4,
59 [0x2e] = KC_5,
60 [0x36] = KC_6,
61 [0x3d] = KC_7,
62 [0x3e] = KC_8,
63 [0x46] = KC_9,
64 [0x45] = KC_0,
65
66 [0x4e] = KC_MINUS,
67 [0x55] = KC_EQUALS,
68 [0x66] = KC_BACKSPACE,
69
70 [0x0d] = KC_TAB,
71
72 [0x15] = KC_Q,
73 [0x1d] = KC_W,
74 [0x24] = KC_E,
75 [0x2d] = KC_R,
76 [0x2c] = KC_T,
77 [0x35] = KC_Y,
78 [0x3c] = KC_U,
79 [0x43] = KC_I,
80 [0x44] = KC_O,
81 [0x4d] = KC_P,
82
83 [0x54] = KC_LBRACKET,
84 [0x5b] = KC_RBRACKET,
85
86 [0x58] = KC_CAPS_LOCK,
87
88 [0x1c] = KC_A,
89 [0x1b] = KC_S,
90 [0x23] = KC_D,
91 [0x2b] = KC_F,
92 [0x34] = KC_G,
93 [0x33] = KC_H,
94 [0x3b] = KC_J,
95 [0x42] = KC_K,
96 [0x4b] = KC_L,
97
98 [0x4c] = KC_SEMICOLON,
99 [0x52] = KC_QUOTE,
100 [0x5d] = KC_BACKSLASH,
101
102 [0x12] = KC_LSHIFT,
103
104 [0x1a] = KC_Z,
105 [0x22] = KC_X,
106 [0x21] = KC_C,
107 [0x2a] = KC_V,
108 [0x32] = KC_B,
109 [0x31] = KC_N,
110 [0x3a] = KC_M,
111
112 [0x41] = KC_COMMA,
113 [0x49] = KC_PERIOD,
114 [0x4a] = KC_SLASH,
115
116 [0x59] = KC_RSHIFT,
117
118 [0x14] = KC_LCTRL,
119 [0x11] = KC_LALT,
120 [0x29] = KC_SPACE,
121
122 [0x76] = KC_ESCAPE,
123
124 [0x05] = KC_F1,
125 [0x06] = KC_F2,
126 [0x04] = KC_F3,
127 [0x0c] = KC_F4,
128 [0x03] = KC_F5,
129 [0x0b] = KC_F6,
130 [0x02] = KC_F7,
131
132 [0x0a] = KC_F8,
133 [0x01] = KC_F9,
134 [0x09] = KC_F10,
135
136 [0x78] = KC_F11,
137 [0x07] = KC_F12,
138
139 [0x7e] = KC_SCROLL_LOCK,
140
141 [0x5a] = KC_ENTER,
142
143 [0x77] = KC_NUM_LOCK,
144 [0x7c] = KC_NTIMES,
145 [0x7b] = KC_NMINUS,
146 [0x79] = KC_NPLUS,
147 [0x6c] = KC_N7,
148 [0x75] = KC_N8,
149 [0x7d] = KC_N9,
150 [0x6b] = KC_N4,
151 [0x73] = KC_N5,
152 [0x74] = KC_N6,
153 [0x69] = KC_N1,
154 [0x72] = KC_N2,
155 [0x7a] = KC_N3,
156 [0x70] = KC_N0,
157 [0x71] = KC_NPERIOD,
158};
159
160#define KBD_SCANCODE_SET_EXTENDED 0xe0
161#define KBD_SCANCODE_SET_EXTENDED_SPECIAL 0xe1
162#define KBD_SCANCODE_KEY_RELEASE 0xf0
163
164static const unsigned int scanmap_e0[] = {
165 [0x65] = KC_RALT,
166 [0x59] = KC_RSHIFT,
167
168 [0x64] = KC_PRTSCR,
169
170 [0x70] = KC_INSERT,
171 [0x6c] = KC_HOME,
172 [0x7d] = KC_PAGE_UP,
173
174 [0x71] = KC_DELETE,
175 [0x69] = KC_END,
176 [0x7a] = KC_PAGE_DOWN,
177
178 [0x75] = KC_UP,
179 [0x6b] = KC_LEFT,
180 [0x72] = KC_DOWN,
181 [0x74] = KC_RIGHT,
182
183 [0x4a] = KC_NSLASH,
184 [0x5a] = KC_NENTER
185};
186
187static void push_event(async_sess_t *sess, kbd_event_type_t type,
188 unsigned int key)
189{
190 async_exch_t *exch = async_exchange_begin(sess);
191 async_msg_4(exch, KBDEV_EVENT, type, key, 0, 0);
192 async_exchange_end(exch);
193}
194
195/** Get data and parse scancodes.
196 *
197 * @param arg Pointer to at_kbd_t structure.
198 *
199 * @return EIO on error.
200 *
201 */
202static errno_t polling(void *arg)
203{
204 at_kbd_t *kbd = arg;
205 size_t nwr;
206 errno_t rc;
207
208 while (true) {
209 uint8_t code = 0;
210 rc = chardev_read(kbd->chardev, &code, 1, &nwr);
211 if (rc != EOK)
212 return EIO;
213
214 const unsigned int *map;
215 size_t map_size;
216
217 if (code == KBD_SCANCODE_SET_EXTENDED) {
218 map = scanmap_e0;
219 map_size = sizeof(scanmap_e0) / sizeof(unsigned int);
220
221 rc = chardev_read(kbd->chardev, &code, 1, &nwr);
222 if (rc != EOK)
223 return EIO;
224 } else if (code == KBD_SCANCODE_SET_EXTENDED_SPECIAL) {
225 rc = chardev_read(kbd->chardev, &code, 1, &nwr);
226 if (rc != EOK)
227 return EIO;
228 if (code != 0x14)
229 continue;
230
231 rc = chardev_read(kbd->chardev, &code, 1, &nwr);
232 if (rc != EOK)
233 return EIO;
234 if (code != 0x77)
235 continue;
236
237 rc = chardev_read(kbd->chardev, &code, 1, &nwr);
238 if (rc != EOK)
239 return EIO;
240 if (code != 0xe1)
241 continue;
242
243 rc = chardev_read(kbd->chardev, &code, 1, &nwr);
244 if (rc != EOK)
245 return EIO;
246 if (code != 0xf0)
247 continue;
248
249 rc = chardev_read(kbd->chardev, &code, 1, &nwr);
250 if (rc != EOK)
251 return EIO;
252 if (code != 0x14)
253 continue;
254
255 rc = chardev_read(kbd->chardev, &code, 1, &nwr);
256 if (rc != EOK)
257 return EIO;
258 if (code != 0xf0)
259 continue;
260
261 rc = chardev_read(kbd->chardev, &code, 1, &nwr);
262 if (rc != EOK)
263 return EIO;
264 if (code == 0x77)
265 push_event(kbd->client_sess, KEY_PRESS, KC_BREAK);
266
267 continue;
268 } else {
269 map = scanmap_simple;
270 map_size = sizeof(scanmap_simple) / sizeof(unsigned int);
271 }
272
273 kbd_event_type_t type;
274 if (code == KBD_SCANCODE_KEY_RELEASE) {
275 type = KEY_RELEASE;
276 rc = chardev_read(kbd->chardev, &code, 1, &nwr);
277 if (rc != EOK)
278 return EIO;
279 } else {
280 type = KEY_PRESS;
281 }
282
283 const unsigned int key = (code < map_size) ? map[code] : 0;
284
285 if (key != 0)
286 push_event(kbd->client_sess, type, key);
287 else
288 ddf_msg(LVL_WARN, "Unknown scancode: %hhx", code);
289 }
290}
291
292/** Default handler for IPC methods not handled by DDF.
293 *
294 * @param fun Device function handling the call.
295 * @param icall Call data.
296 *
297 */
298static void default_connection_handler(ddf_fun_t *fun, ipc_call_t *icall)
299{
300 const sysarg_t method = IPC_GET_IMETHOD(*icall);
301 at_kbd_t *kbd = ddf_dev_data_get(ddf_fun_get_dev(fun));
302 async_sess_t *sess;
303
304 switch (method) {
305 case KBDEV_SET_IND:
306 async_answer_0(icall, ENOTSUP);
307 break;
308 case IPC_M_CONNECT_TO_ME:
309 /*
310 * This might be ugly but async_callback_receive_start makes no
311 * difference for incorrect call and malloc failure.
312 */
313 sess = async_callback_receive_start(EXCHANGE_SERIALIZE, icall);
314
315 /* Probably ENOMEM error, try again. */
316 if (sess == NULL) {
317 ddf_msg(LVL_WARN,
318 "Failed creating callback session");
319 async_answer_0(icall, EAGAIN);
320 break;
321 }
322
323 if (kbd->client_sess == NULL) {
324 kbd->client_sess = sess;
325 ddf_msg(LVL_DEBUG, "Set client session");
326 async_answer_0(icall, EOK);
327 } else {
328 ddf_msg(LVL_ERROR, "Client session already set");
329 async_answer_0(icall, ELIMIT);
330 }
331
332 break;
333 default:
334 ddf_msg(LVL_ERROR, "Unknown method: %d.", (int)method);
335 async_answer_0(icall, EINVAL);
336 break;
337 }
338}
339
340/** Keyboard function ops. */
341static ddf_dev_ops_t kbd_ops = {
342 .default_handler = default_connection_handler
343};
344
345/** Initialize keyboard driver structure.
346 *
347 * @param kbd Keyboard driver structure to initialize.
348 * @param dev DDF device structure.
349 *
350 * Connects to parent, creates keyboard function, starts polling fibril.
351 *
352 */
353errno_t at_kbd_init(at_kbd_t *kbd, ddf_dev_t *dev)
354{
355 async_sess_t *parent_sess;
356 errno_t rc;
357
358 assert(kbd);
359 assert(dev);
360
361 kbd->client_sess = NULL;
362 parent_sess = ddf_dev_parent_sess_get(dev);
363 if (parent_sess == NULL) {
364 ddf_msg(LVL_ERROR, "Failed creating parent session.");
365 rc = EIO;
366 goto error;
367 }
368
369 rc = chardev_open(parent_sess, &kbd->chardev);
370 if (rc != EOK) {
371 ddf_msg(LVL_ERROR, "Failed opening character device.");
372 return EIO;
373 }
374
375 kbd->kbd_fun = ddf_fun_create(dev, fun_exposed, "kbd");
376 if (!kbd->kbd_fun) {
377 ddf_msg(LVL_ERROR, "Failed creating function 'kbd'.");
378 return ENOMEM;
379 }
380
381 ddf_fun_set_ops(kbd->kbd_fun, &kbd_ops);
382
383 errno_t ret = ddf_fun_bind(kbd->kbd_fun);
384 if (ret != EOK) {
385 ddf_msg(LVL_ERROR, "Failed binding function 'kbd'.");
386 ddf_fun_destroy(kbd->kbd_fun);
387 return EEXIST;
388 }
389
390 ret = ddf_fun_add_to_category(kbd->kbd_fun, "keyboard");
391 if (ret != EOK) {
392 ddf_msg(LVL_ERROR, "Failed adding function 'kbd' to category "
393 "'keyboard'.");
394 ddf_fun_unbind(kbd->kbd_fun);
395 ddf_fun_destroy(kbd->kbd_fun);
396 return ENOMEM;
397 }
398
399 kbd->polling_fibril = fibril_create(polling, kbd);
400 if (!kbd->polling_fibril) {
401 ddf_msg(LVL_ERROR, "Failed creating polling fibril.");
402 ddf_fun_unbind(kbd->kbd_fun);
403 ddf_fun_destroy(kbd->kbd_fun);
404 return ENOMEM;
405 }
406
407 fibril_add_ready(kbd->polling_fibril);
408 return EOK;
409error:
410 chardev_close(kbd->chardev);
411 kbd->chardev = NULL;
412 return rc;
413}
Note: See TracBrowser for help on using the repository browser.