diff options
| author | Marcel Holtmann <marcel@holtmann.org> | 2008-08-04 20:58:34 +0200 | 
|---|---|---|
| committer | Marcel Holtmann <marcel@holtmann.org> | 2008-08-04 20:58:34 +0200 | 
| commit | 40372f9dc3ab115870a10343124216a041e55d17 (patch) | |
| tree | ef5bfa80e3f560cf6856fd7a0d5c27c75c2cefe0 /compat/hidd.c | |
| parent | 295eb0c879802eacff46cf3c962646363d81f6d3 (diff) | |
Move hidd pand and dund into compat directory
Diffstat (limited to 'compat/hidd.c')
| -rw-r--r-- | compat/hidd.c | 861 | 
1 files changed, 861 insertions, 0 deletions
| diff --git a/compat/hidd.c b/compat/hidd.c new file mode 100644 index 00000000..6a0058c4 --- /dev/null +++ b/compat/hidd.c @@ -0,0 +1,861 @@ +/* + * + *  BlueZ - Bluetooth protocol stack for Linux + * + *  Copyright (C) 2003-2008  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 as published by + *  the Free Software Foundation; either version 2 of the License, or + *  (at your option) any later version. + * + *  This program is distributed in the hope that it will be useful, + *  but WITHOUT ANY WARRANTY; without even the implied warranty of + *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + *  GNU General Public License for more details. + * + *  You should have received a copy of the GNU General Public License + *  along with this program; if not, write to the Free Software + *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#define _GNU_SOURCE +#include <stdio.h> +#include <errno.h> +#include <fcntl.h> +#include <unistd.h> +#include <stdlib.h> +#include <string.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 "sdp.h" +#include "hidd.h" + +#ifdef NEED_PPOLL +#include "ppoll.h" +#endif + +enum { +	NONE, +	SHOW, +	SERVER, +	SEARCH, +	CONNECT, +	KILL +}; + +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_connect(bdaddr_t *src, bdaddr_t *dst, unsigned short psm) +{ +	struct sockaddr_l2 addr; +	struct l2cap_options opts; +	int sk; + +	if ((sk = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP)) < 0) +		return -1; + +	memset(&addr, 0, sizeof(addr)); +	addr.l2_family  = AF_BLUETOOTH; +	bacpy(&addr.l2_bdaddr, src); + +	if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) { +		close(sk); +		return -1; +	} + +	memset(&opts, 0, sizeof(opts)); +	opts.imtu = HIDP_DEFAULT_MTU; +	opts.omtu = HIDP_DEFAULT_MTU; +	opts.flush_to = 0xffff; + +	setsockopt(sk, SOL_L2CAP, L2CAP_OPTIONS, &opts, sizeof(opts)); + +	memset(&addr, 0, sizeof(addr)); +	addr.l2_family  = AF_BLUETOOTH; +	bacpy(&addr.l2_bdaddr, dst); +	addr.l2_psm = htobs(psm); + +	if (connect(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) { +		close(sk); +		return -1; +	} + +	return sk; +} + +static int l2cap_listen(const bdaddr_t *bdaddr, unsigned short psm, int lm, int backlog) +{ +	struct sockaddr_l2 addr; +	struct l2cap_options opts; +	int sk; + +	if ((sk = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP)) < 0) +		return -1; + +	memset(&addr, 0, sizeof(addr)); +	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)); + +	memset(&opts, 0, sizeof(opts)); +	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 request_authentication(bdaddr_t *src, bdaddr_t *dst) +{ +	struct hci_conn_info_req *cr; +	char addr[18]; +	int err, dd, dev_id; + +	ba2str(src, addr); +	dev_id = hci_devid(addr); +	if (dev_id < 0) +		return dev_id; + +	dd = hci_open_dev(dev_id); +	if (dd < 0) +		return dd; + +	cr = malloc(sizeof(*cr) + sizeof(struct hci_conn_info)); +	if (!cr) +		return -ENOMEM; + +	bacpy(&cr->bdaddr, dst); +	cr->type = ACL_LINK; +	err = ioctl(dd, HCIGETCONNINFO, (unsigned long) cr); +	if (err < 0) { +		free(cr); +		hci_close_dev(dd); +		return err; +	} + +	err = hci_authenticate_link(dd, htobs(cr->conn_info->handle), 25000); + +	free(cr); +	hci_close_dev(dd); + +	return err; +} + +static int request_encryption(bdaddr_t *src, bdaddr_t *dst) +{ +	struct hci_conn_info_req *cr; +	char addr[18]; +	int err, dd, dev_id; + +	ba2str(src, addr); +	dev_id = hci_devid(addr); +	if (dev_id < 0) +		return dev_id; + +	dd = hci_open_dev(dev_id); +	if (dd < 0) +		return dd; + +	cr = malloc(sizeof(*cr) + sizeof(struct hci_conn_info)); +	if (!cr) +		return -ENOMEM; + +	bacpy(&cr->bdaddr, dst); +	cr->type = ACL_LINK; +	err = ioctl(dd, HCIGETCONNINFO, (unsigned long) cr); +	if (err < 0) { +		free(cr); +		hci_close_dev(dd); +		return err; +	} + +	err = hci_encrypt_link(dd, htobs(cr->conn_info->handle), 1, 25000); + +	free(cr); +	hci_close_dev(dd); + +	return err; +} + +static void enable_sixaxis(int csk) +{ +	const unsigned char buf[] = { +		0x53 /*HIDP_TRANS_SET_REPORT | HIDP_DATA_RTYPE_FEATURE*/, +		0xf4,  0x42, 0x03, 0x00, 0x00 }; +	int err; + +	err = write(csk, buf, sizeof(buf)); +} + +static int create_device(int ctl, int csk, int isk, uint8_t subclass, int nosdp, int nocheck, int bootonly, int encrypt, 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); + +	memset(&req, 0, sizeof(req)); +	req.ctrl_sock = csk; +	req.intr_sock = isk; +	req.flags     = 0; +	req.idle_to   = timeout * 60; + +	err = get_stored_device_info(&src, &dst, &req); +	if (!err) +		goto create; + +	if (!nocheck) { +		ba2str(&dst, bda); +		syslog(LOG_ERR, "Rejected connection from unknown device %s", bda); +		/* Return no error to avoid run_server() complaining too */ +		return 0; +	} + +	if (!nosdp) { +		err = get_sdp_device_info(&src, &dst, &req); +		if (err < 0) +			goto error; +	} else { +		struct l2cap_conninfo conn; +		socklen_t size; +		uint8_t class[3]; + +		memset(&conn, 0, sizeof(conn)); +		size = sizeof(conn); +		if (getsockopt(csk, SOL_L2CAP, L2CAP_CONNINFO, &conn, &size) < 0) +			memset(class, 0, 3); +		else +			memcpy(class, conn.dev_class, 3); + +		if (class[1] == 0x25 && (class[2] == 0x00 || class[2] == 0x01)) +			req.subclass = class[0]; +		else +			req.subclass = 0xc0; +	} + +create: +	if (subclass != 0x00) +		req.subclass = subclass; + +	ba2str(&dst, bda); +	syslog(LOG_INFO, "New HID device %s (%s)", bda, req.name); + +	if (encrypt && (req.subclass & 0x40)) { +		err = request_authentication(&src, &dst); +		if (err < 0) { +			syslog(LOG_ERR, "Authentication for %s failed", bda); +			goto error; +		} + +		err = request_encryption(&src, &dst); +		if (err < 0) +			syslog(LOG_ERR, "Encryption for %s failed", bda); +	} + +	if (bootonly) { +		req.rd_size = 0; +		req.flags |= (1 << HIDP_BOOT_PROTOCOL_MODE); +	} + +	if (req.vendor == 0x054c && req.product == 0x0268) +		enable_sixaxis(csk); + +	err = ioctl(ctl, HIDPCONNADD, &req); + +error: +	if (req.rd_data) +		free(req.rd_data); + +	return err; +} + +static void run_server(int ctl, int csk, int isk, uint8_t subclass, int nosdp, int nocheck, int bootonly, int encrypt, int timeout) +{ +	struct pollfd p[2]; +	sigset_t sigs; +	short events; +	int err, ncsk, nisk; + +	sigfillset(&sigs); +	sigdelset(&sigs, SIGCHLD); +	sigdelset(&sigs, SIGPIPE); +	sigdelset(&sigs, SIGTERM); +	sigdelset(&sigs, SIGINT); +	sigdelset(&sigs, SIGHUP); + +	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; + +		if (ppoll(p, 2, NULL, &sigs) < 1) +			continue; + +		events = p[0].revents | p[1].revents; + +		if (events & POLLIN) { +			ncsk = l2cap_accept(csk, NULL); +			nisk = l2cap_accept(isk, NULL); + +			err = create_device(ctl, ncsk, nisk, subclass, nosdp, nocheck, bootonly, encrypt, timeout); +			if (err < 0) +				syslog(LOG_ERR, "HID create error %d (%s)", +						errno, strerror(errno)); + +			close(nisk); +			sleep(1); +			close(ncsk); +		} +	} +} + +static char *hidp_state[] = { +	"unknown", +	"connected", +	"open", +	"bound", +	"listening", +	"connecting", +	"connecting", +	"config", +	"disconnecting", +	"closed" +}; + +static char *hidp_flagstostr(uint32_t flags) +{ +	static char str[100]; +	str[0] = 0; + +	strcat(str, "["); + +	if (flags & (1 << HIDP_BOOT_PROTOCOL_MODE)) +		strcat(str, "boot-protocol"); + +	strcat(str, "]"); + +	return str; +} + +static void do_show(int ctl) +{ +	struct hidp_connlist_req req; +	struct hidp_conninfo ci[16]; +	char addr[18]; +	int i; + +	req.cnum = 16; +	req.ci   = ci; + +	if (ioctl(ctl, HIDPGETCONNLIST, &req) < 0) { +		perror("Can't get connection list"); +		close(ctl); +		exit(1); +	} + +	for (i = 0; i < req.cnum; i++) { +		ba2str(&ci[i].bdaddr, addr); +		printf("%s %s [%04x:%04x] %s %s\n", addr, ci[i].name, +			ci[i].vendor, ci[i].product, hidp_state[ci[i].state], +			ci[i].flags ? hidp_flagstostr(ci[i].flags) : ""); +	} +} + +static void do_connect(int ctl, bdaddr_t *src, bdaddr_t *dst, uint8_t subclass, int fakehid, int bootonly, int encrypt, int timeout) +{ +	struct hidp_connadd_req req; +	uint16_t uuid = HID_SVCLASS_ID; +	uint8_t channel = 0; +	char name[256]; +	int csk, isk, err; + +	memset(&req, 0, sizeof(req)); + +	err = get_sdp_device_info(src, dst, &req); +	if (err < 0 && fakehid) +		err = get_alternate_device_info(src, dst, +				&uuid, &channel, name, sizeof(name) - 1); + +	if (err < 0) { +		perror("Can't get device information"); +		close(ctl); +		exit(1); +	} + +	switch (uuid) { +	case HID_SVCLASS_ID: +		goto connect; + +	case SERIAL_PORT_SVCLASS_ID: +		if (subclass == 0x40 || !strcmp(name, "Cable Replacement")) { +			if (epox_presenter(src, dst, channel) < 0) { +				close(ctl); +				exit(1); +			} +			break; +		} +		if (subclass == 0x1f || !strcmp(name, "SPP slave")) { +			if (jthree_keyboard(src, dst, channel) < 0) { +				close(ctl); +				exit(1); +			} +			break; +		} +		if (subclass == 0x02 || !strcmp(name, "Serial Port")) { +			if (celluon_keyboard(src, dst, channel) < 0) { +				close(ctl); +				exit(1); +			} +			break; +		} +		break; + +	case HEADSET_SVCLASS_ID: +	case HANDSFREE_SVCLASS_ID: +		if (headset_presenter(src, dst, channel) < 0) { +			close(ctl); +			exit(1); +		} +		break; +	} + +	return; + +connect: +	csk = l2cap_connect(src, dst, L2CAP_PSM_HIDP_CTRL); +	if (csk < 0) { +		perror("Can't create HID control channel"); +		close(ctl); +		exit(1); +	} + +	isk = l2cap_connect(src, dst, L2CAP_PSM_HIDP_INTR); +	if (isk < 0) { +		perror("Can't create HID interrupt channel"); +		close(csk); +		close(ctl); +		exit(1); +	} + +	err = create_device(ctl, csk, isk, subclass, 1, 1, bootonly, encrypt, timeout); +	if (err < 0) { +		fprintf(stderr, "HID create error %d (%s)\n", +						errno, strerror(errno)); +		close(isk); +		sleep(1); +		close(csk); +		close(ctl); +		exit(1); +	} +} + +static void do_search(int ctl, bdaddr_t *bdaddr, uint8_t subclass, int fakehid, int bootonly, int encrypt, int timeout) +{ +	inquiry_info *info = NULL; +	bdaddr_t src, dst; +	int i, dev_id, num_rsp, length, flags; +	char addr[18]; +	uint8_t class[3]; + +	ba2str(bdaddr, addr); +	dev_id = hci_devid(addr); +	if (dev_id < 0) { +		dev_id = hci_get_route(NULL); +		hci_devba(dev_id, &src); +	} else +		bacpy(&src, bdaddr); + +	length  = 8;	/* ~10 seconds */ +	num_rsp = 0; +	flags   = IREQ_CACHE_FLUSH; + +	printf("Searching ...\n"); + +	num_rsp = hci_inquiry(dev_id, length, num_rsp, NULL, &info, flags); + +	for (i = 0; i < num_rsp; i++) { +		memcpy(class, (info+i)->dev_class, 3); +		if (class[1] == 0x25 && (class[2] == 0x00 || class[2] == 0x01)) { +			bacpy(&dst, &(info+i)->bdaddr); +			ba2str(&dst, addr); + +			printf("\tConnecting to device %s\n", addr); +			do_connect(ctl, &src, &dst, subclass, fakehid, bootonly, encrypt, timeout); +		} +	} + +	if (!fakehid) +		goto done; + +	for (i = 0; i < num_rsp; i++) { +		memcpy(class, (info+i)->dev_class, 3); +		if ((class[0] == 0x00 && class[2] == 0x00 &&  +				(class[1] == 0x40 || class[1] == 0x1f)) || +				(class[0] == 0x10 && class[1] == 0x02 && class[2] == 0x40)) { +			bacpy(&dst, &(info+i)->bdaddr); +			ba2str(&dst, addr); + +			printf("\tConnecting to device %s\n", addr); +			do_connect(ctl, &src, &dst, subclass, 1, bootonly, 0, timeout); +		} +	} + +done: +	bt_free(info); + +	if (!num_rsp) { +		fprintf(stderr, "\tNo devices in range or visible\n"); +		close(ctl); +		exit(1); +	} +} + +static void do_kill(int ctl, bdaddr_t *bdaddr, uint32_t flags) +{ +	struct hidp_conndel_req req; +	struct hidp_connlist_req cl; +	struct hidp_conninfo ci[16]; +	int i; + +	if (!bacmp(bdaddr, BDADDR_ALL)) { +		cl.cnum = 16; +		cl.ci   = ci; + +		if (ioctl(ctl, HIDPGETCONNLIST, &cl) < 0) { +			perror("Can't get connection list"); +			close(ctl); +			exit(1); +		} + +		for (i = 0; i < cl.cnum; i++) { +			bacpy(&req.bdaddr, &ci[i].bdaddr); +			req.flags = flags; + +			if (ioctl(ctl, HIDPCONNDEL, &req) < 0) { +				perror("Can't release connection"); +				close(ctl); +				exit(1); +			} +		} + +	} else { +		bacpy(&req.bdaddr, bdaddr); +		req.flags = flags; + +		if (ioctl(ctl, HIDPCONNDEL, &req) < 0) { +			perror("Can't release connection"); +			close(ctl); +			exit(1); +		} +	} +} + +static void usage(void) +{ +	printf("hidd - Bluetooth HID daemon version %s\n\n", VERSION); + +	printf("Usage:\n" +		"\thidd [options] [commands]\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-b <subclass>        Overwrite the boot mode subclass\n" +		"\t-n, --nodaemon       Don't fork daemon to background\n" +		"\t-h, --help           Display help\n" +		"\n"); + +	printf("Commands:\n" +		"\t--server             Start HID server\n" +		"\t--search             Search for HID devices\n" +		"\t--connect <bdaddr>   Connect remote HID device\n" +		"\t--unplug <bdaddr>    Unplug the HID connection\n" +		"\t--kill <bdaddr>      Terminate HID connection\n" +		"\t--killall            Terminate all connections\n" +		"\t--show               List current HID connections\n" +		"\n"); +} + +static struct option main_options[] = { +	{ "help",	0, 0, 'h' }, +	{ "nodaemon",	0, 0, 'n' }, +	{ "subclass",	1, 0, 'b' }, +	{ "timeout",	1, 0, 't' }, +	{ "device",	1, 0, 'i' }, +	{ "master",	0, 0, 'M' }, +	{ "encrypt",	0, 0, 'E' }, +	{ "nosdp",	0, 0, 'D' }, +	{ "nocheck",	0, 0, 'Z' }, +	{ "bootonly",	0, 0, 'B' }, +	{ "hidonly",	0, 0, 'H' }, +	{ "show",	0, 0, 'l' }, +	{ "list",	0, 0, 'l' }, +	{ "server",	0, 0, 'd' }, +	{ "listen",	0, 0, 'd' }, +	{ "search",	0, 0, 's' }, +	{ "create",	1, 0, 'c' }, +	{ "connect",	1, 0, 'c' }, +	{ "disconnect",	1, 0, 'k' }, +	{ "terminate",	1, 0, 'k' }, +	{ "release",	1, 0, 'k' }, +	{ "kill",	1, 0, 'k' }, +	{ "killall",	0, 0, 'K' }, +	{ "unplug",	1, 0, 'u' }, +	{ 0, 0, 0, 0 } +}; + +int main(int argc, char *argv[]) +{ +	struct sigaction sa; +	bdaddr_t bdaddr, dev; +	uint32_t flags = 0; +	uint8_t subclass = 0x00; +	char addr[18]; +	int log_option = LOG_NDELAY | LOG_PID; +	int opt, ctl, csk, isk; +	int mode = SHOW, detach = 1, nosdp = 0, nocheck = 0, bootonly = 0; +	int fakehid = 1, encrypt = 0, timeout = 30, lm = 0; + +	bacpy(&bdaddr, BDADDR_ANY); + +	while ((opt = getopt_long(argc, argv, "+i:nt:b:MEDZBHldsc:k:Ku: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': +			detach = 0; +			break; +		case 't': +			timeout = atoi(optarg); +			break; +		case 'b': +			if (!strncasecmp(optarg, "0x", 2)) +				subclass = (uint8_t) strtol(optarg, NULL, 16); +			else +				subclass = atoi(optarg); +			break; +		case 'M': +			lm |= L2CAP_LM_MASTER; +			break; +		case 'E': +			encrypt = 1; +			break; +		case 'D': +			nosdp = 1; +			break; +		case 'Z': +			nocheck = 1; +			break; +		case 'B': +			bootonly = 1; +			break; +		case 'H': +			fakehid = 0; +			break; +		case 'l': +			mode = SHOW; +			break; +		case 'd': +			mode = SERVER; +			break; +		case 's': +			mode = SEARCH; +			break; +		case 'c': +			str2ba(optarg, &dev); +			mode = CONNECT; +			break; +		case 'k': +			str2ba(optarg, &dev); +			mode = KILL; +			break; +		case 'K': +			bacpy(&dev, BDADDR_ALL); +			mode = KILL; +			break; +		case 'u': +			str2ba(optarg, &dev); +			flags = (1 << HIDP_VIRTUAL_CABLE_UNPLUG); +			mode = KILL; +			break; +		case 'h': +			usage(); +			exit(0); +		default: +			exit(0); +		} +	} + +	ba2str(&bdaddr, addr); + +	ctl = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HIDP); +	if (ctl < 0) { +		perror("Can't open HIDP control socket"); +		exit(1); +	} + +	switch (mode) { +	case SERVER: +		csk = l2cap_listen(&bdaddr, L2CAP_PSM_HIDP_CTRL, lm, 10); +		if (csk < 0) { +			perror("Can't listen on HID control channel"); +			close(ctl); +			exit(1); +		} + +		isk = l2cap_listen(&bdaddr, L2CAP_PSM_HIDP_INTR, lm, 10); +		if (isk < 0) { +			perror("Can't listen on HID interrupt channel"); +			close(ctl); +			close(csk); +			exit(1); +		} +		break; + +	case SEARCH: +		do_search(ctl, &bdaddr, subclass, fakehid, bootonly, encrypt, timeout); +		close(ctl); +		exit(0); + +	case CONNECT: +		do_connect(ctl, &bdaddr, &dev, subclass, fakehid, bootonly, encrypt, timeout); +		close(ctl); +		exit(0); + +	case KILL: +		do_kill(ctl, &dev, flags); +		close(ctl); +		exit(0); + +	default: +		do_show(ctl); +		close(ctl); +		exit(0); +	} + +        if (detach) { +		if (daemon(0, 0)) { +			perror("Can't start daemon"); +        	        exit(1); +		} +	} 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_server(ctl, csk, isk, subclass, nosdp, nocheck, bootonly, encrypt, timeout); + +	syslog(LOG_INFO, "Exit"); + +	close(csk); +	close(isk); +	close(ctl); + +	return 0; +} | 
