diff options
Diffstat (limited to 'usr.sbin/bluetooth/bthidcontrol')
| -rw-r--r-- | usr.sbin/bluetooth/bthidcontrol/Makefile | 15 | ||||
| -rw-r--r-- | usr.sbin/bluetooth/bthidcontrol/Makefile.depend | 19 | ||||
| -rw-r--r-- | usr.sbin/bluetooth/bthidcontrol/bthidcontrol.8 | 101 | ||||
| -rw-r--r-- | usr.sbin/bluetooth/bthidcontrol/bthidcontrol.c | 217 | ||||
| -rw-r--r-- | usr.sbin/bluetooth/bthidcontrol/bthidcontrol.h | 51 | ||||
| -rw-r--r-- | usr.sbin/bluetooth/bthidcontrol/hid.c | 216 | ||||
| -rw-r--r-- | usr.sbin/bluetooth/bthidcontrol/sdp.c | 503 | 
7 files changed, 1122 insertions, 0 deletions
| diff --git a/usr.sbin/bluetooth/bthidcontrol/Makefile b/usr.sbin/bluetooth/bthidcontrol/Makefile new file mode 100644 index 000000000000..bc6cc15ab9e7 --- /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 $ + +.PATH:	${.CURDIR:H}/bthidd + +PACKAGE=	bluetooth +PROG=		bthidcontrol +MAN=		bthidcontrol.8 +SRCS=		bthidcontrol.c hid.c lexer.l  parser.y sdp.c +WARNS?=		1 +CFLAGS+=	-DBTHIDCONTROL=1 -I${.CURDIR:H}/bthidd -I${SRCTOP}/lib/libsdp \ +		-I${SRCTOP}/lib/libbluetooth + +LIBADD+=	bluetooth sdp usbhid + +.include <bsd.prog.mk> diff --git a/usr.sbin/bluetooth/bthidcontrol/Makefile.depend b/usr.sbin/bluetooth/bthidcontrol/Makefile.depend new file mode 100644 index 000000000000..0269d05a05a0 --- /dev/null +++ b/usr.sbin/bluetooth/bthidcontrol/Makefile.depend @@ -0,0 +1,19 @@ +# Autogenerated - do NOT edit! + +DIRDEPS = \ +	include \ +	include/xlocale \ +	lib/${CSU_DIR} \ +	lib/libbluetooth \ +	lib/libc \ +	lib/libcompiler_rt \ +	lib/libsdp \ +	lib/libusbhid \ +	usr.bin/yacc.host \ + + +.include <dirdeps.mk> + +.if ${DEP_RELDIR} == ${_DEP_RELDIR} +# local dependencies - needed for -jN in clean tree +.endif diff --git a/usr.sbin/bluetooth/bthidcontrol/bthidcontrol.8 b/usr.sbin/bluetooth/bthidcontrol/bthidcontrol.8 new file mode 100644 index 000000000000..c74a93f99082 --- /dev/null +++ b/usr.sbin/bluetooth/bthidcontrol/bthidcontrol.8 @@ -0,0 +1,101 @@ +.\" 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 $ +.\" +.Dd October 30, 2006 +.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 +.Op Fl v +.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 Fl v +Be verbose and show items that are being used for padding. +.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 -width "Forget" -offset indent -compact +.It Cm Query +.It Cm Dump +.It Cm Known +.It Cm Forget +.El +.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 EXIT STATUS +.Ex -std +.Sh SEE ALSO +.Xr bthidd 8 +.Sh AUTHORS +.An Maksim Yevmenkin Aq Mt 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 000000000000..40ece6ce0635 --- /dev/null +++ b/usr.sbin/bluetooth/bthidcontrol/bthidcontrol.c @@ -0,0 +1,217 @@ +/*- + * bthidcontrol.c + * + * SPDX-License-Identifier: BSD-2-Clause + * + * 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 $ + */ + +#include <sys/queue.h> +#include <assert.h> +#define L2CAP_SOCKET_CHECKED +#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) __dead2; + +int32_t hid_sdp_query(bdaddr_t const *local, bdaddr_t const *remote, int32_t *error); + +uint32_t verbose = 0; + +/* + * 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:hv")) != -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 'v': /* verbose */ +			verbose++; +			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" \ +"	-v		be verbose\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 000000000000..e44b670f8f7e --- /dev/null +++ b/usr.sbin/bluetooth/bthidcontrol/bthidcontrol.h @@ -0,0 +1,51 @@ +/*- + * bthidcontrol.h + * + * SPDX-License-Identifier: BSD-2-Clause + * + * 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 $ + */ + +#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 000000000000..727eb8a716e7 --- /dev/null +++ b/usr.sbin/bluetooth/bthidcontrol/hid.c @@ -0,0 +1,216 @@ +/*- + * hid.c + * + * SPDX-License-Identifier: BSD-2-Clause + * + * 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 $ + */ + +#include <sys/queue.h> +#define L2CAP_SOCKET_CHECKED +#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" + +extern uint32_t verbose; + +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) +{ +	if ((h->flags & HIO_CONST) && !verbose) +		return; + +	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 000000000000..4754744a866a --- /dev/null +++ b/usr.sbin/bluetooth/bthidcontrol/sdp.c @@ -0,0 +1,503 @@ +/*- + * sdp.c + * + * SPDX-License-Identifier: BSD-2-Clause + * + * 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 $ + */ + +#include <sys/param.h> +#include <sys/queue.h> +#include <sys/sysctl.h> +#define L2CAP_SOCKET_CHECKED +#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); + +/* + * Hard coded attribute IDs taken from the + * DEVICE IDENTIFICATION PROFILE SPECIFICATION V13 p.12 + */ + +#define SDP_ATTR_DEVICE_ID_SERVICE_VENDORID  0x0201 +#define SDP_ATTR_DEVICE_ID_SERVICE_PRODUCTID 0x0202 +#define SDP_ATTR_DEVICE_ID_SERVICE_VERSION   0x0203 +#define SDP_ATTR_DEVICE_ID_RANGE SDP_ATTR_RANGE( \ + SDP_ATTR_DEVICE_ID_SERVICE_VENDORID, SDP_ATTR_DEVICE_ID_SERVICE_VERSION ) + +static uint16_t		service = SDP_SERVICE_CLASS_HUMAN_INTERFACE_DEVICE; +static uint16_t		service_devid = SDP_SERVICE_CLASS_PNP_INFORMATION; +static uint32_t 	attrs_devid   = SDP_ATTR_DEVICE_ID_RANGE; + +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 */ +		0x0205), +SDP_ATTR_RANGE(	0x0206,		/* HIDDescriptorList */ +		0x0206), +SDP_ATTR_RANGE(	0x0209,		/* HIDBatteryPower */ +		0x0209), +SDP_ATTR_RANGE(	0x020d,		/* HIDNormallyConnectable */ +		0x020d) +	}; +#define	nattrs	nitems(attrs) + +static sdp_attr_t	values[8]; +#define	nvalues	nitems(values) + +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 void +hid_init_return_values() { +	int i; +	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]; +	} +} + +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, *v; +	int32_t			 i, control_psm = -1, interrupt_psm = -1, +				 reconnect_initiate = -1, +				 normally_connectable = 0, battery_power = 0, +				 hid_descriptor_length = -1, type; +	int16_t 		 vendor_id = 0, product_id = 0, version = 0; +	bdaddr_t		 sdp_local; +	char			 devname[HCI_DEVNAME_SIZE]; + +	if (local == NULL) +		local = NG_HCI_BDADDR_ANY; +	if (hd == NULL) +		hid_sdp_query_exit(EINVAL); + +	hid_init_return_values(); + +	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)); + +	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: /* HIDDescriptorList */ +			if (hid_sdp_parse_hid_descriptor(&values[i]) == 0) { +				hid_descriptor = values[i].value; +				hid_descriptor_length = values[i].vlen; +			} +			break; + +		case 0x0209: /* HIDBatteryPower */ +			battery_power = hid_sdp_parse_boolean(&values[i]); +			break; + +		case 0x020d: /* HIDNormallyConnectable */ +			normally_connectable = hid_sdp_parse_boolean(&values[i]); +			break; +		} +	} + +	hid_init_return_values(); + +	if (sdp_search(ss, 1, &service_devid, 1, &attrs_devid, nvalues, values) != 0) +                hid_sdp_query_exit(sdp_error(ss)); + +	/* Try extract HCI bdaddr from opened SDP session */ +	if (sdp_get_lcaddr(ss, &sdp_local) != 0 || +	    bt_devname(devname, &sdp_local) == 0) +		hid_sdp_query_exit(ENOATTR); + +        sdp_close(ss); +        ss = NULL; + +	/* If search is successful, scan through return vals */ +	for (i = 0; i < 3; i ++ ) { +		if (values[i].flags == SDP_ATTR_INVALID ) +			continue; + +		/* Expecting tag + uint16_t on all 3 attributes */ +		if (values[i].vlen != 3) +			continue; + +		/* Make sure, we're reading a uint16_t */ +		v = values[i].value; +		SDP_GET8(type, v); +		if (type != SDP_DATA_UINT16 ) +			continue; + +		switch (values[i].attr) { +			case SDP_ATTR_DEVICE_ID_SERVICE_VENDORID: +				SDP_GET16(vendor_id, v); +				break; +			case SDP_ATTR_DEVICE_ID_SERVICE_PRODUCTID: +				SDP_GET16(product_id, v); +				break; +			case SDP_ATTR_DEVICE_ID_SERVICE_VERSION: +				SDP_GET16(version, v); +				break; +			default: +				break; +		} +	} + +	if (control_psm == -1 || interrupt_psm == -1 || +	    reconnect_initiate == -1 || +	    hid_descriptor == NULL || hid_descriptor_length == -1) +		hid_sdp_query_exit(ENOATTR); +	hd->name = bt_devremote_name_gen(devname, &hd->bdaddr); +	hd->vendor_id = vendor_id; +	hd->product_id = product_id; +	hd->version = version; +	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 } +}; + | 
