Update bthidd(8) code and hook it up to the build.

bthidd(8) now was integrated with vkbd(4) and supports
multiple keyboards via vkbd(4)/kbdmux(4).

The code was tested with Apple Bluetooth keyboard and
SE k700i cell phone (remote control feature).

MFC after:	1 month
This commit is contained in:
Maksim Yevmenkin 2006-09-07 21:47:49 +00:00
parent 0f31538c9d
commit 7aebfa93ac
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=162128
14 changed files with 644 additions and 464 deletions

View File

@ -4,6 +4,8 @@
SUBDIR= \
bcmfw \
bt3cfw \
bthidcontrol \
bthidd \
hccontrol \
hcsecd \
hcseriald \

View File

@ -1,14 +1,14 @@
# $Id: Makefile,v 1.3 2004/08/17 21:49:46 max Exp $
# $Id: Makefile,v 1.6 2006/09/07 21:36:55 max Exp $
# $FreeBSD$
PROG= bthidd
#MAN= bthidd.8 bthidd.conf.5
NO_MAN=
MAN= bthidd.8
# bthidd.conf.5
SRCS= bthidd.c client.c hid.c kbd.c lexer.l parser.y server.c \
session.c
CFLAGS+= -I${.CURDIR}
WARNS?= 2
WARNS?= 6
DEBUG_FLAGS= -g
DPADD= ${LIBBLUETOOTH} ${LIBSDP}

View File

@ -1,7 +1,9 @@
/*
* bthid_config.h
*
* Copyright (c) 2004 Maksim Yevmenkin <m_evmenkin@yahoo.com>
*/
/*-
* Copyright (c) 2006 Maksim Yevmenkin <m_evmenkin@yahoo.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -25,7 +27,7 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $Id: bthid_config.h,v 1.3 2004/02/17 22:05:02 max Exp $
* $Id: bthid_config.h,v 1.4 2006/09/07 21:06:53 max Exp $
* $FreeBSD$
*/
@ -44,24 +46,25 @@ struct hid_device
unsigned reconnect_initiate : 1;
unsigned battery_power : 1;
unsigned normally_connectable : 1;
unsigned reserved : 12;
unsigned keyboard : 1;
unsigned reserved : 11;
report_desc_t desc; /* HID report descriptor */
LIST_ENTRY(hid_device) next; /* link to the next */
};
typedef struct hid_device hid_device_t;
typedef struct hid_device * hid_device_p;
extern char *config_file;
extern char *hids_file;
extern char const *config_file;
extern char const *hids_file;
int read_config_file (void);
int32_t read_config_file (void);
void clean_config (void);
hid_device_p get_hid_device (bdaddr_p bdaddr);
hid_device_p get_next_hid_device (hid_device_p d);
void print_hid_device (hid_device_p hid_device, FILE *f);
int read_hids_file (void);
int write_hids_file (void);
int32_t read_hids_file (void);
int32_t write_hids_file (void);
#endif /* ndef _BTHID_CONFIG_H_ */

View File

@ -0,0 +1,128 @@
.\" Copyright (c) 2006 Maksim Yevmenkin <m_evmenkin@yahoo.com>
.\" 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: bthidd.8,v 1.1 2006/09/07 21:36:55 max Exp $
.\" $FreeBSD$
.\"
.Dd September 7, 2006
.Dt BTHIDD 8
.Os
.Sh NAME
.Nm bthidd
.Nd Bluetooth HID daemon
.Sh SYNOPSIS
.Nm
.Fl h
.Nm
.Op Fl a Ar BD_ADDR
.Op Fl c Ar file
.Op Fl H Ar file
.Op Fl p Ar file
.Op Fl t Ar val
.Sh DESCRIPTION
The
.Nm
daemon handles remote Bluetooth HID devices.
.Pp
The options are as follows:
.Bl -tag -width indent
.It Fl a Ar BD_ADDR
Specify the local address to listen on.
By default, server will listen on
.Dv ANY
address.
The address can be specified as BD_ADDR or name.
If name was specified then the
.Nm
daemon will attempt to resolve the name via
.Xr bt_gethostbyname 3 .
.It Fl c Ar file
Specify path to the configuration file.
The default path is
.Pa /etc/bluetooth/bthidd.conf .
.It Fl d
Do not detach from the controlling terminal, i.e., run in foreground.
.It Fl H Ar file
Specify path to the known HIDs file.
The default path is
.Pa /var/db/bthidd.hids .
.It Fl h
Display usage message and exit.
.It Fl p Ar file
Specify path to the PID file.
The default path is
.Pa /var/run/bthidd.pid .
.It Fl t Ar val
Specify client rescan interval in seconds.
The
.Nm
daemon will periodically scan for newly configured Bluetooth HID devices or
disconnected
.Dq passive
Bluetooth HID devices and will attempt to establish outgoing connection.
Default rescan interval is 10 seconds.
.El
.Sh CAVEAT
Any Bluetooth HID device that has
.Dv HUP_KEYBOARD
or
.Dv HUP_CONSUMER
entries in its descriptor is considered as
.Dq keyboard .
For each
.Dq keyboard
Bluetooth HID device,
the
.Nm
daemon will use separate instance of
.Xr vkbd 4
virtual keyboard.
Therefore
.Xr kbdmux 4
driver must be used to properly multiplex input from multiple keyboards.
.Sh KNOWN LIMITATIONS
The
.Nm
daemon currently does not handle key auto repeat and double click mouse events.
Those events work under
.Xr X 7
just fine,
but not in text console.
.Pp
This man page needs more work.
Also need a man page documenting format of the
.Pa /etc/bluetooth/bthidd.conf
configuraion file.
.Sh FILES
.Bl -tag -width ".Pa /etc/bluetooth/bthidd.conf" -compact
.It Pa /etc/bluetooth/bthidd.conf
.It Pa /var/db/bthidd.hids
.It Pa /var/run/bthidd.pid
.El
.Sh SEE ALSO
.Xr kbdmux 4 ,
.Xr vkbd 4 ,
.Xr bthidcontrol 8
.Sh AUTHORS
.An Maksim Yevmenkin Aq m_evmenkin@yahoo.com

View File

