summaryrefslogtreecommitdiffstats
path: root/usr.sbin
diff options
context:
space:
mode:
authoremax <emax@FreeBSD.org>2004-04-10 00:18:00 +0000
committeremax <emax@FreeBSD.org>2004-04-10 00:18:00 +0000
commit043ec21649d9c0e33013b9092b6ea700f0d15ab8 (patch)
tree7d74dd469839b4cadda98c84728a960fffee125a /usr.sbin
parent96b75c2267d1d5caf4783f60f78743070ccfa0c8 (diff)
downloadFreeBSD-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.
Diffstat (limited to 'usr.sbin')
-rw-r--r--usr.sbin/bluetooth/bthidcontrol/Makefile15
-rw-r--r--usr.sbin/bluetooth/bthidcontrol/bthidcontrol.899
-rw-r--r--usr.sbin/bluetooth/bthidcontrol/bthidcontrol.c208
-rw-r--r--usr.sbin/bluetooth/bthidcontrol/bthidcontrol.h50
-rw-r--r--usr.sbin/bluetooth/bthidcontrol/hid.c209
-rw-r--r--usr.sbin/bluetooth/bthidcontrol/sdp.c432
-rw-r--r--usr.sbin/bluetooth/bthidd/Makefile14
-rw-r--r--usr.sbin/bluetooth/bthidd/bthid_config.h67
-rw-r--r--usr.sbin/bluetooth/bthidd/bthidd.c256
-rw-r--r--usr.sbin/bluetooth/bthidd/bthidd.conf.sample72
-rw-r--r--usr.sbin/bluetooth/bthidd/bthidd.h88
-rw-r--r--usr.sbin/bluetooth/bthidd/client.c244
-rw-r--r--usr.sbin/bluetooth/bthidd/hid.c262
-rw-r--r--usr.sbin/bluetooth/bthidd/lexer.l100
-rw-r--r--usr.sbin/bluetooth/bthidd/parser.y447
-rw-r--r--usr.sbin/bluetooth/bthidd/server.c316
-rw-r--r--usr.sbin/bluetooth/bthidd/session.c136
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);
+}
+
OpenPOWER on IntegriCloud