summaryrefslogtreecommitdiff
path: root/hidd/main.c
diff options
context:
space:
mode:
Diffstat (limited to 'hidd/main.c')
-rw-r--r--hidd/main.c860
1 files changed, 860 insertions, 0 deletions
diff --git a/hidd/main.c b/hidd/main.c
new file mode 100644
index 000000000..d0883b363
--- /dev/null
+++ b/hidd/main.c
@@ -0,0 +1,860 @@
+/*
+ *
+ * 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 "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;
+}