From d4ffd83e778e80db410d375dac46287dfdf6f5f7 Mon Sep 17 00:00:00 2001 From: emax Date: Thu, 18 Nov 2004 18:05:15 +0000 Subject: [PATCH] Check in updated bthidd(8). This is still work in progress. --- usr.sbin/bluetooth/bthidd/Makefile | 11 +- usr.sbin/bluetooth/bthidd/bthidd.c | 36 +- usr.sbin/bluetooth/bthidd/bthidd.h | 35 +- usr.sbin/bluetooth/bthidd/hid.c | 134 ++++++- usr.sbin/bluetooth/bthidd/kbd.c | 592 ++++++++++++++++++++++++++++ usr.sbin/bluetooth/bthidd/kbd.h | 41 ++ usr.sbin/bluetooth/bthidd/parser.y | 6 +- usr.sbin/bluetooth/bthidd/server.c | 51 ++- usr.sbin/bluetooth/bthidd/session.c | 16 +- 9 files changed, 874 insertions(+), 48 deletions(-) create mode 100644 usr.sbin/bluetooth/bthidd/kbd.c create mode 100644 usr.sbin/bluetooth/bthidd/kbd.h diff --git a/usr.sbin/bluetooth/bthidd/Makefile b/usr.sbin/bluetooth/bthidd/Makefile index 4bd7edc90b82..9183d6df5262 100644 --- a/usr.sbin/bluetooth/bthidd/Makefile +++ b/usr.sbin/bluetooth/bthidd/Makefile @@ -1,12 +1,15 @@ -# $Id: Makefile,v 1.2 2004/02/13 21:46:20 max Exp $ +# $Id: Makefile,v 1.3 2004/08/17 21:49:46 max Exp $ # $FreeBSD$ PROG= bthidd #MAN= bthidd.8 bthidd.conf.5 -NOMAN= -SRCS= bthidd.c client.c hid.c lexer.l parser.y server.c session.c -WARNS?= 1 +NOMAN= 1 +SRCS= bthidd.c client.c hid.c kbd.c lexer.l parser.y server.c \ + session.c + CFLAGS+= -I${.CURDIR} +WARNS?= 2 +DEBUG_FLAGS= -g DPADD= ${LIBBLUETOOTH} ${LIBSDP} LDADD= -lbluetooth -lusbhid diff --git a/usr.sbin/bluetooth/bthidd/bthidd.c b/usr.sbin/bluetooth/bthidd/bthidd.c index 2a0206bc34f9..7340a0c17cd1 100644 --- a/usr.sbin/bluetooth/bthidd/bthidd.c +++ b/usr.sbin/bluetooth/bthidd/bthidd.c @@ -25,7 +25,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: bthidd.c,v 1.4 2004/02/26 21:48:44 max Exp $ + * $Id: bthidd.c,v 1.7 2004/11/17 21:59:42 max Exp $ * $FreeBSD$ */ @@ -62,14 +62,16 @@ main(int argc, char *argv[]) { struct bthid_server srv; struct sigaction sa; - char const *pid_file = BTHIDD_PIDFILE; + char const *pid_file = BTHIDD_PIDFILE, *ep = NULL; int opt, detach, tval; + memset(&srv, 0, sizeof(srv)); memcpy(&srv.bdaddr, NG_HCI_BDADDR_ANY, sizeof(srv.bdaddr)); + srv.windex = -1; detach = 1; tval = 10; /* sec */ - while ((opt = getopt(argc, argv, "a:c:dH:hp:t:")) != -1) { + while ((opt = getopt(argc, argv, "a:c:dH:hp:s:t:")) != -1) { switch (opt) { case 'a': /* BDADDR */ if (!bt_aton(optarg, &srv.bdaddr)) { @@ -98,13 +100,21 @@ main(int argc, char *argv[]) pid_file = optarg; break; - case 't': { /* rescan interval */ - char *ep = NULL; + case 's': /* switch script */ + srv.script = optarg; + break; - tval = strtol(optarg, &ep, 10); + case 't': /* rescan interval */ + tval = strtol(optarg, (char **) &ep, 10); if (*ep != '\0' || tval <= 0) usage(); - } break; + break; + + case 'u': /* wired keyboard index */ + srv.windex = strtol(optarg, (char **) &ep, 10); + if (*ep != '\0' || srv.windex < 0) + usage(); + break; case 'h': default: @@ -141,6 +151,14 @@ main(int argc, char *argv[]) exit(1); } + sa.sa_handler = SIG_IGN; + sa.sa_flags = SA_NOCLDSTOP|SA_NOCLDWAIT; + if (sigaction(SIGCHLD, &sa, NULL) < 0) { + syslog(LOG_CRIT, "Could not install signal handlers. %s (%d)", + strerror(errno), errno); + exit(1); + } + if (read_config_file() < 0 || read_hids_file() < 0 || server_init(&srv) < 0 || write_pid_file(pid_file) < 0) exit(1); @@ -249,7 +267,9 @@ usage(void) " -H file specify known HIDs file name\n" \ " -h display this message\n" \ " -p file specify PID file name\n" \ -" -t tval client rescan interval (sec)\n" \ +" -s script specify keyboard switching script\n" \ +" -t tval specify client rescan interval (sec)\n" \ +" -u unit specify wired keyboard unit\n" \ "", BTHIDD_IDENT); exit(255); } diff --git a/usr.sbin/bluetooth/bthidd/bthidd.h b/usr.sbin/bluetooth/bthidd/bthidd.h index d9cd01502600..d4ebbc6ebd95 100644 --- a/usr.sbin/bluetooth/bthidd/bthidd.h +++ b/usr.sbin/bluetooth/bthidd/bthidd.h @@ -25,7 +25,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: bthidd.h,v 1.4 2004/02/26 21:44:20 max Exp $ + * $Id: bthidd.h,v 1.6 2004/11/17 21:59:42 max Exp $ * $FreeBSD$ */ @@ -39,14 +39,18 @@ struct bthid_session; struct bthid_server { - bdaddr_t bdaddr; /* local bdaddr */ - int cons; /* /dev/consolectl */ - int ctrl; /* control channel (listen) */ - int intr; /* interrupt channel (listen) */ - int maxfd; /* max fd in sets */ - fd_set rfdset; /* read descriptor set */ - fd_set wfdset; /* write descriptor set */ - LIST_HEAD(, bthid_session) sessions; + bdaddr_t bdaddr; /* local bdaddr */ + int cons; /* /dev/consolectl */ + int vkbd; /* /dev/vkbdctl */ + char const *script; /* keyboard switching script */ + int windex; /* wired keyboard index */ + bitstr_t *keys; /* pressed keys map */ + int ctrl; /* control channel (listen) */ + int intr; /* intr. channel (listen) */ + int maxfd; /* max fd in sets */ + fd_set rfdset; /* read descriptor set */ + fd_set wfdset; /* write descriptor set */ + LIST_HEAD(, bthid_session) sessions; }; typedef struct bthid_server bthid_server_t; @@ -54,16 +58,17 @@ typedef struct bthid_server * bthid_server_p; struct bthid_session { - bthid_server_p srv; /* pointer back to server */ - int ctrl; /* control channel */ - int intr; /* interrupt channel */ - bdaddr_t bdaddr; /* remote bdaddr */ - short state; /* session state */ + bthid_server_p srv; /* pointer back to server */ + int ctrl; /* control channel */ + int intr; /* interrupt channel */ + bdaddr_t bdaddr;/* remote bdaddr */ + short state; /* session state */ #define CLOSED 0 #define W4CTRL 1 #define W4INTR 2 #define OPEN 3 - LIST_ENTRY(bthid_session) next; /* link to next */ + bitstr_t *keys; /* pressed keys map */ + LIST_ENTRY(bthid_session) next; /* link to next */ }; typedef struct bthid_session bthid_session_t; diff --git a/usr.sbin/bluetooth/bthidd/hid.c b/usr.sbin/bluetooth/bthidd/hid.c index 5a7ab665d0a5..4495dbc5f9b1 100644 --- a/usr.sbin/bluetooth/bthidd/hid.c +++ b/usr.sbin/bluetooth/bthidd/hid.c @@ -25,7 +25,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: hid.c,v 1.3 2004/02/26 21:47:35 max Exp $ + * $Id: hid.c,v 1.4 2004/11/17 21:59:42 max Exp $ * $FreeBSD$ */ @@ -40,13 +40,18 @@ #include #include #include +#include #include #include "bthidd.h" #include "bthid_config.h" +#include "kbd.h" #undef min #define min(x, y) (((x) < (y))? (x) : (y)) +#undef ASIZE +#define ASIZE(a) (sizeof(a)/sizeof(a[0])) + /* * Process data from control channel */ @@ -123,9 +128,10 @@ hid_interrupt(bthid_session_p s, char *data, int len) hid_item_t h; int report_id, usage, page, val, mouse_x, mouse_y, mouse_z, mouse_butt, - nkeys, keys[32]; /* XXX how big keys[] should be? */ + mevents, kevents; assert(s != NULL); + assert(s->srv != NULL); assert(data != NULL); if (len < 3) { @@ -148,7 +154,7 @@ hid_interrupt(bthid_session_p s, char *data, int len) hid_device = get_hid_device(&s->bdaddr); assert(hid_device != NULL); - mouse_x = mouse_y = mouse_z = mouse_butt = nkeys = 0; + mouse_x = mouse_y = mouse_z = mouse_butt = mevents = kevents = 0; for (d = hid_start_parse(hid_device->desc, 1 << hid_input, -1); hid_get_item(d, &h) > 0; ) { @@ -164,14 +170,17 @@ hid_interrupt(bthid_session_p s, char *data, int len) switch (usage) { case HUG_X: mouse_x = val; + mevents ++; break; case HUG_Y: mouse_y = val; + mevents ++; break; case HUG_WHEEL: mouse_z = -val; + mevents ++; break; case HUG_SYSTEM_SLEEP: @@ -182,20 +191,24 @@ hid_interrupt(bthid_session_p s, char *data, int len) break; case HUP_KEYBOARD: + kevents ++; + if (h.flags & HIO_VARIABLE) { - if (val && nkeys < sizeof(keys)) - keys[nkeys ++] = usage; + if (val && usage < kbd_maxkey()) + bit_set(s->srv->keys, usage); } else { - if (val && nkeys < sizeof(keys)) - keys[nkeys ++] = val; + if (val && val < kbd_maxkey()) + bit_set(s->srv->keys, val); + data ++; len --; len = min(len, h.report_size); while (len > 0) { val = hid_get_data(data, &h); - if (val && nkeys < sizeof(keys)) - keys[nkeys ++] = val; + if (val && val < kbd_maxkey()) + bit_set(s->srv->keys, val); + data ++; len --; } @@ -204,6 +217,97 @@ hid_interrupt(bthid_session_p s, char *data, int len) case HUP_BUTTON: mouse_butt |= (val << (usage - 1)); + mevents ++; + break; + + case HUP_CONSUMER: + if (!val) + break; + + switch (usage) { + case 0xb5: /* Scan Next Track */ + val = 0x19; + break; + + case 0xb6: /* Scan Previous Track */ + val = 0x10; + break; + + case 0xb7: /* Stop */ + val = 0x24; + break; + + case 0xcd: /* Play/Pause */ + val = 0x22; + break; + + case 0xe2: /* Mute */ + val = 0x20; + break; + + case 0xe9: /* Volume Up */ + val = 0x30; + break; + + case 0xea: /* Volume Down */ + val = 0x2E; + break; + + case 0x183: /* Media Select */ + val = 0x6D; + break; + + case 0x018a: /* Mail */ + val = 0x6C; + break; + + case 0x192: /* Calculator */ + val = 0x21; + break; + + case 0x194: /* My Computer */ + val = 0x6B; + break; + + case 0x221: /* WWW Search */ + val = 0x65; + break; + + case 0x223: /* WWW Home */ + val = 0x32; + break; + + case 0x224: /* WWW Back */ + val = 0x6A; + break; + + case 0x225: /* WWW Forward */ + val = 0x69; + break; + + case 0x226: /* WWW Stop */ + val = 0x68; + break; + + case 0227: /* WWW Refresh */ + val = 0x67; + break; + + case 0x22a: /* WWW Favorites */ + val = 0x66; + break; + + default: + val = 0; + break; + } + + /* XXX FIXME - UGLY HACK */ + if (val != 0) { + int buf[4] = { 0xe0, val, 0xe0, val|0x80 }; + + write(s->srv->vkbd, buf, sizeof(buf)); + } break; case HUP_MICROSOFT: @@ -236,13 +340,17 @@ hid_interrupt(bthid_session_p s, char *data, int len) } hid_end_parse(d); + /* Feed keyboard events into kernel */ + if (kevents > 0) + kbd_process_keys(s); + /* - * XXX FIXME Feed mouse and keyboard events into kernel - * The code block below works, but it is not - * good enough + * XXX FIXME Feed mouse events into kernel. + * The code block below works, but it is not good enough. + * Need to track double-clicks etc. */ - if (mouse_x != 0 || mouse_y != 0 || mouse_z != 0 || mouse_butt != 0) { + if (mevents > 0) { struct mouse_info mi; mi.operation = MOUSE_ACTION; diff --git a/usr.sbin/bluetooth/bthidd/kbd.c b/usr.sbin/bluetooth/bthidd/kbd.c new file mode 100644 index 000000000000..888dedc0798b --- /dev/null +++ b/usr.sbin/bluetooth/bthidd/kbd.c @@ -0,0 +1,592 @@ +/* + * kbd.c + * + * Copyright (c) 2004 Maksim Yevmenkin + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id: kbd.c,v 1.2 2004/11/17 21:59:42 max Exp $ + * $FreeBSD$ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "bthidd.h" +#include "kbd.h" + +static void kbd_write(bitstr_t *m, int fb, int make, int fd); +static int kbd_xlate(int code, int make, int *b, int const *eob); + +/* + * HID code to PS/2 set 1 code translation table. + * + * http://www.microsoft.com/whdc/device/input/Scancode.mspx + * + * The table only contains "make" (key pressed) codes. + * The "break" (key released) code is generated as "make" | 0x80 + */ + +#define E0PREFIX (1 << 31) +#define NOBREAK (1 << 30) +#define CODEMASK (~(E0PREFIX|NOBREAK)) + +static int const x[] = +{ +/*==================================================*/ +/* Name HID code Make Break*/ +/*==================================================*/ +/* No Event 00 */ -1, /* None */ +/* Overrun Error 01 */ NOBREAK|0xFF, /* None */ +/* POST Fail 02 */ NOBREAK|0xFC, /* None */ +/* ErrorUndefined 03 */ -1, /* Unassigned */ +/* a A 04 */ 0x1E, /* 9E */ +/* b B 05 */ 0x30, /* B0 */ +/* c C 06 */ 0x2E, /* AE */ +/* d D 07 */ 0x20, /* A0 */ +/* e E 08 */ 0x12, /* 92 */ +/* f F 09 */ 0x21, /* A1 */ +/* g G 0A */ 0x22, /* A2 */ +/* h H 0B */ 0x23, /* A3 */ +/* i I 0C */ 0x17, /* 97 */ +/* j J 0D */ 0x24, /* A4 */ +/* k K 0E */ 0x25, /* A5 */ +/* l L 0F */ 0x26, /* A6 */ +/* m M 10 */ 0x32, /* B2 */ +/* n N 11 */ 0x31, /* B1 */ +/* o O 12 */ 0x18, /* 98 */ +/* p P 13 */ 0x19, /* 99 */ +/* q Q 14 */ 0x10, /* 90 */ +/* r R 15 */ 0x13, /* 93 */ +/* s S 16 */ 0x1F, /* 9F */ +/* t T 17 */ 0x14, /* 94 */ +/* u U 18 */ 0x16, /* 96 */ +/* v V 19 */ 0x2F, /* AF */ +/* w W 1A */ 0x11, /* 91 */ +/* x X 1B */ 0x2D, /* AD */ +/* y Y 1C */ 0x15, /* 95 */ +/* z Z 1D */ 0x2C, /* AC */ +/* 1 ! 1E */ 0x02, /* 82 */ +/* 2 @ 1F */ 0x03, /* 83 */ +/* 3 # 20 */ 0x04, /* 84 */ +/* 4 $ 21 */ 0x05, /* 85 */ +/* 5 % 22 */ 0x06, /* 86 */ +/* 6 ^ 23 */ 0x07, /* 87 */ +/* 7 & 24 */ 0x08, /* 88 */ +/* 8 * 25 */ 0x09, /* 89 */ +/* 9 ( 26 */ 0x0A, /* 8A */ +/* 0 ) 27 */ 0x0B, /* 8B */ +/* Return 28 */ 0x1C, /* 9C */ +/* Escape 29 */ 0x01, /* 81 */ +/* Backspace 2A */ 0x0E, /* 8E */ +/* Tab 2B */ 0x0F, /* 8F */ +/* Space 2C */ 0x39, /* B9 */ +/* - _ 2D */ 0x0C, /* 8C */ +/* = + 2E */ 0x0D, /* 8D */ +/* [ { 2F */ 0x1A, /* 9A */ +/* ] } 30 */ 0x1B, /* 9B */ +/* \ | 31 */ 0x2B, /* AB */ +/* Europe 1 32 */ 0x2B, /* AB */ +/* ; : 33 */ 0x27, /* A7 */ +/* " ' 34 */ 0x28, /* A8 */ +/* ` ~ 35 */ 0x29, /* A9 */ +/* comma < 36 */ 0x33, /* B3 */ +/* . > 37 */ 0x34, /* B4 */ +/* / ? 38 */ 0x35, /* B5 */ +/* Caps Lock 39 */ 0x3A, /* BA */ +/* F1 3A */ 0x3B, /* BB */ +/* F2 3B */ 0x3C, /* BC */ +/* F3 3C */ 0x3D, /* BD */ +/* F4 3D */ 0x3E, /* BE */ +/* F5 3E */ 0x3F, /* BF */ +/* F6 3F */ 0x40, /* C0 */ +/* F7 40 */ 0x41, /* C1 */ +/* F8 41 */ 0x42, /* C2 */ +/* F9 42 */ 0x43, /* C3 */ +/* F10 43 */ 0x44, /* C4 */ +/* F11 44 */ 0x57, /* D7 */ +/* F12 45 */ 0x58, /* D8 */ +/* Print Screen 46 */ E0PREFIX|37, /* E0 B7 */ +/* Scroll Lock 47 */ 0x46, /* C6 */ +#if 0 +/* Break (Ctrl-Pause) 48 */ E0 46 E0 C6, /* None */ +/* Pause 48 */ E1 1D 45 E1 9D C5, /* None */ +#else +/* Break (Ctrl-Pause)/Pause 48 */ NOBREAK /* Special case */, /* None */ +#endif +/* Insert 49 */ E0PREFIX|0x52, /* E0 D2 */ +/* Home 4A */ E0PREFIX|0x47, /* E0 C7 */ +/* Page Up 4B */ E0PREFIX|0x49, /* E0 C9 */ +/* Delete 4C */ E0PREFIX|0x53, /* E0 D3 */ +/* End 4D */ E0PREFIX|0x4F, /* E0 CF */ +/* Page Down 4E */ E0PREFIX|0x51, /* E0 D1 */ +/* Right Arrow 4F */ E0PREFIX|0x4D, /* E0 CD */ +/* Left Arrow 50 */ E0PREFIX|0x4B, /* E0 CB */ +/* Down Arrow 51 */ E0PREFIX|0x50, /* E0 D0 */ +/* Up Arrow 52 */ E0PREFIX|0x48, /* E0 C8 */ +/* Num Lock 53 */ 0x45, /* C5 */ +/* Keypad / 54 */ E0PREFIX|0x35, /* E0 B5 */ +/* Keypad * 55 */ 0x37, /* B7 */ +/* Keypad - 56 */ 0x4A, /* CA */ +/* Keypad + 57 */ 0x4E, /* CE */ +/* Keypad Enter 58 */ E0PREFIX|0x1C, /* E0 9C */ +/* Keypad 1 End 59 */ 0x4F, /* CF */ +/* Keypad 2 Down 5A */ 0x50, /* D0 */ +/* Keypad 3 PageDn 5B */ 0x51, /* D1 */ +/* Keypad 4 Left 5C */ 0x4B, /* CB */ +/* Keypad 5 5D */ 0x4C, /* CC */ +/* Keypad 6 Right 5E */ 0x4D, /* CD */ +/* Keypad 7 Home 5F */ 0x47, /* C7 */ +/* Keypad 8 Up 60 */ 0x48, /* C8 */ +/* Keypad 9 PageUp 61 */ 0x49, /* C9 */ +/* Keypad 0 Insert 62 */ 0x52, /* D2 */ +/* Keypad . Delete 63 */ 0x53, /* D3 */ +/* Europe 2 64 */ 0x56, /* D6 */ +/* App 65 */ E0PREFIX|0x5D, /* E0 DD */ +/* Keyboard Power 66 */ E0PREFIX|0x5E, /* E0 DE */ +/* Keypad = 67 */ 0x59, /* D9 */ +/* F13 68 */ 0x64, /* E4 */ +/* F14 69 */ 0x65, /* E5 */ +/* F15 6A */ 0x66, /* E6 */ +/* F16 6B */ 0x67, /* E7 */ +/* F17 6C */ 0x68, /* E8 */ +/* F18 6D */ 0x69, /* E9 */ +/* F19 6E */ 0x6A, /* EA */ +/* F20 6F */ 0x6B, /* EB */ +/* F21 70 */ 0x6C, /* EC */ +/* F22 71 */ 0x6D, /* ED */ +/* F23 72 */ 0x6E, /* EE */ +/* F24 73 */ 0x76, /* F6 */ +/* Keyboard Execute 74 */ -1, /* Unassigned */ +/* Keyboard Help 75 */ -1, /* Unassigned */ +/* Keyboard Menu 76 */ -1, /* Unassigned */ +/* Keyboard Select 77 */ -1, /* Unassigned */ +/* Keyboard Stop 78 */ -1, /* Unassigned */ +/* Keyboard Again 79 */ -1, /* Unassigned */ +/* Keyboard Undo 7A */ -1, /* Unassigned */ +/* Keyboard Cut 7B */ -1, /* Unassigned */ +/* Keyboard Copy 7C */ -1, /* Unassigned */ +/* Keyboard Paste 7D */ -1, /* Unassigned */ +/* Keyboard Find 7E */ -1, /* Unassigned */ +/* Keyboard Mute 7F */ -1, /* Unassigned */ +/* Keyboard Volume Up 80 */ -1, /* Unassigned */ +/* Keyboard Volume Dn 81 */ -1, /* Unassigned */ +/* Keyboard Locking Caps Lock 82 */ -1, /* Unassigned */ +/* Keyboard Locking Num Lock 83 */ -1, /* Unassigned */ +/* Keyboard Locking Scroll Lock 84 */ -1, /* Unassigned */ +/* Keypad comma 85 */ 0x7E, /* FE */ +/* Keyboard Equal Sign 86 */ -1, /* Unassigned */ +/* Keyboard Int'l 1 87 */ 0x73, /* F3 */ +/* Keyboard Int'l 2 88 */ 0x70, /* F0 */ +/* Keyboard Int'l 2 89 */ 0x7D, /* FD */ +/* Keyboard Int'l 4 8A */ 0x79, /* F9 */ +/* Keyboard Int'l 5 8B */ 0x7B, /* FB */ +/* Keyboard Int'l 6 8C */ 0x5C, /* DC */ +/* Keyboard Int'l 7 8D */ -1, /* Unassigned */ +/* Keyboard Int'l 8 8E */ -1, /* Unassigned */ +/* Keyboard Int'l 9 8F */ -1, /* Unassigned */ +/* Keyboard Lang 1 90 */ NOBREAK|0xF2, /* None */ +/* Keyboard Lang 2 91 */ NOBREAK|0xF1, /* None */ +/* Keyboard Lang 3 92 */ 0x78, /* F8 */ +/* Keyboard Lang 4 93 */ 0x77, /* F7 */ +/* Keyboard Lang 5 94 */ 0x76, /* F6 */ +/* Keyboard Lang 6 95 */ -1, /* Unassigned */ +/* Keyboard Lang 7 96 */ -1, /* Unassigned */ +/* Keyboard Lang 8 97 */ -1, /* Unassigned */ +/* Keyboard Lang 9 98 */ -1, /* Unassigned */ +/* Keyboard Alternate Erase 99 */ -1, /* Unassigned */ +/* Keyboard SysReq/Attention 9A */ -1, /* Unassigned */ +/* Keyboard Cancel 9B */ -1, /* Unassigned */ +/* Keyboard Clear 9C */ -1, /* Unassigned */ +/* Keyboard Prior 9D */ -1, /* Unassigned */ +/* Keyboard Return 9E */ -1, /* Unassigned */ +/* Keyboard Separator 9F */ -1, /* Unassigned */ +/* Keyboard Out A0 */ -1, /* Unassigned */ +/* Keyboard Oper A1 */ -1, /* Unassigned */ +/* Keyboard Clear/Again A2 */ -1, /* Unassigned */ +/* Keyboard CrSel/Props A3 */ -1, /* Unassigned */ +/* Keyboard ExSel A4 */ -1, /* Unassigned */ +/* Reserved A5 */ -1, /* Reserved */ +/* Reserved A6 */ -1, /* Reserved */ +/* Reserved A7 */ -1, /* Reserved */ +/* Reserved A8 */ -1, /* Reserved */ +/* Reserved A9 */ -1, /* Reserved */ +/* Reserved AA */ -1, /* Reserved */ +/* Reserved AB */ -1, /* Reserved */ +/* Reserved AC */ -1, /* Reserved */ +/* Reserved AD */ -1, /* Reserved */ +/* Reserved AE */ -1, /* Reserved */ +/* Reserved AF */ -1, /* Reserved */ +/* Reserved B0 */ -1, /* Reserved */ +/* Reserved B1 */ -1, /* Reserved */ +/* Reserved B2 */ -1, /* Reserved */ +/* Reserved B3 */ -1, /* Reserved */ +/* Reserved B4 */ -1, /* Reserved */ +/* Reserved B5 */ -1, /* Reserved */ +/* Reserved B6 */ -1, /* Reserved */ +/* Reserved B7 */ -1, /* Reserved */ +/* Reserved B8 */ -1, /* Reserved */ +/* Reserved B9 */ -1, /* Reserved */ +/* Reserved BA */ -1, /* Reserved */ +/* Reserved BB */ -1, /* Reserved */ +/* Reserved BC */ -1, /* Reserved */ +/* Reserved BD */ -1, /* Reserved */ +/* Reserved BE */ -1, /* Reserved */ +/* Reserved BF */ -1, /* Reserved */ +/* Reserved C0 */ -1, /* Reserved */ +/* Reserved C1 */ -1, /* Reserved */ +/* Reserved C2 */ -1, /* Reserved */ +/* Reserved C3 */ -1, /* Reserved */ +/* Reserved C4 */ -1, /* Reserved */ +/* Reserved C5 */ -1, /* Reserved */ +/* Reserved C6 */ -1, /* Reserved */ +/* Reserved C8 */ -1, /* Reserved */ +/* Reserved C9 */ -1, /* Reserved */ +/* Reserved CA */ -1, /* Reserved */ +/* Reserved CB */ -1, /* Reserved */ +/* Reserved CC */ -1, /* Reserved */ +/* Reserved CD */ -1, /* Reserved */ +/* Reserved CE */ -1, /* Reserved */ +/* Reserved CF */ -1, /* Reserved */ +/* Reserved D0 */ -1, /* Reserved */ +/* Reserved D1 */ -1, /* Reserved */ +/* Reserved D2 */ -1, /* Reserved */ +/* Reserved D3 */ -1, /* Reserved */ +/* Reserved D4 */ -1, /* Reserved */ +/* Reserved D5 */ -1, /* Reserved */ +/* Reserved D6 */ -1, /* Reserved */ +/* Reserved D8 */ -1, /* Reserved */ +/* Reserved D9 */ -1, /* Reserved */ +/* Reserved DA */ -1, /* Reserved */ +/* Reserved DB */ -1, /* Reserved */ +/* Reserved DC */ -1, /* Reserved */ +/* Reserved DD */ -1, /* Reserved */ +/* Reserved DE */ -1, /* Reserved */ +/* Reserved DF */ -1, /* Reserved */ +/* Left Control E0 */ 0x1D, /* 9D */ +/* Left Control E0 */ 0x1D, /* 9D */ +/* Left Shift E1 */ 0x2A, /* AA */ +/* Left Alt E2 */ 0x38, /* B8 */ +/* Left GUI E3 */ E0PREFIX|0x5B, /* E0 DB */ +/* Right Control E4 */ E0PREFIX|0x1D, /* E0 9D */ +/* Right Shift E5 */ 0x36, /* B6 */ +/* Right Alt E6 */ E0PREFIX|0x38, /* E0 B8 */ +/* Right GUI E7 */ E0PREFIX|0x5C /* E0 DC */ +}; + +#define xsize (sizeof(x)/sizeof(x[0])) + +/* + * Get a max HID keycode (aligned) + */ + +int +kbd_maxkey(void) +{ + return (xsize); +} + +/* + * Process keys + */ + +int +kbd_process_keys(bthid_session_p s) +{ + bitstr_t r[bitstr_size(xsize)]; + int f0, f1, i; + + assert(s != NULL); + assert(s->srv != NULL); + + bit_ffs(s->srv->keys, xsize, &f0); + bit_ffs(s->keys, xsize, &f1); + + if (f0 == -1) { + /* all keys are released, no keys pressed */ + if (f1 != -1) { + kbd_write(s->keys, f1, 0, s->srv->vkbd); + memset(s->keys, 0, bitstr_size(xsize)); + } + + return (0); + } + + if (f1 == -1) { + /* some keys got pressed, no keys released */ + if (f0 != -1) { + memcpy(s->keys, s->srv->keys, bitstr_size(xsize)); + kbd_write(s->keys, f0, 1, s->srv->vkbd); + memset(s->srv->keys, 0, bitstr_size(xsize)); + } + + return (0); + } + + /* some keys got pressed, some keys got released */ + memset(r, 0, bitstr_size(xsize)); + + for (i = f1; i < xsize; i++) { + if (bit_test(s->keys, i)) { + if (!bit_test(s->srv->keys, i)) { + bit_clear(s->keys, i); + bit_set(r, i); + } else + bit_clear(s->srv->keys, i); + } + } + + for (i = f0; i < xsize; i++) { + if (bit_test(s->srv->keys, i)) { + if (!bit_test(s->keys, i)) + bit_set(s->keys, i); + else + bit_clear(s->srv->keys, i); + } + } + + bit_ffs(r, xsize, &f0); + bit_ffs(s->srv->keys, xsize, &f1); + + if (f0 > 0) + kbd_write(r, f0, 0, s->srv->vkbd); + + if (f1 > 0) { + kbd_write(s->srv->keys, f1, 1, s->srv->vkbd); + memset(s->srv->keys, 0, bitstr_size(xsize)); + } + + return (0); +} + +/* + * Get current keyboard index (fd version) + */ + +int +kbd_get_index_fd(int fd) +{ + keyboard_info_t info; + + return ((ioctl(fd, KDGKBINFO, &info) < 0)? -1 : info.kb_index); +} + +/* + * Get current keyboard index (device node version) + */ + +int +kbd_get_index(char const *device) +{ + int fd, index; + + fd = open(device, O_RDONLY); + if (fd < 0) + return (-1); + + index = kbd_get_index_fd(fd); + + close(fd); + + return (index); +} + +/* + * Switch keyboards. Execute external script to switch keyboards. The keyboard + * index will be passed to the script in the first argument (argv[1]). We use + * external script here to allow user to customize his/her wireless keyboard, + * i.e. set mapping etc. In theory, all parameters could be picked up from the + * rc.conf. + */ + +int +kbd_switch(char const *script, int index) +{ + pid_t pid; + int status; + + if (script == NULL) { + syslog(LOG_NOTICE, "Could not switch keyboards. " \ + "Switch script is not defined"); + return (-1); + } + + if (access(script, X_OK) < 0) { + syslog(LOG_ERR, "The %s is not executable. %s (%d)", + script, strerror(errno), errno); + return (-1); + } + + pid = fork(); + + if (pid == (pid_t) -1) { + syslog(LOG_ERR, "Could not create process for %s. %s (%d)", + script, strerror(errno), errno); + return (-1); + } + + if (pid == 0) { + char arg[16]; + char *argv[3] = { (char *) script, arg, NULL }; + + snprintf(arg, sizeof(arg), "%d", index); + execv(script, argv); + + syslog(LOG_ERR, "Could not execute '%s %d'. %s (%d)", + script, index, strerror(errno), errno); + + exit(1); + } + + if (waitpid(pid, &status, 0) < 0) { + syslog(LOG_ERR, "Could not waitpid for %s. %s (%d)", + script, strerror(errno), errno); + return (-1); + } + + if (WIFEXITED(status) && WEXITSTATUS(status)) { + syslog(LOG_ERR, "External command '%s %d' failed, exit code %d", + script, index, WEXITSTATUS(status)); + return (-1); + } + + return (0); +} + +/* + * Translate given keymap and write keyscodes + */ + +static void +kbd_write(bitstr_t *m, int fb, int make, int fd) +{ + int i, *b, *eob, n, buf[64]; + + b = buf; + eob = b + sizeof(buf)/sizeof(buf[0]); + i = fb; + + while (i < xsize) { + if (bit_test(m, i)) { + n = kbd_xlate(i, make, b, eob); + if (n == -1) { + write(fd, buf, (b - buf) * sizeof(buf[0])); + b = buf; + continue; + } + + b += n; + } + + i ++; + } + + if (b != buf) + write(fd, buf, (b - buf) * sizeof(buf[0])); +} + + +/* + * Translate HID code into PS/2 code and put codes into buffer b. + * Returns the number of codes put in b. Return -1 if buffer has not + * enough space. + */ + +#undef PUT +#define PUT(c, n, b, eob) \ +do { \ + if ((b) >= (eob)) \ + return (-1); \ + *(b) = (c); \ + (b) ++; \ + (n) ++; \ +} while (0) + +static int +kbd_xlate(int code, int make, int *b, int const *eob) +{ + int c, n; + + n = 0; + + if (code >= xsize) + return (0); /* HID code is not in the table */ + + /* Handle special case - Pause/Break */ + if (code == 0x48) { + if (!make) + return (0); /* No break code */ + +#if 0 +XXX FIXME + if (ctrl_is_pressed) { + /* Break (Ctrl-Pause) */ + PUT(0xe0, n, b, eob); + PUT(0x46, n, b, eob); + PUT(0xe0, n, b, eob); + PUT(0xc6, n, b, eob); + } else { + /* Pause */ + PUT(0xe1, n, b, eob); + PUT(0x1d, n, b, eob); + PUT(0x45, n, b, eob); + PUT(0xe1, n, b, eob); + PUT(0x9d, n, b, eob); + PUT(0xc5, n, b, eob); + } +#endif + + return (n); + } + + if ((c = x[code]) == -1) + return (0); /* HID code translation is not defined */ + + if (make) { + if (c & E0PREFIX) + PUT(0xe0, n, b, eob); + + PUT((c & CODEMASK), n, b, eob); + } else if (!(c & NOBREAK)) { + if (c & E0PREFIX) + PUT(0xe0, n, b, eob); + + PUT((0x80|(c & CODEMASK)), n, b, eob); + } + + return (n); +} + diff --git a/usr.sbin/bluetooth/bthidd/kbd.h b/usr.sbin/bluetooth/bthidd/kbd.h new file mode 100644 index 000000000000..06e2cafeb8e2 --- /dev/null +++ b/usr.sbin/bluetooth/bthidd/kbd.h @@ -0,0 +1,41 @@ +/* + * kbd.h + * + * Copyright (c) 2004 Maksim Yevmenkin + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id: kbd.h,v 1.2 2004/11/17 21:59:42 max Exp $ + * $FreeBSD$ + */ + +#ifndef _KBD_H_ +#define _KBD_H_ + +int kbd_maxkey (void); +int kbd_process_keys(bthid_session_p s); +int kbd_get_index_fd(int fd); +int kbd_get_index (char const *device); +int kbd_switch (char const *script, int index); + +#endif /* ndef _KBD_H_ */ diff --git a/usr.sbin/bluetooth/bthidd/parser.y b/usr.sbin/bluetooth/bthidd/parser.y index f4fd560d34fa..7c4140c6a2ce 100644 --- a/usr.sbin/bluetooth/bthidd/parser.y +++ b/usr.sbin/bluetooth/bthidd/parser.y @@ -26,18 +26,18 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: parser.y,v 1.3 2004/02/13 21:46:21 max Exp $ + * $Id: parser.y,v 1.4 2004/11/17 21:59:42 max Exp $ * $FreeBSD$ */ #include #include #include -#include #include #include #include #include +#include #ifndef BTHIDCONTROL #include @@ -52,7 +52,7 @@ #define LOGCRIT stderr #define LOGERR stderr #define LOGWARNING stderr -#define EOL "\n" +#define EOL "\n" #endif /* ndef BTHIDCONTROL */ #include "bthid_config.h" diff --git a/usr.sbin/bluetooth/bthidd/server.c b/usr.sbin/bluetooth/bthidd/server.c index d1887db0071c..e2f716e22170 100644 --- a/usr.sbin/bluetooth/bthidd/server.c +++ b/usr.sbin/bluetooth/bthidd/server.c @@ -25,7 +25,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: server.c,v 1.5 2004/02/26 21:43:36 max Exp $ + * $Id: server.c,v 1.7 2004/11/17 21:59:42 max Exp $ * $FreeBSD$ */ @@ -42,6 +42,7 @@ #include #include "bthidd.h" #include "bthid_config.h" +#include "kbd.h" #undef max #define max(x, y) (((x) > (y))? (x) : (y)) @@ -65,6 +66,25 @@ server_init(bthid_server_p srv) FD_ZERO(&srv->wfdset); LIST_INIT(&srv->sessions); + /* Allocate HID keycodes buffer */ + srv->keys = bit_alloc(kbd_maxkey()); + if (srv->keys == NULL) { + syslog(LOG_ERR, "Could not allocate HID keys buffer"); + return (-1); + } + memset(srv->keys, 0, bitstr_size(kbd_maxkey())); + + /* Get wired keyboard index (if was not specified) */ + if (srv->windex == -1) { + srv->windex = kbd_get_index("/dev/console"); + if (srv->windex < 0) { + syslog(LOG_ERR, "Could not open get wired keyboard " \ + "index. %s (%d)", strerror(errno), errno); + free(srv->keys); + return (-1); + } + } + /* Open /dev/consolectl */ srv->cons = open("/dev/consolectl", O_RDWR); if (srv->cons < 0) { @@ -73,12 +93,24 @@ server_init(bthid_server_p srv) return (-1); } + /* Open /dev/vkbdctl */ + srv->vkbd = open("/dev/vkbdctl", O_RDWR); + if (srv->vkbd < 0) { + syslog(LOG_ERR, "Could not open /dev/vkbdctl. %s (%d)", + strerror(errno), errno); + close(srv->cons); + free(srv->keys); + return (-1); + } + /* Create control socket */ srv->ctrl = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BLUETOOTH_PROTO_L2CAP); if (srv->ctrl < 0) { syslog(LOG_ERR, "Could not create control L2CAP socket. " \ "%s (%d)", strerror(errno), errno); + close(srv->vkbd); close(srv->cons); + free(srv->keys); return (-1); } @@ -90,14 +122,20 @@ server_init(bthid_server_p srv) if (bind(srv->ctrl, (struct sockaddr *) &l2addr, sizeof(l2addr)) < 0) { syslog(LOG_ERR, "Could not bind control L2CAP socket. " \ "%s (%d)", strerror(errno), errno); + close(srv->ctrl); + close(srv->vkbd); close(srv->cons); + free(srv->keys); return (-1); } if (listen(srv->ctrl, 10) < 0) { syslog(LOG_ERR, "Could not listen on control L2CAP socket. " \ "%s (%d)", strerror(errno), errno); + close(srv->ctrl); + close(srv->vkbd); close(srv->cons); + free(srv->keys); return (-1); } @@ -107,7 +145,9 @@ server_init(bthid_server_p srv) syslog(LOG_ERR, "Could not create interrupt L2CAP socket. " \ "%s (%d)", strerror(errno), errno); close(srv->ctrl); + close(srv->vkbd); close(srv->cons); + free(srv->keys); return (-1); } @@ -116,16 +156,22 @@ server_init(bthid_server_p srv) if (bind(srv->intr, (struct sockaddr *) &l2addr, sizeof(l2addr)) < 0) { syslog(LOG_ERR, "Could not bind interrupt L2CAP socket. " \ "%s (%d)", strerror(errno), errno); + close(srv->intr); close(srv->ctrl); + close(srv->vkbd); close(srv->cons); + free(srv->keys); return (-1); } if (listen(srv->intr, 10) < 0) { syslog(LOG_ERR, "Could not listen on interrupt L2CAP socket. "\ "%s (%d)", strerror(errno), errno); + close(srv->intr); close(srv->ctrl); + close(srv->vkbd); close(srv->cons); + free(srv->keys); return (-1); } @@ -146,12 +192,15 @@ server_shutdown(bthid_server_p srv) assert(srv != NULL); close(srv->cons); + close(srv->vkbd); close(srv->ctrl); close(srv->intr); while (!LIST_EMPTY(&srv->sessions)) session_close(LIST_FIRST(&srv->sessions)); + free(srv->keys); + memset(srv, 0, sizeof(*srv)); } diff --git a/usr.sbin/bluetooth/bthidd/session.c b/usr.sbin/bluetooth/bthidd/session.c index 523b9185151e..eefd98ae9603 100644 --- a/usr.sbin/bluetooth/bthidd/session.c +++ b/usr.sbin/bluetooth/bthidd/session.c @@ -25,7 +25,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: session.c,v 1.1 2004/02/12 22:46:59 max Exp $ + * $Id: session.c,v 1.2 2004/11/17 21:59:42 max Exp $ * $FreeBSD$ */ @@ -37,6 +37,7 @@ #include #include #include "bthidd.h" +#include "kbd.h" /* * Create new session @@ -53,10 +54,15 @@ session_open(bthid_server_p srv, bdaddr_p bdaddr) if ((s = (bthid_session_p) malloc(sizeof(*s))) != NULL) { s->srv = srv; memcpy(&s->bdaddr, bdaddr, sizeof(s->bdaddr)); - s->ctrl = s->intr = -1; + s->ctrl = -1; + s->intr = -1; s->state = CLOSED; - - LIST_INSERT_HEAD(&srv->sessions, s, next); + s->keys = bit_alloc(kbd_maxkey()); + if (s->keys == NULL) { + free(s); + s = NULL; + } else + LIST_INSERT_HEAD(&srv->sessions, s, next); } return (s); @@ -130,6 +136,8 @@ session_close(bthid_session_p s) s->srv->maxfd --; } + free(s->keys); + memset(s, 0, sizeof(*s)); free(s); }