Bluetooth: Controller: ctrl pdu processing based on available CPU time

On CPUs like nRF51 which run at 16MHz, certain BLE control
procedure PDU processing take more CPU time than permitted
inside tIFS (150us). Current implementation of Data Length
Update procedure does not span over multiple connection
interval (unlike Encryption Setup, which is another control
procedure processing that would consume more CPU time)
hence taking more CPU time inside tIFS on nRF51.

During the radio ISR, the active clock and packet timer are
active and it is used to profile the CPU time taken which
is used to decide on whether there is sufficient time in
the current radio event to process the control packet.

This commit also fixes a potential bug that would cause
disconnection due to MIC failure on encrypted connections
that performed Data Length Update. Controller used to NACK
the request/response PDU if it was not in a state to resize
the receive buffers but did not reset the CCM counter. This
is now fixed by the change done to NACK control PDU based
on available CPU time in radio ISR.

Change-id: Id58322ad76a0dbc284738cdd9a7c0437c9e8c423
Signed-off-by: Vinayak Chettimada <vinayak.kariappa.chettimada@nordicsemi.no>
This commit is contained in:
Vinayak Chettimada 2016-12-14 05:05:37 +01:00 committed by Johan Hedberg
commit 6de7a808af
3 changed files with 52 additions and 15 deletions

View file

@ -421,6 +421,16 @@ uint32_t radio_tmr_end_get(void)
return NRF_TIMER0->CC[2];
}
void radio_tmr_sample(void)
{
NRF_TIMER0->TASKS_CAPTURE[3] = 1;
}
uint32_t radio_tmr_sample_get(void)
{
return NRF_TIMER0->CC[3];
}
static uint8_t ALIGNED(4) _ccm_scratch[(RADIO_PDU_LEN_MAX - 4) + 16];
void *radio_ccm_rx_pkt_set(struct ccm *ccm, void *pkt)

View file

@ -94,6 +94,8 @@ void radio_tmr_aa_capture(void);
uint32_t radio_tmr_aa_get(void);
void radio_tmr_end_capture(void);
uint32_t radio_tmr_end_get(void);
void radio_tmr_sample(void);
uint32_t radio_tmr_sample_get(void);
void *radio_ccm_rx_pkt_set(struct ccm *ccm, void *pkt);
void *radio_ccm_tx_pkt_set(struct ccm *ccm, void *pkt);

View file

