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