source: mainline/uspace/srv/hid/input/input.c@ ce3efa0

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since ce3efa0 was ce3efa0, checked in by Martin Decky <martin@…>, 11 years ago

cstyle
(no change in functionality)

  • Property mode set to 100644
File size: 15.4 KB
Line 
1/*
2 * Copyright (c) 2006 Josef Cejka
3 * Copyright (c) 2011 Jiri Svoboda
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/**
31 * @addtogroup inputgen generic
32 * @brief HelenOS input server.
33 * @ingroup input
34 * @{
35 */
36/** @file
37 */
38
39#include <adt/list.h>
40#include <stdbool.h>
41#include <fibril_synch.h>
42#include <ipc/services.h>
43#include <ipc/input.h>
44#include <sysinfo.h>
45#include <stdio.h>
46#include <unistd.h>
47#include <stdlib.h>
48#include <stdio.h>
49#include <ns.h>
50#include <async.h>
51#include <errno.h>
52#include <adt/fifo.h>
53#include <io/console.h>
54#include <io/keycode.h>
55#include <loc.h>
56#include "layout.h"
57#include "kbd.h"
58#include "kbd_port.h"
59#include "kbd_ctl.h"
60#include "mouse.h"
61#include "mouse_proto.h"
62#include "input.h"
63
64#define NUM_LAYOUTS 4
65
66static layout_ops_t *layout[NUM_LAYOUTS] = {
67 &us_qwerty_ops,
68 &us_dvorak_ops,
69 &cz_ops,
70 &ar_ops
71};
72
73static void kbd_devs_yield(void);
74static void kbd_devs_reclaim(void);
75
76async_sess_t *client_sess = NULL;
77
78/** List of keyboard devices */
79static list_t kbd_devs;
80
81/** List of mouse devices */
82static list_t mouse_devs;
83
84bool irc_service = false;
85async_sess_t *irc_sess = NULL;
86
87static FIBRIL_MUTEX_INITIALIZE(discovery_lock);
88
89void kbd_push_data(kbd_dev_t *kdev, sysarg_t data)
90{
91 (*kdev->ctl_ops->parse)(data);
92}
93
94void mouse_push_data(mouse_dev_t *mdev, sysarg_t data)
95{
96 (*mdev->proto_ops->parse)(data);
97}
98
99void kbd_push_event(kbd_dev_t *kdev, int type, unsigned int key)
100{
101 kbd_event_t ev;
102 unsigned int mod_mask;
103
104 switch (key) {
105 case KC_LCTRL:
106 mod_mask = KM_LCTRL;
107 break;
108 case KC_RCTRL:
109 mod_mask = KM_RCTRL;
110 break;
111 case KC_LSHIFT:
112 mod_mask = KM_LSHIFT;
113 break;
114 case KC_RSHIFT:
115 mod_mask = KM_RSHIFT;
116 break;
117 case KC_LALT:
118 mod_mask = KM_LALT;
119 break;
120 case KC_RALT:
121 mod_mask = KM_RALT;
122 break;
123 default:
124 mod_mask = 0;
125 }
126
127 if (mod_mask != 0) {
128 if (type == KEY_PRESS)
129 kdev->mods = kdev->mods | mod_mask;
130 else
131 kdev->mods = kdev->mods & ~mod_mask;
132 }
133
134 switch (key) {
135 case KC_CAPS_LOCK:
136 mod_mask = KM_CAPS_LOCK;
137 break;
138 case KC_NUM_LOCK:
139 mod_mask = KM_NUM_LOCK;
140 break;
141 case KC_SCROLL_LOCK:
142 mod_mask = KM_SCROLL_LOCK;
143 break;
144 default:
145 mod_mask = 0;
146 }
147
148 if (mod_mask != 0) {
149 if (type == KEY_PRESS) {
150 /*
151 * Only change lock state on transition from released
152 * to pressed. This prevents autorepeat from messing
153 * up the lock state.
154 */
155 kdev->mods = kdev->mods ^ (mod_mask & ~kdev->lock_keys);
156 kdev->lock_keys = kdev->lock_keys | mod_mask;
157
158 /* Update keyboard lock indicator lights. */
159 (*kdev->ctl_ops->set_ind)(kdev, kdev->mods);
160 } else {
161 kdev->lock_keys = kdev->lock_keys & ~mod_mask;
162 }
163 }
164
165 // TODO: More elegant layout switching
166
167 if ((type == KEY_PRESS) && (kdev->mods & KM_LCTRL) &&
168 (key == KC_F1)) {
169 layout_destroy(kdev->active_layout);
170 kdev->active_layout = layout_create(layout[0]);
171 return;
172 }
173
174 if ((type == KEY_PRESS) && (kdev->mods & KM_LCTRL) &&
175 (key == KC_F2)) {
176 layout_destroy(kdev->active_layout);
177 kdev->active_layout = layout_create(layout[1]);
178 return;
179 }
180
181 if ((type == KEY_PRESS) && (kdev->mods & KM_LCTRL) &&
182 (key == KC_F3)) {
183 layout_destroy(kdev->active_layout);
184 kdev->active_layout = layout_create(layout[2]);
185 return;
186 }
187
188 if ((type == KEY_PRESS) && (kdev->mods & KM_LCTRL) &&
189 (key == KC_F4)) {
190 layout_destroy(kdev->active_layout);
191 kdev->active_layout = layout_create(layout[3]);
192 return;
193 }
194
195 ev.type = type;
196 ev.key = key;
197 ev.mods = kdev->mods;
198
199 ev.c = layout_parse_ev(kdev->active_layout, &ev);
200
201 async_exch_t *exch = async_exchange_begin(client_sess);
202 async_msg_4(exch, INPUT_EVENT_KEY, ev.type, ev.key, ev.mods, ev.c);
203 async_exchange_end(exch);
204}
205
206/** Mouse pointer has moved. */
207void mouse_push_event_move(mouse_dev_t *mdev, int dx, int dy, int dz)
208{
209 async_exch_t *exch = async_exchange_begin(client_sess);
210 if (dx || dy)
211 async_msg_2(exch, INPUT_EVENT_MOVE, dx, dy);
212 if (dz) {
213 // TODO: Implement proper wheel support
214 keycode_t code = dz > 0 ? KC_UP : KC_DOWN;
215 for (int i = 0; i < 3; ++i) {
216 async_msg_4(exch, INPUT_EVENT_KEY, KEY_PRESS, code, 0, 0);
217 }
218 async_msg_4(exch, INPUT_EVENT_KEY, KEY_RELEASE, code, 0, 0);
219 }
220 async_exchange_end(exch);
221}
222
223/** Mouse pointer has moved in absolute mode. */
224void mouse_push_event_abs_move(mouse_dev_t *mdev, unsigned int x, unsigned int y,
225 unsigned int max_x, unsigned int max_y)
226{
227 if (max_x && max_y) {
228 async_exch_t *exch = async_exchange_begin(client_sess);
229 async_msg_4(exch, INPUT_EVENT_ABS_MOVE, x, y, max_x, max_y);
230 async_exchange_end(exch);
231 }
232}
233
234/** Mouse button has been pressed. */
235void mouse_push_event_button(mouse_dev_t *mdev, int bnum, int press)
236{
237 async_exch_t *exch = async_exchange_begin(client_sess);
238 async_msg_2(exch, INPUT_EVENT_BUTTON, bnum, press);
239 async_exchange_end(exch);
240}
241
242static void client_connection(ipc_callid_t iid, ipc_call_t *icall, void *arg)
243{
244 async_answer_0(iid, EOK);
245
246 while (true) {
247 ipc_call_t call;
248 ipc_callid_t callid = async_get_call(&call);
249
250 if (!IPC_GET_IMETHOD(call)) {
251 if (client_sess != NULL) {
252 async_hangup(client_sess);
253 client_sess = NULL;
254 }
255
256 async_answer_0(callid, EOK);
257 return;
258 }
259
260 async_sess_t *sess =
261 async_callback_receive_start(EXCHANGE_SERIALIZE, &call);
262 if (sess != NULL) {
263 if (client_sess == NULL) {
264 client_sess = sess;
265 async_answer_0(callid, EOK);
266 } else
267 async_answer_0(callid, ELIMIT);
268 } else {
269 switch (IPC_GET_IMETHOD(call)) {
270 case INPUT_YIELD:
271 kbd_devs_yield();
272 async_answer_0(callid, EOK);
273 break;
274 case INPUT_RECLAIM:
275 kbd_devs_reclaim();
276 async_answer_0(callid, EOK);
277 break;
278 default:
279 async_answer_0(callid, EINVAL);
280 }
281 }
282 }
283}
284
285static kbd_dev_t *kbd_dev_new(void)
286{
287 kbd_dev_t *kdev = calloc(1, sizeof(kbd_dev_t));
288 if (kdev == NULL) {
289 printf("%s: Error allocating keyboard device. "
290 "Out of memory.\n", NAME);
291 return NULL;
292 }
293
294 link_initialize(&kdev->kbd_devs);
295
296 kdev->mods = KM_NUM_LOCK;
297 kdev->lock_keys = 0;
298 kdev->active_layout = layout_create(layout[0]);
299
300 return kdev;
301}
302
303static mouse_dev_t *mouse_dev_new(void)
304{
305 mouse_dev_t *mdev = calloc(1, sizeof(mouse_dev_t));
306 if (mdev == NULL) {
307 printf("%s: Error allocating keyboard device. "
308 "Out of memory.\n", NAME);
309 return NULL;
310 }
311
312 link_initialize(&mdev->mouse_devs);
313
314 return mdev;
315}
316
317/** Add new legacy keyboard device. */
318static void kbd_add_dev(kbd_port_ops_t *port, kbd_ctl_ops_t *ctl)
319{
320 kbd_dev_t *kdev = kbd_dev_new();
321 if (kdev == NULL)
322 return;
323
324 kdev->port_ops = port;
325 kdev->ctl_ops = ctl;
326 kdev->svc_id = 0;
327
328 /* Initialize port driver. */
329 if ((*kdev->port_ops->init)(kdev) != 0)
330 goto fail;
331
332 /* Initialize controller driver. */
333 if ((*kdev->ctl_ops->init)(kdev) != 0) {
334 /* XXX Uninit port */
335 goto fail;
336 }
337
338 list_append(&kdev->kbd_devs, &kbd_devs);
339 return;
340
341fail:
342 free(kdev);
343}
344
345/** Add new legacy mouse device. */
346static void mouse_add_dev(mouse_port_ops_t *port, mouse_proto_ops_t *proto)
347{
348 mouse_dev_t *mdev = mouse_dev_new();
349 if (mdev == NULL)
350 return;
351
352 mdev->port_ops = port;
353 mdev->proto_ops = proto;
354 mdev->svc_id = 0;
355
356 /* Initialize port driver. */
357 if ((*mdev->port_ops->init)(mdev) != 0)
358 goto fail;
359
360 /* Initialize protocol driver. */
361 if ((*mdev->proto_ops->init)(mdev) != 0) {
362 /* XXX Uninit port */
363 goto fail;
364 }
365
366 list_append(&mdev->mouse_devs, &mouse_devs);
367 return;
368
369fail:
370 free(mdev);
371}
372
373/** Add new kbdev device.
374 *
375 * @param service_id Service ID of the keyboard device
376 *
377 */
378static int kbd_add_kbdev(service_id_t service_id, kbd_dev_t **kdevp)
379{
380 kbd_dev_t *kdev = kbd_dev_new();
381 if (kdev == NULL)
382 return -1;
383
384 kdev->svc_id = service_id;
385 kdev->port_ops = NULL;
386 kdev->ctl_ops = &kbdev_ctl;
387
388 int rc = loc_service_get_name(service_id, &kdev->svc_name);
389 if (rc != EOK) {
390 kdev->svc_name = NULL;
391 goto fail;
392 }
393
394 /* Initialize controller driver. */
395 if ((*kdev->ctl_ops->init)(kdev) != 0) {
396 goto fail;
397 }
398
399 list_append(&kdev->kbd_devs, &kbd_devs);
400 *kdevp = kdev;
401 return EOK;
402
403fail:
404 if (kdev->svc_name != NULL)
405 free(kdev->svc_name);
406 free(kdev);
407 return -1;
408}
409
410/** Add new mousedev device.
411 *
412 * @param service_id Service ID of the mouse device
413 *
414 */
415static int mouse_add_mousedev(service_id_t service_id, mouse_dev_t **mdevp)
416{
417 mouse_dev_t *mdev = mouse_dev_new();
418 if (mdev == NULL)
419 return -1;
420
421 mdev->svc_id = service_id;
422 mdev->port_ops = NULL;
423 mdev->proto_ops = &mousedev_proto;
424
425 int rc = loc_service_get_name(service_id, &mdev->svc_name);
426 if (rc != EOK) {
427 mdev->svc_name = NULL;
428 goto fail;
429 }
430
431 /* Initialize controller driver. */
432 if ((*mdev->proto_ops->init)(mdev) != 0) {
433 goto fail;
434 }
435
436 list_append(&mdev->mouse_devs, &mouse_devs);
437 *mdevp = mdev;
438 return EOK;
439
440fail:
441 free(mdev);
442 return -1;
443}
444
445/** Add legacy drivers/devices. */
446static void kbd_add_legacy_devs(void)
447{
448 /*
449 * Need to add these drivers based on config unless we can probe
450 * them automatically.
451 */
452#if defined(UARCH_arm32) && defined(MACHINE_gta02)
453 kbd_add_dev(&chardev_port, &stty_ctl);
454#endif
455#if defined(UARCH_arm32) && defined(MACHINE_integratorcp)
456 kbd_add_dev(&pl050_port, &pc_ctl);
457#endif
458#if defined(MACHINE_ski)
459 kbd_add_dev(&ski_port, &stty_ctl);
460#endif
461#if defined(MACHINE_msim)
462 kbd_add_dev(&msim_port, &stty_ctl);
463#endif
464#if defined(UARCH_ppc32)
465 kbd_add_dev(&adb_port, &apple_ctl);
466#endif
467#if defined(UARCH_sparc64) && defined(PROCESSOR_sun4v)
468 kbd_add_dev(&niagara_port, &stty_ctl);
469#endif
470#if defined(UARCH_sparc64) && defined(MACHINE_generic)
471 kbd_add_dev(&ns16550_port, &sun_ctl);
472#endif
473 /* Silence warning on abs32le about kbd_add_dev() being unused */
474 (void) kbd_add_dev;
475}
476
477/** Add legacy drivers/devices. */
478static void mouse_add_legacy_devs(void)
479{
480 /*
481 * Need to add these drivers based on config unless we can probe
482 * them automatically.
483 */
484#if defined(UARCH_ppc32)
485 mouse_add_dev(&adb_mouse_port, &adb_proto);
486#endif
487 /* Silence warning on abs32le about mouse_add_dev() being unused */
488 (void) mouse_add_dev;
489}
490
491static void kbd_devs_yield(void)
492{
493 /* For each keyboard device */
494 list_foreach(kbd_devs, kbd_devs, kbd_dev_t, kdev) {
495 /* Yield port */
496 if (kdev->port_ops != NULL)
497 (*kdev->port_ops->yield)();
498 }
499}
500
501static void kbd_devs_reclaim(void)
502{
503 /* For each keyboard device */
504 list_foreach(kbd_devs, kbd_devs, kbd_dev_t, kdev) {
505 /* Reclaim port */
506 if (kdev->port_ops != NULL)
507 (*kdev->port_ops->reclaim)();
508 }
509}
510
511static int dev_check_new_kbdevs(void)
512{
513 category_id_t keyboard_cat;
514 service_id_t *svcs;
515 size_t count, i;
516 bool already_known;
517 int rc;
518
519 rc = loc_category_get_id("keyboard", &keyboard_cat, IPC_FLAG_BLOCKING);
520 if (rc != EOK) {
521 printf("%s: Failed resolving category 'keyboard'.\n", NAME);
522 return ENOENT;
523 }
524
525 /*
526 * Check for new keyboard devices
527 */
528 rc = loc_category_get_svcs(keyboard_cat, &svcs, &count);
529 if (rc != EOK) {
530 printf("%s: Failed getting list of keyboard devices.\n",
531 NAME);
532 return EIO;
533 }
534
535 for (i = 0; i < count; i++) {
536 already_known = false;
537
538 /* Determine whether we already know this device. */
539 list_foreach(kbd_devs, kbd_devs, kbd_dev_t, kdev) {
540 if (kdev->svc_id == svcs[i]) {
541 already_known = true;
542 break;
543 }
544 }
545
546 if (!already_known) {
547 kbd_dev_t *kdev;
548 if (kbd_add_kbdev(svcs[i], &kdev) == EOK) {
549 printf("%s: Connected keyboard device '%s'\n",
550 NAME, kdev->svc_name);
551 }
552 }
553 }
554
555 free(svcs);
556
557 /* XXX Handle device removal */
558
559 return EOK;
560}
561
562static int dev_check_new_mousedevs(void)
563{
564 category_id_t mouse_cat;
565 service_id_t *svcs;
566 size_t count, i;
567 bool already_known;
568 int rc;
569
570 rc = loc_category_get_id("mouse", &mouse_cat, IPC_FLAG_BLOCKING);
571 if (rc != EOK) {
572 printf("%s: Failed resolving category 'mouse'.\n", NAME);
573 return ENOENT;
574 }
575
576 /*
577 * Check for new mouse devices
578 */
579 rc = loc_category_get_svcs(mouse_cat, &svcs, &count);
580 if (rc != EOK) {
581 printf("%s: Failed getting list of mouse devices.\n",
582 NAME);
583 return EIO;
584 }
585
586 for (i = 0; i < count; i++) {
587 already_known = false;
588
589 /* Determine whether we already know this device. */
590 list_foreach(mouse_devs, mouse_devs, mouse_dev_t, mdev) {
591 if (mdev->svc_id == svcs[i]) {
592 already_known = true;
593 break;
594 }
595 }
596
597 if (!already_known) {
598 mouse_dev_t *mdev;
599 if (mouse_add_mousedev(svcs[i], &mdev) == EOK) {
600 printf("%s: Connected mouse device '%s'\n",
601 NAME, mdev->svc_name);
602 }
603 }
604 }
605
606 free(svcs);
607
608 /* XXX Handle device removal */
609
610 return EOK;
611}
612
613static int dev_check_new(void)
614{
615 int rc;
616
617 fibril_mutex_lock(&discovery_lock);
618
619 rc = dev_check_new_kbdevs();
620 if (rc != EOK) {
621 fibril_mutex_unlock(&discovery_lock);
622 return rc;
623 }
624
625 rc = dev_check_new_mousedevs();
626 if (rc != EOK) {
627 fibril_mutex_unlock(&discovery_lock);
628 return rc;
629 }
630
631 fibril_mutex_unlock(&discovery_lock);
632
633 return EOK;
634}
635
636static void cat_change_cb(void)
637{
638 dev_check_new();
639}
640
641/** Start listening for new devices. */
642static int input_start_dev_discovery(void)
643{
644 int rc = loc_register_cat_change_cb(cat_change_cb);
645 if (rc != EOK) {
646 printf("%s: Failed registering callback for device discovery. "
647 "(%d)\n", NAME, rc);
648 return rc;
649 }
650
651 return dev_check_new();
652}
653
654static void usage(char *name)
655{
656 printf("Usage: %s <service_name>\n", name);
657}
658
659int main(int argc, char **argv)
660{
661 if (argc < 2) {
662 usage(argv[0]);
663 return 1;
664 }
665
666 printf("%s: HelenOS input service\n", NAME);
667
668 sysarg_t obio;
669
670 list_initialize(&kbd_devs);
671 list_initialize(&mouse_devs);
672
673 if ((sysinfo_get_value("kbd.cir.obio", &obio) == EOK) && (obio))
674 irc_service = true;
675
676 if (irc_service) {
677 while (irc_sess == NULL)
678 irc_sess = service_connect_blocking(EXCHANGE_SERIALIZE,
679 SERVICE_IRC, 0, 0);
680 }
681
682 /* Add legacy keyboard devices. */
683 kbd_add_legacy_devs();
684
685 /* Add legacy mouse devices. */
686 mouse_add_legacy_devs();
687
688 /* Register driver */
689 async_set_client_connection(client_connection);
690
691 int rc = loc_server_register(NAME);
692 if (rc != EOK) {
693 printf("%s: Unable to register server\n", NAME);
694 return rc;
695 }
696
697 service_id_t service_id;
698 rc = loc_service_register(argv[1], &service_id);
699 if (rc != EOK) {
700 printf("%s: Unable to register service %s\n", NAME, argv[1]);
701 return rc;
702 }
703
704 /* Start looking for new input devices */
705 input_start_dev_discovery();
706
707 printf("%s: Accepting connections\n", NAME);
708 task_retval(0);
709 async_manager();
710
711 /* Not reached. */
712 return 0;
713}
714
715/**
716 * @}
717 */
Note: See TracBrowser for help on using the repository browser.