Skip to content

Commit 1207511

Browse files
ppryga-nordiccfriedt
authored andcommitted
Bluetooth: controller: ULL: Add handling of chains in disable CTE
Direction finding functionality allows to send a number of periodic advertising PDUs in a chain that have CTE. Disable sending CTE requiers additional steps while removing cte_info from periodic advertising chains. Removal of cte_info fields may be just delete of that filed from extended advertising header. In case the PDUs are empty PDUs created just to transport CTE. Those PDUs should be removed from a periodic advertising chain. Signed-off-by: Piotr Pryga <[email protected]>
1 parent 9c23fe0 commit 1207511

File tree

1 file changed

+185
-18
lines changed
  • subsys/bluetooth/controller/ll_sw

1 file changed

+185
-18
lines changed

subsys/bluetooth/controller/ll_sw/ull_df.c

Lines changed: 185 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,8 @@ static uint8_t mem_link_iq_report_quota_pdu;
8181
#if defined(CONFIG_BT_CTLR_DF_ADV_CTE_TX)
8282
static struct lll_df_adv_cfg lll_df_adv_cfg_pool[CONFIG_BT_CTLR_ADV_AUX_SET];
8383
static void *df_adv_cfg_free;
84+
static uint8_t cte_info_clear(struct ll_adv_set *adv, struct lll_df_adv_cfg *df_cfg,
85+
uint8_t *ter_idx, struct pdu_adv **first_pdu);
8486
#endif /* CONFIG_BT_CTLR_DF_ADV_CTE_TX */
8587

8688
/* @brief Function performs common steps for initialization and reset
@@ -274,13 +276,12 @@ uint8_t ll_df_set_cl_cte_tx_params(uint8_t adv_handle, uint8_t cte_len,
274276
*/
275277
uint8_t ll_df_set_cl_cte_tx_enable(uint8_t adv_handle, uint8_t cte_enable)
276278
{
277-
void *extra_data_prev, *extra_data;
278-
struct pdu_adv *pdu_prev, *pdu;
279279
struct lll_adv_sync *lll_sync;
280280
struct lll_df_adv_cfg *df_cfg;
281281
struct ll_adv_sync_set *sync;
282282
struct ll_adv_set *adv;
283283
uint8_t err, ter_idx;
284+
struct pdu_adv *pdu;
284285

285286
/* Get the advertising set instance */
286287
adv = ull_adv_is_created_get(adv_handle);
@@ -315,22 +316,7 @@ uint8_t ll_df_set_cl_cte_tx_enable(uint8_t adv_handle, uint8_t cte_enable)
315316
return BT_HCI_ERR_CMD_DISALLOWED;
316317
}
317318

