diff options
author | emax <emax@FreeBSD.org> | 2004-04-10 00:18:00 +0000 |
---|---|---|
committer | emax <emax@FreeBSD.org> | 2004-04-10 00:18:00 +0000 |
commit | 043ec21649d9c0e33013b9092b6ea700f0d15ab8 (patch) | |
tree | 7d74dd469839b4cadda98c84728a960fffee125a | |
parent | 96b75c2267d1d5caf4783f60f78743070ccfa0c8 (diff) | |
download | FreeBSD-src-043ec21649d9c0e33013b9092b6ea700f0d15ab8.zip FreeBSD-src-043ec21649d9c0e33013b9092b6ea700f0d15ab8.tar.gz |
Start committing Bluetooth HID (Human Interface Device) support.
Note: bthidd(8) is still not complete. Need to commit kernel
support (a-la Linux /dev/input) to feed HID events into kernel.
Also need to write bthidd(8) and bthidd.conf(5) man pages.
-rw-r--r-- | usr.sbin/bluetooth/bthidcontrol/Makefile | 15 | ||||
-rw-r--r-- | usr.sbin/bluetooth/bthidcontrol/bthidcontrol.8 | 99 | ||||
-rw-r--r-- | usr.sbin/bluetooth/bthidcontrol/bthidcontrol.c | 208 | ||||
-rw-r--r-- | usr.sbin/bluetooth/bthidcontrol/bthidcontrol.h | 50 | ||||
-rw-r--r-- | usr.sbin/bluetooth/bthidcontrol/hid.c | 209 | ||||
-rw-r--r-- | usr.sbin/bluetooth/bthidcontrol/sdp.c | 432 | ||||
-rw-r--r-- | usr.sbin/bluetooth/bthidd/Makefile | 14 | ||||
-rw-r--r-- | usr.sbin/bluetooth/bthidd/bthid_config.h | 67 | ||||
-rw-r--r-- | usr.sbin/bluetooth/bthidd/bthidd.c | 256 | ||||
-rw-r--r-- | usr.sbin/bluetooth/bthidd/bthidd.conf.sample | 72 | ||||
-rw-r--r-- | usr.sbin/bluetooth/bthidd/bthidd.h | 88 | ||||
-rw-r--r-- | usr.sbin/bluetooth/bthidd/client.c | 244 | ||||
-rw-r--r-- | usr.sbin/bluetooth/bthidd/hid.c | 262 | ||||
-rw-r--r-- | usr.sbin/bluetooth/bthidd/lexer.l | 100 | ||||
-rw-r--r-- | usr.sbin/bluetooth/bthidd/parser.y | 447 | ||||
-rw-r--r-- | usr.sbin/bluetooth/bthidd/server.c | 316 | ||||
-rw-r--r-- | usr.sbin/bluetooth/bthidd/session.c | 136 |
17 files changed, 3015 insertions, 0 deletions
diff --git a/usr.sbin/bluetooth/bthidcontrol/Makefile b/usr.sbin/bluetooth/bthidcontrol/Makefile new file mode 100644 index 0000000..6c9eafb --- /dev/null +++ b/usr.sbin/bluetooth/bthidcontrol/Makefile @@ -0,0 +1,15 @@ +# $Id: Makefile,v 1.2 2004/02/13 21:44:41 max Exp $ +# $FreeBSD$ + +.PATH: ${.CURDIR}/../bthidd + +PROG= bthidcontrol +MAN= bthidcontrol.8 +SRCS= bthidcontrol.c hid.c lexer.l parser.y sdp.c +WARNS?= 1 +CFLAGS+= -DBTHIDCONTROL=1 -I${.CURDIR}/../bthidd + +DPADD= ${LIBBLUETOOTH} ${LIBSDP} ${LIBUSBHID} +LDADD= -lbluetooth -lsdp -lusbhid + +.include <bsd.prog.mk> diff --git a/usr.sbin/bluetooth/bthidcontrol/bthidcontrol.8 b/usr.sbin/bluetooth/bthidcontrol/bthidcontrol.8 new file mode 100644 index 0000000..95327df --- /dev/null +++ b/usr.sbin/bluetooth/bthidcontrol/bthidcontrol.8 @@ -0,0 +1,99 @@ +.\" Copyright (c) 2004 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: bthidcontrol.8,v 1.1 2004/02/13 21:44:41 max Exp $ +.\" $FreeBSD$ +.\" +.Dd February 13, 2004 +.Dt BTHIDCONTROL 8 +.Os +.Sh NAME +.Nm bthidcontrol +.Nd Bluetooth HID control utility +.Sh SYNOPSIS +.Nm +.Fl h +.Nm +.Op Fl a Ar BD_ADDR +.Op Fl c Ar file +.Op Fl H Ar file +.Ar command +.Sh DESCRIPTION +The +.Nm +utility can be used to query remote Bluetooth HID devices, dump HID descriptors +in human readable form and perform simple manipulations on the Bluetooth HID +daemon configuration files. +.Pp +The +.Nm +utility will print results to the standard output and error messages to the +standard error. +.Pp +The options are as follows: +.Bl -tag -width indent +.It Fl a Ar BD_ADDR +Specify BD_ADDR for the HID device. +Example: +.Fl a Li 00:01:02:03:04:05 . +.It Fl c Ar file +Specify path to the Bluetooth HID daemon configuration file. +The default path is +.Pa /etc/bluetooth/bthidd.conf . +.It Fl H Ar file +Specify path to the Bluetooth HID daemon known HIDs file. +The default path is +.Pa /var/db/bthidd.hids . +.It Fl h +Display usage message and exit. +.It Ar command +One of the supported commands (see below). +Special command +.Cm help +can be used to obtain the list of all supported commands. +To get more information about specific command use +.Cm help Ar command . +.El +.Sh COMMANDS +The currently supported node commands in +.Nm +are: +.Pp +.Bl -tag -offset indent -compact +.It Cm Query +.It Cm Dump +.It Cm Known +.It Cm Forget +.El +.Sh DIAGNOSTICS +.Ex -std +.Sh FILES +.Bl -tag -width ".Pa /etc/bluetooth/bthidd.conf" -compact +.It Pa /etc/bluetooth/bthidd.conf +.It Pa /var/db/bthidd.hids +.El +.Sh SEE ALSO +.Xr bthidd 8 +.Sh AUTHORS +.An Maksim Yevmenkin Aq m_evmenkin@yahoo.com diff --git a/usr.sbin/bluetooth/bthidcontrol/bthidcontrol.c b/usr.sbin/bluetooth/bthidcontrol/bthidcontrol.c new file mode 100644 index 0000000..21539e3 --- /dev/null +++ b/usr.sbin/bluetooth/bthidcontrol/bthidcontrol.c @@ -0,0 +1,208 @@ +/* + * bthidcontrol.c + * + * Copyright (c) 2004 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: bthidcontrol.c,v 1.2 2004/02/13 21:44:41 max Exp $ + * $FreeBSD$ + */ + +#include <sys/queue.h> +#include <assert.h> +#include <bluetooth.h> +#include <err.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <usbhid.h> +#include "bthid_config.h" +#include "bthidcontrol.h" + +static int do_bthid_command(bdaddr_p bdaddr, int argc, char **argv); +static struct bthid_command * find_bthid_command(char const *command, struct bthid_command *category); +static void print_bthid_command(struct bthid_command *category); +static void usage(void); + +int32_t hid_sdp_query(bdaddr_t const *local, bdaddr_t const *remote, int32_t *error); + +/* + * bthidcontrol + */ + +int +main(int argc, char *argv[]) +{ + bdaddr_t bdaddr; + int opt; + + hid_init(NULL); + memcpy(&bdaddr, NG_HCI_BDADDR_ANY, sizeof(bdaddr)); + + while ((opt = getopt(argc, argv, "a:c:H:h")) != -1) { + switch (opt) { + case 'a': /* bdaddr */ + if (!bt_aton(optarg, &bdaddr)) { + struct hostent *he = NULL; + + if ((he = bt_gethostbyname(optarg)) == NULL) + errx(1, "%s: %s", optarg, hstrerror(h_errno)); + + memcpy(&bdaddr, he->h_addr, sizeof(bdaddr)); + } + break; + + case 'c': /* config file */ + config_file = optarg; + break; + + case 'H': /* HIDs file */ + hids_file = optarg; + break; + + case 'h': + default: + usage(); + /* NOT REACHED */ + } + } + + argc -= optind; + argv += optind; + + if (*argv == NULL) + usage(); + + return (do_bthid_command(&bdaddr, argc, argv)); +} /* main */ + +/* Execute commands */ +static int +do_bthid_command(bdaddr_p bdaddr, int argc, char **argv) +{ + char *cmd = argv[0]; + struct bthid_command *c = NULL; + int e, help; + + help = 0; + if (strcasecmp(cmd, "help") == 0) { + argc --; + argv ++; + + if (argc <= 0) { + fprintf(stdout, "Supported commands:\n"); + print_bthid_command(sdp_commands); + print_bthid_command(hid_commands); + fprintf(stdout, "\nFor more information use " \ + "'help command'\n"); + + return (OK); + } + + help = 1; + cmd = argv[0]; + } + + c = find_bthid_command(cmd, sdp_commands); + if (c == NULL) + c = find_bthid_command(cmd, hid_commands); + + if (c == NULL) { + fprintf(stdout, "Unknown command: \"%s\"\n", cmd); + return (ERROR); + } + + if (!help) + e = (c->handler)(bdaddr, -- argc, ++ argv); + else + e = USAGE; + + switch (e) { + case OK: + case FAILED: + break; + + case ERROR: + fprintf(stdout, "Could not execute command \"%s\". %s\n", + cmd, strerror(errno)); + break; + + case USAGE: + fprintf(stdout, "Usage: %s\n%s\n", c->command, c->description); + break; + + default: assert(0); break; + } + + return (e); +} /* do_bthid_command */ + +/* Try to find command in specified category */ +static struct bthid_command * +find_bthid_command(char const *command, struct bthid_command *category) +{ + struct bthid_command *c = NULL; + + for (c = category; c->command != NULL; c++) { + char *c_end = strchr(c->command, ' '); + + if (c_end != NULL) { + int len = c_end - c->command; + + if (strncasecmp(command, c->command, len) == 0) + return (c); + } else if (strcasecmp(command, c->command) == 0) + return (c); + } + + return (NULL); +} /* find_bthid_command */ + +/* Print commands in specified category */ +static void +print_bthid_command(struct bthid_command *category) +{ + struct bthid_command *c = NULL; + + for (c = category; c->command != NULL; c++) + fprintf(stdout, "\t%s\n", c->command); +} /* print_bthid_command */ + +/* Usage */ +static void +usage(void) +{ + fprintf(stderr, +"Usage: bthidcontrol options command\n" \ +"Where options are:\n" +" -a bdaddr specify bdaddr\n" \ +" -c file specify path to the bthidd config file\n" \ +" -H file specify path to the bthidd HIDs file\n" \ +" -h display usage and quit\n" \ +" command one of the supported commands\n"); + exit(255); +} /* usage */ + diff --git a/usr.sbin/bluetooth/bthidcontrol/bthidcontrol.h b/usr.sbin/bluetooth/bthidcontrol/bthidcontrol.h new file mode 100644 index 0000000..50ed2fa --- /dev/null +++ b/usr.sbin/bluetooth/bthidcontrol/bthidcontrol.h @@ -0,0 +1,50 @@ +/* + * bthidcontrol.h + * + * Copyright (c) 2004 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: bthidcontrol.h,v 1.1 2004/02/12 23:25:51 max Exp $ + * $FreeBSD$ + */ + +#ifndef __BTHIDCONTROL_H__ +#define __BTHIDCONTROL_H__ + +#define OK 0 /* everything was OK */ +#define ERROR 1 /* could not execute command */ +#define FAILED 2 /* error was reported */ +#define USAGE 3 /* invalid parameters */ + +struct bthid_command { + char const *command; + char const *description; + int (*handler)(bdaddr_t *, int, char **); +}; + +extern struct bthid_command hid_commands[]; +extern struct bthid_command sdp_commands[]; + +#endif /* __BTHIDCONTROL_H__ */ + diff --git a/usr.sbin/bluetooth/bthidcontrol/hid.c b/usr.sbin/bluetooth/bthidcontrol/hid.c new file mode 100644 index 0000000..78b4105 --- /dev/null +++ b/usr.sbin/bluetooth/bthidcontrol/hid.c @@ -0,0 +1,209 @@ +/* + * hid.c + * + * Copyright (c) 2004 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: hid.c,v 1.3 2004/02/17 22:14:57 max Exp $ + * $FreeBSD$ + */ + +#include <sys/queue.h> +#include <bluetooth.h> +#include <dev/usb/usb.h> +#include <dev/usb/usbhid.h> +#include <stdio.h> +#include <string.h> +#include <usbhid.h> +#include "bthid_config.h" +#include "bthidcontrol.h" + +static void hid_dump_descriptor (report_desc_t r); +static void hid_dump_item (char const *label, struct hid_item *h); + +static int +hid_dump(bdaddr_t *bdaddr, int argc, char **argv) +{ + struct hid_device *hd = NULL; + int e = FAILED; + + if (read_config_file() == 0) { + if ((hd = get_hid_device(bdaddr)) != NULL) { + hid_dump_descriptor(hd->desc); + e = OK; + } + + clean_config(); + } + + return (e); +} + +static int +hid_forget(bdaddr_t *bdaddr, int argc, char **argv) +{ + struct hid_device *hd = NULL; + int e = FAILED; + + if (read_config_file() == 0) { + if (read_hids_file() == 0) { + if ((hd = get_hid_device(bdaddr)) != NULL) { + hd->new_device = 1; + if (write_hids_file() == 0) + e = OK; + } + } + + clean_config(); + } + + return (e); +} + +static int +hid_known(bdaddr_t *bdaddr, int argc, char **argv) +{ + struct hid_device *hd = NULL; + struct hostent *he = NULL; + int e = FAILED; + + if (read_config_file() == 0) { + if (read_hids_file() == 0) { + e = OK; + + for (hd = get_next_hid_device(hd); + hd != NULL; + hd = get_next_hid_device(hd)) { + if (hd->new_device) + continue; + + he = bt_gethostbyaddr((char *) &hd->bdaddr, + sizeof(hd->bdaddr), + AF_BLUETOOTH); + + fprintf(stdout, +"%s %s\n", bt_ntoa(&hd->bdaddr, NULL), + (he != NULL && he->h_name != NULL)? + he->h_name : ""); + } + } + + clean_config(); + } + + return (e); +} + +static void +hid_dump_descriptor(report_desc_t r) +{ + struct hid_data *d = NULL; + struct hid_item h; + + for (d = hid_start_parse(r, ~0, -1); hid_get_item(d, &h); ) { + switch (h.kind) { + case hid_collection: + fprintf(stdout, +"Collection page=%s usage=%s\n", hid_usage_page(HID_PAGE(h.usage)), + hid_usage_in_page(h.usage)); + break; + + case hid_endcollection: + fprintf(stdout, "End collection\n"); + break; + + case hid_input: + hid_dump_item("Input ", &h); + break; + + case hid_output: + hid_dump_item("Output ", &h); + break; + + case hid_feature: + hid_dump_item("Feature", &h); + break; + } + } + + hid_end_parse(d); +} + +static void +hid_dump_item(char const *label, struct hid_item *h) +{ + fprintf(stdout, +"%s id=%u size=%u count=%u page=%s usage=%s%s%s%s%s%s%s%s%s%s", + label, (uint8_t) h->report_ID, h->report_size, h->report_count, + hid_usage_page(HID_PAGE(h->usage)), + hid_usage_in_page(h->usage), + h->flags & HIO_CONST ? " Const" : "", + h->flags & HIO_VARIABLE ? " Variable" : "", + h->flags & HIO_RELATIVE ? " Relative" : "", + h->flags & HIO_WRAP ? " Wrap" : "", + h->flags & HIO_NONLINEAR ? " NonLinear" : "", + h->flags & HIO_NOPREF ? " NoPref" : "", + h->flags & HIO_NULLSTATE ? " NullState" : "", + h->flags & HIO_VOLATILE ? " Volatile" : "", + h->flags & HIO_BUFBYTES ? " BufBytes" : ""); + + fprintf(stdout, +", logical range %d..%d", + h->logical_minimum, h->logical_maximum); + + if (h->physical_minimum != h->physical_maximum) + fprintf(stdout, +", physical range %d..%d", + h->physical_minimum, h->physical_maximum); + + if (h->unit) + fprintf(stdout, +", unit=0x%02x exp=%d", h->unit, h->unit_exponent); + + fprintf(stdout, "\n"); +} + +struct bthid_command hid_commands[] = { +{ +"Dump", +"Dump HID descriptor for the specified device in human readable form. The\n" \ +"device must have an entry in the Bluetooth HID daemon configuration file.\n", +hid_dump +}, +{ +"Known", +"List all known to the Bluetooth HID daemon devices.\n", +hid_known +}, +{ +"Forget", +"Forget (mark as new) specified HID device. This command is useful when it\n" \ +"is required to remove device from the known HIDs file. This should be done\n" \ +"when reset button was pressed on the device or the battery was changed. The\n"\ +"Bluetooth HID daemon should be restarted.\n", +hid_forget +}, +{ NULL, NULL, NULL } +}; + diff --git a/usr.sbin/bluetooth/bthidcontrol/sdp.c b/usr.sbin/bluetooth/bthidcontrol/sdp.c new file mode 100644 index 0000000..e39c8ca --- /dev/null +++ b/usr.sbin/bluetooth/bthidcontrol/sdp.c @@ -0,0 +1,432 @@ +/* + * sdp.c + * + * Copyright (c) 2004 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: sdp.c,v 1.3 2004/02/17 22:14:57 max Exp $ + * $FreeBSD$ + */ + +#include <sys/queue.h> +#include <bluetooth.h> +#include <dev/usb/usb.h> +#include <dev/usb/usbhid.h> +#include <errno.h> +#include <sdp.h> +#include <stdio.h> +#include <string.h> +#include <usbhid.h> +#include "bthid_config.h" +#include "bthidcontrol.h" + +static int32_t hid_sdp_query (bdaddr_t const *local, struct hid_device *hd, int32_t *error); +static int32_t hid_sdp_parse_protocol_descriptor_list (sdp_attr_p a); +static int32_t hid_sdp_parse_hid_descriptor (sdp_attr_p a); +static int32_t hid_sdp_parse_boolean (sdp_attr_p a); + +static uint16_t service = SDP_SERVICE_CLASS_HUMAN_INTERFACE_DEVICE; + +static uint32_t attrs[] = { +SDP_ATTR_RANGE( SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST, + SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST), +SDP_ATTR_RANGE (SDP_ATTR_ADDITIONAL_PROTOCOL_DESCRIPTOR_LISTS, + SDP_ATTR_ADDITIONAL_PROTOCOL_DESCRIPTOR_LISTS), +SDP_ATTR_RANGE( 0x0205, /* HIDReconnectInitiate */ + 0x0206), /* HIDDesctiptorList */ +SDP_ATTR_RANGE( 0x020a, /* HIDBatteryPower */ + 0x020a), +SDP_ATTR_RANGE( 0x020d, /* HIDNormallyConnectable */ + 0x020d) + }; +#define nattrs (sizeof(attrs)/sizeof(attrs[0])) + +static sdp_attr_t values[8]; +#define nvalues (sizeof(values)/sizeof(values[0])) + +static uint8_t buffer[nvalues][512]; + +/* + * Query remote device + */ + +#undef hid_sdp_query_exit +#define hid_sdp_query_exit(e) { \ + if (error != NULL) \ + *error = (e); \ + if (ss != NULL) { \ + sdp_close(ss); \ + ss = NULL; \ + } \ + return (((e) == 0)? 0 : -1); \ +} + +static int32_t +hid_sdp_query(bdaddr_t const *local, struct hid_device *hd, int32_t *error) +{ + void *ss = NULL; + uint8_t *hid_descriptor = NULL; + int32_t i, control_psm = -1, interrupt_psm = -1, + reconnect_initiate = -1, + normally_connectable = 0, battery_power = 0, + hid_descriptor_length = -1; + + if (local == NULL) + local = NG_HCI_BDADDR_ANY; + if (hd == NULL) + hid_sdp_query_exit(EINVAL); + + for (i = 0; i < nvalues; i ++) { + values[i].flags = SDP_ATTR_INVALID; + values[i].attr = 0; + values[i].vlen = sizeof(buffer[i]); + values[i].value = buffer[i]; + } + + if ((ss = sdp_open(local, &hd->bdaddr)) == NULL) + hid_sdp_query_exit(ENOMEM); + if (sdp_error(ss) != 0) + hid_sdp_query_exit(sdp_error(ss)); + if (sdp_search(ss, 1, &service, nattrs, attrs, nvalues, values) != 0) + hid_sdp_query_exit(sdp_error(ss)); + + sdp_close(ss); + ss = NULL; + + for (i = 0; i < nvalues; i ++) { + if (values[i].flags != SDP_ATTR_OK) + continue; + + switch (values[i].attr) { + case SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST: + control_psm = hid_sdp_parse_protocol_descriptor_list(&values[i]); + break; + + case SDP_ATTR_ADDITIONAL_PROTOCOL_DESCRIPTOR_LISTS: + interrupt_psm = hid_sdp_parse_protocol_descriptor_list(&values[i]); + break; + + case 0x0205: /* HIDReconnectInitiate */ + reconnect_initiate = hid_sdp_parse_boolean(&values[i]); + break; + + case 0x0206: /* HIDDesctiptorList */ + if (hid_sdp_parse_hid_descriptor(&values[i]) == 0) { + hid_descriptor = values[i].value; + hid_descriptor_length = values[i].vlen; + } + break; + + case 0x020a: /* HIDBatteryPower */ + battery_power = hid_sdp_parse_boolean(&values[i]); + break; + + case 0x020d: /* HIDNormallyConnectable */ + normally_connectable = hid_sdp_parse_boolean(&values[i]); + break; + } + } + + if (control_psm == -1 || interrupt_psm == -1 || + reconnect_initiate == -1 || normally_connectable == -1 || + hid_descriptor == NULL || hid_descriptor_length == -1) + hid_sdp_query_exit(ENOATTR); + + hd->control_psm = control_psm; + hd->interrupt_psm = interrupt_psm; + hd->reconnect_initiate = reconnect_initiate? 1 : 0; + hd->battery_power = battery_power? 1 : 0; + hd->normally_connectable = normally_connectable? 1 : 0; + hd->desc = hid_use_report_desc(hid_descriptor, hid_descriptor_length); + if (hd->desc == NULL) + hid_sdp_query_exit(ENOMEM); + + return (0); +} + +/* + * seq len 2 + * seq len 2 + * uuid value 3 + * uint16 value 3 + * seq len 2 + * uuid value 3 + */ + +static int32_t +hid_sdp_parse_protocol_descriptor_list(sdp_attr_p a) +{ + uint8_t *ptr = a->value; + uint8_t *end = a->value + a->vlen; + int32_t type, len, uuid, psm; + + if (end - ptr < 15) + return (-1); + + if (a->attr == SDP_ATTR_ADDITIONAL_PROTOCOL_DESCRIPTOR_LISTS) { + SDP_GET8(type, ptr); + switch (type) { + case SDP_DATA_SEQ8: + SDP_GET8(len, ptr); + break; + + case SDP_DATA_SEQ16: + SDP_GET16(len, ptr); + break; + + case SDP_DATA_SEQ32: + SDP_GET32(len, ptr); + break; + + default: + return (-1); + } + if (ptr + len > end) + return (-1); + } + + SDP_GET8(type, ptr); + switch (type) { + case SDP_DATA_SEQ8: + SDP_GET8(len, ptr); + break; + + case SDP_DATA_SEQ16: + SDP_GET16(len, ptr); + break; + + case SDP_DATA_SEQ32: + SDP_GET32(len, ptr); + break; + + default: + return (-1); + } + if (ptr + len > end) + return (-1); + + /* Protocol */ + SDP_GET8(type, ptr); + switch (type) { + case SDP_DATA_SEQ8: + SDP_GET8(len, ptr); + break; + + case SDP_DATA_SEQ16: + SDP_GET16(len, ptr); + break; + + case SDP_DATA_SEQ32: + SDP_GET32(len, ptr); + break; + + default: + return (-1); + } + if (ptr + len > end) + return (-1); + + /* UUID */ + if (ptr + 3 > end) + return (-1); + SDP_GET8(type, ptr); + switch (type) { + case SDP_DATA_UUID16: + SDP_GET16(uuid, ptr); + if (uuid != SDP_UUID_PROTOCOL_L2CAP) + return (-1); + break; + + case SDP_DATA_UUID32: /* XXX FIXME can we have 32-bit UUID */ + case SDP_DATA_UUID128: /* XXX FIXME can we have 128-bit UUID */ + default: + return (-1); + } + + /* PSM */ + if (ptr + 3 > end) + return (-1); + SDP_GET8(type, ptr); + if (type != SDP_DATA_UINT16) + return (-1); + SDP_GET16(psm, ptr); + + return (psm); +} + +/* + * seq len 2 + * seq len 2 + * uint8 value8 2 + * str value 3 + */ + +static int32_t +hid_sdp_parse_hid_descriptor(sdp_attr_p a) +{ + uint8_t *ptr = a->value; + uint8_t *end = a->value + a->vlen; + int32_t type, len, descriptor_type; + + if (end - ptr < 9) + return (-1); + + SDP_GET8(type, ptr); + switch (type) { + case SDP_DATA_SEQ8: + SDP_GET8(len, ptr); + break; + + case SDP_DATA_SEQ16: + SDP_GET16(len, ptr); + break; + + case SDP_DATA_SEQ32: + SDP_GET32(len, ptr); + break; + + default: + return (-1); + } + if (ptr + len > end) + return (-1); + + while (ptr < end) { + /* Descriptor */ + SDP_GET8(type, ptr); + switch (type) { + case SDP_DATA_SEQ8: + if (ptr + 1 > end) + return (-1); + SDP_GET8(len, ptr); + break; + + case SDP_DATA_SEQ16: + if (ptr + 2 > end) + return (-1); + SDP_GET16(len, ptr); + break; + + case SDP_DATA_SEQ32: + if (ptr + 4 > end) + return (-1); + SDP_GET32(len, ptr); + break; + + default: + return (-1); + } + + /* Descripor type */ + if (ptr + 1 > end) + return (-1); + SDP_GET8(type, ptr); + if (type != SDP_DATA_UINT8 || ptr + 1 > end) + return (-1); + SDP_GET8(descriptor_type, ptr); + + /* Descriptor value */ + if (ptr + 1 > end) + return (-1); + SDP_GET8(type, ptr); + switch (type) { + case SDP_DATA_STR8: + if (ptr + 1 > end) + return (-1); + SDP_GET8(len, ptr); + break; + + case SDP_DATA_STR16: + if (ptr + 2 > end) + return (-1); + SDP_GET16(len, ptr); + break; + + case SDP_DATA_STR32: + if (ptr + 4 > end) + return (-1); + SDP_GET32(len, ptr); + break; + + default: + return (-1); + } + if (ptr + len > end) + return (-1); + + if (descriptor_type == UDESC_REPORT && len > 0) { + a->value = ptr; + a->vlen = len; + + return (0); + } + + ptr += len; + } + + return (-1); +} + +/* bool8 int8 */ +static int32_t +hid_sdp_parse_boolean(sdp_attr_p a) +{ + if (a->vlen != 2 || a->value[0] != SDP_DATA_BOOL) + return (-1); + + return (a->value[1]); +} + +/* Perform SDP query */ +static int32_t +hid_query(bdaddr_t *bdaddr, int argc, char **argv) +{ + struct hid_device hd; + int e; + + memcpy(&hd.bdaddr, bdaddr, sizeof(hd.bdaddr)); + if (hid_sdp_query(NULL, &hd, &e) < 0) { + fprintf(stderr, "Could not perform SDP query on the " \ + "device %s. %s (%d)\n", bt_ntoa(bdaddr, NULL), + strerror(e), e); + return (FAILED); + } + + print_hid_device(&hd, stdout); + + return (OK); +} + +struct bthid_command sdp_commands[] = +{ +{ +"Query", +"Perform SDP query to the specified device and print HID configuration entry\n"\ +"for the device. The configuration entry should be appended to the Bluetooth\n"\ +"HID daemon configuration file and the daemon should be restarted.\n", +hid_query +}, +{ NULL, NULL, NULL } +}; + diff --git a/usr.sbin/bluetooth/bthidd/Makefile b/usr.sbin/bluetooth/bthidd/Makefile new file mode 100644 index 0000000..c3bab372 --- /dev/null +++ b/usr.sbin/bluetooth/bthidd/Makefile @@ -0,0 +1,14 @@ +# $Id: Makefile,v 1.2 2004/02/13 21:46:20 max Exp $ +# $FreeBSD$ + +PROG= bthidd +#MAN= bthidd.8 bthidd.conf.5 +NOMAN= 1 +SRCS= bthidd.c client.c hid.c lexer.l parser.y server.c session.c +WARNS?= 1 +CFLAGS+= -I${.CURDIR} + +DPADD= ${LIBBLUETOOTH} ${LIBSDP} +LDADD= -lbluetooth -lusbhid + +.include <bsd.prog.mk> diff --git a/usr.sbin/bluetooth/bthidd/bthid_config.h b/usr.sbin/bluetooth/bthidd/bthid_config.h new file mode 100644 index 0000000..6be7c7e --- /dev/null +++ b/usr.sbin/bluetooth/bthidd/bthid_config.h @@ -0,0 +1,67 @@ +/* + * bthid_config.h + * + * Copyright (c) 2004 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: bthid_config.h,v 1.3 2004/02/17 22:05:02 max Exp $ + * $FreeBSD$ + */ + +#ifndef _BTHID_CONFIG_H_ +#define _BTHID_CONFIG_H_ 1 + +#define BTHIDD_CONFFILE "/etc/bluetooth/bthidd.conf" +#define BTHIDD_HIDSFILE "/var/db/bthidd.hids" + +struct hid_device +{ + bdaddr_t bdaddr; /* HID device BDADDR */ + uint16_t control_psm; /* control PSM */ + uint16_t interrupt_psm; /* interrupt PSM */ + unsigned new_device : 1; + unsigned reconnect_initiate : 1; + unsigned battery_power : 1; + unsigned normally_connectable : 1; + unsigned reserved : 12; + 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; + +int 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); + +#endif /* ndef _BTHID_CONFIG_H_ */ + diff --git a/usr.sbin/bluetooth/bthidd/bthidd.c b/usr.sbin/bluetooth/bthidd/bthidd.c new file mode 100644 index 0000000..0782a36 --- /dev/null +++ b/usr.sbin/bluetooth/bthidd/bthidd.c @@ -0,0 +1,256 @@ +/* + * bthidd.c + * + * Copyright (c) 2004 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.c,v 1.4 2004/02/26 21:48:44 max Exp $ + * $FreeBSD$ + */ + +#include <sys/time.h> +#include <sys/queue.h> +#include <assert.h> +#include <bluetooth.h> +#include <err.h> +#include <errno.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <syslog.h> +#include <unistd.h> +#include <usbhid.h> +#include "bthidd.h" +#include "bthid_config.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 usage (void); + +/* + * bthidd + */ + +static int done = 0; /* are we done? */ + +int +main(int argc, char *argv[]) +{ + struct bthid_server srv; + struct sigaction sa; + char const *pid_file = BTHIDD_PIDFILE; + int opt, detach, tval; + + memcpy(&srv.bdaddr, NG_HCI_BDADDR_ANY, sizeof(srv.bdaddr)); + detach = 1; + tval = 10; /* sec */ + + 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; + + if ((he = bt_gethostbyname(optarg)) == NULL) + errx(1, "%s: %s", optarg, hstrerror(h_errno)); + + memcpy(&srv.bdaddr, he->h_addr, sizeof(srv.bdaddr)); + } + break; + + case 'c': /* config file */ + config_file = optarg; + break; + + case 'd': /* do not detach */ + detach = 0; + break; + + case 'H': /* hids file */ + hids_file = optarg; + break; + + case 'p': /* pid file */ + pid_file = optarg; + break; + + case 't': { /* rescan interval */ + char *ep = NULL; + + tval = strtol(optarg, &ep, 10); + if (*ep != '\0' || tval <= 0) + usage(); + } break; + + case 'h': + default: + usage(); + /* NOT REACHED */ + } + } + + openlog(BTHIDD_IDENT, LOG_PID|LOG_PERROR|LOG_NDELAY, LOG_USER); + + /* Become daemon if required */ + if (detach && daemon(0, 0) < 0) { + syslog(LOG_CRIT, "Could not become daemon. %s (%d)", + strerror(errno), errno); + exit(1); + } + + /* Install signal handler */ + memset(&sa, 0, sizeof(sa)); + 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 = SIG_IGN; + if (sigaction(SIGPIPE, &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); + + for (done = 0; !done; ) { + if (elapsed(tval)) + client_rescan(&srv); + + if (server_do(&srv) < 0) + break; + } + + server_shutdown(&srv); + remove_pid_file(pid_file); + clean_config(); + closelog(); + + return (0); +} + +/* + * Write pid file + */ + +static int +write_pid_file(char const *file) +{ + FILE *pid = NULL; + + assert(file != NULL); + + if ((pid = fopen(file, "w")) == NULL) { + syslog(LOG_ERR, "Could not open file %s. %s (%d)", + file, strerror(errno), errno); + return (-1); + } + + fprintf(pid, "%d", getpid()); + fclose(pid); + + return (0); +} + +/* + * Remote pid file + */ + +static int +remove_pid_file(char const *file) +{ + assert(file != NULL); + + if (unlink(file) < 0) { + syslog(LOG_ERR, "Could not unlink file %s. %s (%d)", + file, strerror(errno), errno); + return (-1); + } + + return (0); +} + +/* + * Returns true if desired time interval has elapsed + */ + +static int +elapsed(int tval) +{ + static struct timeval last = { 0, }; + struct timeval now; + + gettimeofday(&now, NULL); + + if (now.tv_sec - last.tv_sec >= tval) { + last = now; + return (1); + } + + return (0); +} + +/* + * Signal handler + */ + +static void +sighandler(int s) +{ + syslog(LOG_NOTICE, "Got signal %d, total number of signals %d", + s, ++ done); +} + +/* + * Display usage and exit + */ + +static void +usage(void) +{ + fprintf(stderr, +"Usage: %s [options]\n" \ +"Where options are:\n" \ +" -a bdaddr specify BDADDR to listen on (default ANY)\n" \ +" -c file specify config file name\n" \ +" -d run in foreground\n" \ +" -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" \ +"", BTHIDD_IDENT); + exit(255); +} + diff --git a/usr.sbin/bluetooth/bthidd/bthidd.conf.sample b/usr.sbin/bluetooth/bthidd/bthidd.conf.sample new file mode 100644 index 0000000..e23dc12 --- /dev/null +++ b/usr.sbin/bluetooth/bthidd/bthidd.conf.sample @@ -0,0 +1,72 @@ +# $FreeBSD$ + +device { + bdaddr 00:50:f2:e5:68:84; + control_psm 0x11; + interrupt_psm 0x13; + reconnect_initiate true; + normally_connectable false; + hid_descriptor { + 0x05 0x01 0x09 0x02 0xa1 0x01 0x85 0x02 + 0x09 0x01 0xa1 0x00 0x05 0x09 0x19 0x01 + 0x29 0x05 0x15 0x00 0x25 0x01 0x75 0x01 + 0x95 0x05 0x81 0x02 0x75 0x03 0x95 0x01 + 0x81 0x01 0x05 0x01 0x09 0x30 0x09 0x31 + 0x09 0x38 0x15 0x81 0x25 0x7f 0x75 0x08 + 0x95 0x03 0x81 0x06 0xc0 0xc0 0x05 0x0c + 0x09 0x01 0xa1 0x01 0x85 0x03 0x05 0x01 + 0x09 0x02 0xa1 0x02 0x06 0x00 0xff 0x15 + 0x00 0x25 0x03 0x95 0x01 0x75 0x02 0x0a + 0x01 0xfe 0x81 0x02 0x75 0x06 0x81 0x01 + 0xc0 0xc0 + }; +} + +device { + bdaddr 00:50:f2:e3:fb:e1; + control_psm 0x11; + interrupt_psm 0x13; + reconnect_initiate true; + normally_connectable false; + hid_descriptor { + 0x05 0x01 0x09 0x06 0xa1 0x01 0x85 0x01 + 0x05 0x08 0x19 0x01 0x29 0x03 0x15 0x00 + 0x25 0x01 0x75 0x01 0x95 0x03 0x91 0x02 + 0x09 0x4b 0x95 0x01 0x91 0x02 0x95 0x04 + 0x91 0x01 0x05 0x07 0x19 0xe0 0x29 0xe7 + 0x95 0x08 0x81 0x02 0x75 0x08 0x95 0x01 + 0x81 0x01 0x19 0x00 0x29 0x91 0x26 0xff + 0x00 0x95 0x06 0x81 0x00 0xc0 0x05 0x0c + 0x09 0x01 0xa1 0x01 0x85 0x02 0x05 0x0c + 0x15 0x00 0x25 0x01 0x75 0x01 0x95 0x1c + 0x09 0xe2 0x09 0xb7 0x09 0xcd 0x09 0xea + 0x09 0xe9 0x09 0xb6 0x09 0xb5 0x0a 0x83 + 0x01 0x0a 0x1a 0x02 0x0a 0x79 0x02 0x0a + 0xab 0x01 0x0a 0x08 0x02 0x0a 0x02 0x02 + 0x0a 0x03 0x02 0x0a 0x07 0x02 0x0a 0x01 + 0x02 0x0a 0x92 0x01 0x0a 0x9c 0x01 0x09 + 0x95 0x0a 0x23 0x02 0x0a 0x89 0x02 0x0a + 0x8b 0x02 0x0a 0x8c 0x02 0x0a 0x8a 0x01 + 0x0a 0x99 0x01 0x0a 0xa7 0x01 0x0a 0xb6 + 0x01 0x0a 0xb7 0x01 0x81 0x02 0x75 0x01 + 0x95 0x04 0x81 0x01 0x06 0x00 0xff 0x0a + 0x02 0xff 0x26 0xff 0x00 0x95 0x01 0x75 + 0x08 0x81 0x02 0xc0 0x05 0x01 0x09 0x80 + 0xa1 0x01 0x85 0x03 0x19 0x81 0x29 0x83 + 0x25 0x01 0x95 0x03 0x75 0x01 0x81 0x02 + 0x95 0x05 0x81 0x01 0xc0 0x05 0x0c 0x09 + 0x01 0xa1 0x01 0x85 0x04 0x05 0x01 0x09 + 0x06 0xa1 0x02 0x06 0x00 0xff 0x15 0x00 + 0x25 0x03 0x95 0x01 0x75 0x02 0x0a 0x01 + 0xfe 0x81 0x02 0x75 0x06 0x81 0x01 0xc0 + 0xc0 0x05 0x0c 0x09 0x01 0xa1 0x01 0x85 + 0x05 0x05 0x01 0x09 0x06 0xa1 0x02 0x06 + 0x00 0xff 0x25 0x01 0x75 0x01 0x95 0x02 + 0x0a 0x03 0xfe 0x0a 0x04 0xfe 0x81 0x02 + 0x95 0x06 0x81 0x01 0xc0 0xc0 0x05 0x0c + 0x09 0x01 0xa1 0x01 0x85 0xff 0x05 0x06 + 0x95 0x01 0x75 0x02 0x19 0x24 0x29 0x26 + 0x81 0x02 0x75 0x06 0x81 0x01 0xc0 + }; +} + diff --git a/usr.sbin/bluetooth/bthidd/bthidd.h b/usr.sbin/bluetooth/bthidd/bthidd.h new file mode 100644 index 0000000..d9cd015 --- /dev/null +++ b/usr.sbin/bluetooth/bthidd/bthidd.h @@ -0,0 +1,88 @@ +/* + * bthidd.h + * + * Copyright (c) 2004 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.h,v 1.4 2004/02/26 21:44:20 max Exp $ + * $FreeBSD$ + */ + +#ifndef _BTHIDD_H_ +#define _BTHIDD_H_ 1 + +#define BTHIDD_IDENT "bthidd" +#define BTHIDD_PIDFILE "/var/run/" BTHIDD_IDENT ".pid" + +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; +}; + +typedef struct bthid_server bthid_server_t; +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 */ +#define CLOSED 0 +#define W4CTRL 1 +#define W4INTR 2 +#define OPEN 3 + 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); +void server_shutdown (bthid_server_p srv); +int server_do (bthid_server_p srv); + +int client_rescan (bthid_server_p srv); +int client_connect (bthid_server_p srv, int fd); + +bthid_session_p 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 session_by_fd (bthid_server_p srv, int 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); + +#endif /* ndef _BTHIDD_H_ */ + diff --git a/usr.sbin/bluetooth/bthidd/client.c b/usr.sbin/bluetooth/bthidd/client.c new file mode 100644 index 0000000..cc611bd --- /dev/null +++ b/usr.sbin/bluetooth/bthidd/client.c @@ -0,0 +1,244 @@ +/* + * client.c + * + * Copyright (c) 2004 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: client.c,v 1.6 2004/02/26 21:57:55 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 "bthidd.h" +#include "bthid_config.h" + +static int client_socket(bdaddr_p bdaddr, int psm); + +/* + * Get next config entry and create outbound connection (if required) + * + * XXX Do only one device at a time. At least one of my devices (3COM + * Bluetooth PCCARD) rejects Create_Connection command if another + * Create_Connection command is still pending. Weird... + */ + +static int connect_in_progress = 0; + +int +client_rescan(bthid_server_p srv) +{ + static hid_device_p d = NULL; + bthid_session_p s = NULL; + + assert(srv != NULL); + + if (connect_in_progress) + return (0); /* another connect is still pending */ + + d = get_next_hid_device(d); + if (d == NULL) + return (0); /* XXX should not happen? empty config? */ + + if ((s = session_by_bdaddr(srv, &d->bdaddr)) != NULL) + return (0); /* session already active */ + + if (!d->new_device) { + if (d->reconnect_initiate) + return (0); /* device will initiate reconnect */ + } + + syslog(LOG_NOTICE, "Opening outbound session for %s " \ + "(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)); + return (-1); + } + + /* Open control channel */ + s->ctrl = client_socket(&s->bdaddr, d->control_psm); + if (s->ctrl < 0) { + syslog(LOG_ERR, "Could not open control channel to %s. %s (%d)", + bt_ntoa(&s->bdaddr, NULL), strerror(errno), errno); + session_close(s); + return (-1); + } + + s->state = W4CTRL; + + FD_SET(s->ctrl, &srv->wfdset); + if (s->ctrl > srv->maxfd) + srv->maxfd = s->ctrl; + + connect_in_progress = 1; + + return (0); +} + +/* + * Process connect on the socket + */ + +int +client_connect(bthid_server_p srv, int fd) +{ + bthid_session_p s = NULL; + hid_device_p d = NULL; + int error, len; + + assert(srv != NULL); + assert(fd >= 0); + + s = session_by_fd(srv, fd); + assert(s != NULL); + + d = get_hid_device(&s->bdaddr); + assert(d != NULL); + + error = 0; + len = sizeof(error); + if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &error, &len) < 0) { + syslog(LOG_ERR, "Could not get socket error for %s. %s (%d)", + bt_ntoa(&s->bdaddr, NULL), strerror(errno), errno); + session_close(s); + connect_in_progress = 0; + + return (-1); + } + + if (error != 0) { + syslog(LOG_ERR, "Could not connect to %s. %s (%d)", + bt_ntoa(&s->bdaddr, NULL), strerror(error), error); + session_close(s); + connect_in_progress = 0; + + return (0); + } + + switch (s->state) { + case W4CTRL: /* Control channel is open */ + assert(s->ctrl == fd); + assert(s->intr == -1); + + /* Open interrupt channel */ + s->intr = client_socket(&s->bdaddr, d->interrupt_psm); + if (s->intr < 0) { + syslog(LOG_ERR, "Could not open interrupt channel " \ + "to %s. %s (%d)", bt_ntoa(&s->bdaddr, NULL), + strerror(errno), errno); + session_close(s); + connect_in_progress = 0; + + return (-1); + } + + s->state = W4INTR; + + FD_SET(s->intr, &srv->wfdset); + if (s->intr > srv->maxfd) + srv->maxfd = s->intr; + + d->new_device = 0; /* reset new device flag */ + write_hids_file(); + break; + + case W4INTR: /* Interrupt channel is open */ + assert(s->ctrl != -1); + assert(s->intr == fd); + + s->state = OPEN; + connect_in_progress = 0; + break; + + default: + assert(0); + break; + } + + /* Move fd to from the write fd set into read fd set */ + FD_CLR(fd, &srv->wfdset); + FD_SET(fd, &srv->rfdset); + + return (0); +} + +/* + * Create bound non-blocking socket and initiate connect + */ + +static int +client_socket(bdaddr_p bdaddr, int psm) +{ + struct sockaddr_l2cap l2addr; + int s, m; + + s = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BLUETOOTH_PROTO_L2CAP); + if (s < 0) + return (-1); + + m = fcntl(s, F_GETFL); + if (m < 0) { + close(s); + return (-1); + } + + if (fcntl(s, F_SETFL, (m|O_NONBLOCK)) < 0) { + close(s); + return (-1); + } + + l2addr.l2cap_len = sizeof(l2addr); + l2addr.l2cap_family = AF_BLUETOOTH; + memcpy(&l2addr.l2cap_bdaddr, NG_HCI_BDADDR_ANY, sizeof(l2addr.l2cap_bdaddr)); + l2addr.l2cap_psm = 0; + + if (bind(s, (struct sockaddr *) &l2addr, sizeof(l2addr)) < 0) { + close(s); + return (-1); + } + + memcpy(&l2addr.l2cap_bdaddr, bdaddr, sizeof(l2addr.l2cap_bdaddr)); + l2addr.l2cap_psm = psm; + + if (connect(s, (struct sockaddr *) &l2addr, sizeof(l2addr)) < 0 && + errno != EINPROGRESS) { + close(s); + return (-1); + } + + return (s); +} + diff --git a/usr.sbin/bluetooth/bthidd/hid.c b/usr.sbin/bluetooth/bthidd/hid.c new file mode 100644 index 0000000..5a7ab66 --- /dev/null +++ b/usr.sbin/bluetooth/bthidd/hid.c @@ -0,0 +1,262 @@ +/* + * hid.c + * + * Copyright (c) 2004 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: hid.c,v 1.3 2004/02/26 21:47:35 max Exp $ + * $FreeBSD$ + */ + +#include <sys/consio.h> +#include <sys/mouse.h> +#include <sys/queue.h> +#include <assert.h> +#include <bluetooth.h> +#include <errno.h> +#include <dev/usb/usb.h> +#include <dev/usb/usbhid.h> +#include <stdio.h> +#include <string.h> +#include <syslog.h> +#include <usbhid.h> +#include "bthidd.h" +#include "bthid_config.h" + +#undef min +#define min(x, y) (((x) < (y))? (x) : (y)) + +/* + * Process data from control channel + */ + +int +hid_control(bthid_session_p s, char *data, int len) +{ + assert(s != NULL); + assert(data != NULL); + assert(len > 0); + + switch (data[0] >> 4) { + case 0: /* Handshake (response to command) */ + if (data[0] & 0xf) + syslog(LOG_ERR, "Got handshake message with error " \ + "response 0x%x from %s", + data[0], bt_ntoa(&s->bdaddr, NULL)); + break; + + case 1: /* HID Control */ + switch (data[0] & 0xf) { + case 0: /* NOP */ + break; + + case 1: /* Hard reset */ + case 2: /* Soft reset */ + syslog(LOG_WARNING, "Device %s requested %s reset", + bt_ntoa(&s->bdaddr, NULL), + ((data[0] & 0xf) == 1)? "hard" : "soft"); + break; + + case 3: /* Suspend */ + syslog(LOG_NOTICE, "Device %s requested Suspend", + bt_ntoa(&s->bdaddr, NULL)); + break; + + case 4: /* Exit suspend */ + syslog(LOG_NOTICE, "Device %s requested Exit Suspend", + bt_ntoa(&s->bdaddr, NULL)); + break; + + case 5: /* Virtual cable unplug */ + syslog(LOG_NOTICE, "Device %s unplugged virtual cable", + bt_ntoa(&s->bdaddr, NULL)); + session_close(s); + break; + + default: + syslog(LOG_WARNING, "Device %s sent unknown " \ + "HID_Control message 0x%x", + bt_ntoa(&s->bdaddr, NULL), data[0]); + break; + } + break; + + default: + syslog(LOG_WARNING, "Got unexpected message 0x%x on Control " \ + "channel from %s", data[0], bt_ntoa(&s->bdaddr, NULL)); + break; + } + + return (0); +} + +/* + * Process data from the interrupt channel + */ + +int +hid_interrupt(bthid_session_p s, char *data, int len) +{ + hid_device_p hid_device = NULL; + hid_data_t d; + 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? */ + + assert(s != NULL); + assert(data != NULL); + + if (len < 3) { + syslog(LOG_ERR, "Got short message (%d bytes) on Interrupt " \ + "channel from %s", len, bt_ntoa(&s->bdaddr, NULL)); + return (-1); + } + + if ((unsigned char) data[0] != 0xa1) { + syslog(LOG_ERR, "Got unexpected message 0x%x on " \ + "Interrupt channel from %s", + data[0], bt_ntoa(&s->bdaddr, NULL)); + return (-1); + } + + report_id = data[1]; + data += 2; + len -= 2; + + hid_device = get_hid_device(&s->bdaddr); + assert(hid_device != NULL); + + mouse_x = mouse_y = mouse_z = mouse_butt = nkeys = 0; + + for (d = hid_start_parse(hid_device->desc, 1 << hid_input, -1); + hid_get_item(d, &h) > 0; ) { + if ((h.flags & HIO_CONST) || (h.report_ID != report_id)) + continue; + + page = HID_PAGE(h.usage); + usage = HID_USAGE(h.usage); + val = hid_get_data(data, &h); + + switch (page) { + case HUP_GENERIC_DESKTOP: + switch (usage) { + case HUG_X: + mouse_x = val; + break; + + case HUG_Y: + mouse_y = val; + break; + + case HUG_WHEEL: + mouse_z = -val; + break; + + case HUG_SYSTEM_SLEEP: + if (val) + syslog(LOG_NOTICE, "Sleep button pressed"); + break; + } + break; + + case HUP_KEYBOARD: + if (h.flags & HIO_VARIABLE) { + if (val && nkeys < sizeof(keys)) + keys[nkeys ++] = usage; + } else { + if (val && nkeys < sizeof(keys)) + keys[nkeys ++] = 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; + data ++; + len --; + } + } + break; + + case HUP_BUTTON: + mouse_butt |= (val << (usage - 1)); + break; + + case HUP_MICROSOFT: + switch (usage) { + case 0xfe01: + if (!hid_device->battery_power) + break; + + switch (val) { + case 1: + syslog(LOG_INFO, "Battery is OK on %s", + bt_ntoa(&s->bdaddr, NULL)); + break; + + case 2: + syslog(LOG_NOTICE, "Low battery on %s", + bt_ntoa(&s->bdaddr, NULL)); + break; + + case 3: + syslog(LOG_WARNING, "Very low battery "\ + "on %s", + bt_ntoa(&s->bdaddr, NULL)); + break; + } + break; + } + break; + } + } + hid_end_parse(d); + + /* + * XXX FIXME Feed mouse and keyboard events into kernel + * The code block below works, but it is not + * good enough + */ + + if (mouse_x != 0 || mouse_y != 0 || mouse_z != 0 || mouse_butt != 0) { + struct mouse_info mi; + + mi.operation = MOUSE_ACTION; + mi.u.data.x = mouse_x; + mi.u.data.y = mouse_y; + mi.u.data.z = mouse_z; + mi.u.data.buttons = mouse_butt; + + if (ioctl(s->srv->cons, CONS_MOUSECTL, &mi) < 0) + syslog(LOG_ERR, "Could not process mouse events from " \ + "%s. %s (%d)", bt_ntoa(&s->bdaddr, NULL), + strerror(errno), errno); + } + + return (0); +} + diff --git a/usr.sbin/bluetooth/bthidd/lexer.l b/usr.sbin/bluetooth/bthidd/lexer.l new file mode 100644 index 0000000..93ad394 --- /dev/null +++ b/usr.sbin/bluetooth/bthidd/lexer.l @@ -0,0 +1,100 @@ +%{ +/* + * lexer.l + * + * Copyright (c) 2004 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: lexer.l,v 1.2 2004/02/13 21:46:20 max Exp $ + * $FreeBSD$ + */ + +#include <bluetooth.h> +#include <stdlib.h> +#include "parser.h" +%} + +%option yylineno noyywrap nounput + +delim [ \t\n] +ws {delim}+ +empty {delim}* +comment \#.* + +hexdigit [0-9a-fA-F] +hexbyte {hexdigit}{hexdigit}? + +device_word device +bdaddr_word bdaddr +control_psm_word control_psm +interrupt_psm_word interrupt_psm +reconnect_initiate_word reconnect_initiate +battery_power_word battery_power +normally_connectable_word normally_connectable +hid_descriptor_word hid_descriptor +true_word true +false_word false + +bdaddrstring {hexbyte}:{hexbyte}:{hexbyte}:{hexbyte}:{hexbyte}:{hexbyte} +hexbytestring 0x{hexbyte} + +%% + +\; return (';'); +\: return (':'); +\{ return ('{'); +\} return ('}'); + +{ws} ; +{empty} ; +{comment} ; + +{device_word} return (T_DEVICE); +{bdaddr_word} return (T_BDADDR); +{control_psm_word} return (T_CONTROL_PSM); +{interrupt_psm_word} return (T_INTERRUPT_PSM); +{reconnect_initiate_word} return (T_RECONNECT_INITIATE); +{battery_power_word} return (T_BATTERY_POWER); +{normally_connectable_word} return (T_NORMALLY_CONNECTABLE); +{hid_descriptor_word} return (T_HID_DESCRIPTOR); +{true_word} return (T_TRUE); +{false_word} return (T_FALSE); + +{bdaddrstring} { + return (bt_aton(yytext, &yylval.bdaddr)? + T_BDADDRSTRING : T_ERROR); + } + +{hexbytestring} { + char *ep = NULL; + + yylval.num = strtoul(yytext, &ep, 16); + + return (*ep == '\0'? T_HEXBYTE : T_ERROR); + } + +. return (T_ERROR); + +%% + diff --git a/usr.sbin/bluetooth/bthidd/parser.y b/usr.sbin/bluetooth/bthidd/parser.y new file mode 100644 index 0000000..f4fd560 --- /dev/null +++ b/usr.sbin/bluetooth/bthidd/parser.y @@ -0,0 +1,447 @@ +%{ +/* + * parser.y + * + * Copyright (c) 2004 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: parser.y,v 1.3 2004/02/13 21:46:21 max Exp $ + * $FreeBSD$ + */ + +#include <sys/queue.h> +#include <bluetooth.h> +#include <errno.h> +#include <libusbhid.h> +#include <limits.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> + +#ifndef BTHIDCONTROL +#include <stdarg.h> +#include <syslog.h> +#define SYSLOG syslog +#define LOGCRIT LOG_CRIT +#define LOGERR LOG_ERR +#define LOGWARNING LOG_WARNING +#define EOL +#else +#define SYSLOG fprintf +#define LOGCRIT stderr +#define LOGERR stderr +#define LOGWARNING stderr +#define EOL "\n" +#endif /* ndef BTHIDCONTROL */ + +#include "bthid_config.h" + + int yyparse (void); + int yylex (void); +static int check_hid_device(hid_device_p hid_device); +static void free_hid_device (hid_device_p hid_device); + +extern int yylineno; + char *config_file = BTHIDD_CONFFILE; + char *hids_file = BTHIDD_HIDSFILE; + +static char buffer[1024]; +static int hid_descriptor_size; +static hid_device_t *hid_device = NULL; +static LIST_HEAD(, hid_device) hid_devices; + +%} + +%union { + bdaddr_t bdaddr; + int num; +} + +%token <bdaddr> T_BDADDRSTRING +%token <num> T_HEXBYTE +%token T_DEVICE T_BDADDR T_CONTROL_PSM T_INTERRUPT_PSM T_RECONNECT_INITIATE +%token T_BATTERY_POWER T_NORMALLY_CONNECTABLE T_HID_DESCRIPTOR +%token T_TRUE T_FALSE T_ERROR + +%% + +config: line + | config line + ; + +line: T_DEVICE + { + hid_device = (hid_device_t *) calloc(1, sizeof(*hid_device)); + if (hid_device == NULL) { + SYSLOG(LOGCRIT, "Could not allocate new " \ + "config entry" EOL); + YYABORT; + } + + hid_device->new_device = 1; + } + '{' options '}' + { + if (check_hid_device(hid_device)) + LIST_INSERT_HEAD(&hid_devices,hid_device,next); + else + free_hid_device(hid_device); + + hid_device = NULL; + } + ; + +options: option ';' + | options option ';' + ; + +option: bdaddr + | control_psm + | interrupt_psm + | reconnect_initiate + | battery_power + | normally_connectable + | hid_descriptor + | parser_error + ; + +bdaddr: T_BDADDR T_BDADDRSTRING + { + memcpy(&hid_device->bdaddr, &$2, sizeof(hid_device->bdaddr)); + } + ; + +control_psm: T_CONTROL_PSM T_HEXBYTE + { + hid_device->control_psm = $2; + } + ; + +interrupt_psm: T_INTERRUPT_PSM T_HEXBYTE + { + hid_device->interrupt_psm = $2; + } + ; + +reconnect_initiate: T_RECONNECT_INITIATE T_TRUE + { + hid_device->reconnect_initiate = 1; + } + | T_RECONNECT_INITIATE T_FALSE + { + hid_device->reconnect_initiate = 0; + } + ; + +battery_power: T_BATTERY_POWER T_TRUE + { + hid_device->battery_power = 1; + } + | T_BATTERY_POWER T_FALSE + { + hid_device->battery_power = 0; + } + ; + +normally_connectable: T_NORMALLY_CONNECTABLE T_TRUE + { + hid_device->normally_connectable = 1; + } + | T_NORMALLY_CONNECTABLE T_FALSE + { + hid_device->normally_connectable = 0; + } + ; + +hid_descriptor: T_HID_DESCRIPTOR + { + hid_descriptor_size = 0; + } + '{' hid_descriptor_bytes '}' + { + if (hid_device->desc != NULL) + hid_dispose_report_desc(hid_device->desc); + + hid_device->desc = hid_use_report_desc(buffer, hid_descriptor_size); + if (hid_device->desc == NULL) { + SYSLOG(LOGCRIT, "Could not use HID descriptor" EOL); + YYABORT; + } + } + ; + +hid_descriptor_bytes: hid_descriptor_byte + | hid_descriptor_bytes hid_descriptor_byte + ; + +hid_descriptor_byte: T_HEXBYTE + { + if (hid_descriptor_size >= sizeof(buffer)) { + SYSLOG(LOGCRIT, "HID descriptor is too big" EOL); + YYABORT; + } + + buffer[hid_descriptor_size ++] = $1; + } + ; + +parser_error: T_ERROR + { + YYABORT; + } + +%% + +/* Display parser error message */ +void +yyerror(char const *message) +{ + SYSLOG(LOGERR, "%s in line %d" EOL, message, yylineno); +} + +/* Re-read config file */ +int +read_config_file(void) +{ + extern FILE *yyin; + int e; + + if (config_file == NULL) { + SYSLOG(LOGERR, "Unknown config file name!" EOL); + return (-1); + } + + if ((yyin = fopen(config_file, "r")) == NULL) { + SYSLOG(LOGERR, "Could not open config file '%s'. %s (%d)" EOL, + config_file, strerror(errno), errno); + return (-1); + } + + clean_config(); + if (yyparse() < 0) { + SYSLOG(LOGERR, "Could not parse config file '%s'" EOL, + config_file); + e = -1; + } else + e = 0; + + fclose(yyin); + yyin = NULL; + + return (e); +} + +/* Clean config */ +void +clean_config(void) +{ + while (!LIST_EMPTY(&hid_devices)) { + hid_device_p hid_device = LIST_FIRST(&hid_devices); + + LIST_REMOVE(hid_device, next); + free_hid_device(hid_device); + } +} + +/* Lookup config entry */ +hid_device_p +get_hid_device(bdaddr_p bdaddr) +{ + hid_device_p hid_device; + + LIST_FOREACH(hid_device, &hid_devices, next) + if (memcmp(&hid_device->bdaddr, bdaddr, sizeof(bdaddr_t)) == 0) + break; + + return (hid_device); +} + +/* Get next config entry */ +hid_device_p +get_next_hid_device(hid_device_p d) +{ + return ((d == NULL)? LIST_FIRST(&hid_devices) : LIST_NEXT(d, next)); +} + +/* Print config entry */ +void +print_hid_device(hid_device_p hid_device, FILE *f) +{ + /* XXX FIXME hack! */ + struct report_desc { + unsigned int size; + unsigned char data[1]; + }; + /* XXX FIXME hack! */ + + struct report_desc *desc = (struct report_desc *) hid_device->desc; + int i; + + fprintf(f, +"device {\n" \ +" bdaddr %s;\n" \ +" control_psm 0x%x;\n" \ +" interrupt_psm 0x%d;\n" \ +" reconnect_initiate %s;\n" \ +" 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"); + + for (i = 0; i < desc->size; i ++) { + if ((i % 8) == 0) + fprintf(stdout, "\n "); + + fprintf(f, "0x%2.2x ", desc->data[i]); + } + + fprintf(stdout, +"\n" \ +" };\n" \ +"}\n"); +} + +/* Check config entry */ +static int +check_hid_device(hid_device_p hid_device) +{ + if (get_hid_device(&hid_device->bdaddr) != NULL) { + SYSLOG(LOGERR, "Ignoring duplicated entry for bdaddr %s" EOL, + bt_ntoa(&hid_device->bdaddr, NULL)); + return (0); + } + + if (hid_device->control_psm == 0) { + SYSLOG(LOGERR, "Ignoring entry with invalid control PSM" EOL); + return (0); + } + + if (hid_device->interrupt_psm == 0) { + SYSLOG(LOGERR, "Ignoring entry with invalid interrupt PSM" EOL); + return (0); + } + + if (hid_device->desc == NULL) { + SYSLOG(LOGERR, "Ignoring entry without HID descriptor" EOL); + return (0); + } + + return (1); +} + +/* Free config entry */ +static void +free_hid_device(hid_device_p hid_device) +{ + if (hid_device->desc != NULL) + hid_dispose_report_desc(hid_device->desc); + + memset(hid_device, 0, sizeof(*hid_device)); + free(hid_device); +} + +/* Re-read hids file */ +int +read_hids_file(void) +{ + FILE *f = NULL; + hid_device_t *hid_device = NULL; + char *line = NULL; + bdaddr_t bdaddr; + int lineno; + + if (hids_file == NULL) { + SYSLOG(LOGERR, "Unknown HIDs file name!" EOL); + return (-1); + } + + if ((f = fopen(hids_file, "r")) == NULL) { + if (errno == ENOENT) + return (0); + + SYSLOG(LOGERR, "Could not open HIDs file '%s'. %s (%d)" EOL, + hids_file, strerror(errno), errno); + return (-1); + } + + for (lineno = 1; fgets(buffer, sizeof(buffer), f) != NULL; lineno ++) { + if ((line = strtok(buffer, "\r\n\t ")) == NULL) + continue; /* ignore empty lines */ + + if (!bt_aton(line, &bdaddr)) { + SYSLOG(LOGWARNING, "Ignoring unparseable BD_ADDR in " \ + "%s:%d" EOL, hids_file, lineno); + continue; + } + + if ((hid_device = get_hid_device(&bdaddr)) != NULL) + hid_device->new_device = 0; + } + + fclose(f); + + return (0); +} + +/* Write hids file */ +int +write_hids_file(void) +{ + char path[PATH_MAX]; + FILE *f = NULL; + hid_device_t *hid_device = NULL; + + if (hids_file == NULL) { + SYSLOG(LOGERR, "Unknown HIDs file name!" EOL); + return (-1); + } + + snprintf(path, sizeof(path), "%s.new", hids_file); + + if ((f = fopen(path, "w")) == NULL) { + SYSLOG(LOGERR, "Could not open HIDs file '%s'. %s (%d)" EOL, + path, strerror(errno), errno); + return (-1); + } + + LIST_FOREACH(hid_device, &hid_devices, next) + if (!hid_device->new_device) + fprintf(f, "%s\n", bt_ntoa(&hid_device->bdaddr, NULL)); + + fclose(f); + + if (rename(path, hids_file) < 0) { + SYSLOG(LOGERR, "Could not rename new HIDs file '%s' to '%s'. " \ + "%s (%d)" EOL, path, hids_file, strerror(errno), errno); + unlink(path); + return (-1); + } + + return (0); +} + diff --git a/usr.sbin/bluetooth/bthidd/server.c b/usr.sbin/bluetooth/bthidd/server.c new file mode 100644 index 0000000..d1887db --- /dev/null +++ b/usr.sbin/bluetooth/bthidd/server.c @@ -0,0 +1,316 @@ +/* + * server.c + * + * Copyright (c) 2004 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: server.c,v 1.5 2004/02/26 21:43:36 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 "bthidd.h" +#include "bthid_config.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); + +/* + * Initialize server + */ + +int +server_init(bthid_server_p srv) +{ + struct sockaddr_l2cap l2addr; + + assert(srv != NULL); + + srv->ctrl = srv->intr = -1; + FD_ZERO(&srv->rfdset); + FD_ZERO(&srv->wfdset); + LIST_INIT(&srv->sessions); + + /* Open /dev/consolectl */ + srv->cons = open("/dev/consolectl", O_RDWR); + if (srv->cons < 0) { + syslog(LOG_ERR, "Could not open /dev/consolectl. %s (%d)", + strerror(errno), errno); + 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->cons); + return (-1); + } + + l2addr.l2cap_len = sizeof(l2addr); + l2addr.l2cap_family = AF_BLUETOOTH; + memcpy(&l2addr.l2cap_bdaddr, &srv->bdaddr, sizeof(l2addr.l2cap_bdaddr)); + l2addr.l2cap_psm = 0x11; + + 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->cons); + 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->cons); + return (-1); + } + + /* Create intrrupt socket */ + srv->intr = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BLUETOOTH_PROTO_L2CAP); + if (srv->intr < 0) { + syslog(LOG_ERR, "Could not create interrupt L2CAP socket. " \ + "%s (%d)", strerror(errno), errno); + close(srv->ctrl); + close(srv->cons); + return (-1); + } + + l2addr.l2cap_psm = 0x13; + + 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->ctrl); + close(srv->cons); + 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->ctrl); + close(srv->cons); + return (-1); + } + + FD_SET(srv->ctrl, &srv->rfdset); + FD_SET(srv->intr, &srv->rfdset); + srv->maxfd = max(srv->ctrl, srv->intr); + + return (0); +} + +/* + * Shutdown server + */ + +void +server_shutdown(bthid_server_p srv) +{ + assert(srv != NULL); + + close(srv->cons); + close(srv->ctrl); + close(srv->intr); + + while (!LIST_EMPTY(&srv->sessions)) + session_close(LIST_FIRST(&srv->sessions)); + + memset(srv, 0, sizeof(*srv)); +} + +/* + * Do one server iteration + */ + +int +server_do(bthid_server_p srv) +{ + struct timeval tv; + fd_set rfdset, wfdset; + int n, fd; + + assert(srv != NULL); + + tv.tv_sec = 1; + tv.tv_usec = 0; + + /* Copy cached version of the fd sets and call select */ + memcpy(&rfdset, &srv->rfdset, sizeof(rfdset)); + memcpy(&wfdset, &srv->wfdset, sizeof(wfdset)); + + n = select(srv->maxfd + 1, &rfdset, &wfdset, NULL, &tv); + if (n < 0) { + if (errno == EINTR) + return (0); + + syslog(LOG_ERR, "Could not select(%d, %p, %p). %s (%d)", + srv->maxfd + 1, &rfdset, &wfdset, strerror(errno), errno); + + return (-1); + } + + /* Process descriptors (if any) */ + for (fd = 0; fd < srv->maxfd + 1 && n > 0; fd ++) { + if (FD_ISSET(fd, &rfdset)) { + n --; + + if (fd == srv->ctrl || fd == srv->intr) + server_accept(srv, fd); + else + server_process(srv, fd); + } else if (FD_ISSET(fd, &wfdset)) { + n --; + + client_connect(srv, fd); + } + } + + return (0); +} + +/* + * Accept new connection + */ + +static int +server_accept(bthid_server_p srv, int fd) +{ + bthid_session_p s = NULL; + hid_device_p d = NULL; + struct sockaddr_l2cap l2addr; + int len, new_fd; + + len = sizeof(l2addr); + if ((new_fd = accept(fd, (struct sockaddr *) &l2addr, &len)) < 0) { + syslog(LOG_ERR, "Could not accept %s connection. %s (%d)", + (fd == srv->ctrl)? "control" : "interrupt", + strerror(errno), errno); + 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)); + close(new_fd); + return (-1); + } + } + + /* Update descriptors */ + if (fd == srv->ctrl) { + assert(s->ctrl == -1); + s->ctrl = new_fd; + s->state = (s->intr == -1)? W4INTR : OPEN; + } else { + assert(s->intr == -1); + s->intr = new_fd; + s->state = (s->ctrl == -1)? W4CTRL : OPEN; + } + + FD_SET(new_fd, &srv->rfdset); + if (new_fd > srv->maxfd) + srv->maxfd = new_fd; + + syslog(LOG_NOTICE, "Accepted %s connection from %s", + (fd == srv->ctrl)? "control" : "interrupt", + bt_ntoa(&l2addr.l2cap_bdaddr, NULL)); + + return (0); +} + +/* + * Process data on the connection + */ + +static int +server_process(bthid_server_p srv, int fd) +{ + bthid_session_p s = session_by_fd(srv, fd); + char data[1024]; + int len; + + assert(s != NULL); + + do { + len = read(fd, data, sizeof(data)); + } while (len < 0 && errno == EINTR); + + if (len < 0) { + syslog(LOG_ERR, "Could not read data from %s (%s). %s (%d)", + bt_ntoa(&s->bdaddr, NULL), + (fd == s->ctrl)? "control" : "interrupt", + strerror(errno), errno); + session_close(s); + return (0); + } + + if (len == 0) { + syslog(LOG_NOTICE, "Remote device %s has closed %s connection", + bt_ntoa(&s->bdaddr, NULL), + (fd == s->ctrl)? "control" : "interrupt"); + session_close(s); + return (0); + } + + if (fd == s->ctrl) + hid_control(s, data, len); + else + hid_interrupt(s, data, len); + + return (0); +} + diff --git a/usr.sbin/bluetooth/bthidd/session.c b/usr.sbin/bluetooth/bthidd/session.c new file mode 100644 index 0000000..523b918 --- /dev/null +++ b/usr.sbin/bluetooth/bthidd/session.c @@ -0,0 +1,136 @@ +/* + * session.c + * + * Copyright (c) 2004 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: session.c,v 1.1 2004/02/12 22:46:59 max Exp $ + * $FreeBSD$ + */ + +#include <sys/queue.h> +#include <assert.h> +#include <bluetooth.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include "bthidd.h" + +/* + * Create new session + */ + +bthid_session_p +session_open(bthid_server_p srv, bdaddr_p bdaddr) +{ + bthid_session_p s = NULL; + + assert(srv != NULL); + assert(bdaddr != NULL); + + 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->state = CLOSED; + + LIST_INSERT_HEAD(&srv->sessions, s, next); + } + + return (s); +} + +/* + * Lookup session by bdaddr + */ + +bthid_session_p +session_by_bdaddr(bthid_server_p srv, bdaddr_p bdaddr) +{ + bthid_session_p s = NULL; + + assert(srv != NULL); + assert(bdaddr != NULL); + + LIST_FOREACH(s, &srv->sessions, next) + if (memcmp(&s->bdaddr, bdaddr, sizeof(s->bdaddr)) == 0) + break; + + return (s); +} + +/* + * Lookup session by fd + */ + +bthid_session_p +session_by_fd(bthid_server_p srv, int fd) +{ + bthid_session_p s = NULL; + + assert(srv != NULL); + assert(fd >= 0); + + LIST_FOREACH(s, &srv->sessions, next) + if (s->ctrl == fd || s->intr == fd) + break; + + return (s); +} + +/* + * Close session + */ + +void +session_close(bthid_session_p s) +{ + assert(s != NULL); + assert(s->srv != NULL); + + LIST_REMOVE(s, next); + + if (s->intr != -1) { + FD_CLR(s->intr, &s->srv->rfdset); + FD_CLR(s->intr, &s->srv->wfdset); + close(s->intr); + + if (s->srv->maxfd == s->intr) + s->srv->maxfd --; + } + + if (s->ctrl != -1) { + FD_CLR(s->ctrl, &s->srv->rfdset); + FD_CLR(s->ctrl, &s->srv->wfdset); + close(s->ctrl); + + if (s->srv->maxfd == s->ctrl) + s->srv->maxfd --; + } + + memset(s, 0, sizeof(*s)); + free(s); +} + |