Radio Control Layer (RCL)
Advertiser Command Handler

Introduction

In Bluetooth® Low Energy (BLE), the process of "advertising" is a critical part of the communication protocol, serving as a means for devices to broadcast their presence and capabilities to potential counterparts. Furthermore, with the introduction of extended advertising, the capabilities of traditional or legacy advertising are augmented offering enhanced features for more extensive communication between BLE devices.

Regardless of whether legacy or extended advertising is used, advertising PDUs are used as information carriers during the advertising process and the specific type of PDU plays a significant role in the way that the command handler and the RCL behave. Therefore, before describing how the advertising command works, it is worth mentioning the different advertising PDUs involved in the command handler's lifecycle.

PDU Type PDU Name Physical Channel LE 1M LE 2M LE Coded Currently Supported
0b0000 ADV_IND Primary * *
0b0001 ADV_DIRECT_IND Primary * *
0b0010 ADV_NONCONN_IND Primary * *
0b0011 SCAN_REQ Primary * *
0b0011 AUX_SCAN_REQ Secondary * * *
0b0100 SCAN_RSP Primary * *
0b0101 CONNECT_IND Primary * *
0b0101 AUX_CONNECT_REQ Secondary * * *
0b0110 ADV_SCAN_IND Primary * *
0b0111 ADV_EXT_IND Primary * * *
0b0111 AUX_ADV_IND Secondary * * * *
0b0111 AUX_SCAN_RSP Secondary * * *
0b0111 AUX_SYNC_IND Periodic * * *
0b0111 AUX_CHAIN_IND Secondary and Periodic * * * *
0b0111 AUX_SYNC_SUBEVENT_IND Periodic * * *
0b0111 AUX_SYNC_SUBEVENT_RSP Periodic * * *
0b1000 AUX_CONNECT_RSP Secondary * * *

Please note that legacy advertising PDUs are limited to primary advertising channels and have a specific payloads, while extended advertising PDUs share the same PDU payload format known as the Common Extended Advertising Payload Format.

The advertiser command handler supports both legacy and extended advertising by extracting the PDU type and if needed, the AdvMode associated with the PDU.

advertising_physical_channel_pdu.png
Advertising Physical Channel PDU

Usage

How the advertiser command handler is configured and used depends on the type of PDU that is supposed to be sent. Nevertheless, the following basic steps must have taken place before submitting the an advertiser command:

  1. RCL has been initialized (See RCL_init) and a handle must exist (See RCL_open).
  2. The RCL_CMD_BLE5_ADV_t command has been initialized and configured.
  3. The necessary Tx buffer(s) have been initialized and configured.
  4. If needed, the necessary Multibuffer for reception has been initialized and configured.

Once these steps have been completed, RCL_Command_submit is called to start the command. Once submitted, the caller can either use RCL_Command_pend or a callback to wait for the command's conclusion and proceed with any additional tasks such as post-processing or the submitting of new commands.

As an example, the following code snippet shows how the advertiser command handler can be used to start an extended advertising event composed by one AXU_ADV_IND PDU and one or more AUX_CHAIN_IND PDUs.