318-
err = ull_adv_sync_pdu_alloc(adv, ULL_ADV_PDU_EXTRA_DATA_ALLOC_NEVER, &pdu_prev,
319-
&pdu, &extra_data_prev, &extra_data, &ter_idx);
320-
if (err) {
321-
return err;
322-
}
323-
324-
if (extra_data) {
325-
ull_adv_sync_extra_data_set_clear(extra_data_prev,
326-
extra_data, 0,
327-
ULL_ADV_PDU_HDR_FIELD_CTE_INFO,
328-
NULL);
329-
}
330-
331-
err = ull_adv_sync_pdu_set_clear(lll_sync, pdu_prev, pdu, 0,
332-
ULL_ADV_PDU_HDR_FIELD_CTE_INFO,
333-
NULL);
319+
err = cte_info_clear(adv, df_cfg, &ter_idx, &pdu);
334320
if (err) {
335321
return err;
336322
}
@@ -841,4 +827,185 @@ static uint8_t cte_info_set(struct ll_adv_set *adv, struct lll_df_adv_cfg *df_cf
841827

842828
return BT_HCI_ERR_SUCCESS;
843829
}
830+
831+
#if (CONFIG_BT_CTLR_DF_PER_ADV_CTE_NUM_MAX > 1)
832+
static bool pdu_ext_adv_is_empty_without_cte(const struct pdu_adv *pdu)
833+
{
834+
if (pdu->len != PDU_AC_PAYLOAD_SIZE_MIN) {
835+
const struct pdu_adv_ext_hdr *ext_hdr;
836+
uint8_t size_rem = 0;
837+
838+
if ((pdu->adv_ext_ind.ext_hdr_len + PDU_AC_EXT_HEADER_SIZE_MIN) != pdu->len) {
839+
/* There are adv. data in PDU */
840+
return false;
841+
}
842+
843+
/* Check size of the ext. header without cte_info and aux_ptr. If that is minimum
844+
* extended PDU size then the PDU was allocated to transport CTE only.
845+
*/
846+
ext_hdr = &pdu->adv_ext_ind.ext_hdr;
847+
848+
if (ext_hdr->cte_info) {
849+
size_rem += sizeof(struct pdu_cte_info);
850+
}
851+
if (ext_hdr->aux_ptr) {
852+
size_rem += sizeof(struct pdu_adv_aux_ptr);
853+
}
854+
855+
if ((pdu->adv_ext_ind.ext_hdr_len - size_rem) != PDU_AC_EXT_HEADER_SIZE_MIN) {
856+
return false;
857+
}
858+
}
859+
860+
return true;
861+
}
862+
863+
/*
864+
* @brief Function removes content of cte_info field in periodic advertising chain.
865+
*
866+
* The function removes cte_info from extended advertising header in all PDUs in periodic
867+
* advertising chain. If particular PDU is empty (holds cte_info only) it will be removed from
868+
* chain.
869+
*
870+
* @param lll_sync Pointer to periodic advertising sync object.
871+
* @param pdu_prev Pointer to a PDU that is already in use by LLL or was updated with new PDU
872+
* payload.
873+
* @param pdu Pointer to a new head of periodic advertising chain. The pointer may have
874+
* the same value as @p pdu_prev, if payload of PDU pointerd by @p pdu_prev was
875+
* already updated.
876+
*
877+
* @return Zero in case of success, other value in case of failure.
878+
*/
879+
static uint8_t rem_cte_info_from_per_adv_chain(struct lll_adv_sync *lll_sync,
880+
struct pdu_adv *pdu_prev, struct pdu_adv *pdu)
881+
{
882+
struct pdu_adv *pdu_new, *pdu_chained;
883+
uint8_t pdu_rem_field_flags;
884+
bool new_chain;
885+
uint8_t err;
886+
887+
pdu_rem_field_flags = ULL_ADV_PDU_HDR_FIELD_CTE_INFO;
888+
889+
/* It is possible that the function is called after e.g. advertising data were updated.
890+
* In such situation the function will run on already allocated chain. Do not allocate
891+
* new chain then. Reuse already allocated PDUs and allocate new ones only if the chain
892+
* was not updated yet.
893+
*/
894+
new_chain = (pdu_prev == pdu ? false : true);
895+
896+
/* Get next PDU in a chain. Alway use pdu_prev because it points to actual
897+
* former chain.
898+
*/
899+
pdu_chained = lll_adv_pdu_linked_next_get(pdu_prev);
900+
901+
/* Go through existing chain and remove CTE info. */
902+
while (pdu_chained) {
903+
if (pdu_ext_adv_is_empty_without_cte(pdu_chained)) {
904+
/* If there is an empty PDU then all remaining PDUs shoudl be released. */
905+
lll_adv_pdu_linked_release_all(pdu_chained);
906+
pdu_chained = NULL;
907+
/* Set new end of chain in PDUs linked list. If pdu differs from prev_pdu
908+
* then it is alread end of a chain. If it doesn't differ, then chain end
909+
* is changed in rigth place by use of pdu_prev. That makes sure there
910+
* is no PDU released twice (here and when LLL swaps PDU buffers).
911+
*/
912+
lll_adv_pdu_linked_append(NULL, pdu_prev);
913+
} else {
914+
/* Update one before pdu_chained */
915+
err = ull_adv_sync_pdu_set_clear(lll_sync, pdu_prev, pdu, 0,
916+
pdu_rem_field_flags, NULL);
917+
if (err != BT_HCI_ERR_SUCCESS) {
918+
/* TODO: return here leaves periodic advertising chain in
919+
* an inconsisten state. Add gracefull return or assert.
920+
*/
921+
return err;
922+
}
923+
924+
/* Prepare for next iteration. Allocate new PDU or move to next one in
925+
* a chain.
926+
*/
927+
if (new_chain) {
928+
pdu_new = lll_adv_pdu_alloc_pdu_adv();
929+
lll_adv_pdu_linked_append(pdu_new, pdu);
930+
pdu = pdu_new;
931+
} else {
932+
pdu = lll_adv_pdu_linked_next_get(pdu);
933+
}
934+
935+
/* Move to next chained PDU (it moves through chain that is in use
936+
* by LLL or is new one with updated advertising payload).
937+
*/
938+
pdu_prev = pdu_chained;
939+
pdu_chained = lll_adv_pdu_linked_next_get(pdu_prev);
940+
}
941+
}
942+
943+
return BT_HCI_ERR_SUCCESS;
944+
}
945+
#endif /* (CONFIG_BT_CTLR_DF_PER_ADV_CTE_NUM_MAX > 1) */
946+
947+
/*
948+
* @brief Function removes content of cte_info field from periodic advertising PDUs.
949+
*
950+
* @param adv Pointer to periodic advertising set.
951+
* @param df_cfg Pointer to direction finding configuration
952+
* @param[out] ter_idx Pointer used to return index of allocated or updated PDU.
953+
* Index is required for scheduling the PDU for transmission in LLL.
954+
* @param[out] first_pdu Pointer to return address of first PDU in a chain
955+
*
956+
* @return Zero in case of success, other value in case of failure.
957+
*/
958+
static uint8_t cte_info_clear(struct ll_adv_set *adv, struct lll_df_adv_cfg *df_cfg,
959+
uint8_t *ter_idx, struct pdu_adv **first_pdu)
960+
{
961+
void *extra_data_prev, *extra_data;
962+
struct pdu_adv *pdu_prev, *pdu;
963+
struct lll_adv_sync *lll_sync;
964+
uint8_t pdu_rem_field_flags;
965+
uint8_t err;
966+
967+
lll_sync = adv->lll.sync;
968+
969+
/* NOTE: ULL_ADV_PDU_HDR_FIELD_CTE_INFO is just information that extra_data
970+
* should be removed in case of this call ull_adv_sync_pdu_alloc.
971+
* Other flags here do not change anything. It may be changed to use
972+
* true/false flag for extra_data allocation.
973+
*/
974+
err = ull_adv_sync_pdu_alloc(adv, ULL_ADV_PDU_EXTRA_DATA_ALLOC_NEVER, &pdu_prev, &pdu,
975+
&extra_data_prev, &extra_data, ter_idx);
976+
if (err != BT_HCI_ERR_SUCCESS) {
977+
return err;
978+
}
979+
980+
if (extra_data) {
981+
ull_adv_sync_extra_data_set_clear(extra_data_prev, extra_data, 0,
982+
ULL_ADV_PDU_HDR_FIELD_CTE_INFO, NULL);
983+
}
984+
985+
*first_pdu = pdu;
986+
987+
pdu_rem_field_flags = ULL_ADV_PDU_HDR_FIELD_CTE_INFO;
988+
989+
#if (CONFIG_BT_CTLR_DF_PER_ADV_CTE_NUM_MAX > 1)
990+
err = rem_cte_info_from_per_adv_chain(lll_sync, pdu_prev, pdu);
991+
if (err != BT_HCI_ERR_SUCCESS) {
992+
return err;
993+
}
994+
995+
/* Update last PDU in a chain. It may not have aux_ptr.
996+
* NOTE: If there is no AuxPtr flag in the PDU, attempt to remove it does not harm.
997+
*/
998+
pdu_rem_field_flags |= ULL_ADV_PDU_HDR_FIELD_AUX_PTR;
999+
#endif /* CONFIG_BT_CTLR_DF_PER_ADV_CTE_NUM_MAX > 1 */
1000+
1001+
err = ull_adv_sync_pdu_set_clear(lll_sync, pdu_prev, pdu, 0, pdu_rem_field_flags, NULL);
1002+
if (err != BT_HCI_ERR_SUCCESS) {
1003+
/* TODO: return here leaves periodic advertising chain in an inconsisten state.
1004+
* Add gracefull return or assert.
1005+
*/
1006+
return err;
1007+
}
1008+
1009+
return BT_HCI_ERR_SUCCESS;
1010+
}
8441011
#endif /* CONFIG_BT_CTLR_DF_ADV_CTE_TX */

0 commit comments

Comments
 (0)