/** * Called when the LL receives a scan request or connection request * * Context: Called from interrupt context. * * @param rxbuf * * @return -1: request not for us or is a connect request. * 0: request (scan) is for us and we successfully went from rx to tx. * > 0: PHY error attempting to go from rx to tx. */ static int ble_ll_adv_rx_req(uint8_t pdu_type, struct os_mbuf *rxpdu) { int rc; uint8_t chk_whitelist; uint8_t txadd; uint8_t *rxbuf; struct ble_mbuf_hdr *ble_hdr; struct ble_ll_adv_sm *advsm; rxbuf = rxpdu->om_data; if (ble_ll_adv_addr_cmp(rxbuf)) { return -1; } /* Set device match bit if we are whitelisting */ advsm = &g_ble_ll_adv_sm; if (pdu_type == BLE_ADV_PDU_TYPE_SCAN_REQ) { chk_whitelist = advsm->adv_filter_policy & 1; } else { chk_whitelist = advsm->adv_filter_policy & 2; } /* Set device match bit if we are whitelisting */ ble_hdr = BLE_MBUF_HDR_PTR(rxpdu); if (chk_whitelist) { /* Get the scanners address type */ if (rxbuf[0] & BLE_ADV_PDU_HDR_TXADD_MASK) { txadd = BLE_ADDR_TYPE_RANDOM; } else { txadd = BLE_ADDR_TYPE_PUBLIC; } /* Check for whitelist match */ if (!ble_ll_whitelist_match(rxbuf + BLE_LL_PDU_HDR_LEN, txadd)) { return -1; } ble_hdr->rxinfo.flags |= BLE_MBUF_HDR_F_DEVMATCH; } /* Setup to transmit the scan response if appropriate */ rc = -1; if (pdu_type == BLE_ADV_PDU_TYPE_SCAN_REQ) { ble_phy_set_txend_cb(ble_ll_adv_tx_done, &g_ble_ll_adv_sm); rc = ble_phy_tx(advsm->scan_rsp_pdu, BLE_PHY_TRANSITION_NONE); if (!rc) { ble_hdr->rxinfo.flags |= BLE_MBUF_HDR_F_SCAN_RSP_TXD; STATS_INC(ble_ll_stats, scan_rsp_txg); } } return rc; }
/** * Called when the LL receives a scan request or connection request * * Context: Called from interrupt context. * * @param rxbuf * * @return -1: request not for us or is a connect request. * 0: request (scan) is for us and we successfully went from rx to tx. * > 0: PHY error attempting to go from rx to tx. */ static int ble_ll_adv_rx_req(uint8_t pdu_type, struct os_mbuf *rxpdu) { int rc; int resolved; uint8_t chk_wl; uint8_t txadd; uint8_t peer_addr_type; uint8_t *rxbuf; uint8_t *adva; uint8_t *peer; struct ble_mbuf_hdr *ble_hdr; struct ble_ll_adv_sm *advsm; struct os_mbuf *scan_rsp; /* See if adva in the request (scan or connect) matches what we sent */ advsm = &g_ble_ll_adv_sm; rxbuf = rxpdu->om_data; adva = rxbuf + BLE_LL_PDU_HDR_LEN + BLE_DEV_ADDR_LEN; if (memcmp(advsm->adva, adva, BLE_DEV_ADDR_LEN)) { return -1; } /* Set device match bit if we are whitelisting */ if (pdu_type == BLE_ADV_PDU_TYPE_SCAN_REQ) { chk_wl = advsm->adv_filter_policy & 1; } else { chk_wl = advsm->adv_filter_policy & 2; } /* Get the peer address type */ if (rxbuf[0] & BLE_ADV_PDU_HDR_TXADD_MASK) { txadd = BLE_ADDR_TYPE_RANDOM; } else { txadd = BLE_ADDR_TYPE_PUBLIC; } ble_hdr = BLE_MBUF_HDR_PTR(rxpdu); peer = rxbuf + BLE_LL_PDU_HDR_LEN; peer_addr_type = txadd; resolved = 0; #if (MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) == 1) if (ble_ll_is_rpa(peer, txadd) && ble_ll_resolv_enabled()) { advsm->adv_rpa_index = ble_hw_resolv_list_match(); if (advsm->adv_rpa_index >= 0) { ble_hdr->rxinfo.flags |= BLE_MBUF_HDR_F_RESOLVED; if (chk_wl) { peer = g_ble_ll_resolv_list[advsm->adv_rpa_index].rl_identity_addr; peer_addr_type = g_ble_ll_resolv_list[advsm->adv_rpa_index].rl_addr_type; resolved = 1; } } else { if (chk_wl) { return -1; } } } #endif /* Set device match bit if we are whitelisting */ if (chk_wl && !ble_ll_whitelist_match(peer, peer_addr_type, resolved)) { return -1; } /* * We set the device match bit to tell the upper layer that we will * accept the request */ ble_hdr->rxinfo.flags |= BLE_MBUF_HDR_F_DEVMATCH; /* Setup to transmit the scan response if appropriate */ rc = -1; if (pdu_type == BLE_ADV_PDU_TYPE_SCAN_REQ) { scan_rsp = ble_ll_adv_scan_rsp_pdu_make(advsm); if (scan_rsp) { ble_phy_set_txend_cb(ble_ll_adv_tx_done, &g_ble_ll_adv_sm); rc = ble_phy_tx(scan_rsp, BLE_PHY_TRANSITION_NONE); if (!rc) { ble_hdr->rxinfo.flags |= BLE_MBUF_HDR_F_SCAN_RSP_TXD; STATS_INC(ble_ll_stats, scan_rsp_txg); } os_mbuf_free_chain(scan_rsp); } } return rc; }
/** * This is the scheduler callback (called from interrupt context) which * transmits an advertisement. * * Context: Interrupt (scheduler) * * @param sch * * @return int */ static int ble_ll_adv_tx_start_cb(struct ble_ll_sched_item *sch) { int rc; uint8_t end_trans; uint32_t txstart; struct ble_ll_adv_sm *advsm; struct os_mbuf *adv_pdu; /* Get the state machine for the event */ advsm = (struct ble_ll_adv_sm *)sch->cb_arg; /* Set channel */ rc = ble_phy_setchan(advsm->adv_chan, 0, 0); assert(rc == 0); /* Set transmit start time. */ txstart = sch->start_time + XCVR_PROC_DELAY_USECS; rc = ble_phy_tx_set_start_time(txstart); if (rc) { STATS_INC(ble_ll_stats, adv_late_starts); goto adv_tx_done; } #if (MYNEWT_VAL(BLE_LL_CFG_FEAT_LE_ENCRYPTION) == 1) /* XXX: automatically do this in the phy based on channel? */ ble_phy_encrypt_disable(); #endif #if (MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) == 1) advsm->adv_rpa_index = -1; if (ble_ll_resolv_enabled()) { ble_phy_resolv_list_enable(); } else { ble_phy_resolv_list_disable(); } #endif /* Set phy mode based on type of advertisement */ if (advsm->adv_type == BLE_HCI_ADV_TYPE_ADV_NONCONN_IND) { end_trans = BLE_PHY_TRANSITION_NONE; ble_phy_set_txend_cb(ble_ll_adv_tx_done, advsm); } else { end_trans = BLE_PHY_TRANSITION_TX_RX; ble_phy_set_txend_cb(NULL, NULL); } /* Get an advertising mbuf (packet header) */ adv_pdu = os_msys_get_pkthdr(BLE_ADV_MAX_PKT_LEN, sizeof(struct ble_mbuf_hdr)); if (!adv_pdu) { ble_phy_disable(); goto adv_tx_done; } ble_ll_adv_pdu_make(advsm, adv_pdu); /* Transmit advertisement */ rc = ble_phy_tx(adv_pdu, end_trans); os_mbuf_free_chain(adv_pdu); if (rc) { goto adv_tx_done; } /* Enable/disable whitelisting based on filter policy */ if (advsm->adv_filter_policy != BLE_HCI_ADV_FILT_NONE) { ble_ll_whitelist_enable(); } else { ble_ll_whitelist_disable(); } /* Set link layer state to advertising */ ble_ll_state_set(BLE_LL_STATE_ADV); /* Count # of adv. sent */ STATS_INC(ble_ll_stats, adv_txg); return BLE_LL_SCHED_STATE_RUNNING; adv_tx_done: ble_ll_adv_tx_done(advsm); return BLE_LL_SCHED_STATE_DONE; }