@ -1219,12 +1219,12 @@ isr_rx_conn_pkt_ctrl_rej(struct radio_pdu_node_rx *radio_pdu_node_rx,
}
static inline void isr_rx_conn_pkt_ctrl_dle(struct pdu_data *pdu_data_rx,
static inline uint8_t isr_rx_conn_pkt_ctrl_dle(struct pdu_data *pdu_data_rx,
uint8_t *rx_enqueue)
{
uint16_t eff_rx_octets;
uint16_t eff_tx_octets;
uint8_t no_resp = 0;
uint8_t nack = 0;
eff_rx_octets = _radio.conn_curr->max_rx_octets;
eff_tx_octets = _radio.conn_curr->max_tx_octets;
@ -1300,10 +1300,7 @@ static inline void isr_rx_conn_pkt_ctrl_dle(struct pdu_data *pdu_data_rx,
*/
_radio.state = STATE_CLOSE;
} else {
/* nack ctrl packet */
_radio.conn_curr->nesn--;
no_resp = 1;
nack = 1;
}
} else {
/* resume data packet tx */
@ -1333,16 +1330,20 @@ static inline void isr_rx_conn_pkt_ctrl_dle(struct pdu_data *pdu_data_rx,
}
if ((PDU_DATA_LLCTRL_TYPE_LENGTH_REQ ==
pdu_data_rx->payload.llctrl.opcode) && !no_resp) {
pdu_data_rx->payload.llctrl.opcode) && !nack) {
length_resp_send(_radio.conn_curr, eff_rx_octets,
eff_tx_octets);
}
return nack;
}
static inline void
static inline uint8_t
isr_rx_conn_pkt_ctrl(struct radio_pdu_node_rx *radio_pdu_node_rx,
struct pdu_data *pdu_data_rx, uint8_t *rx_enqueue)
{
uint8_t nack = 0;
switch (pdu_data_rx->payload.llctrl.opcode) {
case PDU_DATA_LLCTRL_TYPE_CONN_UPDATE_REQ:
if (conn_update(_radio.conn_curr, pdu_data_rx) == 0) {
@ -1733,7 +1734,7 @@ isr_rx_conn_pkt_ctrl(struct radio_pdu_node_rx *radio_pdu_node_rx,
case PDU_DATA_LLCTRL_TYPE_LENGTH_RSP:
case PDU_DATA_LLCTRL_TYPE_LENGTH_REQ:
isr_rx_conn_pkt_ctrl_dle(pdu_data_rx, rx_enqueue);
nack = isr_rx_conn_pkt_ctrl_dle(pdu_data_rx, rx_enqueue);
break;
default:
@ -1742,12 +1743,14 @@ isr_rx_conn_pkt_ctrl(struct radio_pdu_node_rx *radio_pdu_node_rx,
break;
}
return nack;
}
static inline uint32_t
isr_rx_conn_pkt(struct radio_pdu_node_rx *radio_pdu_node_rx,
struct pdu_data *pdu_data_rx)
{
uint8_t nack = 0;
uint8_t terminate = 0;
struct pdu_data *pdu_data_tx;
@ -1821,13 +1824,12 @@ isr_rx_conn_pkt(struct radio_pdu_node_rx *radio_pdu_node_rx,
((_radio.fc_req != 0) &&
(_radio.fc_handle[_radio.fc_req - 1] ==
_radio.conn_curr->handle)))))) {
_radio.conn_curr->nesn++;
uint8_t ccm_rx_increment = 0;
if (pdu_data_rx->len != 0) {
uint8_t rx_enqueue = 0;
/* If required wait for CCM to finish and then
* increment counter
/* If required, wait for CCM to finish
*/
if (_radio.conn_curr->enc_rx) {
uint32_t done;
@ -1835,7 +1837,7 @@ isr_rx_conn_pkt(struct radio_pdu_node_rx *radio_pdu_node_rx,
done = radio_ccm_is_done();
LL_ASSERT(done);
_radio.conn_curr->ccm_rx.counter++;
ccm_rx_increment = 1;
}
/* MIC Failure Check or data rx during pause */
@ -1867,8 +1869,23 @@ isr_rx_conn_pkt(struct radio_pdu_node_rx *radio_pdu_node_rx,
break;
case PDU_DATA_LLID_CTRL:
isr_rx_conn_pkt_ctrl(radio_pdu_node_rx,
pdu_data_rx, &rx_enqueue);
/* Handling control procedure takes more CPU
* time hence check how much CPU time we have
* used up inside tIFS (150us) and decide to
* NACK rx-ed packet if consumed more than half
* the tIFS value. Control Procedure will be
* handled in the next re-transmission of the
* packet by peer.
*/
radio_tmr_sample();
if ((radio_tmr_sample_get() -
radio_tmr_end_get()) < 75) {
nack = isr_rx_conn_pkt_ctrl(
radio_pdu_node_rx,
pdu_data_rx, &rx_enqueue);
} else {
nack = 1;
}
break;
case PDU_DATA_LLID_RESV:
default:
@ -1897,6 +1914,14 @@ isr_rx_conn_pkt(struct radio_pdu_node_rx *radio_pdu_node_rx,
_radio.conn_curr->apto_reload;
}
}
if (!nack) {
_radio.conn_curr->nesn++;
if (ccm_rx_increment) {
_radio.conn_curr->ccm_rx.counter++;
}
}
}
return 0;