void runExtendedAdvertiser(void)
{
RCL_Handle h = RCL_open(&rclClient, &LRF_configBle);
RCL_Buffer_TxBuffer *advExtTxBuffer = (RCL_Buffer_TxBuffer *)txBuffer;
RCL_Buffer_TxBuffer *auxAdvTxBuffer = (RCL_Buffer_TxBuffer *)txBuffer2;
AuxPtr advExtAuxPtr;
AuxPtr auxAdvAuxPtr;
advCmd.ctx = &advCtx;
advCmd.stats = &advStats;
advCmd.common.scheduling = RCL_Schedule_Now;
advCmd.common.runtime.callback = advCallback;
advCmd.common.runtime.lrfCallbackMask.value = 0;
advCmd.common.runtime.rclCallbackMask.value = RCL_EventLastCmdDone.value |
advStats = (RCL_StatsAdvScanInit) { 0 };
advStats.config.accumulate = true;
/* AuxPtr to be used for the ADV_EXT_IND indicating that the auxiliary packet (i.e. AUX_ADV_IND) will be sent on:
* BLE 2M PHY, channel 25, and as soon as possible.
*/
advExtAuxPtr.auxOffset = 0;
advExtAuxPtr.offsetUnits = 1;
advExtAuxPtr.auxPhy = 1;
advExtAuxPtr.ca = 0;
advExtAuxPtr.chIndex = 25;
/* AuxPtr to be used for the AUX_ADV_IND indicating that a chained packet will be sent on:
* Channel 16, and as soon as possible. No PHY change allowed at this point.
*/
auxAdvAuxPtr.auxOffset = 0;
auxAdvAuxPtr.offsetUnits = 1;
auxAdvAuxPtr.ca = 0;
auxAdvAuxPtr.chIndex = 16;
auxAdvAuxPtr.auxPhy = 1;
/* Populate Tx buffer used for the ADV_EXT_IND that will be sent on all primary advertisement channels */
setAdvExtBuffer(advExtTxBuffer, &advExtAuxPtr);
/* Populate Tx buffer used for the AUX_ADV_IND that will be sent on a secondary advertisement channel */
setAuxAdvBuffer(auxAdvTxBuffer, &auxAdvAuxPtr, AUX_ADV_DATA_LEN);
advExtTxBuffer->state = RCL_BufferStatePending;
auxAdvTxBuffer->state = RCL_BufferStatePending;
RCL_TxBuffer_put(&advCtx.txBuffers, advExtTxBuffer);
RCL_TxBuffer_put(&advCtx.txBuffers, auxAdvTxBuffer);
RCL_Command_submit(h, &advCmd);
/* Wait for command to conclude */
RCL_Command_pend(&advCmd);
}

Configuration of the command is the same regardless of whether the caller wants to use legacy advertising or extended advertising. Nevertheless, it is the job of the caller to provide the appropriate number of Tx buffers with the appropriate structure for the type of advertising.

In legacy advertising, either one or two Tx buffers will be needed, depending on whether a scan response is required by the type of advertising.

In extended advertising, at least two buffers must be present in the Tx buffer list at command start. One of these Tx buffers corresponds to the EXT_ADV_IND PDU sent over the primary advertising channels, while the second Tx Buffer in the list corresponds to the AUX_ADV_IND PDU that indicates the start of the auxiliary advertising segment.

In any case, the command handler will inspect the provided Tx Buffer(s), and based on the type of advertising, it will determine whether it's legacy advertising or extended advertising.

In the previous code snippet Tx buffers are built with helper functions which populate the Tx buffer with appropriate values for each of the fields present in the Common Extended Advertising Payload Format.

static void setAdvExtBuffer(RCL_Buffer_TxBuffer *buffer, AuxPtr *advExtAuxPtr)
{
/* Include extra octet corresponding to the extended header length and AdvMode */
uint8_t payloadLength = 1 + ADV_EXT_HDR_LEN;
uint8_t *txData;
txData = RCL_TxBuffer_init(buffer, NUM_PAD, BLE_HDR_LEN, payloadLength);
txData[0] = BLE_HDR_ADV_EXT_IND;
txData[1] = payloadLength;
txData[2] = (BLE_ADV_MODE_NONCONN_NONSCAN << 6) | ADV_EXT_HDR_LEN;
txData[3] = BLE5_EXT_HDR_FLAG_ADI_BM | BLE5_EXT_HDR_FLAG_AUX_PTR_BM;
/* ADI */
txData[4] = 0;
txData[5] = 0;
/* AuxPtr: Channel index, CA, and Offset Units */
txData[6] = (advExtAuxPtr->chIndex | (advExtAuxPtr->ca << 6) | (advExtAuxPtr->offsetUnits << 7));
/* 8 least significant bits of auxOffset */
txData[7] = advExtAuxPtr->auxOffset & 0xFF;
/* 5 most significant bits of auxOffset and auxPhy */
txData[8] = ((advExtAuxPtr->auxOffset >> 8) & 0x1F) | (advExtAuxPtr->auxPhy << 5);
/* ADV_EXT_IND does not contain any advertising data */
}
static void setAuxAdvBuffer(RCL_Buffer_TxBuffer *buffer, AuxPtr *auxAdvAuxPtr, uint8_t advDataLen)
{
/* Include extra octet corresponding to the extended header length and AdvMode */
uint8_t payloadLength = 1 + AUX_ADV_HDR_LEN + advDataLen;
uint8_t *txData;
txData = RCL_TxBuffer_init(buffer, NUM_PAD, BLE_HDR_LEN, payloadLength);
txData[0] = BLE_HDR_AUX_ADV_IND;
txData[1] = payloadLength;
txData[2] = (BLE_ADV_MODE_NONCONN_NONSCAN << 6) | AUX_ADV_HDR_LEN;
txData[3] = BLE5_EXT_HDR_FLAG_ADI_BM | BLE5_EXT_HDR_FLAG_AUX_PTR_BM;
/* ADI */
txData[4] = 0;
txData[5] = 0;
/* AuxPtr: Channel index, CA, and Offset Units */
txData[6] = (auxAdvAuxPtr->chIndex | (auxAdvAuxPtr->ca << 6) | (auxAdvAuxPtr->offsetUnits << 7));
/* 8 least significant bits of auxOffset */
txData[7] = auxAdvAuxPtr->auxOffset & 0xFF;
/* 5 most significant bits of auxOffset and auxPhy */
txData[8] = ((auxAdvAuxPtr->auxOffset >> 8) & 0x1F) | (auxAdvAuxPtr->auxPhy << 5);
/* AdvData */
if (advDataLen > 0)
{
for(uint32_t i = 0; i < advDataLen; i++)
{
txData[9 + i] = i & 0xFF;
}
}
}

