diff options
-rw-r--r-- | hidd/main.c | 205 | ||||
-rw-r--r-- | hidd/sdp.c | 94 |
2 files changed, 255 insertions, 44 deletions
diff --git a/hidd/main.c b/hidd/main.c index 46b25ec4..aa2a7b1f 100644 --- a/hidd/main.c +++ b/hidd/main.c @@ -63,6 +63,42 @@ 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; + + addr.l2_family = AF_BLUETOOTH; + bacpy(&addr.l2_bdaddr, src); + addr.l2_psm = 0; + + if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + close(sk); + return -1; + } + + opts.imtu = HIDP_DEFAULT_MTU; + opts.omtu = HIDP_DEFAULT_MTU; + opts.flush_to = 0xffff; + + setsockopt(sk, SOL_L2CAP, L2CAP_OPTIONS, &opts, sizeof(opts)); + + 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 backlog) { struct sockaddr_l2 addr; @@ -115,7 +151,7 @@ static int l2cap_accept(int sk, bdaddr_t *bdaddr) return nsk; } -static int create(int ctl, int csk, int isk, int timeout) +static int create_device(int ctl, int csk, int isk, int timeout) { struct hidp_connadd_req req; struct sockaddr_l2 addr; @@ -140,16 +176,18 @@ static int create(int ctl, int csk, int isk, int timeout) 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 = get_hid_device_info(&src, &dst, &req); + if (err < 0) + return err; + + ba2str(&dst, bda); + syslog(LOG_INFO, "New HID device %s (%s)", bda, req.name); err = ioctl(ctl, HIDPCONNADD, &req); @@ -159,7 +197,7 @@ static int create(int ctl, int csk, int isk, int timeout) return err; } -static void run(int ctl, int csk, int isk, int timeout) +static void run_server(int ctl, int csk, int isk, int timeout) { struct pollfd p[2]; short events; @@ -185,7 +223,7 @@ static void run(int ctl, int csk, int isk, int timeout) ncsk = l2cap_accept(csk, NULL); nisk = l2cap_accept(isk, NULL); - err = create(ctl, ncsk, nisk, timeout); + err = create_device(ctl, ncsk, nisk, timeout); if (err < 0) syslog(LOG_ERR, "HID create error %d (%s)", errno, strerror(errno)); @@ -196,12 +234,87 @@ static void run(int ctl, int csk, int isk, int timeout) } } +static void do_connect(int ctl, bdaddr_t *src, bdaddr_t *dst, int timeout) +{ + int csk, isk, err; + + 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, 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, 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 = 0; + + 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) { + bacpy(&dst, &(info+i)->bdaddr); + ba2str(&dst, addr); + + printf("\tConnecting to device %s\n", addr); + do_connect(ctl, &src, &dst, timeout); + } + } + + free(info); + + if (!num_rsp) { + fprintf(stderr, "\tNo devices in range or visible\n"); + close(ctl); + exit(1); + } +} + static void usage(void) { printf("hidd - Bluetooth HID daemon\n\n"); printf("Usage:\n" - "\thidd [options]\n" + "\thidd [options] [commands]\n" "\n"); printf("Options:\n" @@ -210,6 +323,12 @@ static void usage(void) "\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" + "\n"); } static struct option main_options[] = { @@ -217,21 +336,25 @@ static struct option main_options[] = { { "nodaemon", 0, 0, 'n' }, { "timeout", 1, 0, 't' }, { "device", 1, 0, 'i' }, + { "server", 0, 0, 'd' }, + { "listen", 0, 0, 'd' }, + { "search", 0, 0, 's' }, + { "connect", 1, 0, 'c' }, { 0, 0, 0, 0 } }; int main(int argc, char *argv[]) { struct sigaction sa; - bdaddr_t bdaddr; + bdaddr_t bdaddr, dev; char addr[18]; int log_option = LOG_NDELAY | LOG_PID; int opt, fd, ctl, csk, isk; - int daemon = 1, timeout = 30; + int mode = 0, daemon = 1, timeout = 30; bacpy(&bdaddr, BDADDR_ANY); - while ((opt = getopt_long(argc, argv, "+i:nt:h", main_options, NULL)) != -1) { + while ((opt = getopt_long(argc, argv, "+i:nt:dsc:h", main_options, NULL)) != -1) { switch(opt) { case 'i': if (!strncasecmp(optarg, "hci", 3)) @@ -245,6 +368,16 @@ int main(int argc, char *argv[]) case 't': timeout = atoi(optarg); break; + case 'd': + mode = 1; + break; + case 's': + mode = 2; + break; + case 'c': + str2ba(optarg, &dev); + mode = 3; + break; case 'h': usage(); exit(0); @@ -255,24 +388,42 @@ int main(int argc, char *argv[]) ba2str(&bdaddr, addr); - csk = l2cap_listen(&bdaddr, L2CAP_PSM_HIDP_CTRL, 10); - if (csk < 0) { - perror("Can't listen on HID control channel"); + ctl = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HIDP); + if (ctl < 0) { + perror("Can't open HIDP control socket"); 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); - } + switch (mode) { + case 1: + csk = l2cap_listen(&bdaddr, L2CAP_PSM_HIDP_CTRL, 10); + if (csk < 0) { + perror("Can't listen on HID control channel"); + close(ctl); + exit(1); + } - ctl = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HIDP); - if (ctl < 0) { - perror("Can't open HIDP control socket"); - close(csk); - close(isk); + isk = l2cap_listen(&bdaddr, L2CAP_PSM_HIDP_INTR, 10); + if (isk < 0) { + perror("Can't listen on HID interrupt channel"); + close(ctl); + close(csk); + exit(1); + } + break; + + case 2: + do_search(ctl, &bdaddr, timeout); + close(ctl); + exit(0); + + case 3: + do_connect(ctl, &bdaddr, &dev, timeout); + close(ctl); + exit(0); + + default: + usage(); exit(1); } @@ -309,13 +460,13 @@ int main(int argc, char *argv[]) sigaction(SIGCHLD, &sa, NULL); sigaction(SIGPIPE, &sa, NULL); - run(ctl, csk, isk, timeout); + run_server(ctl, csk, isk, timeout); syslog(LOG_INFO, "Exit"); - close(ctl); close(csk); close(isk); + close(ctl); return 0; } @@ -51,43 +51,103 @@ int get_hid_device_info(bdaddr_t *src, bdaddr_t *dst, struct hidp_connadd_req *req) { + uint32_t range = 0x0000ffff; sdp_session_t *s; - sdp_list_t *srch, *attrs, *rsp; + sdp_list_t *search, *attrid, *pnp_rsp, *hid_rsp; sdp_record_t *rec; - sdp_data_t *pdlist; + sdp_data_t *pdlist, *pdlist2; uuid_t svclass; - uint16_t attr; int err; s = sdp_connect(src, dst, 0); if (!s) return -1; + sdp_uuid16_create(&svclass, PNP_INFO_SVCLASS_ID); + search = sdp_list_append(NULL, &svclass); + attrid = sdp_list_append(NULL, &range); + + err = sdp_service_search_attr_req(s, search, SDP_ATTR_REQ_RANGE, attrid, &pnp_rsp); + + sdp_list_free(search, 0); + sdp_list_free(attrid, 0); + sdp_uuid16_create(&svclass, HID_SVCLASS_ID); - srch = sdp_list_append(NULL, &svclass); + search = sdp_list_append(NULL, &svclass); + attrid = sdp_list_append(NULL, &range); - attr = 0x0206; - attrs = sdp_list_append(NULL, &attr); + err = sdp_service_search_attr_req(s, search, SDP_ATTR_REQ_RANGE, attrid, &hid_rsp); - err = sdp_service_search_attr_req(s, srch, SDP_ATTR_REQ_INDIVIDUAL, attrs, &rsp); + sdp_list_free(search, 0); + sdp_list_free(attrid, 0); sdp_close(s); - if (err || !rsp) + if (err || !hid_rsp) return -1; - rec = (sdp_record_t *) rsp->data; + if (pnp_rsp) { + rec = (sdp_record_t *) pnp_rsp->data; + + pdlist = sdp_data_get(rec, 0x0201); + if (pdlist) + req->vendor = pdlist->val.uint16; + + pdlist = sdp_data_get(rec, 0x0202); + if (pdlist) + req->product = pdlist->val.uint16; + + pdlist = sdp_data_get(rec, 0x0203); + if (pdlist) + req->version = pdlist->val.uint16; + + sdp_record_free(rec); + } + + rec = (sdp_record_t *) hid_rsp->data; + + pdlist = sdp_data_get(rec, 0x0101); + pdlist2 = sdp_data_get(rec, 0x0102); + if (pdlist) { + if (pdlist2) { + if (strncmp(pdlist->val.str, pdlist2->val.str, 5)) { + strncpy(req->name, pdlist2->val.str, sizeof(req->name) - 1); + strcat(req->name, " "); + } + strncat(req->name, pdlist->val.str, + sizeof(req->name) - strlen(req->name)); + } else + strncpy(req->name, pdlist->val.str, sizeof(req->name)); + } else { + pdlist2 = sdp_data_get(rec, 0x0100); + if (pdlist2) + strncpy(req->name, pdlist2->val.str, sizeof(req->name)); + } + + pdlist = sdp_data_get(rec, 0x0201); + if (pdlist) + req->parser = pdlist->val.uint16; + else + req->parser = 0x0100; + + pdlist = sdp_data_get(rec, 0x0203); + if (pdlist) + req->country = pdlist->val.uint8; 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; + if (pdlist) { + 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; + } } + sdp_record_free(rec); + return 0; } |