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

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

xtkbd: Ignore AT command response.

Fixes warnings on LED indicator change.

  • Property mode set to 100644
File size: 8.5 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 <device/char_dev.h>
38#include <ddf/log.h>
39#include <io/keycode.h>
40#include <io/console.h>
41#include <ipc/kbdev.h>
42#include <abi/ipc/methods.h>
43
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 while (1) {
262 const int *map = scanmap_simple;
263 size_t map_size = sizeof(scanmap_simple) / sizeof(int);
264
265 uint8_t code = 0;
266 ssize_t size = char_dev_read(kbd->parent_sess, &code, 1);
267
268 /** Ignore AT command reply */
269 if (code == KBD_ACK || code == KBD_RESEND) {
270 continue;
271 }
272
273 if (code == KBD_SCANCODE_SET_EXTENDED) {
274 map = scanmap_e0;
275 map_size = sizeof(scanmap_e0) / sizeof(int);
276 size = char_dev_read(kbd->parent_sess, &code, 1);
277 // TODO handle print screen
278 }
279
280 /* Invalid read. */
281 if (size != 1) {
282 continue;
283 }
284
285
286 /* Bit 7 indicates press/release */
287 const kbd_event_type_t type =
288 (code & 0x80) ? KEY_RELEASE : KEY_PRESS;
289 code &= ~0x80;
290
291 const unsigned key = (code < map_size) ? map[code] : 0;
292 if (key != 0) {
293 async_exch_t *exch =
294 async_exchange_begin(kbd->input_sess);
295 if (!exch) {
296 ddf_msg(LVL_ERROR,
297 "Failed to create input exchange.");
298 continue;
299 }
300 async_msg_4(exch, KBDEV_EVENT, type, key, 0, 0);
301 async_exchange_end(exch);
302 } else {
303 ddf_msg(LVL_WARN, "Unknown scancode: %hhx", code);
304 }
305 }
306}
307
308/** Default handler for IPC methods not handled by DDF.
309 *
310 * @param fun Device function handling the call.
311 * @param icallid Call id.
312 * @param icall Call data.
313 */
314void default_connection_handler(ddf_fun_t *fun,
315 ipc_callid_t icallid, ipc_call_t *icall)
316{
317 if (fun == NULL || fun->driver_data == NULL) {
318 ddf_msg(LVL_ERROR, "%s: Missing parameter.", __FUNCTION__);
319 async_answer_0(icallid, EINVAL);
320 return;
321 }
322
323 const sysarg_t method = IPC_GET_IMETHOD(*icall);
324 xt_kbd_t *kbd = fun->driver_data;
325
326 switch (method) {
327 case KBDEV_SET_IND: {
328 /* XT keyboards do not support setting mods,
329 * assume AT keyboard with Scan Code Set 1 */
330 const unsigned mods = IPC_GET_ARG1(*icall);
331 const uint8_t status = 0 |
332 ((mods & KM_CAPS_LOCK) ? LI_CAPS : 0) |
333 ((mods & KM_NUM_LOCK) ? LI_NUM : 0) |
334 ((mods & KM_SCROLL_LOCK) ? LI_SCROLL : 0);
335 uint8_t cmds[] = { KBD_CMD_SET_LEDS, status };
336 const ssize_t size =
337 char_dev_write(kbd->parent_sess, cmds, sizeof(cmds));
338 async_answer_0(icallid, size < 0 ? size : EOK);
339 break;
340 }
341 /* This might be ugly but async_callback_receive_start makes no
342 * difference for incorrect call and malloc failure. */
343 case IPC_M_CONNECT_TO_ME: {
344 async_sess_t *sess =
345 async_callback_receive_start(EXCHANGE_SERIALIZE, icall);
346 /* Probably ENOMEM error, try again. */
347 if (sess == NULL) {
348 ddf_msg(LVL_WARN,
349 "Failed to create start input session");
350 async_answer_0(icallid, EAGAIN);
351 break;
352 }
353 if (kbd->input_sess == NULL) {
354 kbd->input_sess = sess;
355 ddf_msg(LVL_DEBUG, "Set input session");
356 async_answer_0(icallid, EOK);
357 } else {
358 ddf_msg(LVL_ERROR, "Input session already set");
359 async_answer_0(icallid, ELIMIT);
360 }
361 break;
362 }
363 default:
364 ddf_msg(LVL_ERROR, "Unknown method: %d.", (int)method);
365 async_answer_0(icallid, EINVAL);
366 break;
367 }
368}
Note: See TracBrowser for help on using the repository browser.