source: mainline/uspace/drv/hid/atkbd/atkbd.c@ 2791fbb7

Last change on this file since 2791fbb7 was a635535, checked in by Jiri Svoboda <jiri@…>, 23 months ago

Move console/con_srv out of libc into a separate library

  • Property mode set to 100644
File size: 9.9 KB
RevLine 
[5012203]1/*
[15c5418]2 * Copyright (c) 2017 Jiri Svoboda
[5012203]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
[c8ea6eca]31/** @addtogroup atkbd
[5012203]32 * @{
33 */
34/** @file
35 * @brief AT keyboard driver
36 */
37
38#include <errno.h>
39#include <ddf/log.h>
[a635535]40#include <io/kbd_event.h>
[5012203]41#include <io/keycode.h>
42#include <io/chardev.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
[21a0d8a]51#define AT_KBD_ENABLE 0xF4
52#define AT_KBD_ACK 0xFA
53
[5012203]54/* Set 2 scan codes (AT keyboard) */
55static const unsigned int scanmap_simple[] = {
56 [0x0e] = KC_BACKTICK,
57
58 [0x16] = KC_1,
59 [0x1e] = KC_2,
60 [0x26] = KC_3,
61 [0x25] = KC_4,
62 [0x2e] = KC_5,
63 [0x36] = KC_6,
64 [0x3d] = KC_7,
65 [0x3e] = KC_8,
66 [0x46] = KC_9,
67 [0x45] = KC_0,
68
69 [0x4e] = KC_MINUS,
70 [0x55] = KC_EQUALS,
71 [0x66] = KC_BACKSPACE,
72
73 [0x0d] = KC_TAB,
74
75 [0x15] = KC_Q,
76 [0x1d] = KC_W,
77 [0x24] = KC_E,
78 [0x2d] = KC_R,
79 [0x2c] = KC_T,
80 [0x35] = KC_Y,
81 [0x3c] = KC_U,
82 [0x43] = KC_I,
83 [0x44] = KC_O,
84 [0x4d] = KC_P,
85
86 [0x54] = KC_LBRACKET,
87 [0x5b] = KC_RBRACKET,
88
89 [0x58] = KC_CAPS_LOCK,
90
91 [0x1c] = KC_A,
92 [0x1b] = KC_S,
93 [0x23] = KC_D,
94 [0x2b] = KC_F,
95 [0x34] = KC_G,
96 [0x33] = KC_H,
97 [0x3b] = KC_J,
98 [0x42] = KC_K,
99 [0x4b] = KC_L,
100
101 [0x4c] = KC_SEMICOLON,
102 [0x52] = KC_QUOTE,
103 [0x5d] = KC_BACKSLASH,
104
105 [0x12] = KC_LSHIFT,
106
107 [0x1a] = KC_Z,
108 [0x22] = KC_X,
109 [0x21] = KC_C,
110 [0x2a] = KC_V,
111 [0x32] = KC_B,
112 [0x31] = KC_N,
113 [0x3a] = KC_M,
114
115 [0x41] = KC_COMMA,
116 [0x49] = KC_PERIOD,
117 [0x4a] = KC_SLASH,
118
119 [0x59] = KC_RSHIFT,
120
121 [0x14] = KC_LCTRL,
122 [0x11] = KC_LALT,
123 [0x29] = KC_SPACE,
124
125 [0x76] = KC_ESCAPE,
126
127 [0x05] = KC_F1,
128 [0x06] = KC_F2,
129 [0x04] = KC_F3,
130 [0x0c] = KC_F4,
131 [0x03] = KC_F5,
132 [0x0b] = KC_F6,
133 [0x02] = KC_F7,
134
135 [0x0a] = KC_F8,
136 [0x01] = KC_F9,
137 [0x09] = KC_F10,
138
139 [0x78] = KC_F11,
140 [0x07] = KC_F12,
141
142 [0x7e] = KC_SCROLL_LOCK,
143
144 [0x5a] = KC_ENTER,
145
146 [0x77] = KC_NUM_LOCK,
147 [0x7c] = KC_NTIMES,
148 [0x7b] = KC_NMINUS,
149 [0x79] = KC_NPLUS,
150 [0x6c] = KC_N7,
151 [0x75] = KC_N8,
152 [0x7d] = KC_N9,
153 [0x6b] = KC_N4,
154 [0x73] = KC_N5,
155 [0x74] = KC_N6,
156 [0x69] = KC_N1,
157 [0x72] = KC_N2,
158 [0x7a] = KC_N3,
159 [0x70] = KC_N0,
160 [0x71] = KC_NPERIOD,
161};
162
163#define KBD_SCANCODE_SET_EXTENDED 0xe0
164#define KBD_SCANCODE_SET_EXTENDED_SPECIAL 0xe1
165#define KBD_SCANCODE_KEY_RELEASE 0xf0
166
167static const unsigned int scanmap_e0[] = {
168 [0x65] = KC_RALT,
169 [0x59] = KC_RSHIFT,
170
171 [0x64] = KC_PRTSCR,
172
173 [0x70] = KC_INSERT,
174 [0x6c] = KC_HOME,
175 [0x7d] = KC_PAGE_UP,
176
177 [0x71] = KC_DELETE,
178 [0x69] = KC_END,
179 [0x7a] = KC_PAGE_DOWN,
180
181 [0x75] = KC_UP,
182 [0x6b] = KC_LEFT,
183 [0x72] = KC_DOWN,
184 [0x74] = KC_RIGHT,
185
186 [0x4a] = KC_NSLASH,
187 [0x5a] = KC_NENTER
188};
189
190static void push_event(async_sess_t *sess, kbd_event_type_t type,
191 unsigned int key)
192{
193 async_exch_t *exch = async_exchange_begin(sess);
194 async_msg_4(exch, KBDEV_EVENT, type, key, 0, 0);
195 async_exchange_end(exch);
196}
197
198/** Get data and parse scancodes.
199 *
200 * @param arg Pointer to at_kbd_t structure.
201 *
202 * @return EIO on error.
203 *
204 */
[b7fd2a0]205static errno_t polling(void *arg)
[5012203]206{
[15c5418]207 at_kbd_t *kbd = arg;
[5d50c419]208 size_t nwr;
[b7fd2a0]209 errno_t rc;
[a35b458]210
[5012203]211 while (true) {
212 uint8_t code = 0;
[f2d88f3]213 rc = chardev_read(kbd->chardev, &code, 1, &nwr, chardev_f_none);
[5d50c419]214 if (rc != EOK)
[5012203]215 return EIO;
[a35b458]216
[5012203]217 const unsigned int *map;
218 size_t map_size;
[a35b458]219
[5012203]220 if (code == KBD_SCANCODE_SET_EXTENDED) {
221 map = scanmap_e0;
222 map_size = sizeof(scanmap_e0) / sizeof(unsigned int);
[a35b458]223
[f2d88f3]224 rc = chardev_read(kbd->chardev, &code, 1, &nwr,
225 chardev_f_none);
[5d50c419]226 if (rc != EOK)
[5012203]227 return EIO;
228 } else if (code == KBD_SCANCODE_SET_EXTENDED_SPECIAL) {
[f2d88f3]229 rc = chardev_read(kbd->chardev, &code, 1, &nwr,
230 chardev_f_none);
[5d50c419]231 if (rc != EOK)
[5012203]232 return EIO;
233 if (code != 0x14)
234 continue;
235
[f2d88f3]236 rc = chardev_read(kbd->chardev, &code, 1, &nwr,
237 chardev_f_none);
[5d50c419]238 if (rc != EOK)
[5012203]239 return EIO;
240 if (code != 0x77)
241 continue;
242
[f2d88f3]243 rc = chardev_read(kbd->chardev, &code, 1, &nwr,
244 chardev_f_none);
[5d50c419]245 if (rc != EOK)
[5012203]246 return EIO;
247 if (code != 0xe1)
248 continue;
249
[f2d88f3]250 rc = chardev_read(kbd->chardev, &code, 1, &nwr,
251 chardev_f_none);
[5d50c419]252 if (rc != EOK)
[5012203]253 return EIO;
254 if (code != 0xf0)
255 continue;
256
[f2d88f3]257 rc = chardev_read(kbd->chardev, &code, 1, &nwr,
258 chardev_f_none);
[5d50c419]259 if (rc != EOK)
[5012203]260 return EIO;
261 if (code != 0x14)
262 continue;
263
[f2d88f3]264 rc = chardev_read(kbd->chardev, &code, 1, &nwr,
265 chardev_f_none);
[5d50c419]266 if (rc != EOK)
[5012203]267 return EIO;
268 if (code != 0xf0)
269 continue;
270
[f2d88f3]271 rc = chardev_read(kbd->chardev, &code, 1, &nwr,
272 chardev_f_none);
[5d50c419]273 if (rc != EOK)
[5012203]274 return EIO;
275 if (code == 0x77)
276 push_event(kbd->client_sess, KEY_PRESS, KC_BREAK);
277
278 continue;
279 } else {
280 map = scanmap_simple;
281 map_size = sizeof(scanmap_simple) / sizeof(unsigned int);
282 }
[a35b458]283
[5012203]284 kbd_event_type_t type;
285 if (code == KBD_SCANCODE_KEY_RELEASE) {
286 type = KEY_RELEASE;
[f2d88f3]287 rc = chardev_read(kbd->chardev, &code, 1, &nwr,
288 chardev_f_none);
[5d50c419]289 if (rc != EOK)
[5012203]290 return EIO;
291 } else {
292 type = KEY_PRESS;
293 }
[a35b458]294
[5012203]295 const unsigned int key = (code < map_size) ? map[code] : 0;
[a35b458]296
[5012203]297 if (key != 0)
298 push_event(kbd->client_sess, type, key);
299 else
300 ddf_msg(LVL_WARN, "Unknown scancode: %hhx", code);
301 }
302}
303
304/** Default handler for IPC methods not handled by DDF.
305 *
[984a9ba]306 * @param fun Device function handling the call.
307 * @param icall Call data.
[5012203]308 *
309 */
[984a9ba]310static void default_connection_handler(ddf_fun_t *fun, ipc_call_t *icall)
[5012203]311{
[fafb8e5]312 const sysarg_t method = ipc_get_imethod(icall);
[5012203]313 at_kbd_t *kbd = ddf_dev_data_get(ddf_fun_get_dev(fun));
[338d54a7]314 async_sess_t *sess;
[5012203]315
316 switch (method) {
[338d54a7]317 case KBDEV_SET_IND:
[984a9ba]318 async_answer_0(icall, ENOTSUP);
[5012203]319 break;
[338d54a7]320 case IPC_M_CONNECT_TO_ME:
[6ff23ff]321 /*
322 * This might be ugly but async_callback_receive_start makes no
323 * difference for incorrect call and malloc failure.
324 */
[338d54a7]325 sess = async_callback_receive_start(EXCHANGE_SERIALIZE, icall);
[a35b458]326
[5012203]327 /* Probably ENOMEM error, try again. */
328 if (sess == NULL) {
329 ddf_msg(LVL_WARN,
330 "Failed creating callback session");
[984a9ba]331 async_answer_0(icall, EAGAIN);
[5012203]332 break;
333 }
[a35b458]334
[5012203]335 if (kbd->client_sess == NULL) {
336 kbd->client_sess = sess;
337 ddf_msg(LVL_DEBUG, "Set client session");
[984a9ba]338 async_answer_0(icall, EOK);
[5012203]339 } else {
340 ddf_msg(LVL_ERROR, "Client session already set");
[984a9ba]341 async_answer_0(icall, ELIMIT);
[5012203]342 }
[a35b458]343
[5012203]344 break;
345 default:
346 ddf_msg(LVL_ERROR, "Unknown method: %d.", (int)method);
[984a9ba]347 async_answer_0(icall, EINVAL);
[5012203]348 break;
349 }
350}
351
352/** Keyboard function ops. */
353static ddf_dev_ops_t kbd_ops = {
354 .default_handler = default_connection_handler
355};
356
357/** Initialize keyboard driver structure.
358 *
359 * @param kbd Keyboard driver structure to initialize.
360 * @param dev DDF device structure.
361 *
362 * Connects to parent, creates keyboard function, starts polling fibril.
363 *
364 */
[b7fd2a0]365errno_t at_kbd_init(at_kbd_t *kbd, ddf_dev_t *dev)
[5012203]366{
[15c5418]367 async_sess_t *parent_sess;
[b7fd2a0]368 errno_t rc;
[a35b458]369
[5012203]370 assert(kbd);
371 assert(dev);
[a35b458]372
[5012203]373 kbd->client_sess = NULL;
[15c5418]374 parent_sess = ddf_dev_parent_sess_get(dev);
375 if (parent_sess == NULL) {
[5012203]376 ddf_msg(LVL_ERROR, "Failed creating parent session.");
[15c5418]377 rc = EIO;
378 goto error;
379 }
[a35b458]380
[15c5418]381 rc = chardev_open(parent_sess, &kbd->chardev);
382 if (rc != EOK) {
383 ddf_msg(LVL_ERROR, "Failed opening character device.");
[5012203]384 return EIO;
385 }
[a35b458]386
[5012203]387 kbd->kbd_fun = ddf_fun_create(dev, fun_exposed, "kbd");
388 if (!kbd->kbd_fun) {
389 ddf_msg(LVL_ERROR, "Failed creating function 'kbd'.");
390 return ENOMEM;
391 }
[a35b458]392
[5012203]393 ddf_fun_set_ops(kbd->kbd_fun, &kbd_ops);
[a35b458]394
[b7fd2a0]395 errno_t ret = ddf_fun_bind(kbd->kbd_fun);
[5012203]396 if (ret != EOK) {
397 ddf_msg(LVL_ERROR, "Failed binding function 'kbd'.");
398 ddf_fun_destroy(kbd->kbd_fun);
399 return EEXIST;
400 }
[a35b458]401
[5012203]402 ret = ddf_fun_add_to_category(kbd->kbd_fun, "keyboard");
403 if (ret != EOK) {
404 ddf_msg(LVL_ERROR, "Failed adding function 'kbd' to category "
405 "'keyboard'.");
406 ddf_fun_unbind(kbd->kbd_fun);
407 ddf_fun_destroy(kbd->kbd_fun);
408 return ENOMEM;
409 }
[a35b458]410
[5012203]411 kbd->polling_fibril = fibril_create(polling, kbd);
412 if (!kbd->polling_fibril) {
413 ddf_msg(LVL_ERROR, "Failed creating polling fibril.");
414 ddf_fun_unbind(kbd->kbd_fun);
415 ddf_fun_destroy(kbd->kbd_fun);
416 return ENOMEM;
417 }
[a35b458]418
[21a0d8a]419 uint8_t code = AT_KBD_ENABLE;
420 size_t bytes;
421 rc = chardev_write(kbd->chardev, &code, 1, &bytes);
422 if (rc != EOK) {
423 ddf_msg(LVL_ERROR, "Failed to enable keyboard.");
424 fibril_destroy(kbd->polling_fibril);
425 ddf_fun_unbind(kbd->kbd_fun);
426 ddf_fun_destroy(kbd->kbd_fun);
427 return EIO;
428 }
[f2d88f3]429 rc = chardev_read(kbd->chardev, &code, 1, &bytes,
430 chardev_f_none);
[21a0d8a]431 if (rc != EOK || code != AT_KBD_ACK) {
432 ddf_msg(LVL_ERROR, "Failed to confirm keyboard enable: %hhx.",
433 code);
434 fibril_destroy(kbd->polling_fibril);
435 ddf_fun_unbind(kbd->kbd_fun);
436 ddf_fun_destroy(kbd->kbd_fun);
437 return EIO;
438 }
439
[5012203]440 fibril_add_ready(kbd->polling_fibril);
441 return EOK;
[15c5418]442error:
443 chardev_close(kbd->chardev);
444 kbd->chardev = NULL;
445 return rc;
[5012203]446}
Note: See TracBrowser for help on using the repository browser.