source: mainline/uspace/drv/char/xtkbd/xtkbd.c@ bd87ae0

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since bd87ae0 was bd87ae0, checked in by Jan Vesely <jano.vesely@…>, 13 years ago

i8042, xtkbd: Switch keyboard away from DDF provided interface.

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