source: mainline/uspace/drv/char/xtkbd/xtkbd.c@ 0dd16778

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 0dd16778 was c072a29, checked in by Martin Decky <martin@…>, 11 years ago

use the PAUSE / BREAK key to activate the kernel console in compositor (F15 on some strange keyboards)
this avoids issues with stuck modifier keys and other surprises
it is also unlikely that any client applications would like to use these keys

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