/* * sco_connect(pcb, sockaddr) * * Initiate a SCO connection to the destination address. */ int sco_connect(struct sco_pcb *pcb, struct sockaddr_bt *dest) { hci_add_sco_con_cp cp; struct hci_unit *unit; struct hci_link *acl, *sco; int err; if (pcb->sp_flags & SP_LISTENING) return EINVAL; bdaddr_copy(&pcb->sp_raddr, &dest->bt_bdaddr); if (bdaddr_any(&pcb->sp_raddr)) return EDESTADDRREQ; if (bdaddr_any(&pcb->sp_laddr)) { err = hci_route_lookup(&pcb->sp_laddr, &pcb->sp_raddr); if (err) return err; } unit = hci_unit_lookup(&pcb->sp_laddr); if (unit == NULL) return ENETDOWN; /* * We must have an already open ACL connection before we open the SCO * connection, and since SCO connections dont happen on their own we * will not open one, the application wanting this should have opened * it previously. */ acl = hci_link_lookup_bdaddr(unit, &pcb->sp_raddr, HCI_LINK_ACL); if (acl == NULL || acl->hl_state != HCI_LINK_OPEN) return EHOSTUNREACH; sco = hci_link_alloc(unit); if (sco == NULL) return ENOMEM; sco->hl_type = HCI_LINK_SCO; bdaddr_copy(&sco->hl_bdaddr, &pcb->sp_raddr); sco->hl_link = hci_acl_open(unit, &pcb->sp_raddr); KKASSERT(sco->hl_link == acl); cp.con_handle = htole16(acl->hl_handle); cp.pkt_type = htole16(0x00e0); /* HV1, HV2, HV3 */ err = hci_send_cmd(unit, HCI_CMD_ADD_SCO_CON, &cp, sizeof(cp)); if (err) { hci_link_free(sco, err); return err; } sco->hl_sco = pcb; pcb->sp_link = sco; pcb->sp_mtu = unit->hci_max_sco_size; return 0; }
static int bthub_pioctl(dev_t devno, unsigned long cmd, prop_dictionary_t dict, int flag, struct lwp *l) { prop_data_t laddr, raddr; prop_string_t service; prop_dictionary_t prop; prop_object_t obj; device_t dev, self; deviter_t di; int unit; /* validate local address */ laddr = prop_dictionary_get(dict, BTDEVladdr); if (prop_data_size(laddr) != sizeof(bdaddr_t)) return EINVAL; /* locate the relevant bthub */ for (unit = 0 ; ; unit++) { if (unit == bthub_cd.cd_ndevs) return ENXIO; self = device_lookup(&bthub_cd, unit); if (self == NULL) continue; prop = device_properties(self); obj = prop_dictionary_get(prop, BTDEVladdr); if (prop_data_equals(laddr, obj)) break; } /* validate remote address */ raddr = prop_dictionary_get(dict, BTDEVraddr); if (prop_data_size(raddr) != sizeof(bdaddr_t) || bdaddr_any(prop_data_data_nocopy(raddr))) return EINVAL; /* validate service name */ service = prop_dictionary_get(dict, BTDEVservice); if (prop_object_type(service) != PROP_TYPE_STRING) return EINVAL; /* locate matching child device, if any */ deviter_init(&di, 0); while ((dev = deviter_next(&di)) != NULL) { if (device_parent(dev) != self) continue; prop = device_properties(dev); obj = prop_dictionary_get(prop, BTDEVraddr); if (!prop_object_equals(raddr, obj)) continue; obj = prop_dictionary_get(prop, BTDEVservice); if (!prop_object_equals(service, obj)) continue; break; } deviter_release(&di); switch (cmd) { case BTDEV_ATTACH: /* attach BTDEV */ if (dev != NULL) return EADDRINUSE; dev = config_found(self, dict, bthub_print); if (dev == NULL) return ENXIO; prop = device_properties(dev); prop_dictionary_set(prop, BTDEVladdr, laddr); prop_dictionary_set(prop, BTDEVraddr, raddr); prop_dictionary_set(prop, BTDEVservice, service); break; case BTDEV_DETACH: /* detach BTDEV */ if (dev == NULL) return ENXIO; config_detach(dev, DETACH_FORCE); break; } return 0; }
int main(int ac, char *av[]) { bthcid_pin_response_t rp; struct sockaddr_un un; char *pin = NULL; int ch, s, len; memset(&rp, 0, sizeof(rp)); len = -1; memset(&un, 0, sizeof(un)); un.sun_len = sizeof(un); un.sun_family = AF_LOCAL; strlcpy(un.sun_path, BTHCID_SOCKET_NAME, sizeof(un.sun_path)); while ((ch = getopt(ac, av, "a:d:l:p:rs:")) != -1) { switch (ch) { case 'a': if (!bt_aton(optarg, &rp.raddr)) { struct hostent *he = NULL; if ((he = bt_gethostbyname(optarg)) == NULL) errx(EXIT_FAILURE, "%s: %s", optarg, hstrerror(h_errno)); bdaddr_copy(&rp.raddr, (bdaddr_t *)he->h_addr); } break; case 'd': if (!bt_devaddr(optarg, &rp.laddr)) err(EXIT_FAILURE, "%s", optarg); break; case 'l': len = atoi(optarg); if (len < 1 || len > HCI_PIN_SIZE) errx(EXIT_FAILURE, "Invalid PIN length"); break; case 'p': pin = optarg; break; case 'r': if (len == -1) len = 4; break; case 's': strlcpy(un.sun_path, optarg, sizeof(un.sun_path)); break; default: usage(); } } if (bdaddr_any(&rp.raddr)) usage(); if (pin == NULL) { if (len == -1) usage(); srandom(time(NULL)); pin = (char *)rp.pin; while (len-- > 0) *pin++ = '0' + (random() % 10); printf("PIN: %.*s\n", HCI_PIN_SIZE, rp.pin); } else { if (len != -1) usage(); strncpy((char *)rp.pin, pin, HCI_PIN_SIZE); } s = socket(PF_LOCAL, SOCK_STREAM, 0); if (s < 0) err(EXIT_FAILURE, "socket"); if (connect(s, (struct sockaddr *)&un, sizeof(un)) < 0) err(EXIT_FAILURE, "connect(\"%s\")", un.sun_path); if (send(s, &rp, sizeof(rp), 0) != sizeof(rp)) err(EXIT_FAILURE, "send"); close(s); exit(EXIT_SUCCESS); }
void client_init(void) { struct sockaddr_l2cap sa; channel_t *chan; socklen_t len; int fd, n; uint16_t mru, mtu; if (bdaddr_any(&remote_bdaddr)) return; if (service_name) client_query(); fd = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BLUETOOTH_PROTO_L2CAP); if (fd == -1) { log_err("Could not open L2CAP socket: %m"); exit(EXIT_FAILURE); } memset(&sa, 0, sizeof(sa)); sa.l2cap_family = AF_BLUETOOTH; sa.l2cap_len = sizeof(sa); sa.l2cap_bdaddr_type = BDADDR_BREDR; sa.l2cap_cid = 0; bdaddr_copy(&sa.l2cap_bdaddr, &local_bdaddr); if (bind(fd, (struct sockaddr *)&sa, sizeof(sa)) == -1) { log_err("Could not bind client socket: %m"); exit(EXIT_FAILURE); } mru = BNEP_MTU_MIN; if (setsockopt(fd, SOL_L2CAP, SO_L2CAP_IMTU, &mru, sizeof(mru)) == -1) { log_err("Could not set L2CAP IMTU (%d): %m", mru); exit(EXIT_FAILURE); } log_info("Opening connection to service 0x%4.4x at %s", service_class, bt_ntoa(&remote_bdaddr, NULL)); sa.l2cap_psm = htole16(l2cap_psm); bdaddr_copy(&sa.l2cap_bdaddr, &remote_bdaddr); if (connect(fd, (struct sockaddr *)&sa, sizeof(sa)) == -1) { log_err("Could not connect: %m"); exit(EXIT_FAILURE); } len = sizeof(mru); if (getsockopt(fd, SOL_L2CAP, SO_L2CAP_IMTU, &mru, &len) == -1) { log_err("Could not get IMTU: %m"); exit(EXIT_FAILURE); } if (mru < BNEP_MTU_MIN) { log_err("L2CAP IMTU too small (%d)", mru); exit(EXIT_FAILURE); } len = sizeof(n); if (getsockopt(fd, SOL_SOCKET, SO_RCVBUF, &n, &len) == -1) { log_err("Could not read SO_RCVBUF"); exit(EXIT_FAILURE); } if (n < (mru * 10)) { n = mru * 10; if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &n, sizeof(n)) == -1) log_info("Could not increase SO_RCVBUF (from %d)", n); } len = sizeof(mtu); if (getsockopt(fd, SOL_L2CAP, SO_L2CAP_OMTU, &mtu, &len) == -1) { log_err("Could not get L2CAP OMTU: %m"); exit(EXIT_FAILURE); } if (mtu < BNEP_MTU_MIN) { log_err("L2CAP OMTU too small (%d)", mtu); exit(EXIT_FAILURE); } len = sizeof(n); if (getsockopt(fd, SOL_SOCKET, SO_SNDBUF, &n, &len) == -1) { log_err("Could not get socket send buffer size: %m"); close(fd); return; } if (n < (mtu * 2)) { n = mtu * 2; if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &n, sizeof(n)) == -1) { log_err("Could not set socket send buffer size (%d): %m", n); close(fd); return; } } n = mtu; if (setsockopt(fd, SOL_SOCKET, SO_SNDLOWAT, &n, sizeof(n)) == -1) { log_err("Could not set socket low water mark (%d): %m", n); close(fd); return; } chan = channel_alloc(); if (chan == NULL) exit(EXIT_FAILURE); chan->send = bnep_send; chan->recv = bnep_recv; chan->mru = mru; chan->mtu = mtu; b2eaddr(chan->raddr, &remote_bdaddr); b2eaddr(chan->laddr, &local_bdaddr); chan->state = CHANNEL_WAIT_CONNECT_RSP; channel_timeout(chan, 10); if (!channel_open(chan, fd)) exit(EXIT_FAILURE); bnep_send_control(chan, BNEP_SETUP_CONNECTION_REQUEST, 2, service_class, SDP_SERVICE_CLASS_PANU); }