diff --git a/nimble/controller/src/ble_ll_adv.c b/nimble/controller/src/ble_ll_adv.c index 60b60a91b..7f888fe32 100644 --- a/nimble/controller/src/ble_ll_adv.c +++ b/nimble/controller/src/ble_ll_adv.c @@ -3222,27 +3222,20 @@ ble_ll_adv_ext_set_param(const uint8_t *cmdbuf, uint8_t len, props = le16toh(cmd->props); #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PERIODIC_ADV) - /* For now just disallow to reconfigure instance that has periodic - * advertising configured if new configuration is not valid for periodic - * advertising - * - * TODO should we allow to reconfigure but then fail on enable instead? - * spec is unclear here but at least require check for anonymous - * advertising when configuring periodic so lets be consistent with this for - * now + /* If the Host issues this command when periodic advertising is enabled for + * the specified advertising set and connectable, scannable, legacy, or + * anonymous advertising is specified, the Controller shall return the + * error code Invalid HCI Command Parameters (0x12). */ if (advsm->flags & BLE_LL_ADV_SM_FLAG_PERIODIC_CONFIGURED) { if (advsm->periodic_adv_enabled) { - rc = BLE_ERR_CMD_DISALLOWED; - goto done; - } - - if (props & (BLE_HCI_LE_SET_EXT_ADV_PROP_SCANNABLE | - BLE_HCI_LE_SET_EXT_ADV_PROP_CONNECTABLE | - BLE_HCI_LE_SET_EXT_ADV_PROP_LEGACY | - BLE_HCI_LE_SET_EXT_ADV_PROP_ANON_ADV)) { - rc = BLE_ERR_CMD_DISALLOWED; - goto done; + if (props & (BLE_HCI_LE_SET_EXT_ADV_PROP_SCANNABLE | + BLE_HCI_LE_SET_EXT_ADV_PROP_CONNECTABLE | + BLE_HCI_LE_SET_EXT_ADV_PROP_LEGACY | + BLE_HCI_LE_SET_EXT_ADV_PROP_ANON_ADV)) { + rc = BLE_ERR_INV_HCI_CMD_PARMS; + goto done; + } } } #endif @@ -3653,7 +3646,83 @@ ble_ll_adv_clear_all(void) return BLE_ERR_SUCCESS; } + #if MYNEWT_VAL(BLE_LL_CFG_FEAT_LL_PERIODIC_ADV) +static uint16_t +ble_ll_adv_sync_get_pdu_len(uint16_t data_len, uint16_t *data_offset, + uint16_t props) +{ + uint16_t rem_data_len = data_len - *data_offset; + uint8_t hdr_len = BLE_LL_EXT_ADV_HDR_LEN; + uint8_t ext_hdr = 0; + + /* TxPower if configured + * Note: TxPower shall not be present in chain PDU for SYNC + */ + if (*data_offset == 0 && + (props & BLE_HCI_LE_SET_PERIODIC_ADV_PROP_INC_TX_PWR)) { + ext_hdr |= (1 << BLE_LL_EXT_ADV_TX_POWER_BIT); + hdr_len += BLE_LL_EXT_ADV_TX_POWER_SIZE; + } + + /* if we have any fields in ext header we need to add flags, note that Aux + * PTR is handled later and it will account for flags if needed + * + * This could be handled inside TxPower but lets keep code consistent with + * how Aux calculate works and this also make it easier to add more fields + * into flags if needed in future + */ + if (ext_hdr) { + hdr_len += BLE_LL_EXT_ADV_FLAGS_SIZE; + } + + /* AdvData always */ + data_len = min(BLE_LL_MAX_PAYLOAD_LEN - hdr_len, rem_data_len); + + /* AuxPtr if there are more AdvData remaining that we can fit here */ + if (rem_data_len > data_len) { + /* adjust for flags that needs to be added if AuxPtr is only field + * in Extended Header + */ + if (!ext_hdr) { + hdr_len += BLE_LL_EXT_ADV_FLAGS_SIZE; + data_len -= BLE_LL_EXT_ADV_FLAGS_SIZE; + } + + hdr_len += BLE_LL_EXT_ADV_AUX_PTR_SIZE; + data_len -= BLE_LL_EXT_ADV_AUX_PTR_SIZE; + + /* PDU payload should be full if chained */ + BLE_LL_ASSERT(hdr_len + data_len == BLE_LL_MAX_PAYLOAD_LEN); + } + + *data_offset += data_len; + + return hdr_len + data_len; +} + +static bool +ble_ll_adv_periodic_check_data_itvl(uint16_t payload_len, uint16_t props, + uint16_t itvl, uint8_t phy) +{ + uint32_t max_usecs = 0; + uint32_t itvl_usecs; + uint16_t offset = 0; + uint16_t pdu_len; + + while (offset < payload_len) { + pdu_len = ble_ll_adv_sync_get_pdu_len(payload_len, &offset, props); + + max_usecs += ble_ll_pdu_tx_time_get(pdu_len, phy); + max_usecs += ble_ll_usecs_to_ticks_round_up(BLE_LL_MAFS + + MYNEWT_VAL(BLE_LL_SCHED_AUX_CHAIN_MAFS_DELAY)); + } + + itvl_usecs = (uint32_t)itvl * BLE_LL_ADV_PERIODIC_ITVL; + + return max_usecs < itvl_usecs; +} + int ble_ll_adv_periodic_set_param(const uint8_t *cmdbuf, uint8_t len) { @@ -3676,24 +3745,21 @@ ble_ll_adv_periodic_set_param(const uint8_t *cmdbuf, uint8_t len) return BLE_ERR_UNK_ADV_INDENT; } - /* A device in the periodic advertising synchronizability mode shall send - * synchronization information for periodic advertising events in - * non-connectable and non-scannable extended advertising events. - */ - if ((advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_SCANNABLE) || - (advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_CONNECTABLE) || - (advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_LEGACY)) { - return BLE_ERR_CMD_DISALLOWED; - } - /* If the advertising set identified by the Advertising_Handle specified - * anonymous advertising, the Controller shall return the error code Invalid - * HCI Parameters (0x12). + * scannable, connectable, legacy, or anonymous advertising, the Controller + * shall return the error code Invalid HCI Command Parameters (0x12). */ - if (advsm->props & BLE_HCI_LE_SET_EXT_ADV_PROP_ANON_ADV) { + if (advsm->props & (BLE_HCI_LE_SET_EXT_ADV_PROP_ANON_ADV | + BLE_HCI_LE_SET_EXT_ADV_PROP_SCANNABLE | + BLE_HCI_LE_SET_EXT_ADV_PROP_CONNECTABLE | + BLE_HCI_LE_SET_EXT_ADV_PROP_LEGACY)) { return BLE_ERR_INV_HCI_CMD_PARMS; } + /* If the Host issues this command when periodic advertising is enabled for + * the specified advertising set, the Controller shall return the error code + * Command Disallowed (0x0C). + */ if (advsm->periodic_adv_enabled) { return BLE_ERR_CMD_DISALLOWED; } @@ -3709,6 +3775,17 @@ ble_ll_adv_periodic_set_param(const uint8_t *cmdbuf, uint8_t len) return BLE_ERR_INV_HCI_CMD_PARMS; } + /* If the advertising set already contains periodic advertising data and the + * length of the data is greater than the maximum that the Controller can + * transmit within a periodic advertising interval of + * Periodic_Advertising_Interval_Max, the Controller shall return the error + * code Packet Too Long (0x45). + */ + if (!ble_ll_adv_periodic_check_data_itvl(SYNC_DATA_LEN(advsm), props, + adv_itvl_max, advsm->sec_phy)) { + return BLE_ERR_PACKET_TOO_LONG; + } + advsm->periodic_adv_itvl_min = adv_itvl_min; advsm->periodic_adv_itvl_max = adv_itvl_max; advsm->periodic_adv_props = props; @@ -3723,6 +3800,7 @@ ble_ll_adv_periodic_set_data(const uint8_t *cmdbuf, uint8_t len) { const struct ble_hci_le_set_periodic_adv_data_cp *cmd = (const void *) cmdbuf; struct ble_ll_adv_sm *advsm; + uint16_t payload_total_len; bool new_data = false; if (len < sizeof(*cmd)) { @@ -3775,6 +3853,25 @@ ble_ll_adv_periodic_set_data(const uint8_t *cmdbuf, uint8_t len) return BLE_ERR_INV_HCI_CMD_PARMS; } + payload_total_len = cmd->adv_data_len; + if (!new_data) { + payload_total_len += SYNC_DATA_LEN(advsm); + } + + /* If the combined length of the data is greater than the maximum that the + * Controller can transmit within the current periodic advertising interval + * (if periodic advertising is currently enabled) or the + * Periodic_Advertising_Interval_Max for the advertising set (if currently + * disabled), all the data shall be discarded and the Controller shall + * return the error code Packet Too Long (0x45). + */ + if (!ble_ll_adv_periodic_check_data_itvl(payload_total_len, + advsm->periodic_adv_props, + advsm->periodic_adv_itvl_max, + advsm->sec_phy)) { + return BLE_ERR_PACKET_TOO_LONG; + } + if (advsm->periodic_adv_active) { ble_ll_adv_flags_clear(advsm, BLE_LL_ADV_SM_FLAG_PERIODIC_NEW_DATA); @@ -3833,6 +3930,18 @@ ble_ll_adv_periodic_enable(const uint8_t *cmdbuf, uint8_t len) return BLE_ERR_CMD_DISALLOWED; } + /* If Enable is set to 0x01 and the length of the periodic advertising + * data is greater than the maximum that the Controller can transmit + * within the chosen periodicadvertising interval, the Controller shall + * return the error code Packet Too Long (0x45). + */ + if (!ble_ll_adv_periodic_check_data_itvl(SYNC_DATA_LEN(advsm), + advsm->periodic_adv_props, + advsm->periodic_adv_itvl_max, + advsm->sec_phy)) { + return BLE_ERR_PACKET_TOO_LONG; + } + /* If the advertising set is not currently enabled (see the * LE_Set_Extended_Advertising_Enable command), the periodic advertising * is not started until the advertising set is enabled. diff --git a/nimble/include/nimble/ble.h b/nimble/include/nimble/ble.h index e348b02da..3fc2902ec 100644 --- a/nimble/include/nimble/ble.h +++ b/nimble/include/nimble/ble.h @@ -250,6 +250,7 @@ enum ble_error_codes BLE_ERR_UNK_ADV_INDENT = 0x42, BLE_ERR_LIMIT_REACHED = 0x43, BLE_ERR_OPERATION_CANCELLED = 0x44, + BLE_ERR_PACKET_TOO_LONG = 0x45, BLE_ERR_MAX = 0xff };