/** * Start the advertising state machine. This is called when the host sends * the "enable advertising" command and is not called again while in the * advertising state. * * Context: Link-layer task. * * @param advsm Pointer to advertising state machine * * @return int */ static int ble_ll_adv_sm_start(struct ble_ll_adv_sm *advsm) { uint8_t adv_chan; /* * This is not in the specification. I will reject the command with a * command disallowed error if no random address has been sent by the * host. All the parameter errors refer to the command * parameter (which in this case is just enable or disable) so that * is why I chose command disallowed. */ if (advsm->own_addr_type == BLE_HCI_ADV_OWN_ADDR_RANDOM) { if (!ble_ll_is_valid_random_addr(g_random_addr)) { return BLE_ERR_CMD_DISALLOWED; } } /* Set flag telling us that advertising is enabled */ advsm->enabled = 1; /* Determine the advertising interval we will use */ if (advsm->adv_type == BLE_HCI_ADV_TYPE_ADV_DIRECT_IND_HD) { /* Set it to max. allowed for high duty cycle advertising */ advsm->adv_itvl_usecs = BLE_LL_ADV_PDU_ITVL_HD_MS_MAX; } else { advsm->adv_itvl_usecs = (uint32_t)advsm->adv_itvl_max; advsm->adv_itvl_usecs *= BLE_LL_ADV_ITVL; } /* Create scan response PDU (if needed) */ if (advsm->adv_type != BLE_HCI_ADV_TYPE_ADV_NONCONN_IND) { ble_ll_adv_scan_rsp_pdu_make(advsm); } /* Set first advertising channel */ adv_chan = ble_ll_adv_first_chan(advsm); advsm->adv_chan = adv_chan; /* * Schedule advertising. We set the initial schedule start and end * times to the earliest possible start/end. */ ble_ll_adv_set_sched(advsm, 1); ble_ll_sched_adv_new(&advsm->adv_sch); return BLE_ERR_SUCCESS; }
/** * Start the advertising state machine. This is called when the host sends * the "enable advertising" command and is not called again while in the * advertising state. * * Context: Link-layer task. * * @param advsm Pointer to advertising state machine * * @return int */ static int ble_ll_adv_sm_start(struct ble_ll_adv_sm *advsm) { uint8_t adv_chan; uint8_t *addr; uint8_t *evbuf; /* * This is not in the specification. I will reject the command with a * command disallowed error if no random address has been sent by the * host. All the parameter errors refer to the command * parameter (which in this case is just enable or disable) so that * is why I chose command disallowed. */ if (advsm->own_addr_type == BLE_HCI_ADV_OWN_ADDR_RANDOM) { if (!ble_ll_is_valid_random_addr(g_random_addr)) { return BLE_ERR_CMD_DISALLOWED; } } /* * Get an event with which to send the connection complete event if * this is connectable */ switch (advsm->adv_type) { case BLE_HCI_ADV_TYPE_ADV_DIRECT_IND_HD: case BLE_HCI_ADV_TYPE_ADV_DIRECT_IND_LD: case BLE_HCI_ADV_TYPE_ADV_IND: /* We expect this to be NULL but if not we wont allocate one... */ if (advsm->conn_comp_ev == NULL) { evbuf = ble_hci_trans_buf_alloc(BLE_HCI_TRANS_BUF_EVT_HI); if (!evbuf) { return BLE_ERR_MEM_CAPACITY; } advsm->conn_comp_ev = evbuf; } break; default: break; } /* Set advertising address */ if ((advsm->own_addr_type & 1) == 0) { addr = g_dev_addr; advsm->adv_txadd = 0; } else { addr = g_random_addr; advsm->adv_txadd = 1; } memcpy(advsm->adva, addr, BLE_DEV_ADDR_LEN); if (advsm->adv_directed) { memcpy(advsm->initiator_addr, advsm->peer_addr, BLE_DEV_ADDR_LEN); if (advsm->peer_addr_type & 1) { advsm->adv_rxadd = 1; } else { advsm->adv_rxadd = 0; } } /* This will generate an RPA for both initiator addr and adva */ #if (MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) == 1) ble_ll_adv_chk_rpa_timeout(advsm); #endif /* Set flag telling us that advertising is enabled */ advsm->enabled = 1; /* Determine the advertising interval we will use */ if (advsm->adv_type == BLE_HCI_ADV_TYPE_ADV_DIRECT_IND_HD) { /* Set it to max. allowed for high duty cycle advertising */ advsm->adv_itvl_usecs = BLE_LL_ADV_PDU_ITVL_HD_MS_MAX; } else { advsm->adv_itvl_usecs = (uint32_t)advsm->adv_itvl_max; advsm->adv_itvl_usecs *= BLE_LL_ADV_ITVL; } /* Set first advertising channel */ adv_chan = ble_ll_adv_first_chan(advsm); advsm->adv_chan = adv_chan; /* * Schedule advertising. We set the initial schedule start and end * times to the earliest possible start/end. */ ble_ll_adv_set_sched(advsm, 1); ble_ll_sched_adv_new(&advsm->adv_sch); return BLE_ERR_SUCCESS; }
/** * Called when an advertising event is over. * * Context: Link Layer task. * * @param arg Pointer to advertising state machine. */ static void ble_ll_adv_done(struct ble_ll_adv_sm *advsm) { uint8_t mask; uint8_t final_adv_chan; int32_t delta_t; uint32_t itvl; uint32_t start_time; assert(advsm->enabled); /* Remove the element from the schedule if it is still there. */ ble_ll_sched_rmv_elem(&advsm->adv_sch); os_eventq_remove(&g_ble_ll_data.ll_evq, &advsm->adv_txdone_ev); /* * Check if we have ended our advertising event. If our last advertising * packet was sent on the last channel, it means we are done with this * event. */ if (advsm->adv_chanmask & 0x04) { final_adv_chan = BLE_PHY_ADV_CHAN_START + 2; } else if (advsm->adv_chanmask & 0x02) { final_adv_chan = BLE_PHY_ADV_CHAN_START + 1; } else { final_adv_chan = BLE_PHY_ADV_CHAN_START; } if (advsm->adv_chan == final_adv_chan) { /* Check if we need to resume scanning */ ble_ll_scan_chk_resume(); /* This event is over. Set adv channel to first one */ advsm->adv_chan = ble_ll_adv_first_chan(advsm); /* Calculate start time of next advertising event */ itvl = advsm->adv_itvl_usecs; if (advsm->adv_type != BLE_HCI_ADV_TYPE_ADV_DIRECT_IND_HD) { itvl += rand() % (BLE_LL_ADV_DELAY_MS_MAX * 1000); } advsm->adv_event_start_time += os_cputime_usecs_to_ticks(itvl); advsm->adv_pdu_start_time = advsm->adv_event_start_time; /* * The scheduled time better be in the future! If it is not, we will * just keep advancing until we the time is in the future */ start_time = advsm->adv_pdu_start_time - os_cputime_usecs_to_ticks(XCVR_TX_SCHED_DELAY_USECS); delta_t = (int32_t)(start_time - os_cputime_get32()); if (delta_t < 0) { /* Calculate start time of next advertising event */ while (delta_t < 0) { itvl = advsm->adv_itvl_usecs; if (advsm->adv_type != BLE_HCI_ADV_TYPE_ADV_DIRECT_IND_HD) { itvl += rand() % (BLE_LL_ADV_DELAY_MS_MAX * 1000); } itvl = os_cputime_usecs_to_ticks(itvl); advsm->adv_event_start_time += itvl; advsm->adv_pdu_start_time = advsm->adv_event_start_time; delta_t += (int32_t)itvl; } } } else { /* * Move to next advertising channel. If not in the mask, just * increment by 1. We can do this because we already checked if we * just transmitted on the last advertising channel */ ++advsm->adv_chan; mask = 1 << (advsm->adv_chan - BLE_PHY_ADV_CHAN_START); if ((mask & advsm->adv_chanmask) == 0) { ++advsm->adv_chan; } /* * We will transmit right away. Set next pdu start time to now * plus a xcvr start delay just so we dont count late adv starts */ advsm->adv_pdu_start_time = os_cputime_get32() + os_cputime_usecs_to_ticks(XCVR_TX_SCHED_DELAY_USECS); } /* * Stop high duty cycle directed advertising if we have been doing * it for more than 1.28 seconds */ if (advsm->adv_type == BLE_HCI_ADV_TYPE_ADV_DIRECT_IND_HD) { if (advsm->adv_pdu_start_time >= advsm->adv_dir_hd_end_time) { /* Disable advertising */ advsm->enabled = 0; ble_ll_conn_comp_event_send(NULL, BLE_ERR_DIR_ADV_TMO, advsm->conn_comp_ev); advsm->conn_comp_ev = NULL; ble_ll_scan_chk_resume(); return; } } /* We need to regenerate our RPA's if we have passed timeout */ #if (MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PRIVACY) == 1) ble_ll_adv_chk_rpa_timeout(advsm); #endif /* Schedule advertising transmit */ ble_ll_adv_set_sched(advsm, 0); /* * In the unlikely event we cant reschedule this, just post a done * event and we will reschedule the next advertising event */ if (ble_ll_sched_adv_reschedule(&advsm->adv_sch)) { os_eventq_put(&g_ble_ll_data.ll_evq, &advsm->adv_txdone_ev); } }