source: mainline/uspace/srv/hid/input/generic/input.c@ 12f9f0d0

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 12f9f0d0 was 12f9f0d0, checked in by Jiri Svoboda <jiri@…>, 14 years ago

Notifications on changes in loc categories. Limitations:

  • cannot specify single category to watch
  • max one task can register notifications with loc service
  • max one user callback function can be registered with C library

Remove devman tests as they are not applicable anymore.

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