diff options
| -rw-r--r-- | Makefile.am | 2 | ||||
| -rw-r--r-- | configure.in | 2 | ||||
| -rw-r--r-- | hidd/Makefile.am | 11 | ||||
| -rw-r--r-- | hidd/hidd.h | 32 | ||||
| -rw-r--r-- | hidd/main.c | 321 | ||||
| -rw-r--r-- | hidd/sdp.c | 93 | 
6 files changed, 459 insertions, 2 deletions
| diff --git a/Makefile.am b/Makefile.am index 0d627329..d20a2110 100644 --- a/Makefile.am +++ b/Makefile.am @@ -2,7 +2,7 @@  #  $Id$  # -SUBDIRS := hcid tools rfcomm sdpd dund pand test scripts pcmcia +SUBDIRS := hcid tools rfcomm sdpd dund pand hidd test scripts pcmcia  DISTCLEANFILES = conftest.c conftest diff --git a/configure.in b/configure.in index 9a6a9630..58b20bf1 100644 --- a/configure.in +++ b/configure.in @@ -25,4 +25,4 @@ AC_PATH_BLUEZ  AC_PATH_DBUS -AC_OUTPUT(Makefile hcid/Makefile tools/Makefile rfcomm/Makefile sdpd/Makefile dund/Makefile pand/Makefile test/Makefile scripts/Makefile pcmcia/Makefile) +AC_OUTPUT(Makefile hcid/Makefile tools/Makefile rfcomm/Makefile sdpd/Makefile dund/Makefile pand/Makefile hidd/Makefile test/Makefile scripts/Makefile pcmcia/Makefile) diff --git a/hidd/Makefile.am b/hidd/Makefile.am new file mode 100644 index 00000000..dfc7b3e1 --- /dev/null +++ b/hidd/Makefile.am @@ -0,0 +1,11 @@ +# +#  $Id$ +# + +noinst_PROGRAMS = hidd + +hidd_SOURCES = main.c hidd.h sdp.c + +LDFLAGS = @BLUEZ_LIBS@ + +INCLUDES = @BLUEZ_INCLUDES@ diff --git a/hidd/hidd.h b/hidd/hidd.h new file mode 100644 index 00000000..cd0c102c --- /dev/null +++ b/hidd/hidd.h @@ -0,0 +1,32 @@ +/* + * + *  BlueZ - Bluetooth protocol stack for Linux + * + *  Copyright (C) 2003-2004  Marcel Holtmann <marcel@holtmann.org> + * + * + *  This program is free software; you can redistribute it and/or modify + *  it under the terms of the GNU General Public License version 2 as + *  published by the Free Software Foundation; + * + *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + *  OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. + *  IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY + *  CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES  + *  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN  + *  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF  + *  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + *  ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,  + *  COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS  + *  SOFTWARE IS DISCLAIMED. + * + * + *  $Id$ + */ + +#define L2CAP_PSM_HIDP_CTRL 0x11 +#define L2CAP_PSM_HIDP_INTR 0x13 + +int get_hid_device_info(bdaddr_t *src, bdaddr_t *dst, struct hidp_connadd_req *req); diff --git a/hidd/main.c b/hidd/main.c new file mode 100644 index 00000000..46b25ec4 --- /dev/null +++ b/hidd/main.c @@ -0,0 +1,321 @@ +/* + * + *  BlueZ - Bluetooth protocol stack for Linux + * + *  Copyright (C) 2003-2004  Marcel Holtmann <marcel@holtmann.org> + * + * + *  This program is free software; you can redistribute it and/or modify + *  it under the terms of the GNU General Public License version 2 as + *  published by the Free Software Foundation; + * + *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + *  OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. + *  IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY + *  CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES  + *  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN  + *  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF  + *  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + *  ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,  + *  COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS  + *  SOFTWARE IS DISCLAIMED. + * + * + *  $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> +#include <errno.h> +#include <fcntl.h> +#include <unistd.h> +#include <stdlib.h> +#include <malloc.h> +#include <syslog.h> +#include <signal.h> +#include <getopt.h> +#include <sys/poll.h> +#include <sys/ioctl.h> +#include <sys/socket.h> + +#include <bluetooth/bluetooth.h> +#include <bluetooth/hci.h> +#include <bluetooth/hci_lib.h> +#include <bluetooth/l2cap.h> +#include <bluetooth/sdp.h> +#include <bluetooth/hidp.h> + +#include "hidd.h" + +static volatile sig_atomic_t __io_canceled = 0; + +static void sig_hup(int sig) +{ +} + +static void sig_term(int sig) +{ +	__io_canceled = 1; +} + +static int l2cap_listen(const bdaddr_t *bdaddr, unsigned short psm, int backlog) +{ +	struct sockaddr_l2 addr; +	struct l2cap_options opts; +	int sk, lm = L2CAP_LM_MASTER; + +	if ((sk = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP)) < 0) +		return -1; + +	addr.l2_family = AF_BLUETOOTH; +	bacpy(&addr.l2_bdaddr, bdaddr); +	addr.l2_psm = htobs(psm); + +	if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) { +		close(sk); +		return -1; +	} + +	setsockopt(sk, SOL_L2CAP, L2CAP_LM, &lm, sizeof(lm)); + +	opts.imtu = HIDP_DEFAULT_MTU; +	opts.omtu = HIDP_DEFAULT_MTU; +	opts.flush_to = 0xffff; + +	setsockopt(sk, SOL_L2CAP, L2CAP_OPTIONS, &opts, sizeof(opts)); + +	if (listen(sk, backlog) < 0) { +		close(sk); +		return -1; +	} + +	return sk; +} + +static int l2cap_accept(int sk, bdaddr_t *bdaddr) +{ +	struct sockaddr_l2 addr; +	socklen_t addrlen; +	int nsk; + +	memset(&addr, 0, sizeof(addr)); +	addrlen = sizeof(addr); + +	if ((nsk = accept(sk, (struct sockaddr *) &addr, &addrlen)) < 0) +		return -1; + +	if (bdaddr) +		bacpy(bdaddr, &addr.l2_bdaddr); + +	return nsk; +} + +static int create(int ctl, int csk, int isk, int timeout) +{ +	struct hidp_connadd_req req; +	struct sockaddr_l2 addr; +	socklen_t addrlen; +	bdaddr_t src, dst; +	char bda[18]; +	int err; + +	memset(&addr, 0, sizeof(addr)); +	addrlen = sizeof(addr); + +	if (getsockname(csk, (struct sockaddr *) &addr, &addrlen) < 0) +		return -1; + +	bacpy(&src, &addr.l2_bdaddr); + +	memset(&addr, 0, sizeof(addr)); +	addrlen = sizeof(addr); + +	if (getpeername(csk, (struct sockaddr *) &addr, &addrlen) < 0) +		return -1; + +	bacpy(&dst, &addr.l2_bdaddr); + +	ba2str(&dst, bda); +	syslog(LOG_INFO, "New HID connection from %s", bda); + +	memset(&req, 0, sizeof(req)); +	req.ctrl_sock = csk; +	req.intr_sock = isk; +	req.flags     = 0; +	req.idle_to   = timeout * 60; + +	get_hid_device_info(&src, &dst, &req); + +	err = ioctl(ctl, HIDPCONNADD, &req); + +	if (req.rd_data) +		free(req.rd_data); + +	return err; +} + +static void run(int ctl, int csk, int isk, int timeout) +{ +	struct pollfd p[2]; +	short events; +	int err, ncsk, nisk; + +	p[0].fd = csk; +	p[0].events = POLLIN | POLLERR | POLLHUP; + +	p[1].fd = isk; +	p[1].events = POLLIN | POLLERR | POLLHUP; + +	while (!__io_canceled) { +		p[0].revents = 0; +		p[1].revents = 0; + +		err = poll(p, 2, 100); +		if (err <= 0) +			continue; + +		events = p[0].revents | p[1].revents; + +		if (events & POLLIN) { +			ncsk = l2cap_accept(csk, NULL); +			nisk = l2cap_accept(isk, NULL); + +			err = create(ctl, ncsk, nisk, timeout); +			if (err < 0) +				syslog(LOG_ERR, "HID create error %d (%s)", +						errno, strerror(errno)); + +			close(ncsk); +			close(nisk); +		} +	} +} + +static void usage(void) +{ +	printf("hidd - Bluetooth HID daemon\n\n"); + +	printf("Usage:\n" +		"\thidd [options]\n" +		"\n"); + +	printf("Options:\n" +		"\t-i <hciX|bdaddr>     Local HCI device or BD Address\n" +		"\t-t <timeout>         Set idle timeout (in minutes)\n" +		"\t-n, --nodaemon       Don't fork daemon to background\n" +		"\t-h, --help           Display help\n" +		"\n"); +} + +static struct option main_options[] = { +	{ "help",	0, 0, 'h' }, +	{ "nodaemon",	0, 0, 'n' }, +	{ "timeout",	1, 0, 't' }, +	{ "device",	1, 0, 'i' }, +	{ 0, 0, 0, 0 } +}; + +int main(int argc, char *argv[]) +{ +	struct sigaction sa; +	bdaddr_t bdaddr; +	char addr[18]; +	int log_option = LOG_NDELAY | LOG_PID; +	int opt, fd, ctl, csk, isk; +	int daemon = 1, timeout = 30; + +	bacpy(&bdaddr, BDADDR_ANY); + +	while ((opt = getopt_long(argc, argv, "+i:nt:h", main_options, NULL)) != -1) { +		switch(opt) { +		case 'i': +			if (!strncasecmp(optarg, "hci", 3)) +				hci_devba(atoi(optarg + 3), &bdaddr); +			else +				str2ba(optarg, &bdaddr); +			break; +		case 'n': +			daemon = 0; +			break; +		case 't': +			timeout = atoi(optarg); +			break; +		case 'h': +			usage(); +			exit(0); +		default: +			exit(0); +		} +	} + +	ba2str(&bdaddr, addr); + +	csk = l2cap_listen(&bdaddr, L2CAP_PSM_HIDP_CTRL, 10); +	if (csk < 0) { +		perror("Can't listen on HID control channel"); +		exit(1); +	} + +	isk = l2cap_listen(&bdaddr, L2CAP_PSM_HIDP_INTR, 10); +	if (isk < 0) { +		perror("Can't listen on HID interrupt channel"); +		close(csk); +		exit(1); +	} + +	ctl = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HIDP); +	if (ctl < 0) { +		perror("Can't open HIDP control socket"); +		close(csk); +		close(isk); +		exit(1); +	} + +	if (daemon) { +		if (fork()) +			exit(0); + +		fd = open("/dev/null", O_RDWR); +		dup2(fd, 0); dup2(fd, 1); dup2(fd, 2); +		close(fd); + +		setsid(); +		chdir("/"); +	} else +		log_option |= LOG_PERROR; + +	openlog("hidd", log_option, LOG_DAEMON); + +	if (bacmp(&bdaddr, BDADDR_ANY)) +		syslog(LOG_INFO, "Bluetooth HID daemon (%s)", addr); +	else +		syslog(LOG_INFO, "Bluetooth HID daemon"); + +	memset(&sa, 0, sizeof(sa)); +	sa.sa_flags = SA_NOCLDSTOP; + +	sa.sa_handler = sig_term; +	sigaction(SIGTERM, &sa, NULL); +	sigaction(SIGINT,  &sa, NULL); +	sa.sa_handler = sig_hup; +	sigaction(SIGHUP, &sa, NULL); + +	sa.sa_handler = SIG_IGN; +	sigaction(SIGCHLD, &sa, NULL); +	sigaction(SIGPIPE, &sa, NULL); + +	run(ctl, csk, isk, timeout); + +	syslog(LOG_INFO, "Exit"); + +	close(ctl); +	close(csk); +	close(isk); + +	return 0; +} diff --git a/hidd/sdp.c b/hidd/sdp.c new file mode 100644 index 00000000..4c92aa66 --- /dev/null +++ b/hidd/sdp.c @@ -0,0 +1,93 @@ +/* + * + *  BlueZ - Bluetooth protocol stack for Linux + * + *  Copyright (C) 2003-2004  Marcel Holtmann <marcel@holtmann.org> + * + * + *  This program is free software; you can redistribute it and/or modify + *  it under the terms of the GNU General Public License version 2 as + *  published by the Free Software Foundation; + * + *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + *  OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. + *  IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY + *  CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES  + *  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN  + *  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF  + *  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + *  ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,  + *  COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS  + *  SOFTWARE IS DISCLAIMED. + * + * + *  $Id$ + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> +#include <errno.h> +#include <fcntl.h> +#include <unistd.h> +#include <stdlib.h> +#include <syslog.h> +#include <getopt.h> +#include <sys/time.h> +#include <sys/poll.h> +#include <sys/ioctl.h> +#include <sys/socket.h> + +#include <bluetooth/bluetooth.h> +#include <bluetooth/sdp.h> +#include <bluetooth/sdp_lib.h> +#include <bluetooth/hidp.h> + +#include "hidd.h" + +int get_hid_device_info(bdaddr_t *src, bdaddr_t *dst, struct hidp_connadd_req *req) +{ +	sdp_session_t *s; +	sdp_list_t *srch, *attrs, *rsp; +	sdp_record_t *rec; +	sdp_data_t *pdlist; +	uuid_t svclass; +	uint16_t attr; +	int err; + +	s = sdp_connect(src, dst, 0); +	if (!s) +		return -1; + +	sdp_uuid16_create(&svclass, HID_SVCLASS_ID); +	srch  = sdp_list_append(NULL, &svclass); + +	attr  = 0x0206; +	attrs = sdp_list_append(NULL, &attr); + +	err = sdp_service_search_attr_req(s, srch, SDP_ATTR_REQ_INDIVIDUAL, attrs, &rsp); + +	sdp_close(s); + +	if (err || !rsp) +		return -1; + +	rec = (sdp_record_t *) rsp->data; + +	pdlist = sdp_data_get(rec, 0x0206); +	pdlist = pdlist->val.dataseq; +	pdlist = pdlist->val.dataseq; +	pdlist = pdlist->next; + +	req->rd_data = malloc(pdlist->unitSize); +	if (req->rd_data) { +		memcpy(req->rd_data, (unsigned char *) pdlist->val.str, pdlist->unitSize); +		req->rd_size = pdlist->unitSize; +	} + +	return 0; +} | 
