| /* |
| * |
| * BlueZ - Bluetooth protocol stack for Linux |
| * |
| * Copyright (C) 2003-2009 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]; |
| unsigned 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]; |
| unsigned 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; |
| } |