Notice how the AuxPtr previously defined are used to populate the Tx Buffer with the help of a C struct. Additionally, keep in mind that the extended header length field indicates the size of the extended header field and that it varies depending on the event type (i.e. the fields permitted in the PDU). For this particular example the ADV_EXT_IND corresponds to an event of type "Non-Connectable/Non-Scannable Undirected with Auxiliary Packet", and the AUX_ADV_IND corresponds to an event of type "Non-Connectable/Non-Scannable Undirected".

typedef struct
{
uint8_t chIndex: 6;
uint8_t ca: 1;
uint8_t offsetUnits: 1;
uint16_t auxOffset: 13;
uint16_t auxPhy: 3;
} AuxPtr;

Furthermore, in the case of extended advertising, the command handler raises an RCL event indicating that a Tx Buffer is finished (see RCL_EventTxBufferFinished), so that the caller is made aware (through the callback) that it can add additional Tx buffers to the Tx buffer list.

In this particular example, the callback is used to put a Tx buffer corresponding to the first AUX_CHAIN_IND PDU of the extended advertising event.

Finally, it is worth mentioning that depending on the values used in the AuxPtr the command handler will either automatically take care of the AuxPtr calculation for the current packet and submit the next chained packet as soon as possible, or it will simply send the current packet and then finish the operation (meaning that it is up to the caller to schedule a secondary channel advertiser for the next chained packet).

This is accomplished by defining the "invalid" combination of AuxOffset = 0 and OffsetUnits = 1. If this invalid combination is used, the command handler will automatically automatically take care of the AuxPtr calculation. Otherwise, the caller must take care of using the correct AuxPtr values and scheduling the next auxiliary advertising segment.

Architecture

The life cycle of the advertiser command handler depends on the type of advertising and more specifically the PDU type. The PDU type will determine aspects such as the number of Tx buffers needed for the operation, or whether the operation involves both Tx and Rx, or just Tx. It is the responsibility of the caller to provide a Tx Buffer containing a valid advertising physical channel PDU.

Furthermore, unlike other command handlers, the advertiser command handler supports the possibility to do PHY switching (as required by extended advertising). The following activity diagram illustrates the behavior of the advertiser command handler.

RCL Event (In) Description
RCL_EventSetup Setup has been performed
RCL_EventTimerStart Timer-based start signalled
RCL_EventHandlerCmdUpdate PHY switch has been performed
RCL_EventGracefulStop Graceful stop has been observed
RCL_EventHardStop Hard stop has been observed
RCL_EventRxBufferUpdate RX buffer has been updated
RCL Event (Out) Description
RCL_EventLastCmdDone The RCL is finished with the command
RCL_EventCmdStarted Command handler has accepted and started executing
RCL_EventRxBufferFinished An RX multi-buffer is finished
RCL_EventRxEntryAvail An RX entry has been made available
RCL_EventTxBufferFinished An TX buffer is finished
RCL_EventPartialSetup Partial setup is required by handler to perform a PHY switch
LRF Event Description
LRF_EventOpDone The PBE operation has finished
LRF_EventOpError Something went wrong. Cause located in the PBE ENDCAUSE register
LRF_EventRxOk Packet received with CRC OK and not to be ignored by the MCU
LRF_EventRxNok Packet received with CRC error
LRF_EventRxIgnored Packet received, but may be ignored by MCU