Skip to content

Commit f3203c4

Browse files
committed
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 81a4474 commit f3203c4

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
@@ -259,13 +261,12 @@ uint8_t ll_df_set_cl_cte_tx_params(uint8_t adv_handle, uint8_t cte_len,
259261
*/
260262
uint8_t ll_df_set_cl_cte_tx_enable(uint8_t adv_handle, uint8_t cte_enable)
261263
{
262-
void *extra_data_prev, *extra_data;
263-
struct pdu_adv *pdu_prev, *pdu;
264264
struct lll_adv_sync *lll_sync;
265265
struct lll_df_adv_cfg *df_cfg;
266266
struct ll_adv_sync_set *sync;
267267
struct ll_adv_set *adv;
268268
uint8_t err, ter_idx;
269+
struct pdu_adv *pdu;
269270

270271
/* Get the advertising set instance */
271272
adv = ull_adv_is_created_get(adv_handle);
@@ -300,22 +301,7 @@ uint8_t ll_df_set_cl_cte_tx_enable(uint8_t adv_handle, uint8_t cte_enable)
300301
return BT_HCI_ERR_CMD_DISALLOWED;
301302
}
302303

303-
err = ull_adv_sync_pdu_alloc(adv, ULL_ADV_PDU_EXTRA_DATA_ALLOC_NEVER, &pdu_prev,
304-
&pdu, &extra_data_prev, &extra_data, &ter_idx);
305-
if (err) {
306-
return err;
307-
}
308-
309-
if (extra_data) {
310-
ull_adv_sync_extra_data_set_clear(extra_data_prev,
311-
extra_data, 0,
312-
ULL_ADV_PDU_HDR_FIELD_CTE_INFO,
313-
NULL);
314-
}
315-
316-
err = ull_adv_sync_pdu_set_clear(lll_sync, pdu_prev, pdu, 0,
317-
ULL_ADV_PDU_HDR_FIELD_CTE_INFO,
318-
NULL);
304+
err = cte_info_clear(adv, df_cfg, &ter_idx, &pdu);
319305
if (err) {
320306
return err;
321307
}
@@ -826,4 +812,185 @@ static uint8_t cte_info_set(struct ll_adv_set *adv, struct lll_df_adv_cfg *df_cf
826812

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

0 commit comments

Comments
 (0)