source: mainline/uspace/drv/hid/atkbd/atkbd.c@ 0247bd2

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 0247bd2 was 5a6cc679, checked in by Jenda <jenda.jzqk73@…>, 8 years ago

Merge commit '50f19b7ee8e94570b5c63896736c4eb49cfa18db' into forwardport

Not all ints are converted to errno_t in xhci tree yet, however it compiles and works :)

  • Property mode set to 100644
File size: 9.1 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 icallid Call id.
296 * @param icall Call data.
297 *
298 */
299static void default_connection_handler(ddf_fun_t *fun,
300 ipc_callid_t icallid, ipc_call_t *icall)
301{
302 const sysarg_t method = IPC_GET_IMETHOD(*icall);
303 at_kbd_t *kbd = ddf_dev_data_get(ddf_fun_get_dev(fun));
304
305 switch (method) {
306 case KBDEV_SET_IND: {
307 async_answer_0(icallid, ENOTSUP);
308 break;
309 }
310 /*
311 * This might be ugly but async_callback_receive_start makes no
312 * difference for incorrect call and malloc failure.
313 */
314 case IPC_M_CONNECT_TO_ME: {
315 async_sess_t *sess =
316 async_callback_receive_start(EXCHANGE_SERIALIZE, icall);
317
318 /* Probably ENOMEM error, try again. */
319 if (sess == NULL) {
320 ddf_msg(LVL_WARN,
321 "Failed creating callback session");
322 async_answer_0(icallid, EAGAIN);
323 break;
324 }
325
326 if (kbd->client_sess == NULL) {
327 kbd->client_sess = sess;
328 ddf_msg(LVL_DEBUG, "Set client session");
329 async_answer_0(icallid, EOK);
330 } else {
331 ddf_msg(LVL_ERROR, "Client session already set");
332 async_answer_0(icallid, ELIMIT);
333 }
334
335 break;
336 }
337 default:
338 ddf_msg(LVL_ERROR, "Unknown method: %d.", (int)method);
339 async_answer_0(icallid, EINVAL);
340 break;
341 }
342}
343
344/** Keyboard function ops. */
345static ddf_dev_ops_t kbd_ops = {
346 .default_handler = default_connection_handler
347};
348
349/** Initialize keyboard driver structure.
350 *
351 * @param kbd Keyboard driver structure to initialize.
352 * @param dev DDF device structure.
353 *
354 * Connects to parent, creates keyboard function, starts polling fibril.
355 *
356 */
357errno_t at_kbd_init(at_kbd_t *kbd, ddf_dev_t *dev)
358{
359 async_sess_t *parent_sess;
360 errno_t rc;
361
362 assert(kbd);
363 assert(dev);
364
365 kbd->client_sess = NULL;
366 parent_sess = ddf_dev_parent_sess_get(dev);
367 if (parent_sess == NULL) {
368 ddf_msg(LVL_ERROR, "Failed creating parent session.");
369 rc = EIO;
370 goto error;
371 }
372
373 rc = chardev_open(parent_sess, &kbd->chardev);
374 if (rc != EOK) {
375 ddf_msg(LVL_ERROR, "Failed opening character device.");
376 return EIO;
377 }
378
379 kbd->kbd_fun = ddf_fun_create(dev, fun_exposed, "kbd");
380 if (!kbd->kbd_fun) {
381 ddf_msg(LVL_ERROR, "Failed creating function 'kbd'.");
382 return ENOMEM;
383 }
384
385 ddf_fun_set_ops(kbd->kbd_fun, &kbd_ops);
386
387 errno_t ret = ddf_fun_bind(kbd->kbd_fun);
388 if (ret != EOK) {
389 ddf_msg(LVL_ERROR, "Failed binding function 'kbd'.");
390 ddf_fun_destroy(kbd->kbd_fun);
391 return EEXIST;
392 }
393
394 ret = ddf_fun_add_to_category(kbd->kbd_fun, "keyboard");
395 if (ret != EOK) {
396 ddf_msg(LVL_ERROR, "Failed adding function 'kbd' to category "
397 "'keyboard'.");
398 ddf_fun_unbind(kbd->kbd_fun);
399 ddf_fun_destroy(kbd->kbd_fun);
400 return ENOMEM;
401 }
402
403 kbd->polling_fibril = fibril_create(polling, kbd);
404 if (!kbd->polling_fibril) {
405 ddf_msg(LVL_ERROR, "Failed creating polling fibril.");
406 ddf_fun_unbind(kbd->kbd_fun);
407 ddf_fun_destroy(kbd->kbd_fun);
408 return ENOMEM;
409 }
410
411 fibril_add_ready(kbd->polling_fibril);
412 return EOK;
413error:
414 chardev_close(kbd->chardev);
415 kbd->chardev = NULL;
416 return rc;
417}
Note: See TracBrowser for help on using the repository browser.