Skip to content

Commit 41a2e2f

Browse files
Bluetooth: controller: Add initial support to update AD in chain
This adds some initial support to update AD in chain. We still only support placing AD in 1st PDU, but this will properly copy any linked PDUs that may be added due to e.g. CTEInfo present. Signed-off-by: Andrzej Kaczmarek <[email protected]>
1 parent 16deb82 commit 41a2e2f

File tree

1 file changed

+206
-7
lines changed

1 file changed

+206
-7
lines changed

subsys/bluetooth/controller/ll_sw/ull_adv_sync.c

Lines changed: 206 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,181 @@ static uint8_t adv_sync_pdu_init(struct pdu_adv *pdu, uint8_t ext_hdr_flags)
121121

122122
return 0;
123123
}
124+
125+
static uint8_t adv_sync_pdu_init_from_prev_pdu(struct pdu_adv *pdu,
126+
struct pdu_adv *pdu_prev,
127+
uint16_t ext_hdr_flags_add,
128+
uint16_t ext_hdr_flags_rem)
129+
{
130+
struct pdu_adv_com_ext_adv *com_hdr_prev;
131+
struct pdu_adv_ext_hdr *ext_hdr_prev;
132+
struct pdu_adv_com_ext_adv *com_hdr;
133+
struct pdu_adv_ext_hdr *ext_hdr;
134+
uint8_t ext_hdr_flags_prev;
135+
uint8_t ext_hdr_flags;
136+
uint8_t *dptr_prev;
137+
uint8_t len_prev;
138+
uint8_t *dptr;
139+
uint8_t len;
140+
141+
/* Copy complete header, assume it was set properly in old PDU */
142+
*(uint8_t *)pdu = *(uint8_t *)pdu_prev;
143+
144+
com_hdr_prev = &pdu_prev->adv_ext_ind;
145+
com_hdr = &pdu->adv_ext_ind;
146+
147+
com_hdr->adv_mode = 0U;
148+
149+
ext_hdr_prev = &com_hdr_prev->ext_hdr;
150+
ext_hdr = &com_hdr->ext_hdr;
151+
152+
ext_hdr_flags_prev = *(uint8_t *)ext_hdr_prev;
153+
ext_hdr_flags = ext_hdr_flags_prev |
154+
(ext_hdr_flags_add & (~ext_hdr_flags_rem));
155+
156+
*(uint8_t *)ext_hdr = ext_hdr_flags;
157+
158+
LL_ASSERT(!ext_hdr->adv_addr);
159+
LL_ASSERT(!ext_hdr->tgt_addr);
160+
LL_ASSERT(!ext_hdr->adi);
161+
LL_ASSERT(!ext_hdr->sync_info);
162+
163+
dptr = ext_hdr->data;
164+
dptr_prev = ext_hdr_prev->data;
165+
166+
/* Note: skip length verification of ext header writes as we assume that
167+
* all PDUs are large enough to store at least complete ext header.
168+
*/
169+
170+
/* Copy CTEInfo, if applicable */
171+
if (ext_hdr->cte_info) {
172+
if (ext_hdr_prev->cte_info) {
173+
memcpy(dptr, dptr_prev, sizeof(struct pdu_cte_info));
174+
}
175+
dptr += sizeof(struct pdu_cte_info);
176+
}
177+
if (ext_hdr_prev->cte_info) {
178+
dptr_prev += sizeof(struct pdu_cte_info);
179+
}
180+
181+
/* Add AuxPtr, if applicable. Do not copy since it will be updated later
182+
* anyway.
183+
*/
184+
if (ext_hdr->aux_ptr) {
185+
dptr += sizeof(struct pdu_adv_aux_ptr);
186+
}
187+
if (ext_hdr_prev->aux_ptr) {
188+
dptr_prev += sizeof(struct pdu_adv_aux_ptr);
189+
}
190+
191+
/* Copy TxPower, if applicable */
192+
if (ext_hdr->tx_pwr) {
193+
if (ext_hdr_prev->tx_pwr) {
194+
memcpy(dptr, dptr_prev, sizeof(uint8_t));
195+
}
196+
dptr += sizeof(uint8_t);
197+
}
198+
if (ext_hdr_prev->tx_pwr) {
199+
dptr_prev += sizeof(uint8_t);
200+
}
201+
202+
LL_ASSERT(ext_hdr_prev >= 0);
203+
204+
/* Copy ACAD */
205+
len = com_hdr_prev->ext_hdr_len - (dptr_prev - (uint8_t *)ext_hdr_prev);
206+
memcpy(dptr, dptr_prev, len);
207+
dptr += len;
208+
209+
/* Check populated ext header length excluding length itself. If 0, then
210+
* there was neither field nor ACAD populated and we skip ext header
211+
* entirely.
212+
*/
213+
len = dptr - ext_hdr->data;
214+
if (len == 0) {
215+
com_hdr->ext_hdr_len = 0;
216+
} else {
217+
com_hdr->ext_hdr_len = len +
218+
offsetof(struct pdu_adv_ext_hdr, data);
219+
}
220+
221+
/* Both PDUs have now ext header length calculated properly, reset
222+
* pointers to start of AD.
223+
*/
224+
dptr = &com_hdr->ext_hdr_adv_data[com_hdr->ext_hdr_len];
225+
dptr_prev = &com_hdr_prev->ext_hdr_adv_data[com_hdr_prev->ext_hdr_len];
226+
227+
/* Calculate length of AD to copy and AD length available in new PDU */
228+
len_prev = pdu_prev->len - (dptr_prev - pdu_prev->payload);
229+
len = PDU_AC_PAYLOAD_SIZE_MAX - (dptr - pdu->payload);
230+
231+
/* TODO: we should allow partial copy and let caller refragment data */
232+
if (len < len_prev) {
233+
return BT_HCI_ERR_PACKET_TOO_LONG;
234+
}
235+
236+
/* Copy AD */
237+
if (!(ext_hdr_flags_rem & ULL_ADV_PDU_HDR_FIELD_AD_DATA)) {
238+
len = MIN(len, len_prev);
239+
memcpy(dptr, dptr_prev, len);
240+
dptr += len;
241+
}
242+
243+
/* Finalize PDU */
244+
pdu->len = dptr - pdu->payload;
245+
246+
return 0;
247+
}
248+
249+
static uint8_t adv_sync_pdu_ad_data_set(struct pdu_adv *pdu,
250+
const uint8_t *data, uint8_t len)
251+
{
252+
struct pdu_adv_com_ext_adv *com_hdr;
253+
uint8_t len_max;
254+
uint8_t *dptr;
255+
256+
com_hdr = &pdu->adv_ext_ind;
257+
258+
dptr = &com_hdr->ext_hdr_adv_data[com_hdr->ext_hdr_len];
259+
260+
len_max = PDU_AC_PAYLOAD_SIZE_MAX - (dptr - pdu->payload);
261+
/* TODO: we should allow partial copy and let caller refragment data */
262+
if (len > len_max) {
263+
return BT_HCI_ERR_PACKET_TOO_LONG;
264+
}
265+
266+
memcpy(dptr, data, len);
267+
dptr += len;
268+
269+
pdu->len = dptr - pdu->payload;
270+
271+
return 0;
272+
}
273+
274+
#if defined(CONFIG_BT_CTLR_ADV_PDU_LINK)
275+
static void adv_sync_pdu_duplicate_chain(struct pdu_adv *pdu,
276+
struct pdu_adv *pdu_prev)
277+
{
278+
uint8_t err;
279+
280+
while (lll_adv_pdu_linked_next_get(pdu_prev)) {
281+
struct pdu_adv *pdu_new;
282+
283+
pdu_prev = lll_adv_pdu_linked_next_get(pdu_prev);
284+
pdu_new = lll_adv_pdu_alloc_pdu_adv();
285+
286+
/* We make exact copy of old PDU, there's really nothing that
287+
* can go wrong there assuming original PDU was created properly
288+
*/
289+
err = adv_sync_pdu_init_from_prev_pdu(pdu_new, pdu_prev, 0, 0);
290+
LL_ASSERT(err);
291+
292+
lll_adv_pdu_linked_append(pdu_new, pdu);
293+
294+
pdu = pdu_new;
295+
}
296+
}
297+
#endif /* CONFIG_BT_CTLR_ADV_PDU_LINK */
298+
124299
uint8_t ll_adv_sync_param_set(uint8_t handle, uint16_t interval, uint16_t flags)
125300
{
126301
struct lll_adv_sync *lll_sync;
@@ -195,10 +370,11 @@ uint8_t ll_adv_sync_param_set(uint8_t handle, uint16_t interval, uint16_t flags)
195370
uint8_t ll_adv_sync_ad_data_set(uint8_t handle, uint8_t op, uint8_t len,
196371
uint8_t const *const data)
197372
{
198-
struct adv_pdu_field_data pdu_data;
199373
struct lll_adv_sync *lll_sync;
374+
struct pdu_adv *pdu_prev;
200375
struct ll_adv_set *adv;
201-
uint8_t value[5];
376+
void *extra_data_prev;
377+
struct pdu_adv *pdu;
202378
uint8_t ter_idx;
203379
uint8_t err;
204380

@@ -219,16 +395,39 @@ uint8_t ll_adv_sync_ad_data_set(uint8_t handle, uint8_t op, uint8_t len,
219395
return BT_HCI_ERR_UNKNOWN_ADV_IDENTIFIER;
220396
}
221397

222-
pdu_data.field_data = value;
223-
*pdu_data.field_data = len;
224-
sys_put_le32((uint32_t)data, pdu_data.field_data + 1);
398+
pdu_prev = lll_adv_sync_data_peek(lll_sync, &extra_data_prev);
399+
pdu = lll_adv_sync_data_alloc(lll_sync, extra_data_prev, &ter_idx);
400+
401+
/* TODO: pdu_prev will be the same as pdu if data are still enqueued
402+
* and waiting for LLL to swap, creating new chain will not work
403+
* in such case since we cannot update it in-place.
404+
*/
405+
LL_ASSERT(pdu_prev != pdu);
225406

226-
err = ull_adv_sync_pdu_set_clear(adv, ULL_ADV_PDU_HDR_FIELD_AD_DATA,
227-
0, &pdu_data, &ter_idx);
407+
/* Initialize new PDU to be the same as old, except for AD which we
408+
* replace with new ones.
409+
*/
410+
err = adv_sync_pdu_init_from_prev_pdu(pdu, pdu_prev, 0,
411+
ULL_ADV_PDU_HDR_FIELD_AD_DATA);
228412
if (err) {
229413
return err;
230414
}
231415

416+
/* TODO: only data in 1st PDU is supported as for now, need to add
417+
* support for fragmentation.
418+
*/
419+
err = adv_sync_pdu_ad_data_set(pdu, data, len);
420+
if (err) {
421+
return err;
422+
}
423+
424+
#if defined(CONFIG_BT_CTLR_ADV_PDU_LINK)
425+
/* There may be linked PDUs due to e.g. CTEInfo so need to duplicate
426+
* all of them to new chain.
427+
*/
428+
adv_sync_pdu_duplicate_chain(pdu, pdu_prev);
429+
#endif
430+
232431
lll_adv_sync_data_enqueue(lll_sync, ter_idx);
233432

234433
return 0;

0 commit comments

Comments
 (0)