From 043ec21649d9c0e33013b9092b6ea700f0d15ab8 Mon Sep 17 00:00:00 2001 From: emax Date: Sat, 10 Apr 2004 00:18:00 +0000 Subject: 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. --- usr.sbin/bluetooth/bthidcontrol/Makefile | 15 + usr.sbin/bluetooth/bthidcontrol/bthidcontrol.8 | 99 ++++++ usr.sbin/bluetooth/bthidcontrol/bthidcontrol.c | 208 ++++++++++++ usr.sbin/bluetooth/bthidcontrol/bthidcontrol.h | 50 +++ usr.sbin/bluetooth/bthidcontrol/hid.c | 209 ++++++++++++ usr.sbin/bluetooth/bthidcontrol/sdp.c | 432 +++++++++++++++++++++++++ 6 files changed, 1013 insertions(+) create mode 100644 usr.sbin/bluetooth/bthidcontrol/Makefile create mode 100644 usr.sbin/bluetooth/bthidcontrol/bthidcontrol.8 create mode 100644 usr.sbin/bluetooth/bthidcontrol/bthidcontrol.c create mode 100644 usr.sbin/bluetooth/bthidcontrol/bthidcontrol.h create mode 100644 usr.sbin/bluetooth/bthidcontrol/hid.c create mode 100644 usr.sbin/bluetooth/bthidcontrol/sdp.c (limited to 'usr.sbin/bluetooth/bthidcontrol') 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 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 +.\" 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 + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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 + * 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 + * 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 +#include +#include +#include +#include +#include +#include +#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 + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#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 } +}; + -- cgit v1.1