/* * Handle private ioctl. We pass information out about how to talk * to the device and mixer. */ static int btsco_dev_ioctl(void *hdl, u_long cmd, void *addr, int flag, struct lwp *l) { struct btsco_softc *sc = hdl; struct btsco_info *bi = (struct btsco_info *)addr; int err = 0; DPRINTF("%s cmd 0x%lx flag %d\n", sc->sc_name, cmd, flag); switch (cmd) { case BTSCO_GETINFO: memset(bi, 0, sizeof(*bi)); bdaddr_copy(&bi->laddr, &sc->sc_laddr); bdaddr_copy(&bi->raddr, &sc->sc_raddr); bi->channel = sc->sc_channel; bi->vgs = BTSCO_VGS; bi->vgm = BTSCO_VGM; break; default: err = EPASSTHROUGH; break; } return err; }
/* * 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 void bthidev_ctl_connected(void *arg) { struct sockaddr_bt sa; struct bthidev_softc *sc = arg; int err; if (sc->sc_state != BTHID_WAIT_CTL) return; KASSERT(sc->sc_ctl != NULL); KASSERT(sc->sc_int == NULL); if (sc->sc_flags & BTHID_CONNECTING) { /* initiate connect on interrupt PSM */ err = l2cap_attach(&sc->sc_int, &bthidev_int_proto, sc); if (err) goto fail; err = l2cap_setopt(sc->sc_int, &sc->sc_mode); if (err) goto fail; memset(&sa, 0, sizeof(sa)); sa.bt_len = sizeof(sa); sa.bt_family = AF_BLUETOOTH; bdaddr_copy(&sa.bt_bdaddr, &sc->sc_laddr); err = l2cap_bind(sc->sc_int, &sa); if (err) goto fail; sa.bt_psm = sc->sc_intpsm; bdaddr_copy(&sa.bt_bdaddr, &sc->sc_raddr); err = l2cap_connect(sc->sc_int, &sa); if (err) goto fail; } sc->sc_state = BTHID_WAIT_INT; return; fail: l2cap_detach(&sc->sc_ctl); sc->sc_ctl = NULL; aprint_error_dev(sc->sc_dev, "connect failed (%d)\n", err); }
/* * Accept new client connection and register it with index */ static void server_accept_client(server_t *srv, int fd) { struct sockaddr_bt sa; socklen_t len; int cfd; uint16_t omtu; do { cfd = accept(fd, NULL, NULL); } while (cfd == -1 && errno == EINTR); if (cfd == -1) { log_err("Could not accept connection on %s socket. %s (%d)", srv->fdidx[fd].control ? "control" : "L2CAP", strerror(errno), errno); return; } if (cfd >= FD_SETSIZE) { log_crit("File descriptor too large"); close(cfd); return; } assert(!FD_ISSET(cfd, &srv->fdset)); assert(!srv->fdidx[cfd].valid); memset(&sa, 0, sizeof(sa)); omtu = srv->omtu; if (!srv->fdidx[fd].control) { len = sizeof(sa); if (getsockname(cfd, (struct sockaddr *)&sa, &len) == -1) log_warning("getsockname failed, using BDADDR_ANY"); len = sizeof(omtu); if (getsockopt(cfd, BTPROTO_L2CAP, SO_L2CAP_OMTU, &omtu, &len) == -1) log_warning("Could not get L2CAP OMTU, using %d", omtu); else omtu -= sizeof(sdp_pdu_t); } /* Add client descriptor to the index */ if (cfd > srv->fdmax) srv->fdmax = cfd; FD_SET(cfd, &srv->fdset); srv->fdidx[cfd].valid = true; srv->fdidx[cfd].server = false; srv->fdidx[cfd].control = srv->fdidx[fd].control; srv->fdidx[cfd].priv = false; srv->fdidx[cfd].omtu = (omtu > srv->omtu) ? srv->omtu : omtu; srv->fdidx[cfd].offset = 0; bdaddr_copy(&srv->fdidx[cfd].bdaddr, &sa.bt_bdaddr); log_debug("new %s client on fd#%d", srv->fdidx[cfd].control ? "control" : "L2CAP", cfd); }
/* * sco_bind(pcb, sockaddr) * * Bind SCO pcb to local address */ int sco_bind(struct sco_pcb *pcb, struct sockaddr_bt *addr) { bdaddr_copy(&pcb->sp_laddr, &addr->bt_bdaddr); return 0; }
/* * start connecting to our device */ int bthidev_connect(struct bthidev_softc *sc) { struct sockaddr_bt sa; int err; if (sc->sc_attempts++ > 0) printf("%s: connect (#%d)\n", sc->sc_btdev.sc_dev.dv_xname, sc->sc_attempts); memset(&sa, 0, sizeof(sa)); sa.bt_len = sizeof(sa); sa.bt_family = AF_BLUETOOTH; err = l2cap_attach(&sc->sc_ctl, &bthidev_ctl_proto, sc); if (err) { printf("%s: l2cap_attach failed (%d)\n", sc->sc_btdev.sc_dev.dv_xname, err); return err; } err = l2cap_setlinkmode(sc->sc_ctl, sc->sc_mode); if (err) return err; bdaddr_copy(&sa.bt_bdaddr, &sc->sc_laddr); err = l2cap_bind(sc->sc_ctl, &sa); if (err) { printf("%s: l2cap_bind failed (%d)\n", sc->sc_btdev.sc_dev.dv_xname, err); return err; } sa.bt_psm = sc->sc_ctlpsm; bdaddr_copy(&sa.bt_bdaddr, &sc->sc_raddr); err = l2cap_connect(sc->sc_ctl, &sa); if (err) { printf("%s: l2cap_connect failed (%d)\n", sc->sc_btdev.sc_dev.dv_xname, err); return err; } sc->sc_state = BTHID_WAIT_CTL; return 0; }
/* * Open L2CAP server socket */ static bool server_open_l2cap(server_t *srv) { struct sockaddr_bt sa; int fd; fd = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP); if (fd == -1) { log_crit("Could not create L2CAP socket. %s (%d)", strerror(errno), errno); return false; } if (setsockopt(fd, BTPROTO_L2CAP, SO_L2CAP_IMTU, &srv->imtu, sizeof(srv->imtu)) == -1) { log_crit("Could not set L2CAP Incoming MTU. %s (%d)", strerror(errno), errno); close(fd); return false; } memset(&sa, 0, sizeof(sa)); sa.bt_len = sizeof(sa); sa.bt_family = AF_BLUETOOTH; sa.bt_psm = L2CAP_PSM_SDP; bdaddr_copy(&sa.bt_bdaddr, BDADDR_ANY); if (bind(fd, (struct sockaddr *) &sa, sizeof(sa)) == -1) { log_crit("Could not bind L2CAP socket. %s (%d)", strerror(errno), errno); close(fd); return false; } if (listen(fd, 5) == -1) { log_crit("Could not listen on L2CAP socket. %s (%d)", strerror(errno), errno); close(fd); return false; } /* Add L2CAP descriptor to index */ if (fd > srv->fdmax) srv->fdmax = fd; FD_SET(fd, &srv->fdset); srv->fdidx[fd].valid = true; srv->fdidx[fd].server = true; srv->fdidx[fd].control = false; srv->fdidx[fd].priv = false; return true; }
/* * sco_peeraddr(pcb, sockaddr) * * Copy remote address of SCO pcb to sockaddr */ int sco_peeraddr(struct sco_pcb *pcb, struct sockaddr_bt *addr) { memset(addr, 0, sizeof(struct sockaddr_bt)); addr->bt_len = sizeof(struct sockaddr_bt); addr->bt_family = AF_BLUETOOTH; bdaddr_copy(&addr->bt_bdaddr, &pcb->sp_raddr); return 0; }
/* * start connecting to our device */ static int bthidev_connect(struct bthidev_softc *sc) { struct sockaddr_bt sa; int err; if (sc->sc_attempts++ > 0) aprint_verbose_dev(sc->sc_dev, "connect (#%d)\n", sc->sc_attempts); memset(&sa, 0, sizeof(sa)); sa.bt_len = sizeof(sa); sa.bt_family = AF_BLUETOOTH; err = l2cap_attach_pcb(&sc->sc_ctl, &bthidev_ctl_proto, sc); if (err) { aprint_error_dev(sc->sc_dev, "l2cap_attach failed (%d)\n", err); return err; } err = l2cap_setopt(sc->sc_ctl, &sc->sc_mode); if (err) { aprint_error_dev(sc->sc_dev, "l2cap_setopt failed (%d)\n", err); return err; } bdaddr_copy(&sa.bt_bdaddr, &sc->sc_laddr); err = l2cap_bind_pcb(sc->sc_ctl, &sa); if (err) { aprint_error_dev(sc->sc_dev, "l2cap_bind_pcb failed (%d)\n", err); return err; } sa.bt_psm = sc->sc_ctlpsm; bdaddr_copy(&sa.bt_bdaddr, &sc->sc_raddr); err = l2cap_connect_pcb(sc->sc_ctl, &sa); if (err) { aprint_error_dev(sc->sc_dev, "l2cap_connect_pcb failed (%d)\n", err); return err; } sc->sc_state = BTHID_WAIT_CTL; return 0; }
/* * listen for our device */ static int bthidev_listen(struct bthidev_softc *sc) { struct sockaddr_bt sa; int err; memset(&sa, 0, sizeof(sa)); sa.bt_len = sizeof(sa); sa.bt_family = AF_BLUETOOTH; bdaddr_copy(&sa.bt_bdaddr, &sc->sc_laddr); /* * Listen on control PSM */ err = l2cap_attach(&sc->sc_ctl_l, &bthidev_ctl_proto, sc); if (err) return err; err = l2cap_setopt(sc->sc_ctl_l, &sc->sc_mode); if (err) return err; sa.bt_psm = sc->sc_ctlpsm; err = l2cap_bind(sc->sc_ctl_l, &sa); if (err) return err; err = l2cap_listen(sc->sc_ctl_l); if (err) return err; /* * Listen on interrupt PSM */ err = l2cap_attach(&sc->sc_int_l, &bthidev_int_proto, sc); if (err) return err; err = l2cap_setopt(sc->sc_int_l, &sc->sc_mode); if (err) return err; sa.bt_psm = sc->sc_intpsm; err = l2cap_bind(sc->sc_int_l, &sa); if (err) return err; err = l2cap_listen(sc->sc_int_l); if (err) return err; sc->sc_state = BTHID_WAIT_CTL; return 0; }
int bt_devaddr(char const *devname, bdaddr_t *addr) { struct bt_devinfo di; strlcpy(di.devname, devname, sizeof(di.devname)); if (bt_devinfo(&di) < 0) return (0); if (addr != NULL) bdaddr_copy(addr, &di.bdaddr); return (1); }
static void server_open(void) { struct sockaddr_l2cap sa; uint16_t mru; server_fd = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BLUETOOTH_PROTO_L2CAP); if (server_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_psm = htole16(l2cap_psm); sa.l2cap_bdaddr_type = BDADDR_BREDR; sa.l2cap_cid = 0; bdaddr_copy(&sa.l2cap_bdaddr, &local_bdaddr); if (bind(server_fd, (struct sockaddr *)&sa, sizeof(sa)) == -1) { log_err("Could not bind server socket: %m"); exit(EXIT_FAILURE); } mru = BNEP_MTU_MIN; if (setsockopt(server_fd, SOL_L2CAP, SO_L2CAP_IMTU, &mru, sizeof(mru)) == -1) { log_err("Could not set L2CAP IMTU (%d): %m", mru); exit(EXIT_FAILURE); } if (listen(server_fd, 0) == -1) { log_err("Could not listen on server socket: %m"); exit(EXIT_FAILURE); } event_set(&server_ev, server_fd, EV_READ | EV_PERSIST, server_read, NULL); if (event_add(&server_ev, NULL) == -1) { log_err("Could not add server event: %m"); exit(EXIT_FAILURE); } log_info("server socket open"); }
static void bthidev_attach(device_t parent, device_t self, void *aux) { struct bthidev_softc *sc = device_private(self); prop_dictionary_t dict = aux; prop_object_t obj; device_t dev; struct bthidev_attach_args bha; struct bthidev *hidev; struct hid_data *d; struct hid_item h; const void *desc; int locs[BTHIDBUSCF_NLOCS]; int maxid, rep, dlen; /* * Init softc */ sc->sc_dev = self; LIST_INIT(&sc->sc_list); callout_init(&sc->sc_reconnect, 0); callout_setfunc(&sc->sc_reconnect, bthidev_timeout, sc); sc->sc_state = BTHID_CLOSED; sc->sc_flags = BTHID_CONNECTING; sc->sc_ctlpsm = L2CAP_PSM_HID_CNTL; sc->sc_intpsm = L2CAP_PSM_HID_INTR; sockopt_init(&sc->sc_mode, BTPROTO_L2CAP, SO_L2CAP_LM, 0); /* * extract config from proplist */ obj = prop_dictionary_get(dict, BTDEVladdr); bdaddr_copy(&sc->sc_laddr, prop_data_data_nocopy(obj)); obj = prop_dictionary_get(dict, BTDEVraddr); bdaddr_copy(&sc->sc_raddr, prop_data_data_nocopy(obj)); obj = prop_dictionary_get(dict, BTDEVmode); if (prop_object_type(obj) == PROP_TYPE_STRING) { if (prop_string_equals_cstring(obj, BTDEVauth)) sockopt_setint(&sc->sc_mode, L2CAP_LM_AUTH); else if (prop_string_equals_cstring(obj, BTDEVencrypt)) sockopt_setint(&sc->sc_mode, L2CAP_LM_ENCRYPT); else if (prop_string_equals_cstring(obj, BTDEVsecure)) sockopt_setint(&sc->sc_mode, L2CAP_LM_SECURE); else { aprint_error(" unknown %s\n", BTDEVmode); return; } aprint_verbose(" %s %s", BTDEVmode, prop_string_cstring_nocopy(obj)); } obj = prop_dictionary_get(dict, BTHIDEVcontrolpsm); if (prop_object_type(obj) == PROP_TYPE_NUMBER) { sc->sc_ctlpsm = prop_number_integer_value(obj); if (L2CAP_PSM_INVALID(sc->sc_ctlpsm)) { aprint_error(" invalid %s\n", BTHIDEVcontrolpsm); return; } } obj = prop_dictionary_get(dict, BTHIDEVinterruptpsm); if (prop_object_type(obj) == PROP_TYPE_NUMBER) { sc->sc_intpsm = prop_number_integer_value(obj); if (L2CAP_PSM_INVALID(sc->sc_intpsm)) { aprint_error(" invalid %s\n", BTHIDEVinterruptpsm); return; } } obj = prop_dictionary_get(dict, BTHIDEVdescriptor); if (prop_object_type(obj) == PROP_TYPE_DATA) { dlen = prop_data_size(obj); desc = prop_data_data_nocopy(obj); } else { aprint_error(" no %s\n", BTHIDEVdescriptor); return; } obj = prop_dictionary_get(dict, BTHIDEVreconnect); if (prop_object_type(obj) == PROP_TYPE_BOOL && !prop_bool_true(obj)) sc->sc_flags |= BTHID_RECONNECT; /* * Parse the descriptor and attach child devices, one per report. */ maxid = -1; h.report_ID = 0; d = hid_start_parse(desc, dlen, hid_none); while (hid_get_item(d, &h)) { if (h.report_ID > maxid) maxid = h.report_ID; } hid_end_parse(d); if (maxid < 0) { aprint_error(" no reports found\n"); return; } aprint_normal("\n"); for (rep = 0 ; rep <= maxid ; rep++) { if (hid_report_size(desc, dlen, hid_feature, rep) == 0 && hid_report_size(desc, dlen, hid_input, rep) == 0 && hid_report_size(desc, dlen, hid_output, rep) == 0) continue; bha.ba_desc = desc; bha.ba_dlen = dlen; bha.ba_input = bthidev_null; bha.ba_feature = bthidev_null; bha.ba_output = bthidev_output; bha.ba_id = rep; locs[BTHIDBUSCF_REPORTID] = rep; dev = config_found_sm_loc(self, "bthidbus", locs, &bha, bthidev_print, config_stdsubmatch); if (dev != NULL) { hidev = device_private(dev); hidev->sc_dev = dev; hidev->sc_parent = self; hidev->sc_id = rep; hidev->sc_input = bha.ba_input; hidev->sc_feature = bha.ba_feature; LIST_INSERT_HEAD(&sc->sc_list, hidev, sc_next); } } /* * start bluetooth connections */ mutex_enter(bt_lock); if ((sc->sc_flags & BTHID_RECONNECT) == 0) bthidev_listen(sc); if (sc->sc_flags & BTHID_CONNECTING) bthidev_connect(sc); mutex_exit(bt_lock); }
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 bthidev_attach(struct device *parent, struct device *self, void *aux) { struct bthidev_softc *sc = (struct bthidev_softc *)self; struct btdev_attach_args *bda = (struct btdev_attach_args *)aux; struct bthidev_attach_args bha; struct bthidev *hidev; struct hid_data *d; struct hid_item h; int maxid, rep; /* * Init softc */ LIST_INIT(&sc->sc_list); timeout_set(&sc->sc_reconnect, bthidev_timeout, sc); sc->sc_state = BTHID_CLOSED; sc->sc_flags = BTHID_CONNECTING; sc->sc_ctlpsm = L2CAP_PSM_HID_CNTL; sc->sc_intpsm = L2CAP_PSM_HID_INTR; sc->sc_mode = 0; /* * copy in our configuration info */ bdaddr_copy(&sc->sc_laddr, &bda->bd_laddr); bdaddr_copy(&sc->sc_raddr, &bda->bd_raddr); if (bda->bd_mode != BTDEV_MODE_NONE) { if (bda->bd_mode == BTDEV_MODE_AUTH) { sc->sc_mode = L2CAP_LM_AUTH; printf(" auth"); } else if (bda->bd_mode == BTDEV_MODE_ENCRYPT) { sc->sc_mode = L2CAP_LM_ENCRYPT; printf(" encrypt"); } else if (bda->bd_mode == BTDEV_MODE_SECURE) { sc->sc_mode = L2CAP_LM_SECURE; printf(" secure"); } else { printf(" unknown link-mode: %d\n", bda->bd_mode); return; } } if (!L2CAP_PSM_INVALID(bda->bd_hid.hid_ctl)) sc->sc_ctlpsm = bda->bd_hid.hid_ctl; if (!L2CAP_PSM_INVALID(bda->bd_hid.hid_int)) sc->sc_intpsm = bda->bd_hid.hid_int; if (bda->bd_hid.hid_flags & BTHID_INITIATE) sc->sc_flags |= BTHID_RECONNECT; if (bda->bd_hid.hid_desc == NULL || bda->bd_hid.hid_dlen == 0 || bda->bd_hid.hid_dlen > MAX_DESCRIPTOR_LEN) { printf(": no descriptor\n"); return; } sc->sc_dlen = bda->bd_hid.hid_dlen; sc->sc_desc = malloc(bda->bd_hid.hid_dlen, M_BTHIDEV, M_WAITOK | M_CANFAIL); if (sc->sc_desc == NULL) { printf(": no memory\n"); return; } if (copyin(bda->bd_hid.hid_desc, sc->sc_desc, bda->bd_hid.hid_dlen)) { free(sc->sc_desc, M_BTHIDEV); printf(": no descriptor"); return; } /* * Parse the descriptor and attach child devices, one per report. */ maxid = -1; h.report_ID = 0; d = hid_start_parse(sc->sc_desc, sc->sc_dlen, hid_none); while (hid_get_item(d, &h)) { if (h.report_ID > maxid) maxid = h.report_ID; } hid_end_parse(d); if (maxid < 0) { printf(": no reports found\n"); return; } printf("\n"); for (rep = 0 ; rep <= maxid ; rep++) { if (hid_report_size(sc->sc_desc, sc->sc_dlen, hid_feature, rep) == 0 && hid_report_size(sc->sc_desc, sc->sc_dlen, hid_input, rep) == 0 && hid_report_size(sc->sc_desc, sc->sc_dlen, hid_output, rep) == 0) continue; bha.ba_desc = sc->sc_desc; bha.ba_dlen = sc->sc_dlen; bha.ba_input = bthidev_null; bha.ba_feature = bthidev_null; bha.ba_output = bthidev_output; bha.ba_id = rep; hidev = (struct bthidev *)config_found_sm(self, &bha, bthidev_print, bthidevsubmatch); if (hidev != NULL) { hidev->sc_parent = &sc->sc_btdev; hidev->sc_id = rep; hidev->sc_input = bha.ba_input; hidev->sc_feature = bha.ba_feature; LIST_INSERT_HEAD(&sc->sc_list, hidev, sc_next); } } /* * start bluetooth connections */ mutex_enter(&bt_lock); if ((sc->sc_flags & BTHID_RECONNECT) == 0) bthidev_listen(sc); if (sc->sc_flags & BTHID_CONNECTING) bthidev_connect(sc); mutex_exit(&bt_lock); }
static int btsco_open(void *hdl, int flags) { struct sockaddr_bt sa; struct btsco_softc *sc = hdl; struct sockopt sopt; int err, timo; DPRINTF("%s flags 0x%x\n", sc->sc_name, flags); /* flags FREAD & FWRITE? */ if (sc->sc_sco != NULL || sc->sc_sco_l != NULL) return EIO; KASSERT(mutex_owned(bt_lock)); memset(&sa, 0, sizeof(sa)); sa.bt_len = sizeof(sa); sa.bt_family = AF_BLUETOOTH; bdaddr_copy(&sa.bt_bdaddr, &sc->sc_laddr); if (sc->sc_flags & BTSCO_LISTEN) { err = sco_attach_pcb(&sc->sc_sco_l, &btsco_sco_proto, sc); if (err) goto done; err = sco_bind_pcb(sc->sc_sco_l, &sa); if (err) { sco_detach_pcb(&sc->sc_sco_l); goto done; } err = sco_listen_pcb(sc->sc_sco_l); if (err) { sco_detach_pcb(&sc->sc_sco_l); goto done; } timo = 0; /* no timeout */ } else { err = sco_attach_pcb(&sc->sc_sco, &btsco_sco_proto, sc); if (err) goto done; err = sco_bind_pcb(sc->sc_sco, &sa); if (err) { sco_detach_pcb(&sc->sc_sco); goto done; } bdaddr_copy(&sa.bt_bdaddr, &sc->sc_raddr); err = sco_connect_pcb(sc->sc_sco, &sa); if (err) { sco_detach_pcb(&sc->sc_sco); goto done; } timo = BTSCO_TIMEOUT; } sc->sc_state = BTSCO_WAIT_CONNECT; while (err == 0 && sc->sc_state == BTSCO_WAIT_CONNECT) err = cv_timedwait_sig(&sc->sc_connect, bt_lock, timo); switch (sc->sc_state) { case BTSCO_CLOSED: /* disconnected */ err = sc->sc_err; /* fall through to */ case BTSCO_WAIT_CONNECT: /* error */ if (sc->sc_sco != NULL) sco_detach_pcb(&sc->sc_sco); if (sc->sc_sco_l != NULL) sco_detach_pcb(&sc->sc_sco_l); break; case BTSCO_OPEN: /* hurrah */ sockopt_init(&sopt, BTPROTO_SCO, SO_SCO_MTU, 0); (void)sco_getopt(sc->sc_sco, &sopt); (void)sockopt_get(&sopt, &sc->sc_mtu, sizeof(sc->sc_mtu)); sockopt_destroy(&sopt); break; default: UNKNOWN(sc->sc_state); break; } done: DPRINTF("done err=%d, sc_state=%d, sc_mtu=%d\n", err, sc->sc_state, sc->sc_mtu); return err; }
static void btsco_attach(device_t parent, device_t self, void *aux) { struct btsco_softc *sc = device_private(self); prop_dictionary_t dict = aux; prop_object_t obj; /* * Init softc */ sc->sc_vgs = 200; sc->sc_vgm = 200; sc->sc_state = BTSCO_CLOSED; sc->sc_name = device_xname(self); cv_init(&sc->sc_connect, "connect"); mutex_init(&sc->sc_intr_lock, MUTEX_DEFAULT, IPL_NONE); /* * copy in our configuration info */ obj = prop_dictionary_get(dict, BTDEVladdr); bdaddr_copy(&sc->sc_laddr, prop_data_data_nocopy(obj)); obj = prop_dictionary_get(dict, BTDEVraddr); bdaddr_copy(&sc->sc_raddr, prop_data_data_nocopy(obj)); obj = prop_dictionary_get(dict, BTDEVservice); if (prop_string_equals_cstring(obj, "HF")) { sc->sc_flags |= BTSCO_LISTEN; aprint_verbose(" listen mode"); } obj = prop_dictionary_get(dict, BTSCOchannel); if (prop_object_type(obj) != PROP_TYPE_NUMBER || prop_number_integer_value(obj) < RFCOMM_CHANNEL_MIN || prop_number_integer_value(obj) > RFCOMM_CHANNEL_MAX) { aprint_error(" invalid %s", BTSCOchannel); return; } sc->sc_channel = prop_number_integer_value(obj); aprint_verbose(" channel %d", sc->sc_channel); aprint_normal("\n"); DPRINTF("sc=%p\n", sc); /* * set up transmit interrupt */ sc->sc_intr = softint_establish(SOFTINT_NET, btsco_intr, sc); if (sc->sc_intr == NULL) { aprint_error_dev(self, "softint_establish failed\n"); return; } /* * attach audio device */ sc->sc_audio = audio_attach_mi(&btsco_if, sc, self); if (sc->sc_audio == NULL) { aprint_error_dev(self, "audio_attach_mi failed\n"); return; } pmf_device_register(self, NULL, NULL); }
static void server_accept_client(server_p srv, int32_t fd) { uint8_t *rsp = NULL; socklen_t size; int32_t cfd; uint16_t omtu; do { cfd = accept(fd, NULL, NULL); } while (cfd < 0 && errno == EINTR); if (cfd < 0) { log_err("Could not accept connection on %s socket. %s (%d)", srv->fdidx[fd].control? "control" : "L2CAP", strerror(errno), errno); return; } assert(!FD_ISSET(cfd, &srv->fdset)); assert(!srv->fdidx[cfd].valid); if (!srv->fdidx[fd].control) { /* Get local BD_ADDR */ size = sizeof(srv->req_sa); if (getsockname(cfd,(struct sockaddr*)&srv->req_sa, &size) < 0) { log_err("Could not get local BD_ADDR. %s (%d)", strerror(errno), errno); close(cfd); return; } /* Get outgoing MTU */ size = sizeof(omtu); if (getsockopt(cfd, BTPROTO_L2CAP, SO_L2CAP_OMTU, &omtu, &size) < 0) { log_err("Could not get L2CAP OMTU. %s (%d)", strerror(errno), errno); close(cfd); return; } /* * The maximum size of the L2CAP packet is 65536 bytes. * The minimum L2CAP MTU is 43 bytes. That means we need * 65536 / 43 = ~1524 chunks to transfer maximum packet * size with minimum MTU. The "rsp_cs" field in fd_idx_t * is 11 bit wide that gives us upto 2048 chunks. */ if (omtu < L2CAP_MTU_MINIMUM) { log_err("L2CAP OMTU is too small (%d bytes)", omtu); close(cfd); return; } } else { bdaddr_copy(&srv->req_sa.bt_bdaddr, BDADDR_ANY); omtu = srv->fdidx[fd].omtu; } /* * Allocate buffer. This is an overkill, but we can not know how * big our reply is going to be. */ rsp = (uint8_t *) calloc(L2CAP_MTU_MAXIMUM, sizeof(rsp[0])); if (rsp == NULL) { log_crit("Could not allocate response buffer"); close(cfd); return; } /* Add client descriptor to the index */ FD_SET(cfd, &srv->fdset); if (srv->maxfd < cfd) srv->maxfd = cfd; srv->fdidx[cfd].valid = 1; srv->fdidx[cfd].server = 0; srv->fdidx[cfd].control = srv->fdidx[fd].control; srv->fdidx[cfd].priv = 0; srv->fdidx[cfd].rsp_cs = 0; srv->fdidx[cfd].rsp_size = 0; srv->fdidx[cfd].rsp_limit = 0; srv->fdidx[cfd].omtu = omtu; srv->fdidx[cfd].rsp = rsp; }
int32_t server_init(server_p srv, char const *control, char const *sgroup) { struct sockaddr_un un; struct sockaddr_bt l2; socklen_t size; int32_t unsock, l2sock; uint16_t imtu; int opt; assert(srv != NULL); assert(control != NULL); memset(srv, 0, sizeof(srv)); srv->sgroup = sgroup; /* Open control socket */ if (unlink(control) < 0 && errno != ENOENT) { log_crit("Could not unlink(%s). %s (%d)", control, strerror(errno), errno); return (-1); } unsock = socket(PF_LOCAL, SOCK_STREAM, 0); if (unsock < 0) { log_crit("Could not create control socket. %s (%d)", strerror(errno), errno); return (-1); } opt = 1; #if 0 if (setsockopt(unsock, 0, LOCAL_CREDS, &opt, sizeof(opt)) < 0) log_crit("Warning: No credential checks on control socket"); #endif memset(&un, 0, sizeof(un)); un.sun_len = sizeof(un); un.sun_family = AF_LOCAL; strlcpy(un.sun_path, control, sizeof(un.sun_path)); if (bind(unsock, (struct sockaddr *) &un, sizeof(un)) < 0) { log_crit("Could not bind control socket. %s (%d)", strerror(errno), errno); close(unsock); return (-1); } if (chmod(control, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH) < 0) { log_crit("Could not change permissions on control socket. " \ "%s (%d)", strerror(errno), errno); close(unsock); return (-1); } if (listen(unsock, 10) < 0) { log_crit("Could not listen on control socket. %s (%d)", strerror(errno), errno); close(unsock); return (-1); } /* Open L2CAP socket */ l2sock = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP); if (l2sock < 0) { log_crit("Could not create L2CAP socket. %s (%d)", strerror(errno), errno); close(unsock); return (-1); } size = sizeof(imtu); if (getsockopt(l2sock, BTPROTO_L2CAP, SO_L2CAP_IMTU, &imtu, &size) < 0) { log_crit("Could not get L2CAP IMTU. %s (%d)", strerror(errno), errno); close(unsock); close(l2sock); return (-1); } memset(&l2, 0, sizeof(l2)); l2.bt_len = sizeof(l2); l2.bt_family = AF_BLUETOOTH; l2.bt_psm = L2CAP_PSM_SDP; bdaddr_copy(&l2.bt_bdaddr, BDADDR_ANY); if (bind(l2sock, (struct sockaddr *) &l2, sizeof(l2)) < 0) { log_crit("Could not bind L2CAP socket. %s (%d)", strerror(errno), errno); close(unsock); close(l2sock); return (-1); } if (listen(l2sock, 10) < 0) { log_crit("Could not listen on L2CAP socket. %s (%d)", strerror(errno), errno); close(unsock); close(l2sock); return (-1); } /* Allocate incoming buffer */ srv->imtu = (imtu > SDP_LOCAL_MTU)? imtu : SDP_LOCAL_MTU; srv->req = (uint8_t *) calloc(srv->imtu, sizeof(srv->req[0])); if (srv->req == NULL) { log_crit("Could not allocate request buffer"); close(unsock); close(l2sock); return (-1); } /* Allocate memory for descriptor index */ srv->fdidx = (fd_idx_p) calloc(FD_SETSIZE, sizeof(srv->fdidx[0])); if (srv->fdidx == NULL) { log_crit("Could not allocate fd index"); free(srv->req); close(unsock); close(l2sock); return (-1); } /* Register Service Discovery profile (attach it to control socket) */ if (provider_register_sd(unsock) < 0) { log_crit("Could not register Service Discovery profile"); free(srv->fdidx); free(srv->req); close(unsock); close(l2sock); return (-1); } /* * If we got here then everything is fine. Add both control sockets * to the index. */ FD_ZERO(&srv->fdset); srv->maxfd = (unsock > l2sock)? unsock : l2sock; FD_SET(unsock, &srv->fdset); srv->fdidx[unsock].valid = 1; srv->fdidx[unsock].server = 1; srv->fdidx[unsock].control = 1; srv->fdidx[unsock].priv = 0; srv->fdidx[unsock].rsp_cs = 0; srv->fdidx[unsock].rsp_size = 0; srv->fdidx[unsock].rsp_limit = 0; srv->fdidx[unsock].omtu = SDP_LOCAL_MTU; srv->fdidx[unsock].rsp = NULL; FD_SET(l2sock, &srv->fdset); srv->fdidx[l2sock].valid = 1; srv->fdidx[l2sock].server = 1; srv->fdidx[l2sock].control = 0; srv->fdidx[l2sock].priv = 0; srv->fdidx[l2sock].rsp_cs = 0; srv->fdidx[l2sock].rsp_size = 0; srv->fdidx[l2sock].rsp_limit = 0; srv->fdidx[l2sock].omtu = 0; /* unknown */ srv->fdidx[l2sock].rsp = NULL; return (0); }
static int bt_open(struct voss_backend *pbe, const char *devname, int samplerate, int bufsize, int *pchannels, int *pformat, struct bt_config *cfg, int service_class, int isSink) { struct sockaddr_l2cap addr; struct l2cap_info info; socklen_t mtusize = sizeof(uint16_t); int tmpbitpool; int l2cap_psm; int temp; int err; memset(&info, 0, sizeof(info)); if (strstr(devname, "/dev/bluetooth/") != devname) { printf("Invalid device name '%s'", devname); goto error; } /* skip prefix */ devname += sizeof("/dev/bluetooth/") - 1; if (!bt_aton(devname, &info.raddr)) { struct hostent *he = NULL; if ((he = bt_gethostbyname(devname)) == NULL) { DPRINTF("Could not get host by name\n"); goto error; } bdaddr_copy(&info.raddr, (bdaddr_t *)he->h_addr); } retry: switch (samplerate) { case 8000: cfg->freq = FREQ_UNDEFINED; cfg->aacMode1 = 0x80; cfg->aacMode2 = 0x0C; break; case 11025: cfg->freq = FREQ_UNDEFINED; cfg->aacMode1 = 0x40; cfg->aacMode2 = 0x0C; break; case 12000: cfg->freq = FREQ_UNDEFINED; cfg->aacMode1 = 0x20; cfg->aacMode2 = 0x0C; break; case 16000: cfg->freq = FREQ_16K; cfg->aacMode1 = 0x10; cfg->aacMode2 = 0x0C; break; case 22050: cfg->freq = FREQ_UNDEFINED; cfg->aacMode1 = 0x08; cfg->aacMode2 = 0x0C; break; case 24000: cfg->freq = FREQ_UNDEFINED; cfg->aacMode1 = 0x04; cfg->aacMode2 = 0x0C; break; case 32000: cfg->freq = FREQ_32K; cfg->aacMode1 = 0x02; cfg->aacMode2 = 0x0C; break; case 44100: cfg->freq = FREQ_44_1K; cfg->aacMode1 = 0x01; cfg->aacMode2 = 0x0C; break; case 48000: cfg->freq = FREQ_48K; cfg->aacMode1 = 0; cfg->aacMode2 = 0x8C; break; case 64000: cfg->freq = FREQ_UNDEFINED; cfg->aacMode1 = 0; cfg->aacMode2 = 0x4C; break; case 88200: cfg->freq = FREQ_UNDEFINED; cfg->aacMode1 = 0; cfg->aacMode2 = 0x2C; break; case 96000: cfg->freq = FREQ_UNDEFINED; cfg->aacMode1 = 0; cfg->aacMode2 = 0x1C; break; default: DPRINTF("Invalid samplerate %d", samplerate); goto error; } cfg->bands = BANDS_8; cfg->bitpool = 0; switch (*pchannels) { case 1: cfg->aacMode2 &= 0xF8; cfg->chmode = MODE_MONO; break; default: cfg->aacMode2 &= 0xF4; cfg->chmode = MODE_STEREO; break; } cfg->allocm = ALLOC_LOUDNESS; if (cfg->chmode == MODE_MONO || cfg->chmode == MODE_DUAL) tmpbitpool = 16; else tmpbitpool = 32; if (cfg->bands == BANDS_8) tmpbitpool *= 8; else tmpbitpool *= 4; if (tmpbitpool > DEFAULT_MAXBPOOL) tmpbitpool = DEFAULT_MAXBPOOL; cfg->bitpool = tmpbitpool; if (bt_set_format(pformat)) { DPRINTF("Unsupported sample format\n"); goto error; } l2cap_psm = bt_query(&info, service_class); DPRINTF("PSM=0x%02x\n", l2cap_psm); if (l2cap_psm < 0) { DPRINTF("PSM not found\n"); goto error; } cfg->hc = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BLUETOOTH_PROTO_L2CAP); if (cfg->hc < 0) { DPRINTF("Could not create BT socket\n"); goto error; } memset(&addr, 0, sizeof(addr)); addr.l2cap_len = sizeof(addr); addr.l2cap_family = AF_BLUETOOTH; bdaddr_copy(&addr.l2cap_bdaddr, &info.laddr); if (bind(cfg->hc, (struct sockaddr *)&addr, sizeof(addr)) < 0) { DPRINTF("Could not bind to HC\n"); goto error; } bdaddr_copy(&addr.l2cap_bdaddr, &info.raddr); addr.l2cap_psm = l2cap_psm; if (connect(cfg->hc, (struct sockaddr *)&addr, sizeof(addr)) < 0) { DPRINTF("Could not connect to HC: %d\n", errno); goto error; } if (avdtpDiscoverAndConfig(cfg, isSink)) { DPRINTF("DISCOVER FAILED\n"); goto error; } if (avdtpOpen(cfg->hc, cfg->sep)) { DPRINTF("OPEN FAILED\n"); goto error; } cfg->fd = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BLUETOOTH_PROTO_L2CAP); if (cfg->fd < 0) { DPRINTF("Could not create BT socket\n"); goto error; } memset(&addr, 0, sizeof(addr)); addr.l2cap_len = sizeof(addr); addr.l2cap_family = AF_BLUETOOTH; bdaddr_copy(&addr.l2cap_bdaddr, &info.laddr); if (bind(cfg->fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) { DPRINTF("Could not bind\n"); goto error; } bdaddr_copy(&addr.l2cap_bdaddr, &info.raddr); addr.l2cap_psm = l2cap_psm; if (connect(cfg->fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) { DPRINTF("Could not connect: %d\n", errno); goto error; } if (isSink) { if (getsockopt(cfg->fd, SOL_L2CAP, SO_L2CAP_OMTU, &cfg->mtu, &mtusize) == -1) { DPRINTF("Could not get MTU\n"); goto error; } temp = cfg->mtu * 2; if (setsockopt(cfg->fd, SOL_SOCKET, SO_SNDBUF, &temp, sizeof(temp)) == -1) { DPRINTF("Could not set send buffer size\n"); goto error; } temp = cfg->mtu; if (setsockopt(cfg->fd, SOL_SOCKET, SO_SNDLOWAT, &temp, sizeof(temp)) == -1) { DPRINTF("Could not set low water mark\n"); goto error; } } else { if (getsockopt(cfg->fd, SOL_L2CAP, SO_L2CAP_IMTU, &cfg->mtu, &mtusize) == -1) { DPRINTF("Could not get MTU\n"); goto error; } temp = cfg->mtu * 16; if (setsockopt(cfg->fd, SOL_SOCKET, SO_RCVBUF, &temp, sizeof(temp)) == -1) { DPRINTF("Could not set receive buffer size\n"); goto error; } temp = 1; if (setsockopt(cfg->fd, SOL_SOCKET, SO_RCVLOWAT, &temp, sizeof(temp)) == -1) { DPRINTF("Could not set low water mark\n"); goto error; } } if (avdtpStart(cfg->hc, cfg->sep)) { DPRINTF("START FAILED\n"); goto error; } switch (cfg->chmode) { case MODE_MONO: *pchannels = 1; break; default: *pchannels = 2; break; } return (0); error: if (cfg->hc > 0) { close(cfg->hc); cfg->hc = -1; } if (cfg->fd > 0) { close(cfg->fd); cfg->fd = -1; } return (-1); }
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); }