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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since d648e83 was d420b22, checked in by Jiri Svoboda <jiri@…>, 8 years ago

Move keyboard and mouse drivers to a separate HID directory.

  • 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_get(dev);
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.