@ -1,7 +1,9 @@
/*
* bthidd.c
*
* Copyright (c) 2004 Maksim Yevmenkin <m_evmenkin@yahoo.com>
*/
/*-
* Copyright (c) 2006 Maksim Yevmenkin <m_evmenkin@yahoo.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -25,7 +27,7 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $Id: bthidd.c,v 1.7 2004/11/17 21:59:42 max Exp $
* $Id: bthidd.c,v 1.8 2006/09/07 21:06:53 max Exp $
* $FreeBSD$
*/
@ -42,42 +44,40 @@
#include <syslog.h>
#include <unistd.h>
#include <usbhid.h>
#include "bthidd.h"
#include "bthid_config.h"
#include "bthidd.h"
static int write_pid_file (char const *file);
static int remove_pid_file (char const *file);
static int elapsed (int tval);
static void sighandler (int s);
static void sighup (int s);
static int32_t write_pid_file (char const *file);
static int32_t remove_pid_file (char const *file);
static int32_t elapsed (int32_t tval);
static void sighandler (int32_t s);
static void usage (void);
/*
* bthidd
*/
static int done = 0; /* are we done? */
static int reload = 0; /* reload config file */
static int32_t done = 0; /* are we done? */
int
main(int argc, char *argv[])
int32_t
main(int32_t argc, char *argv[])
{
struct bthid_server srv;
struct sigaction sa;
char const *pid_file = BTHIDD_PIDFILE, *ep = NULL;
int opt, detach, tval;
char const *pid_file = BTHIDD_PIDFILE;
char *ep;
int32_t opt, detach, tval;
memset(&srv, 0, sizeof(srv));
memcpy(&srv.bdaddr, NG_HCI_BDADDR_ANY, sizeof(srv.bdaddr));
srv.windex = -1;
memset(&srv.bdaddr, 0, sizeof(srv.bdaddr));
detach = 1;
tval = 10; /* sec */
while ((opt = getopt(argc, argv, "a:c:dH:hp:s:t:")) != -1) {
while ((opt = getopt(argc, argv, "a:c:dH:hp:t:")) != -1) {
switch (opt) {
case 'a': /* BDADDR */
if (!bt_aton(optarg, &srv.bdaddr)) {
struct hostent *he = NULL;
struct hostent *he;
if ((he = bt_gethostbyname(optarg)) == NULL)
errx(1, "%s: %s", optarg, hstrerror(h_errno));
@ -102,22 +102,12 @@ main(int argc, char *argv[])
pid_file = optarg;
break;
case 's': /* switch script */
srv.script = optarg;
break;
case 't': /* rescan interval */
tval = strtol(optarg, (char **) &ep, 10);
if (*ep != '\0' || tval <= 0)
usage();
break;
case 'u': /* wired keyboard index */
srv.windex = strtol(optarg, (char **) &ep, 10);
if (*ep != '\0' || srv.windex < 0)
usage();
break;
case 'h':
default:
usage();
@ -139,19 +129,13 @@ main(int argc, char *argv[])
sa.sa_handler = sighandler;
if (sigaction(SIGTERM, &sa, NULL) < 0 ||
sigaction(SIGHUP, &sa, NULL) < 0 ||
sigaction(SIGINT, &sa, NULL) < 0) {
syslog(LOG_CRIT, "Could not install signal handlers. %s (%d)",
strerror(errno), errno);
exit(1);
}
sa.sa_handler = sighup;
if (sigaction(SIGHUP, &sa, NULL) < 0) {
syslog(LOG_CRIT, "Could not install signal handlers. %s (%d)",
strerror(errno), errno);
exit(1);
}
sa.sa_handler = SIG_IGN;
if (sigaction(SIGPIPE, &sa, NULL) < 0) {
syslog(LOG_CRIT, "Could not install signal handlers. %s (%d)",
@ -177,15 +161,6 @@ main(int argc, char *argv[])
if (server_do(&srv) < 0)
break;
if (reload) {
if (write_hids_file() < 0 ||
read_config_file() < 0 ||
read_hids_file() < 0)
break;
reload = 0;
}
}
server_shutdown(&srv);
@ -200,10 +175,10 @@ main(int argc, char *argv[])
* Write pid file
*/
static int
static int32_t
write_pid_file(char const *file)
{
FILE *pid = NULL;
FILE *pid;
assert(file != NULL);
@ -223,7 +198,7 @@ write_pid_file(char const *file)
* Remote pid file
*/
static int
static int32_t
remove_pid_file(char const *file)
{
assert(file != NULL);
@ -241,10 +216,10 @@ remove_pid_file(char const *file)
* Returns true if desired time interval has elapsed
*/
static int
elapsed(int tval)
static int32_t
elapsed(int32_t tval)
{
static struct timeval last = { 0, };
static struct timeval last = { 0, 0 };
struct timeval now;
gettimeofday(&now, NULL);
@ -258,23 +233,16 @@ elapsed(int tval)
}
/*
* Signal handlers
* Signal handler
*/
static void
sighandler(int s)
sighandler(int32_t s)
{
syslog(LOG_NOTICE, "Got signal %d, total number of signals %d",
s, ++ done);
}
static void
sighup(int s)
{
syslog(LOG_NOTICE, "Got SIGHUP: reload config");
reload = 1;
}
/*
* Display usage and exit
*/
@ -291,9 +259,7 @@ usage(void)
" -H file specify known HIDs file name\n" \
" -h display this message\n" \
" -p file specify PID file name\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);
}

View File

@ -1,7 +1,9 @@
/*
* bthidd.h
*
* Copyright (c) 2004 Maksim Yevmenkin <m_evmenkin@yahoo.com>
*/
/*-
* Copyright (c) 2006 Maksim Yevmenkin <m_evmenkin@yahoo.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -25,7 +27,7 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $Id: bthidd.h,v 1.6 2004/11/17 21:59:42 max Exp $
* $Id: bthidd.h,v 1.7 2006/09/07 21:06:53 max Exp $
* $FreeBSD$
*/
@ -40,14 +42,10 @@ struct bthid_session;
struct bthid_server
{
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 */
int32_t cons; /* /dev/consolectl */
int32_t ctrl; /* control channel (listen) */
int32_t intr; /* intr. channel (listen) */
int32_t maxfd; /* max fd in sets */
fd_set rfdset; /* read descriptor set */
fd_set wfdset; /* write descriptor set */
LIST_HEAD(, bthid_session) sessions;
@ -59,35 +57,37 @@ 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 */
int32_t ctrl; /* control channel */
int32_t intr; /* interrupt channel */
int32_t vkbd; /* virual keyboard */
bdaddr_t bdaddr;/* remote bdaddr */
short state; /* session state */
uint16_t state; /* session state */
#define CLOSED 0
#define W4CTRL 1
#define W4INTR 2
#define OPEN 3
bitstr_t *keys; /* pressed keys map */
bitstr_t *keys1; /* keys map (new) */
bitstr_t *keys2; /* keys map (old) */
LIST_ENTRY(bthid_session) next; /* link to next */
};
typedef struct bthid_session bthid_session_t;
typedef struct bthid_session * bthid_session_p;
int server_init (bthid_server_p srv);
int32_t server_init (bthid_server_p srv);
void server_shutdown (bthid_server_p srv);
int server_do (bthid_server_p srv);
int32_t server_do (bthid_server_p srv);
int client_rescan (bthid_server_p srv);
int client_connect (bthid_server_p srv, int fd);
int32_t client_rescan (bthid_server_p srv);
int32_t client_connect (bthid_server_p srv, int fd);
bthid_session_p session_open (bthid_server_p srv, bdaddr_p bdaddr);
bthid_session_p session_open (bthid_server_p srv, hid_device_p const d);
bthid_session_p session_by_bdaddr(bthid_server_p srv, bdaddr_p bdaddr);
bthid_session_p session_by_fd (bthid_server_p srv, int fd);
bthid_session_p session_by_fd (bthid_server_p srv, int32_t fd);
void session_close (bthid_session_p s);
int hid_control (bthid_session_p s, char *data, int len);
int hid_interrupt (bthid_session_p s, char *data, int len);
int32_t hid_control (bthid_session_p s, uint8_t *data, int32_t len);
int32_t hid_interrupt (bthid_session_p s, uint8_t *data, int32_t len);
#endif /* ndef _BTHIDD_H_ */

View File

@ -1,7 +1,9 @@
/*
* client.c
*
* Copyright (c) 2004 Maksim Yevmenkin <m_evmenkin@yahoo.com>
*/
/*-
* Copyright (c) 2006 Maksim Yevmenkin <m_evmenkin@yahoo.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -25,7 +27,7 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $Id: client.c,v 1.6 2004/02/26 21:57:55 max Exp $
* $Id: client.c,v 1.7 2006/09/07 21:06:53 max Exp $
* $FreeBSD$
*/
@ -40,10 +42,10 @@
#include <syslog.h>
#include <unistd.h>
#include <usbhid.h>
#include "bthidd.h"
#include "bthid_config.h"
#include "bthidd.h"
static int client_socket(bdaddr_p bdaddr, int psm);
static int32_t client_socket(bdaddr_p bdaddr, int32_t psm);
/*
* Get next config entry and create outbound connection (if required)
@ -53,13 +55,13 @@ static int client_socket(bdaddr_p bdaddr, int psm);
* Create_Connection command is still pending. Weird...
*/
static int connect_in_progress = 0;
static int32_t connect_in_progress = 0;
int
int32_t
client_rescan(bthid_server_p srv)
{
static hid_device_p d = NULL;
bthid_session_p s = NULL;
static hid_device_p d;
bthid_session_p s;
assert(srv != NULL);
@ -82,9 +84,9 @@ client_rescan(bthid_server_p srv)
"(new_device=%d, reconnect_initiate=%d)",
bt_ntoa(&d->bdaddr, NULL), d->new_device, d->reconnect_initiate);
if ((s = session_open(srv, &d->bdaddr)) == NULL) {
syslog(LOG_CRIT, "Could not open outbound session for %s. " \
"Not enough memory", bt_ntoa(&d->bdaddr, NULL));
if ((s = session_open(srv, d)) == NULL) {
syslog(LOG_CRIT, "Could not create outbound session for %s",
bt_ntoa(&d->bdaddr, NULL));
return (-1);
}
@ -112,12 +114,12 @@ client_rescan(bthid_server_p srv)
* Process connect on the socket
*/
int
client_connect(bthid_server_p srv, int fd)
int32_t
client_connect(bthid_server_p srv, int32_t fd)
{
bthid_session_p s = NULL;
hid_device_p d = NULL;
int error, len;
bthid_session_p s;
hid_device_p d;
int32_t error, len;
assert(srv != NULL);
assert(fd >= 0);
@ -181,6 +183,15 @@ client_connect(bthid_server_p srv, int fd)
s->state = OPEN;
connect_in_progress = 0;
/* Register session's vkbd descriptor (if any) for read */
if (s->state == OPEN && d->keyboard) {
assert(s->vkbd != -1);
FD_SET(s->vkbd, &srv->rfdset);
if (s->vkbd > srv->maxfd)
srv->maxfd = s->vkbd;
}
break;
default:
@ -200,10 +211,10 @@ client_connect(bthid_server_p srv, int fd)
*/
static int
client_socket(bdaddr_p bdaddr, int psm)
client_socket(bdaddr_p bdaddr, int32_t psm)
{
struct sockaddr_l2cap l2addr;
int s, m;
int32_t s, m;
s = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BLUETOOTH_PROTO_L2CAP);
if (s < 0)
@ -222,7 +233,7 @@ client_socket(bdaddr_p bdaddr, int psm)
l2addr.l2cap_len = sizeof(l2addr);
l2addr.l2cap_family = AF_BLUETOOTH;
memcpy(&l2addr.l2cap_bdaddr, NG_HCI_BDADDR_ANY, sizeof(l2addr.l2cap_bdaddr));
memset(&l2addr.l2cap_bdaddr, 0, sizeof(l2addr.l2cap_bdaddr));
l2addr.l2cap_psm = 0;
if (bind(s, (struct sockaddr *) &l2addr, sizeof(l2addr)) < 0) {
@ -231,7 +242,7 @@ client_socket(bdaddr_p bdaddr, int psm)
}
memcpy(&l2addr.l2cap_bdaddr, bdaddr, sizeof(l2addr.l2cap_bdaddr));
l2addr.l2cap_psm = htole16(psm);
l2addr.l2cap_psm = psm;
if (connect(s, (struct sockaddr *) &l2addr, sizeof(l2addr)) < 0 &&
errno != EINPROGRESS) {

View File

@ -1,7 +1,9 @@
/*
* hid.c
*
* Copyright (c) 2004 Maksim Yevmenkin <m_evmenkin@yahoo.com>
*/
/*-
* Copyright (c) 2006 Maksim Yevmenkin <m_evmenkin@yahoo.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -25,7 +27,7 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $Id: hid.c,v 1.4 2004/11/17 21:59:42 max Exp $
* $Id: hid.c,v 1.5 2006/09/07 21:06:53 max Exp $
* $FreeBSD$
*/
@ -34,16 +36,16 @@
#include <sys/queue.h>
#include <assert.h>
#include <bluetooth.h>
#include <errno.h>
#include <dev/usb/usb.h>
#include <dev/usb/usbhid.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <syslog.h>
#include <unistd.h>
#include <usbhid.h>
#include "bthidd.h"
#include "bthid_config.h"
#include "bthidd.h"
#include "kbd.h"
#undef min
@ -52,15 +54,12 @@
#undef ASIZE
#define ASIZE(a) (sizeof(a)/sizeof(a[0]))
#undef HID_BUT
#define HID_BUT(i) ((i) < 3 ? (((i) ^ 3) % 3) : (i))
/*
* Process data from control channel
*/
int
hid_control(bthid_session_p s, char *data, int len)
int32_t
hid_control(bthid_session_p s, uint8_t *data, int32_t len)
{
assert(s != NULL);
assert(data != NULL);
@ -123,13 +122,13 @@ hid_control(bthid_session_p s, char *data, int len)
* Process data from the interrupt channel
*/
int
hid_interrupt(bthid_session_p s, char *data, int len)
int32_t
hid_interrupt(bthid_session_p s, uint8_t *data, int32_t len)
{
hid_device_p hid_device = NULL;
hid_device_p hid_device;
hid_data_t d;
hid_item_t h;
int report_id, usage, page, val,
int32_t report_id, usage, page, val,
mouse_x, mouse_y, mouse_z, mouse_butt,
mevents, kevents;
@ -143,7 +142,7 @@ hid_interrupt(bthid_session_p s, char *data, int len)
return (-1);
}
if ((unsigned char) data[0] != 0xa1) {
if (data[0] != 0xa1) {
syslog(LOG_ERR, "Got unexpected message 0x%x on " \
"Interrupt channel from %s",
data[0], bt_ntoa(&s->bdaddr, NULL));
@ -198,10 +197,10 @@ hid_interrupt(bthid_session_p s, char *data, int len)
if (h.flags & HIO_VARIABLE) {
if (val && usage < kbd_maxkey())
bit_set(s->srv->keys, usage);
bit_set(s->keys1, usage);
} else {
if (val && val < kbd_maxkey())
bit_set(s->srv->keys, val);
bit_set(s->keys1, val);
data ++;
len --;
@ -210,7 +209,7 @@ hid_interrupt(bthid_session_p s, char *data, int len)
while (len > 0) {
val = hid_get_data(data, &h);
if (val && val < kbd_maxkey())
bit_set(s->srv->keys, val);
bit_set(s->keys1, val);
data ++;
len --;
@ -219,8 +218,15 @@ hid_interrupt(bthid_session_p s, char *data, int len)
break;
case HUP_BUTTON:
mouse_butt |= (val << HID_BUT(usage - 1));
mevents ++;
if (usage != 0) {
if (usage == 2)
usage = 3;
else if (usage == 3)
usage = 2;
mouse_butt |= (val << (usage - 1));
mevents ++;
}
break;
case HUP_CONSUMER:
@ -292,7 +298,7 @@ hid_interrupt(bthid_session_p s, char *data, int len)
val = 0x68;
break;
case 0x227: /* WWW Refresh */
case 0227: /* WWW Refresh */
val = 0x67;
break;
@ -307,9 +313,17 @@ hid_interrupt(bthid_session_p s, char *data, int len)
/* XXX FIXME - UGLY HACK */
if (val != 0) {
int buf[4] = { 0xe0, val, 0xe0, val|0x80 };
if (hid_device->keyboard) {
int32_t buf[4] = { 0xe0, val,
0xe0, val|0x80 };
write(s->srv->vkbd, buf, sizeof(buf));
assert(s->vkbd != -1);
write(s->vkbd, buf, sizeof(buf));
} else
syslog(LOG_ERR, "Keyboard events " \
"received from non-keyboard " \
"device %s. Please report",
bt_ntoa(&s->bdaddr, NULL));
}
break;
@ -343,14 +357,30 @@ 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 keyboard events into kernel.
* The code below works, bit host also needs to track
* and handle repeat.
*
* Key repeat currently works in X, but not in console.
*/
if (kevents > 0) {
if (hid_device->keyboard) {
assert(s->vkbd != -1);
kbd_process_keys(s);
} else
syslog(LOG_ERR, "Keyboard events received from " \
"non-keyboard device %s. Please report",
bt_ntoa(&s->bdaddr, NULL));
}
/*
* XXX FIXME Feed mouse events into kernel.
* The code block below works, but it is not good enough.
* Need to track double-clicks etc.
*
* Double click currently works in X, but not in console.
*/
if (mevents > 0) {
@ -370,4 +400,3 @@ hid_interrupt(bthid_session_p s, char *data, int len)
return (0);
}

View File

@ -1,7 +1,9 @@
/*
* kbd.c
*
* Copyright (c) 2004 Maksim Yevmenkin <m_evmenkin@yahoo.com>
*/
/*-
* Copyright (c) 2006 Maksim Yevmenkin <m_evmenkin@yahoo.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -25,7 +27,7 @@
* 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 $
* $Id: kbd.c,v 1.4 2006/09/07 21:06:53 max Exp $
* $FreeBSD$
*/
@ -36,6 +38,9 @@
#include <sys/wait.h>
#include <assert.h>
#include <bluetooth.h>
#include <dev/usb/usb.h>
#include <dev/usb/usbhid.h>
#include <dev/vkbd/vkbd_var.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
@ -45,11 +50,13 @@
#include <string.h>
#include <syslog.h>
#include <unistd.h>
#include <usbhid.h>
#include "bthid_config.h"
#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);
static void kbd_write(bitstr_t *m, int32_t fb, int32_t make, int32_t fd);
static int32_t kbd_xlate(int32_t code, int32_t make, int32_t *b, int32_t const *eob);
/*
* HID code to PS/2 set 1 code translation table.
@ -64,7 +71,7 @@ static int kbd_xlate(int code, int make, int *b, int const *eob);
#define NOBREAK (1 << 30)
#define CODEMASK (~(E0PREFIX|NOBREAK))
static int const x[] =
static int32_t const x[] =
{
/*==================================================*/
/* Name HID code Make Break*/
@ -308,13 +315,13 @@ static int const x[] =
/* Right GUI E7 */ E0PREFIX|0x5C /* E0 DC */
};
#define xsize (sizeof(x)/sizeof(x[0]))
#define xsize ((int32_t)(sizeof(x)/sizeof(x[0])))
/*
* Get a max HID keycode (aligned)
*/
int
int32_t
kbd_maxkey(void)
{
return (xsize);
@ -324,164 +331,72 @@ kbd_maxkey(void)
* Process keys
*/
int
int32_t
kbd_process_keys(bthid_session_p s)
{
bitstr_t r[bitstr_size(xsize)];
int f0, f1, i;
bitstr_t diff[bitstr_size(xsize)];
int32_t f1, f2, i;
assert(s != NULL);
assert(s->srv != NULL);
bit_ffs(s->srv->keys, xsize, &f0);
bit_ffs(s->keys, xsize, &f1);
/* Check if the new keys have been pressed */
bit_ffs(s->keys1, 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);
}
/* Check if old keys still pressed */
bit_ffs(s->keys2, xsize, &f2);
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));
/* no new key pressed */
if (f2 != -1) {
/* release old keys */
kbd_write(s->keys2, f2, 0, s->vkbd);
memset(s->keys2, 0, bitstr_size(xsize));
}
return (0);
}
/* some keys got pressed, some keys got released */
memset(r, 0, bitstr_size(xsize));
if (f2 == -1) {
/* no old keys, but new keys pressed */
assert(f1 != -1);
memcpy(s->keys2, s->keys1, bitstr_size(xsize));
kbd_write(s->keys1, f1, 1, s->vkbd);
memset(s->keys1, 0, bitstr_size(xsize));
return (0);
}
/* new keys got pressed, old keys got released */
memset(diff, 0, bitstr_size(xsize));
for (i = f2; i < xsize; i ++) {
if (bit_test(s->keys2, i)) {
if (!bit_test(s->keys1, i)) {
bit_clear(s->keys2, i);
bit_set(diff, i);
}
}
}
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);
if (bit_test(s->keys1, i)) {
if (!bit_test(s->keys2, i))
bit_set(s->keys2, i);
else
bit_clear(s->srv->keys, i);
bit_clear(s->keys1, i);
}
}
bit_ffs(r, xsize, &f0);
bit_ffs(s->srv->keys, xsize, &f1);
if (f0 > 0)
kbd_write(r, f0, 0, s->srv->vkbd);
bit_ffs(diff, xsize, &f2);
if (f2 > 0)
kbd_write(diff, f2, 0, s->vkbd);
bit_ffs(s->keys1, xsize, &f1);
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);
kbd_write(s->keys1, f1, 1, s->vkbd);
memset(s->keys1, 0, bitstr_size(xsize));
}
return (0);
@ -492,9 +407,9 @@ kbd_switch(char const *script, int index)
*/
static void
kbd_write(bitstr_t *m, int fb, int make, int fd)
kbd_write(bitstr_t *m, int32_t fb, int32_t make, int32_t fd)
{
int i, *b, *eob, n, buf[64];
int32_t i, *b, *eob, n, buf[64];
b = buf;
eob = b + sizeof(buf)/sizeof(buf[0]);
@ -519,7 +434,6 @@ kbd_write(bitstr_t *m, int fb, int make, int fd)
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
@ -536,10 +450,10 @@ do { \
(n) ++; \
} while (0)
static int
kbd_xlate(int code, int make, int *b, int const *eob)
static int32_t
kbd_xlate(int32_t code, int32_t make, int32_t *b, int32_t const *eob)
{
int c, n;
int32_t c, n;
n = 0;
@ -591,3 +505,76 @@ XXX FIXME
return (n);
}
/*
* Process status change from vkbd(4)
*/
int32_t
kbd_status_changed(bthid_session_p s, uint8_t *data, int32_t len)
{
int32_t leds;
uint8_t hleds, report_id;
hid_device_p hid_device;
hid_data_t d;
hid_item_t h;
assert(s != NULL);
assert(len == sizeof(vkbd_status_t));
leds = ((vkbd_status_p) data)->leds;
hleds = 0;
report_id = NO_REPORT_ID;
hid_device = get_hid_device(&s->bdaddr);
assert(hid_device != NULL);
for (d = hid_start_parse(hid_device->desc, 1 << hid_output, -1);
hid_get_item(d, &h) > 0; ) {
if (HID_PAGE(h.usage) == HUP_LEDS) {
if (report_id == NO_REPORT_ID)
report_id = h.report_ID;
else if (h.report_ID != report_id)
syslog(LOG_WARNING, "Output HID report IDs " \
"for %s do not match: %d vs. %d. " \
"Please report",
bt_ntoa(&s->bdaddr, NULL),
h.report_ID, report_id);
switch(HID_USAGE(h.usage)) {
case 0x01: /* Num Lock LED */
if (leds & LED_NUM)
hid_set_data(&hleds, &h, 1);
break;
case 0x02: /* Caps Lock LED */
if (leds & LED_CAP)
hid_set_data(&hleds, &h, 1);
break;
case 0x03: /* Scroll Lock LED */
if (leds & LED_SCR)
hid_set_data(&hleds, &h, 1);
break;
/* XXX add other LEDs ? */
}
}
}
hid_end_parse(d);
data[0] = 0xa2; /* DATA output (HID output report) */
if (report_id != NO_REPORT_ID) {
data[1] = report_id;
data[2] = hleds;
len = 3;
} else {
data[1] = hleds;
len = 2;
}
write(s->intr, data, len);
return (0);
}

View File

@ -1,7 +1,9 @@
/*
* kbd.h
*
* Copyright (c) 2004 Maksim Yevmenkin <m_evmenkin@yahoo.com>
*/
/*-
* Copyright (c) 2006 Maksim Yevmenkin <m_evmenkin@yahoo.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -25,17 +27,15 @@
* 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 $
* $Id: kbd.h,v 1.3 2006/09/07 21:06:53 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);
int32_t kbd_maxkey (void);
int32_t kbd_process_keys (bthid_session_p s);
int32_t kbd_status_changed(bthid_session_p s, uint8_t *data, int32_t len);
#endif /* ndef _KBD_H_ */

View File

@ -1,8 +1,10 @@
%{
/*
* lexer.l
*
* Copyright (c) 2004 Maksim Yevmenkin <m_evmenkin@yahoo.com>
*/
/*-
* Copyright (c) 2006 Maksim Yevmenkin <m_evmenkin@yahoo.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -26,13 +28,15 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $Id: lexer.l,v 1.2 2004/02/13 21:46:20 max Exp $
* $Id: lexer.l,v 1.3 2006/09/07 21:06:53 max Exp $
* $FreeBSD$
*/
#include <bluetooth.h>
#include <stdlib.h>
#include "parser.h"
int yylex (void);
%}
%option yylineno noyywrap nounput
@ -87,7 +91,7 @@ hexbytestring 0x{hexbyte}
}
{hexbytestring} {
char *ep = NULL;
char *ep;
yylval.num = strtoul(yytext, &ep, 16);

View File

@ -1,8 +1,10 @@
%{
/*
* parser.y
*
* Copyright (c) 2004 Maksim Yevmenkin <m_evmenkin@yahoo.com>
*/
/*-
* Copyright (c) 2006 Maksim Yevmenkin <m_evmenkin@yahoo.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -26,12 +28,14 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $Id: parser.y,v 1.4 2004/11/17 21:59:42 max Exp $
* $Id: parser.y,v 1.7 2006/09/07 21:06:53 max Exp $
* $FreeBSD$
*/
#include <sys/queue.h>
#include <bluetooth.h>
#include <dev/usb/usb.h>
#include <dev/usb/usbhid.h>
#include <errno.h>
#include <limits.h>
#include <stdio.h>
@ -59,15 +63,17 @@
int yyparse (void);
int yylex (void);
static int check_hid_device(hid_device_p hid_device);
void yyerror (char const *);
static int32_t check_hid_device(hid_device_p hid_device);
static void free_hid_device (hid_device_p hid_device);
extern FILE *yyin;
extern int yylineno;
char *config_file = BTHIDD_CONFFILE;
char *hids_file = BTHIDD_HIDSFILE;
char const *config_file = BTHIDD_CONFFILE;
char const *hids_file = BTHIDD_HIDSFILE;
static char buffer[1024];
static int hid_descriptor_size;
static int32_t hid_descriptor_size;
static hid_device_t *hid_device = NULL;
static LIST_HEAD(, hid_device) hid_devices;
@ -75,7 +81,7 @@ static LIST_HEAD(, hid_device) hid_devices;
%union {
bdaddr_t bdaddr;
int num;
int32_t num;
}
%token <bdaddr> T_BDADDRSTRING
@ -197,7 +203,7 @@ hid_descriptor_bytes: hid_descriptor_byte
hid_descriptor_byte: T_HEXBYTE
{
if (hid_descriptor_size >= sizeof(buffer)) {
if (hid_descriptor_size >= (int32_t) sizeof(buffer)) {
SYSLOG(LOGCRIT, "HID descriptor is too big" EOL);
YYABORT;
}
@ -221,11 +227,10 @@ yyerror(char const *message)
}
/* Re-read config file */
int
int32_t
read_config_file(void)
{
extern FILE *yyin;
int e;
int32_t e;
if (config_file == NULL) {
SYSLOG(LOGERR, "Unknown config file name!" EOL);
@ -257,10 +262,10 @@ void
clean_config(void)
{
while (!LIST_EMPTY(&hid_devices)) {
hid_device_p hid_device = LIST_FIRST(&hid_devices);
hid_device_p d = LIST_FIRST(&hid_devices);
LIST_REMOVE(hid_device, next);
free_hid_device(hid_device);
LIST_REMOVE(d, next);
free_hid_device(d);
}
}
@ -268,13 +273,13 @@ clean_config(void)
hid_device_p
get_hid_device(bdaddr_p bdaddr)
{
hid_device_p hid_device;
hid_device_p d;
LIST_FOREACH(hid_device, &hid_devices, next)
if (memcmp(&hid_device->bdaddr, bdaddr, sizeof(bdaddr_t)) == 0)
LIST_FOREACH(d, &hid_devices, next)
if (memcmp(&d->bdaddr, bdaddr, sizeof(bdaddr_t)) == 0)
break;
return (hid_device);
return (d);
}
/* Get next config entry */
@ -286,7 +291,7 @@ get_next_hid_device(hid_device_p d)
/* Print config entry */
void
print_hid_device(hid_device_p hid_device, FILE *f)
print_hid_device(hid_device_p d, FILE *f)
{
/* XXX FIXME hack! */
struct report_desc {
@ -295,8 +300,8 @@ print_hid_device(hid_device_p hid_device, FILE *f)
};
/* XXX FIXME hack! */
struct report_desc *desc = (struct report_desc *) hid_device->desc;
int i;
struct report_desc *desc = (struct report_desc *) d->desc;
uint32_t i;
fprintf(f,
"device {\n" \
@ -307,11 +312,11 @@ print_hid_device(hid_device_p hid_device, FILE *f)
" battery_power %s;\n" \
" normally_connectable %s;\n" \
" hid_descriptor {",
bt_ntoa(&hid_device->bdaddr, NULL),
hid_device->control_psm, hid_device->interrupt_psm,
hid_device->reconnect_initiate? "true" : "false",
hid_device->battery_power? "true" : "false",
hid_device->normally_connectable? "true" : "false");
bt_ntoa(&d->bdaddr, NULL),
d->control_psm, d->interrupt_psm,
d->reconnect_initiate? "true" : "false",
d->battery_power? "true" : "false",
d->normally_connectable? "true" : "false");
for (i = 0; i < desc->size; i ++) {
if ((i % 8) == 0)
@ -327,53 +332,76 @@ print_hid_device(hid_device_p hid_device, FILE *f)
}
/* Check config entry */
static int
check_hid_device(hid_device_p hid_device)
static int32_t
check_hid_device(hid_device_p d)
{
if (get_hid_device(&hid_device->bdaddr) != NULL) {
hid_data_t hd;
hid_item_t hi;
int32_t page;
if (get_hid_device(&d->bdaddr) != NULL) {
SYSLOG(LOGERR, "Ignoring duplicated entry for bdaddr %s" EOL,
bt_ntoa(&hid_device->bdaddr, NULL));
bt_ntoa(&d->bdaddr, NULL));
return (0);
}
if (hid_device->control_psm == 0) {
if (d->control_psm == 0) {
SYSLOG(LOGERR, "Ignoring entry with invalid control PSM" EOL);
return (0);
}
if (hid_device->interrupt_psm == 0) {
if (d->interrupt_psm == 0) {
SYSLOG(LOGERR, "Ignoring entry with invalid interrupt PSM" EOL);
return (0);
}
if (hid_device->desc == NULL) {
if (d->desc == NULL) {
SYSLOG(LOGERR, "Ignoring entry without HID descriptor" EOL);
return (0);
}
/* XXX somehow need to make sure descriptor is valid */
for (hd = hid_start_parse(d->desc, ~0, -1); hid_get_item(hd, &hi) > 0; ) {
switch (hi.kind) {
case hid_collection:
case hid_endcollection:
case hid_output:
case hid_feature:
break;
case hid_input:
/* Check if the device may send keystrokes */
page = HID_PAGE(hi.usage);
if (page == HUP_KEYBOARD || page == HUP_CONSUMER)
d->keyboard = 1;
break;
}
}
hid_end_parse(hd);
return (1);
}
/* Free config entry */
static void
free_hid_device(hid_device_p hid_device)
free_hid_device(hid_device_p d)
{
if (hid_device->desc != NULL)
hid_dispose_report_desc(hid_device->desc);
if (d->desc != NULL)
hid_dispose_report_desc(d->desc);
memset(hid_device, 0, sizeof(*hid_device));
free(hid_device);
memset(d, 0, sizeof(*d));
free(d);
}
/* Re-read hids file */
int
int32_t
read_hids_file(void)
{
FILE *f = NULL;
hid_device_t *hid_device = NULL;
char *line = NULL;
FILE *f;
hid_device_t *d;
char *line;
bdaddr_t bdaddr;
int lineno;
int32_t lineno;
if (hids_file == NULL) {
SYSLOG(LOGERR, "Unknown HIDs file name!" EOL);
@ -399,8 +427,8 @@ read_hids_file(void)
continue;
}
if ((hid_device = get_hid_device(&bdaddr)) != NULL)
hid_device->new_device = 0;
if ((d = get_hid_device(&bdaddr)) != NULL)
d->new_device = 0;
}
fclose(f);
@ -409,12 +437,12 @@ read_hids_file(void)
}
/* Write hids file */
int
int32_t
write_hids_file(void)
{
char path[PATH_MAX];
FILE *f = NULL;
hid_device_t *hid_device = NULL;
FILE *f;
hid_device_t *d;
if (hids_file == NULL) {
SYSLOG(LOGERR, "Unknown HIDs file name!" EOL);
@ -429,9 +457,9 @@ write_hids_file(void)
return (-1);
}
LIST_FOREACH(hid_device, &hid_devices, next)
if (!hid_device->new_device)
fprintf(f, "%s\n", bt_ntoa(&hid_device->bdaddr, NULL));
LIST_FOREACH(d, &hid_devices, next)
if (!d->new_device)
fprintf(f, "%s\n", bt_ntoa(&d->bdaddr, NULL));
fclose(f);

View File

@ -1,7 +1,9 @@
/*
* server.c
*
* Copyright (c) 2004 Maksim Yevmenkin <m_evmenkin@yahoo.com>
*/
/*-
* Copyright (c) 2006 Maksim Yevmenkin <m_evmenkin@yahoo.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -25,13 +27,14 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $Id: server.c,v 1.7 2004/11/17 21:59:42 max Exp $
* $Id: server.c,v 1.9 2006/09/07 21:06:53 max Exp $
* $FreeBSD$
*/
#include <sys/queue.h>
#include <assert.h>
#include <bluetooth.h>
#include <dev/vkbd/vkbd_var.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
@ -40,21 +43,21 @@
#include <syslog.h>
#include <unistd.h>
#include <usbhid.h>
#include "bthidd.h"
#include "bthid_config.h"
#include "bthidd.h"
#include "kbd.h"
#undef max
#define max(x, y) (((x) > (y))? (x) : (y))
static int server_accept (bthid_server_p srv, int fd);
static int server_process(bthid_server_p srv, int fd);
static int32_t server_accept (bthid_server_p srv, int32_t fd);
static int32_t server_process(bthid_server_p srv, int32_t fd);
/*
* Initialize server
*/
int
int32_t
server_init(bthid_server_p srv)
{
struct sockaddr_l2cap l2addr;
@ -66,25 +69,6 @@ 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) {
@ -93,24 +77,12 @@ 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);
}
@ -123,9 +95,7 @@ server_init(bthid_server_p srv)
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);
}
@ -133,9 +103,7 @@ server_init(bthid_server_p srv)
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);
}
@ -145,9 +113,7 @@ 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);
}
@ -158,9 +124,7 @@ server_init(bthid_server_p srv)
"%s (%d)", strerror(errno), errno);
close(srv->intr);
close(srv->ctrl);
close(srv->vkbd);
close(srv->cons);
free(srv->keys);
return (-1);
}
@ -169,9 +133,7 @@ server_init(bthid_server_p srv)
"%s (%d)", strerror(errno), errno);
close(srv->intr);
close(srv->ctrl);
close(srv->vkbd);
close(srv->cons);
free(srv->keys);
return (-1);
}
@ -192,15 +154,12 @@ 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));
}
@ -208,12 +167,12 @@ server_shutdown(bthid_server_p srv)
* Do one server iteration
*/
int
int32_t
server_do(bthid_server_p srv)
{
struct timeval tv;
fd_set rfdset, wfdset;
int n, fd;
int32_t n, fd;
assert(srv != NULL);
@ -258,13 +217,13 @@ server_do(bthid_server_p srv)
* Accept new connection
*/
static int
server_accept(bthid_server_p srv, int fd)
static int32_t
server_accept(bthid_server_p srv, int32_t fd)
{
bthid_session_p s = NULL;
hid_device_p d = NULL;
bthid_session_p s;
hid_device_p d;
struct sockaddr_l2cap l2addr;
int len, new_fd;
int32_t len, new_fd;
len = sizeof(l2addr);
if ((new_fd = accept(fd, (struct sockaddr *) &l2addr, &len)) < 0) {
@ -274,26 +233,25 @@ server_accept(bthid_server_p srv, int fd)
return (-1);
}
/* Is device configured? */
if ((d = get_hid_device(&l2addr.l2cap_bdaddr)) == NULL) {
syslog(LOG_ERR, "Rejecting %s connection from %s. " \
"Device not configured",
(fd == srv->ctrl)? "control" : "interrupt",
bt_ntoa(&l2addr.l2cap_bdaddr, NULL));
close(new_fd);
return (-1);
}
/* Check if we have session for the device */
if ((s = session_by_bdaddr(srv, &l2addr.l2cap_bdaddr)) == NULL) {
/* Is device configured? */
if ((d = get_hid_device(&l2addr.l2cap_bdaddr)) == NULL) {
syslog(LOG_ERR, "Rejecting %s connection from %s. " \
"Device not configured",
(fd == srv->ctrl)? "control" : "interrupt",
bt_ntoa(&l2addr.l2cap_bdaddr, NULL));
close(new_fd);
return (-1);
}
d->new_device = 0; /* reset new device flag */
write_hids_file();
/* Create new inbound session */
if ((s = session_open(srv, &l2addr.l2cap_bdaddr)) == NULL) {
syslog(LOG_CRIT, "Could not open inbound session " \
"for %s. Not enough memory",
bt_ntoa(&l2addr.l2cap_bdaddr, NULL));
if ((s = session_open(srv, d)) == NULL) {
syslog(LOG_CRIT, "Could not open inbound session "
"for %s", bt_ntoa(&l2addr.l2cap_bdaddr, NULL));
close(new_fd);
return (-1);
}
@ -318,6 +276,15 @@ server_accept(bthid_server_p srv, int fd)
(fd == srv->ctrl)? "control" : "interrupt",
bt_ntoa(&l2addr.l2cap_bdaddr, NULL));
/* Register session's vkbd descriptor (if needed) for read */
if (s->state == OPEN && d->keyboard) {
assert(s->vkbd != -1);
FD_SET(s->vkbd, &srv->rfdset);
if (s->vkbd > srv->maxfd)
srv->maxfd = s->vkbd;
}
return (0);
}
@ -325,18 +292,36 @@ server_accept(bthid_server_p srv, int fd)
* Process data on the connection
*/
static int
server_process(bthid_server_p srv, int fd)
static int32_t
server_process(bthid_server_p srv, int32_t fd)
{
bthid_session_p s = session_by_fd(srv, fd);
char data[1024];
int len;
bthid_session_p s = session_by_fd(srv, fd);
int32_t len, to_read;
int32_t (*cb)(bthid_session_p, uint8_t *, int32_t);
union {
uint8_t b[1024];
vkbd_status_t s;
} data;
if (s == NULL)
return (0); /* can happen on device disconnect */
if (fd == s->ctrl) {
cb = hid_control;
to_read = sizeof(data.b);
} else if (fd == s->intr) {
cb = hid_interrupt;
to_read = sizeof(data.b);
} else {
assert(fd == s->vkbd);
cb = kbd_status_changed;
to_read = sizeof(data.s);
}
do {
len = read(fd, data, sizeof(data));
len = read(fd, &data, to_read);
} while (len < 0 && errno == EINTR);
if (len < 0) {
@ -356,10 +341,7 @@ server_process(bthid_server_p srv, int fd)
return (0);
}
if (fd == s->ctrl)
hid_control(s, data, len);
else
hid_interrupt(s, data, len);
(*cb)(s, (uint8_t *) &data, len);
return (0);
}

View File

@ -1,7 +1,9 @@
/*
* session.c
*
* Copyright (c) 2004 Maksim Yevmenkin <m_evmenkin@yahoo.com>
*/
/*-
* Copyright (c) 2006 Maksim Yevmenkin <m_evmenkin@yahoo.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -25,17 +27,22 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $Id: session.c,v 1.2 2004/11/17 21:59:42 max Exp $
* $Id: session.c,v 1.3 2006/09/07 21:06:53 max Exp $
* $FreeBSD$
*/
#include <sys/queue.h>
#include <assert.h>
#include <bluetooth.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include <unistd.h>
#include <usbhid.h>
#include "bthid_config.h"
#include "bthidd.h"
#include "kbd.h"
@ -44,27 +51,51 @@
*/
bthid_session_p
session_open(bthid_server_p srv, bdaddr_p bdaddr)
session_open(bthid_server_p srv, hid_device_p const d)
{
bthid_session_p s = NULL;
bthid_session_p s;
assert(srv != NULL);
assert(bdaddr != NULL);
assert(d != NULL);
if ((s = (bthid_session_p) malloc(sizeof(*s))) != NULL) {
s->srv = srv;
memcpy(&s->bdaddr, bdaddr, sizeof(s->bdaddr));
s->ctrl = -1;
s->intr = -1;
s->state = CLOSED;
s->keys = bit_alloc(kbd_maxkey());
if (s->keys == NULL) {
if ((s = (bthid_session_p) malloc(sizeof(*s))) == NULL)
return (NULL);
s->srv = srv;
memcpy(&s->bdaddr, &d->bdaddr, sizeof(s->bdaddr));
s->ctrl = -1;
s->intr = -1;
if (d->keyboard) {
/* Open /dev/vkbdctl */
s->vkbd = open("/dev/vkbdctl", O_RDWR);
if (s->vkbd < 0) {
syslog(LOG_ERR, "Could not open /dev/vkbdctl " \
"for %s. %s (%d)", bt_ntoa(&s->bdaddr, NULL),
strerror(errno), errno);
free(s);
s = NULL;
} else
LIST_INSERT_HEAD(&srv->sessions, s, next);
return (NULL);
}
} else
s->vkbd = -1;
s->state = CLOSED;
s->keys1 = bit_alloc(kbd_maxkey());
if (s->keys1 == NULL) {
free(s);
return (NULL);
}
s->keys2 = bit_alloc(kbd_maxkey());
if (s->keys2 == NULL) {
free(s->keys1);
free(s);
return (NULL);
}
LIST_INSERT_HEAD(&srv->sessions, s, next);
return (s);
}
@ -75,7 +106,7 @@ session_open(bthid_server_p srv, bdaddr_p bdaddr)
bthid_session_p
session_by_bdaddr(bthid_server_p srv, bdaddr_p bdaddr)
{
bthid_session_p s = NULL;
bthid_session_p s;
assert(srv != NULL);
assert(bdaddr != NULL);
@ -92,15 +123,15 @@ session_by_bdaddr(bthid_server_p srv, bdaddr_p bdaddr)
*/
bthid_session_p
session_by_fd(bthid_server_p srv, int fd)
session_by_fd(bthid_server_p srv, int32_t fd)
{
bthid_session_p s = NULL;
bthid_session_p s;
assert(srv != NULL);
assert(fd >= 0);
LIST_FOREACH(s, &srv->sessions, next)
if (s->ctrl == fd || s->intr == fd)
if (s->ctrl == fd || s->intr == fd || s->vkbd == fd)
break;
return (s);
@ -136,7 +167,16 @@ session_close(bthid_session_p s)
s->srv->maxfd --;
}
free(s->keys);
if (s->vkbd != -1) {
FD_CLR(s->vkbd, &s->srv->rfdset);
close(s->vkbd);
if (s->srv->maxfd == s->vkbd)
s->srv->maxfd --;
}
free(s->keys1);
free(s->keys2);
memset(s, 0, sizeof(*s));
free(s);