source: mainline/uspace/drv/usbkbd/main.c@ e2b1548

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since e2b1548 was 7c169ce, checked in by Lubos Slovak <lubos.slovak@…>, 15 years ago

Merged development

  • Property mode set to 100644
File size: 13.5 KB
Line 
1/*
2 * Copyright (c) 2010 Vojtech Horky
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#include <usb/usbdrv.h>
29#include <driver.h>
30#include <ipc/driver.h>
31#include <ipc/kbd.h>
32#include <io/keycode.h>
33#include <io/console.h>
34#include <errno.h>
35#include <fibril.h>
36#include <usb/classes/hid.h>
37#include <usb/classes/hidparser.h>
38#include <usb/devreq.h>
39#include <usb/descriptor.h>
40#include <io/console.h>
41#include "descparser.h"
42#include "descdump.h"
43#include "conv.h"
44#include "layout.h"
45
46#define BUFFER_SIZE 32
47#define NAME "usbkbd"
48
49#define GUESSED_POLL_ENDPOINT 1
50
51static void default_connection_handler(device_t *, ipc_callid_t, ipc_call_t *);
52static device_ops_t keyboard_ops = {
53 .default_handler = default_connection_handler
54};
55
56static int console_callback_phone = -1;
57
58/** Default handler for IPC methods not handled by DDF.
59 *
60 * @param dev Device handling the call.
61 * @param icallid Call id.
62 * @param icall Call data.
63 */
64void default_connection_handler(device_t *dev,
65 ipc_callid_t icallid, ipc_call_t *icall)
66{
67 sysarg_t method = IPC_GET_IMETHOD(*icall);
68
69 if (method == IPC_M_CONNECT_TO_ME) {
70 int callback = IPC_GET_ARG5(*icall);
71
72 if (console_callback_phone != -1) {
73 ipc_answer_0(icallid, ELIMIT);
74 return;
75 }
76
77 console_callback_phone = callback;
78 ipc_answer_0(icallid, EOK);
79 return;
80 }
81
82 ipc_answer_0(icallid, EINVAL);
83}
84
85#if 0
86static void send_key(int key, int type, wchar_t c) {
87 async_msg_4(console_callback_phone, KBD_EVENT, type, key,
88 KM_NUM_LOCK, c);
89}
90#endif
91
92/*
93 * TODO: Move somewhere else
94 */
95/*
96#define BYTES_PER_LINE 12
97
98static void dump_buffer(const char *msg, const uint8_t *buffer, size_t length)
99{
100 printf("%s\n", msg);
101
102 size_t i;
103 for (i = 0; i < length; i++) {
104 printf(" 0x%02X", buffer[i]);
105 if (((i > 0) && (((i+1) % BYTES_PER_LINE) == 0))
106 || (i + 1 == length)) {
107 printf("\n");
108 }
109 }
110}
111*/
112/*
113 * Copy-paste from srv/hid/kbd/generic/kbd.c
114 */
115
116/** Currently active modifiers.
117 *
118 * TODO: put to device?
119 */
120static unsigned mods = KM_NUM_LOCK;
121
122/** Currently pressed lock keys. We track these to tackle autorepeat.
123 *
124 * TODO: put to device?
125 */
126static unsigned lock_keys;
127
128#define NUM_LAYOUTS 3
129
130static layout_op_t *layout[NUM_LAYOUTS] = {
131 &us_qwerty_op,
132 &us_dvorak_op,
133 &cz_op
134};
135
136// TODO: put to device?
137static int active_layout = 0;
138
139static void kbd_push_ev(int type, unsigned int key)
140{
141 console_event_t ev;
142 unsigned mod_mask;
143
144 // TODO: replace by our own parsing?? or are the key codes identical??
145 switch (key) {
146 case KC_LCTRL: mod_mask = KM_LCTRL; break;
147 case KC_RCTRL: mod_mask = KM_RCTRL; break;
148 case KC_LSHIFT: mod_mask = KM_LSHIFT; break;
149 case KC_RSHIFT: mod_mask = KM_RSHIFT; break;
150 case KC_LALT: mod_mask = KM_LALT; break;
151 case KC_RALT: mod_mask = KM_RALT; break;
152 default: mod_mask = 0; break;
153 }
154
155 if (mod_mask != 0) {
156 if (type == KEY_PRESS)
157 mods = mods | mod_mask;
158 else
159 mods = mods & ~mod_mask;
160 }
161
162 switch (key) {
163 case KC_CAPS_LOCK: mod_mask = KM_CAPS_LOCK; break;
164 case KC_NUM_LOCK: mod_mask = KM_NUM_LOCK; break;
165 case KC_SCROLL_LOCK: mod_mask = KM_SCROLL_LOCK; break;
166 default: mod_mask = 0; break;
167 }
168
169 if (mod_mask != 0) {
170 if (type == KEY_PRESS) {
171 /*
172 * Only change lock state on transition from released
173 * to pressed. This prevents autorepeat from messing
174 * up the lock state.
175 */
176 mods = mods ^ (mod_mask & ~lock_keys);
177 lock_keys = lock_keys | mod_mask;
178
179 /* Update keyboard lock indicator lights. */
180 // TODO
181 //kbd_ctl_set_ind(mods);
182 } else {
183 lock_keys = lock_keys & ~mod_mask;
184 }
185 }
186/*
187 printf("type: %d\n", type);
188 printf("mods: 0x%x\n", mods);
189 printf("keycode: %u\n", key);
190*/
191 /*
192 if (type == KEY_PRESS && (mods & KM_LCTRL) &&
193 key == KC_F1) {
194 active_layout = 0;
195 layout[active_layout]->reset();
196 return;
197 }
198
199 if (type == KEY_PRESS && (mods & KM_LCTRL) &&
200 key == KC_F2) {
201 active_layout = 1;
202 layout[active_layout]->reset();
203 return;
204 }
205
206 if (type == KEY_PRESS && (mods & KM_LCTRL) &&
207 key == KC_F3) {
208 active_layout = 2;
209 layout[active_layout]->reset();
210 return;
211 }
212 */
213 ev.type = type;
214 ev.key = key;
215 ev.mods = mods;
216
217 ev.c = layout[active_layout]->parse_ev(&ev);
218
219 printf("Sending key %d to the console\n", ev.key);
220 assert(console_callback_phone != -1);
221 async_msg_4(console_callback_phone, KBD_EVENT, ev.type, ev.key, ev.mods, ev.c);
222}
223/*
224 * End of copy-paste
225 */
226
227 /*
228 * TODO:
229 * 1) key press / key release - how does the keyboard notify about release?
230 * 2) layouts (use the already defined), not important now
231 * 3)
232 */
233
234/*
235 * Callbacks for parser
236 */
237static void usbkbd_process_keycodes(const uint8_t *key_codes, size_t count,
238 uint8_t modifiers, void *arg)
239{
240 printf("Got keys: ");
241 unsigned i;
242 for (i = 0; i < count; ++i) {
243 printf("%d ", key_codes[i]);
244 // TODO: Key press / release
245
246 // TODO: NOT WORKING
247 unsigned int key = usbkbd_parse_scancode(key_codes[i]);
248 kbd_push_ev(KEY_PRESS, key);
249 }
250 printf("\n");
251}
252
253/*
254 * Kbd functions
255 */
256static int usbkbd_get_report_descriptor(usb_hid_dev_kbd_t *kbd_dev)
257{
258 // iterate over all configurations and interfaces
259 // TODO: more configurations!!
260 unsigned i;
261 for (i = 0; i < kbd_dev->conf->config_descriptor.interface_count; ++i) {
262 // TODO: endianness
263 uint16_t length =
264 kbd_dev->conf->interfaces[i].hid_desc.report_desc_info.length;
265 size_t actual_size = 0;
266
267 // allocate space for the report descriptor
268 kbd_dev->conf->interfaces[i].report_desc = (uint8_t *)malloc(length);
269
270 // get the descriptor from the device
271 int rc = usb_drv_req_get_descriptor(kbd_dev->device->parent_phone,
272 kbd_dev->address, USB_REQUEST_TYPE_CLASS, USB_DESCTYPE_HID_REPORT,
273 0, i, kbd_dev->conf->interfaces[i].report_desc, length,
274 &actual_size);
275
276 if (rc != EOK) {
277 return rc;
278 }
279
280 assert(actual_size == length);
281
282 //dump_hid_class_descriptor(0, USB_DESCTYPE_HID_REPORT,
283 // kbd_dev->conf->interfaces[i].report_desc, length);
284 }
285
286 return EOK;
287}
288
289static int usbkbd_process_descriptors(usb_hid_dev_kbd_t *kbd_dev)
290{
291 // get the first configuration descriptor (TODO: parse also other!)
292 usb_standard_configuration_descriptor_t config_desc;
293
294 int rc = usb_drv_req_get_bare_configuration_descriptor(
295 kbd_dev->device->parent_phone, kbd_dev->address, 0, &config_desc);
296
297 if (rc != EOK) {
298 return rc;
299 }
300
301 // prepare space for all underlying descriptors
302 uint8_t *descriptors = (uint8_t *)malloc(config_desc.total_length);
303 if (descriptors == NULL) {
304 return ENOMEM;
305 }
306
307 size_t transferred = 0;
308 // get full configuration descriptor
309 rc = usb_drv_req_get_full_configuration_descriptor(
310 kbd_dev->device->parent_phone, kbd_dev->address, 0, descriptors,
311 config_desc.total_length, &transferred);
312
313 if (rc != EOK) {
314 return rc;
315 }
316 if (transferred != config_desc.total_length) {
317 return ELIMIT;
318 }
319
320 kbd_dev->conf = (usb_hid_configuration_t *)calloc(1,
321 sizeof(usb_hid_configuration_t));
322 if (kbd_dev->conf == NULL) {
323 free(descriptors);
324 return ENOMEM;
325 }
326
327 rc = usbkbd_parse_descriptors(descriptors, transferred, kbd_dev->conf);
328 free(descriptors);
329 if (rc != EOK) {
330 printf("Problem with parsing standard descriptors.\n");
331 return rc;
332 }
333
334 // get and report descriptors
335 rc = usbkbd_get_report_descriptor(kbd_dev);
336 if (rc != EOK) {
337 printf("Problem with parsing HID REPORT descriptor.\n");
338 return rc;
339 }
340
341 //usbkbd_print_config(kbd_dev->conf);
342
343 /*
344 * TODO:
345 * 1) select one configuration (lets say the first)
346 * 2) how many interfaces?? how to select one??
347 * ("The default setting for an interface is always alternate setting zero.")
348 * 3) find endpoint which is IN and INTERRUPT (parse), save its number
349 * as the endpoint for polling
350 */
351
352 return EOK;
353}
354
355static usb_hid_dev_kbd_t *usbkbd_init_device(device_t *dev)
356{
357 usb_hid_dev_kbd_t *kbd_dev = (usb_hid_dev_kbd_t *)calloc(1,
358 sizeof(usb_hid_dev_kbd_t));
359
360 if (kbd_dev == NULL) {
361 fprintf(stderr, NAME ": No memory!\n");
362 return NULL;
363 }
364
365 kbd_dev->device = dev;
366
367 // get phone to my HC and save it as my parent's phone
368 // TODO: maybe not a good idea if DDF will use parent_phone
369 int rc = kbd_dev->device->parent_phone = usb_drv_hc_connect_auto(dev, 0);
370 if (rc < 0) {
371 printf("Problem setting phone to HC.\n");
372 free(kbd_dev);
373 return NULL;
374 }
375
376 rc = kbd_dev->address = usb_drv_get_my_address(dev->parent_phone, dev);
377 if (rc < 0) {
378 printf("Problem getting address of the device.\n");
379 free(kbd_dev);
380 return NULL;
381 }
382
383 // doesn't matter now that we have no address
384// if (kbd_dev->address < 0) {
385// fprintf(stderr, NAME ": No device address!\n");
386// free(kbd_dev);
387// return NULL;
388// }
389
390 // default endpoint
391 kbd_dev->poll_endpoint = GUESSED_POLL_ENDPOINT;
392
393 /*
394 * will need all descriptors:
395 * 1) choose one configuration from configuration descriptors
396 * (set it to the device)
397 * 2) set endpoints from endpoint descriptors
398 */
399
400 // TODO: get descriptors, parse descriptors and save endpoints
401 usbkbd_process_descriptors(kbd_dev);
402
403 return kbd_dev;
404}
405
406static void usbkbd_process_interrupt_in(usb_hid_dev_kbd_t *kbd_dev,
407 uint8_t *buffer, size_t actual_size)
408{
409 usb_hid_report_in_callbacks_t *callbacks =
410 (usb_hid_report_in_callbacks_t *)malloc(
411 sizeof(usb_hid_report_in_callbacks_t));
412 callbacks->keyboard = usbkbd_process_keycodes;
413
414 //usb_hid_parse_report(kbd_dev->parser, buffer, actual_size, callbacks,
415 // NULL);
416 printf("Calling usb_hid_boot_keyboard_input_report() with size %d\n",
417 actual_size);
418 //dump_buffer("bufffer: ", buffer, actual_size);
419 int rc = usb_hid_boot_keyboard_input_report(buffer, actual_size, callbacks,
420 NULL);
421 if (rc != EOK) {
422 printf("Error in usb_hid_boot_keyboard_input_report(): %d\n", rc);
423 }
424}
425
426static void usbkbd_poll_keyboard(usb_hid_dev_kbd_t *kbd_dev)
427{
428 int rc;
429 usb_handle_t handle;
430 uint8_t buffer[BUFFER_SIZE];
431 size_t actual_size;
432 //usb_endpoint_t poll_endpoint = 1;
433
434// usb_address_t my_address = usb_drv_get_my_address(dev->parent_phone,
435// dev);
436// if (my_address < 0) {
437// return;
438// }
439
440 usb_target_t poll_target = {
441 .address = kbd_dev->address,
442 .endpoint = kbd_dev->poll_endpoint
443 };
444
445 printf("Polling keyboard...\n");
446
447 while (true) {
448 async_usleep(1000 * 1000 * 2);
449 rc = usb_drv_async_interrupt_in(kbd_dev->device->parent_phone,
450 poll_target, buffer, BUFFER_SIZE, &actual_size, &handle);
451
452 if (rc != EOK) {
453 printf("Error in usb_drv_async_interrupt_in(): %d\n", rc);
454 continue;
455 }
456
457 rc = usb_drv_async_wait_for(handle);
458 if (rc != EOK) {
459 printf("Error in usb_drv_async_wait_for(): %d\n", rc);
460 continue;
461 }
462
463 /*
464 * If the keyboard answered with NAK, it returned no data.
465 * This implies that no change happened since last query.
466 */
467 if (actual_size == 0) {
468 printf("Keyboard returned NAK\n");
469 continue;
470 }
471
472 /*
473 * TODO: Process pressed keys.
474 */
475 printf("Calling usbkbd_process_interrupt_in()\n");
476 usbkbd_process_interrupt_in(kbd_dev, buffer, actual_size);
477 }
478
479 // not reached
480 assert(0);
481}
482
483static int usbkbd_fibril_device(void *arg)
484{
485 printf("!!! USB device fibril\n");
486
487 if (arg == NULL) {
488 printf("No device!\n");
489 return -1;
490 }
491
492 device_t *dev = (device_t *)arg;
493
494 // initialize device (get and process descriptors, get address, etc.)
495 usb_hid_dev_kbd_t *kbd_dev = usbkbd_init_device(dev);
496 if (kbd_dev == NULL) {
497 printf("Error while initializing device.\n");
498 return -1;
499 }
500
501 usbkbd_poll_keyboard(kbd_dev);
502
503 return EOK;
504}
505
506static int usbkbd_add_device(device_t *dev)
507{
508 /* For now, fail immediately. */
509 //return ENOTSUP;
510
511 /*
512 * When everything is okay, connect to "our" HC.
513 *
514 * Not supported yet, skip..
515 */
516// int phone = usb_drv_hc_connect_auto(dev, 0);
517// if (phone < 0) {
518// /*
519// * Connecting to HC failed, roll-back and announce
520// * failure.
521// */
522// return phone;
523// }
524
525// dev->parent_phone = phone;
526
527 /*
528 * Create new fibril for handling this keyboard
529 */
530 fid_t fid = fibril_create(usbkbd_fibril_device, dev);
531 if (fid == 0) {
532 printf("%s: failed to start fibril for HID device\n", NAME);
533 return ENOMEM;
534 }
535 fibril_add_ready(fid);
536
537 dev->ops = &keyboard_ops;
538
539 add_device_to_class(dev, "keyboard");
540
541 /*
542 * Hurrah, device is initialized.
543 */
544 return EOK;
545}
546
547static driver_ops_t kbd_driver_ops = {
548 .add_device = usbkbd_add_device,
549};
550
551static driver_t kbd_driver = {
552 .name = NAME,
553 .driver_ops = &kbd_driver_ops
554};
555
556int main(int argc, char *argv[])
557{
558 return driver_main(&kbd_driver);
559}
Note: See TracBrowser for help on using the repository browser.