在LL连接状态中,LL的acknowledgement 和flow control是一直存在的。每次连接时,transmitSeqNum 和nextExpectedSeqNum都必须设置成0。transmitSeqNum 和nextExpectedSeqNum都只有1bit。transmitSeqNum 用来识别数据包是否被传出去。nextExpectedSeqNum 用来区别是否是最新的数据或者被用来请求重传。当重传一个数据通道的的PDU时,这个PDU的LLID,SN,和payload也要与上一个保持一致。
连接时,transmitSeqNum和nextExpectedSeqNum都被初始化为0;
1.发送方:当transmitSeqNum等于接收到的NESH时,说明发送方没收到确认,这时应该重发。此时SN,NESN,payload,LLID应该保持与上一个一致。当transmitSeqNum不等于接收到的NESH时,发送新的数据包,transmitSeqNum加一,并更新到SN。
接收方:当接收到的SN不等于nextExpectedSeqNum时,接收方忽略或者作为重传数据处理。当接收到的SN与本身的nextExpectedSeqNum一致时,认为这是一个新的数据包。
这个图的表述其实是有问题的。实际上只有在接收数据时才会对transmitSeqNum和nextExpectedSeqNum进行加一操作的。
static int isr_rx_pdu(struct lll_conn *lll, struct pdu_data *pdu_data_rx,
struct node_tx **tx_release, u8_t *is_rx_enqueue)
{
/* Ack for tx-ed data */
if (pdu_data_rx->nesn != lll->sn) {
/* Increment serial number */
lll->sn++;
/* First ack (and redundantly any other ack) enable use of
* slave latency.
*/
if (IS_ENABLED(CONFIG_BT_PERIPHERAL) && lll->role) {
lll->slave.latency_enabled = 1;
}
if (!lll->empty) {
struct pdu_data *pdu_data_tx;
u8_t pdu_data_tx_len;
struct node_tx *tx;
memq_link_t *link;
link = memq_peek(lll->memq_tx.head, lll->memq_tx.tail,
(void **)&tx);
LL_ASSERT(link);
pdu_data_tx = (void *)(tx->pdu +
lll->packet_tx_head_offset);
pdu_data_tx_len = pdu_data_tx->len;
#if defined(CONFIG_BT_CTLR_LE_ENC)
if (pdu_data_tx_len != 0U) {
/* if encrypted increment tx counter */
if (lll->enc_tx) {
lll->ccm_tx.counter++;
}
}
#endif /* CONFIG_BT_CTLR_LE_ENC */
lll->packet_tx_head_offset += pdu_data_tx_len;
if (lll->packet_tx_head_offset ==
lll->packet_tx_head_len) {
lll->packet_tx_head_len = 0;
lll->packet_tx_head_offset = 0;
memq_dequeue(lll->memq_tx.tail,
&lll->memq_tx.head, NULL);
link->next = tx->next;
tx->next = link;
*tx_release = tx;
}
} else {
lll->empty = 0;
}
}
/* process received data */
if ((pdu_data_rx->sn == lll->nesn) &&
/* check so that we will NEVER use the rx buffer reserved for empty
* packet and internal control enqueue
*/
(ull_pdu_rx_alloc_peek(3) != 0)) {
/* Increment next expected serial number */
lll->nesn++;
if (pdu_data_rx->len != 0) {
#if defined(CONFIG_BT_CTLR_LE_ENC)
/* If required, wait for CCM to finish
*/
if (lll->enc_rx) {
u32_t done;
done = radio_ccm_is_done();
LL_ASSERT(done);
if (!radio_ccm_mic_is_valid()) {
/* Record MIC invalid */
mic_state = LLL_CONN_MIC_FAIL;
return -EINVAL;
}
/* Increment counter */
lll->ccm_rx.counter++;
/* Record MIC valid */
mic_state = LLL_CONN_MIC_PASS;
}
#endif /* CONFIG_BT_CTLR_LE_ENC */
/* Enqueue non-empty PDU */
*is_rx_enqueue = 1U;
}
}
return 0;
}
其核心在以下两端代码
/* Ack for tx-ed data */
if (pdu_data_rx->nesn != lll->sn) {
/* Increment serial number */
lll->sn++;
和
/* process received data */
if ((pdu_data_rx->sn == lll->nesn) &&
/* check so that we will NEVER use the rx buffer reserved for empty
* packet and internal control enqueue
*/
(ull_pdu_rx_alloc_peek(3) != 0)) {
/* Increment next expected serial number */
lll->nesn++;
在发送前需要更新SN和NESN的。
/* Fill sn and nesn */
pdu_data_tx->sn = lll->sn;
pdu_data_tx->nesn = lll->nesn;
Master | 传输方向 | Slave | ||||||
SN | transmitSeqNum | NESN | nextExpectedSeqNum | SN | transmitSeqNum | NESN | nextExpectedSeqNum | |
0 | 0 | 0 | 0 | -----> | 0 | 0 | 0 | 0 |
0 | 0 | 1 | 0 | <----- | 0 | 0 | 1 | 1 |
1 | 1 | 1 | 1 | -----> | 1 | 0 | 1 | 1 |
1 | 1 | 0 | 1 | <------ | 1 | 1 | 0 | 0 |
0 | 0 | 0 | 0 | -----> | 0 | 0 | 0 | 0 |