Bluetooth: controller: Fix cmd disallowed and collision disconnects
Fix implementation to correctly cache the control procedures initiatable by local and peer. And, fix feature exchange and version information procedures from being disallowed by having then as cached requests to the controller. Relates to #15256. Signed-off-by: Vinayak Kariappa Chettimada <vich@nordicsemi.no>
This commit is contained in:
parent
6212ec9612
commit
3f695f7823
2 changed files with 103 additions and 69 deletions
|
@ -2808,7 +2808,8 @@ isr_rx_conn_pkt_ctrl(struct radio_pdu_node_rx *node_rx,
|
|||
rsp = &pdu_data_rx->llctrl.feature_rsp;
|
||||
|
||||
/* AND the feature set to get Feature USED */
|
||||
_radio.conn_curr->llcp_features &= feat_get(&rsp->features[0]);
|
||||
_radio.conn_curr->llcp_feature.features &=
|
||||
feat_get(&rsp->features[0]);
|
||||
|
||||
/* features exchanged */
|
||||
_radio.conn_curr->common.fex_valid = 1U;
|
||||
|
@ -2817,6 +2818,8 @@ isr_rx_conn_pkt_ctrl(struct radio_pdu_node_rx *node_rx,
|
|||
*rx_enqueue = 1U;
|
||||
|
||||
/* Procedure complete */
|
||||
_radio.conn_curr->llcp_feature.ack =
|
||||
_radio.conn_curr->llcp_feature.req;
|
||||
_radio.conn_curr->procedure_expire = 0U;
|
||||
}
|
||||
break;
|
||||
|
@ -7551,7 +7554,7 @@ static inline void event_enc_reject_prep(struct connection *conn,
|
|||
pdu->ll_id = PDU_DATA_LLID_CTRL;
|
||||
|
||||
if (conn->common.fex_valid &&
|
||||
(conn->llcp_features & BIT(BT_LE_FEAT_BIT_EXT_REJ_IND))) {
|
||||
(conn->llcp_feature.features & BIT(BT_LE_FEAT_BIT_EXT_REJ_IND))) {
|
||||
struct pdu_data_llctrl_reject_ext_ind *p;
|
||||
|
||||
pdu->llctrl.opcode = PDU_DATA_LLCTRL_TYPE_REJECT_EXT_IND;
|
||||
|
@ -7716,12 +7719,17 @@ static inline void event_fex_prep(struct connection *conn)
|
|||
{
|
||||
struct radio_pdu_node_tx *node_tx;
|
||||
|
||||
/* If waiting for response, do nothing */
|
||||
if (!((conn->llcp_feature.ack - conn->llcp_feature.req) & 0x01)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (conn->common.fex_valid) {
|
||||
struct radio_pdu_node_rx *node_rx;
|
||||
struct pdu_data *pdu_ctrl_rx;
|
||||
|
||||
/* procedure request acked */
|
||||
conn->llcp_ack = conn->llcp_req;
|
||||
conn->llcp_feature.ack = conn->llcp_feature.req;
|
||||
|
||||
/* Prepare the rx packet structure */
|
||||
node_rx = packet_rx_reserve_get(2);
|
||||
|
@ -7740,11 +7748,11 @@ static inline void event_fex_prep(struct connection *conn)
|
|||
(void)memset(&pdu_ctrl_rx->llctrl.feature_rsp.features[0], 0x00,
|
||||
sizeof(pdu_ctrl_rx->llctrl.feature_rsp.features));
|
||||
pdu_ctrl_rx->llctrl.feature_req.features[0] =
|
||||
conn->llcp_features & 0xFF;
|
||||
conn->llcp_feature.features & 0xFF;
|
||||
pdu_ctrl_rx->llctrl.feature_req.features[1] =
|
||||
(conn->llcp_features >> 8) & 0xFF;
|
||||
(conn->llcp_feature.features >> 8) & 0xFF;
|
||||
pdu_ctrl_rx->llctrl.feature_req.features[2] =
|
||||
(conn->llcp_features >> 16) & 0xFF;
|
||||
(conn->llcp_feature.features >> 16) & 0xFF;
|
||||
|
||||
/* enqueue feature rsp structure into rx queue */
|
||||
packet_rx_enqueue();
|
||||
|
@ -7756,11 +7764,11 @@ static inline void event_fex_prep(struct connection *conn)
|
|||
if (node_tx) {
|
||||
struct pdu_data *pdu_ctrl_tx = (void *)node_tx->pdu_data;
|
||||
|
||||
/* procedure request acked */
|
||||
conn->llcp_ack = conn->llcp_req;
|
||||
/* procedure request acked, move to waiting state */
|
||||
conn->llcp_feature.ack--;
|
||||
|
||||
/* use initial feature bitmap */
|
||||
conn->llcp_features = LL_FEAT;
|
||||
conn->llcp_feature.features = LL_FEAT;
|
||||
|
||||
/* place the feature exchange req packet as next in tx queue */
|
||||
pdu_ctrl_tx->ll_id = PDU_DATA_LLID_CTRL;
|
||||
|
@ -7775,11 +7783,11 @@ static inline void event_fex_prep(struct connection *conn)
|
|||
0x00,
|
||||
sizeof(pdu_ctrl_tx->llctrl.feature_req.features));
|
||||
pdu_ctrl_tx->llctrl.feature_req.features[0] =
|
||||
conn->llcp_features & 0xFF;
|
||||
conn->llcp_feature.features & 0xFF;
|
||||
pdu_ctrl_tx->llctrl.feature_req.features[1] =
|
||||
(conn->llcp_features >> 8) & 0xFF;
|
||||
(conn->llcp_feature.features >> 8) & 0xFF;
|
||||
pdu_ctrl_tx->llctrl.feature_req.features[2] =
|
||||
(conn->llcp_features >> 16) & 0xFF;
|
||||
(conn->llcp_feature.features >> 16) & 0xFF;
|
||||
|
||||
ctrl_tx_enqueue(conn, node_tx);
|
||||
|
||||
|
@ -7793,6 +7801,10 @@ static inline void event_fex_prep(struct connection *conn)
|
|||
|
||||
static inline void event_vex_prep(struct connection *conn)
|
||||
{
|
||||
/* If waiting for response, do nothing */
|
||||
if (!((conn->llcp_version.ack - conn->llcp_version.req) & 0x01)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (conn->llcp_version.tx == 0) {
|
||||
struct radio_pdu_node_tx *node_tx;
|
||||
|
@ -7802,8 +7814,8 @@ static inline void event_vex_prep(struct connection *conn)
|
|||
struct pdu_data *pdu_ctrl_tx = (void *)
|
||||
node_tx->pdu_data;
|
||||
|
||||
/* procedure request acked */
|
||||
conn->llcp_ack = conn->llcp_req;
|
||||
/* procedure request acked, move to waiting state */
|
||||
conn->llcp_version.ack--;
|
||||
|
||||
/* set version ind tx-ed flag */
|
||||
conn->llcp_version.tx = 1U;
|
||||
|
@ -7834,7 +7846,7 @@ static inline void event_vex_prep(struct connection *conn)
|
|||
struct pdu_data *pdu_ctrl_rx;
|
||||
|
||||
/* procedure request acked */
|
||||
conn->llcp_ack = conn->llcp_req;
|
||||
conn->llcp_version.ack = conn->llcp_version.req;
|
||||
|
||||
/* Prepare the rx packet structure */
|
||||
node_rx = packet_rx_reserve_get(2);
|
||||
|
@ -8741,12 +8753,26 @@ static void event_connection_prepare(u32_t ticks_at_expire,
|
|||
/* calc current event counter value */
|
||||
event_counter = conn->event_counter + conn->latency_prepare;
|
||||
|
||||
#if defined(CONFIG_BT_CTLR_CONN_PARAM_REQ) || defined(CONFIG_BT_CTLR_PHY)
|
||||
/* Check if no other procedure with instant is requested and not in
|
||||
* Encryption setup.
|
||||
*/
|
||||
if ((conn->llcp_ack == conn->llcp_req) && !conn->pause_rx) {
|
||||
if (0) {
|
||||
if (conn->llcp_feature.ack != conn->llcp_feature.req) {
|
||||
/* Stop previous event, to avoid Radio DMA corrupting
|
||||
* the rx queue.
|
||||
*/
|
||||
event_stop(0, 0, 0, (void *)STATE_ABORT);
|
||||
|
||||
event_fex_prep(conn);
|
||||
|
||||
} else if (conn->llcp_version.ack != conn->llcp_version.req) {
|
||||
/* Stop previous event, to avoid Radio DMA corrupting
|
||||
* the rx queue.
|
||||
*/
|
||||
event_stop(0, 0, 0, (void *)STATE_ABORT);
|
||||
|
||||
event_vex_prep(conn);
|
||||
|
||||
#if defined(CONFIG_BT_CTLR_CONN_PARAM_REQ)
|
||||
/* check if CPR procedure is requested */
|
||||
} else if (conn->llcp_conn_param.ack !=
|
||||
|
@ -8761,6 +8787,25 @@ static void event_connection_prepare(u32_t ticks_at_expire,
|
|||
ticks_at_expire);
|
||||
#endif /* CONFIG_BT_CTLR_CONN_PARAM_REQ */
|
||||
|
||||
#if defined(CONFIG_BT_CTLR_DATA_LENGTH)
|
||||
/* check if DLE procedure is requested */
|
||||
} else if (conn->llcp_length.ack != conn->llcp_length.req) {
|
||||
/* Stop previous event, to avoid Radio DMA corrupting
|
||||
* the rx queue
|
||||
*/
|
||||
event_stop(0, 0, 0, (void *)STATE_ABORT);
|
||||
|
||||
/* handle DLU state machine */
|
||||
if (event_len_prep(conn)) {
|
||||
/* NOTE: rx pool could not be resized, lets
|
||||
* skip this event and try in the next event.
|
||||
*/
|
||||
_radio.ticker_id_prepare = 0U;
|
||||
|
||||
goto event_connection_prepare_skip;
|
||||
}
|
||||
#endif /* CONFIG_BT_CTLR_DATA_LENGTH */
|
||||
|
||||
#if defined(CONFIG_BT_CTLR_PHY)
|
||||
/* check if PHY Req procedure is requested */
|
||||
} else if (conn->llcp_phy.ack != conn->llcp_phy.req) {
|
||||
|
@ -8772,29 +8817,8 @@ static void event_connection_prepare(u32_t ticks_at_expire,
|
|||
/* handle PHY Upd state machine */
|
||||
event_phy_req_prep(conn);
|
||||
#endif /* CONFIG_BT_CTLR_PHY */
|
||||
|
||||
#if defined(CONFIG_BT_CTLR_DATA_LENGTH)
|
||||
/* check if procedure is requested */
|
||||
} else if (conn->llcp_length.ack != conn->llcp_length.req) {
|
||||
/* Stop previous event, to avoid Radio DMA corrupting
|
||||
* the rx queue
|
||||
*/
|
||||
event_stop(0, 0, 0, (void *)STATE_ABORT);
|
||||
|
||||
/* handle DLU state machine */
|
||||
if (event_len_prep(conn)) {
|
||||
/* NOTE: rx pool could not be resized,
|
||||
* lets skip this event and try in the next
|
||||
* event.
|
||||
*/
|
||||
_radio.ticker_id_prepare = 0U;
|
||||
|
||||
goto event_connection_prepare_skip;
|
||||
}
|
||||
#endif /* CONFIG_BT_CTLR_DATA_LENGTH */
|
||||
}
|
||||
}
|
||||
#endif /* CONFIG_BT_CTLR_CONN_PARAM_REQ || CONFIG_BT_CTLR_PHY */
|
||||
|
||||
/* check if procedure is requested */
|
||||
if (conn->llcp_ack != conn->llcp_req) {
|
||||
|
@ -8825,14 +8849,6 @@ static void event_connection_prepare(u32_t ticks_at_expire,
|
|||
break;
|
||||
#endif /* CONFIG_BT_CTLR_LE_ENC */
|
||||
|
||||
case LLCP_FEATURE_EXCHANGE:
|
||||
event_fex_prep(conn);
|
||||
break;
|
||||
|
||||
case LLCP_VERSION_EXCHANGE:
|
||||
event_vex_prep(conn);
|
||||
break;
|
||||
|
||||
#if defined(CONFIG_BT_CTLR_LE_PING)
|
||||
case LLCP_PING:
|
||||
event_ping_prep(conn);
|
||||
|
@ -8894,7 +8910,6 @@ static void event_connection_prepare(u32_t ticks_at_expire,
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/* Setup XTAL startup and radio active events */
|
||||
event_common_prepare(ticks_at_expire, remainder,
|
||||
&conn->hdr.ticks_xtal_to_start,
|
||||
|
@ -9695,6 +9710,8 @@ static bool is_enc_req_pause_tx(struct connection *conn)
|
|||
if ((pdu_data_tx->ll_id == PDU_DATA_LLID_CTRL) &&
|
||||
(pdu_data_tx->llctrl.opcode == PDU_DATA_LLCTRL_TYPE_ENC_REQ)) {
|
||||
if ((conn->llcp_req != conn->llcp_ack) ||
|
||||
(conn->llcp_feature.ack != conn->llcp_feature.req) ||
|
||||
(conn->llcp_version.ack != conn->llcp_version.req) ||
|
||||
#if defined(CONFIG_BT_CTLR_CONN_PARAM_REQ)
|
||||
(conn->llcp_conn_param.ack != conn->llcp_conn_param.req) ||
|
||||
#endif /* CONFIG_BT_CTLR_CONN_PARAM_REQ */
|
||||
|
@ -10163,7 +10180,7 @@ static u32_t conn_update_req(struct connection *conn)
|
|||
#if defined(CONFIG_BT_CTLR_CONN_PARAM_REQ)
|
||||
} else if (!conn->llcp_conn_param.disabled &&
|
||||
(!conn->common.fex_valid ||
|
||||
(conn->llcp_features &
|
||||
(conn->llcp_feature.features &
|
||||
BIT(BT_LE_FEAT_BIT_CONN_PARAM_REQ)))) {
|
||||
/** Perform slave intiated conn param req */
|
||||
conn->llcp_conn_param.status = 0U;
|
||||
|
@ -10488,7 +10505,7 @@ static u8_t feature_rsp_send(struct connection *conn,
|
|||
|
||||
/* AND the feature set to get Feature USED */
|
||||
req = &pdu_data_rx->llctrl.feature_req;
|
||||
conn->llcp_features &= feat_get(&req->features[0]);
|
||||
conn->llcp_feature.features &= feat_get(&req->features[0]);
|
||||
|
||||
/* features exchanged */
|
||||
conn->common.fex_valid = 1U;
|
||||
|
@ -10502,11 +10519,11 @@ static u8_t feature_rsp_send(struct connection *conn,
|
|||
(void)memset(&pdu_ctrl_tx->llctrl.feature_rsp.features[0], 0x00,
|
||||
sizeof(pdu_ctrl_tx->llctrl.feature_rsp.features));
|
||||
pdu_ctrl_tx->llctrl.feature_req.features[0] =
|
||||
conn->llcp_features & 0xFF;
|
||||
conn->llcp_feature.features & 0xFF;
|
||||
pdu_ctrl_tx->llctrl.feature_req.features[1] =
|
||||
(conn->llcp_features >> 8) & 0xFF;
|
||||
(conn->llcp_feature.features >> 8) & 0xFF;
|
||||
pdu_ctrl_tx->llctrl.feature_req.features[2] =
|
||||
(conn->llcp_features >> 16) & 0xFF;
|
||||
(conn->llcp_feature.features >> 16) & 0xFF;
|
||||
|
||||
ctrl_tx_sec_enqueue(conn, node_tx);
|
||||
|
||||
|
@ -10545,7 +10562,10 @@ static u8_t version_ind_send(struct connection *conn,
|
|||
empty_tx_enqueue(conn);
|
||||
|
||||
} else if (!conn->llcp_version.rx) {
|
||||
LL_ASSERT(conn->llcp_version.ack != conn->llcp_version.req);
|
||||
|
||||
/* Procedure complete */
|
||||
conn->llcp_version.ack = conn->llcp_version.req;
|
||||
conn->procedure_expire = 0U;
|
||||
|
||||
/* enqueue the version ind */
|
||||
|
@ -11105,7 +11125,7 @@ u32_t radio_adv_enable(u16_t interval, u8_t chan_map, u8_t filter_policy,
|
|||
}
|
||||
|
||||
conn->handle = 0xFFFF;
|
||||
conn->llcp_features = LL_FEAT;
|
||||
conn->llcp_feature.features = LL_FEAT;
|
||||
conn->data_chan_sel = 0U;
|
||||
conn->data_chan_use = 0U;
|
||||
conn->event_counter = 0U;
|
||||
|
@ -11154,6 +11174,10 @@ u32_t radio_adv_enable(u16_t interval, u8_t chan_map, u8_t filter_policy,
|
|||
|
||||
conn->llcp_req = 0U;
|
||||
conn->llcp_ack = 0U;
|
||||
conn->llcp_feature.req = 0U;
|
||||
conn->llcp_feature.ack = 0U;
|
||||
conn->llcp_version.req = 0U;
|
||||
conn->llcp_version.ack = 0U;
|
||||
conn->llcp_version.tx = 0U;
|
||||
conn->llcp_version.rx = 0U;
|
||||
conn->llcp_terminate.req = 0U;
|
||||
|
@ -11625,7 +11649,7 @@ u32_t radio_connect_enable(u8_t adv_addr_type, u8_t *adv_addr, u16_t interval,
|
|||
328 + RADIO_TIFS + 328);
|
||||
|
||||
conn->handle = 0xFFFF;
|
||||
conn->llcp_features = LL_FEAT;
|
||||
conn->llcp_feature.features = LL_FEAT;
|
||||
access_addr = access_addr_get();
|
||||
memcpy(&conn->access_addr[0], &access_addr, sizeof(conn->access_addr));
|
||||
bt_rand(&conn->crc_init[0], 3);
|
||||
|
@ -11694,6 +11718,10 @@ u32_t radio_connect_enable(u8_t adv_addr_type, u8_t *adv_addr, u16_t interval,
|
|||
|
||||
conn->llcp_req = 0U;
|
||||
conn->llcp_ack = 0U;
|
||||
conn->llcp_feature.req = 0U;
|
||||
conn->llcp_feature.ack = 0U;
|
||||
conn->llcp_version.req = 0U;
|
||||
conn->llcp_version.ack = 0U;
|
||||
conn->llcp_version.tx = 0U;
|
||||
conn->llcp_version.rx = 0U;
|
||||
conn->llcp_terminate.req = 0U;
|
||||
|
@ -11797,7 +11825,7 @@ u8_t ll_conn_update(u16_t handle, u8_t cmd, u8_t status, u16_t interval_min,
|
|||
#if defined(CONFIG_BT_CTLR_CONN_PARAM_REQ)
|
||||
if (!conn->llcp_conn_param.disabled &&
|
||||
(!conn->common.fex_valid ||
|
||||
(conn->llcp_features &
|
||||
(conn->llcp_feature.features &
|
||||
BIT(BT_LE_FEAT_BIT_CONN_PARAM_REQ)))) {
|
||||
cmd++;
|
||||
} else if (conn->role) {
|
||||
|
@ -12051,12 +12079,11 @@ u8_t ll_feature_req_send(u16_t handle)
|
|||
return BT_HCI_ERR_UNKNOWN_CONN_ID;
|
||||
}
|
||||
|
||||
if (conn->llcp_req != conn->llcp_ack) {
|
||||
if (conn->llcp_feature.req != conn->llcp_feature.ack) {
|
||||
return BT_HCI_ERR_CMD_DISALLOWED;
|
||||
}
|
||||
|
||||
conn->llcp_type = LLCP_FEATURE_EXCHANGE;
|
||||
conn->llcp_req++;
|
||||
conn->llcp_feature.req++;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -12070,12 +12097,11 @@ u8_t ll_version_ind_send(u16_t handle)
|
|||
return BT_HCI_ERR_UNKNOWN_CONN_ID;
|
||||
}
|
||||
|
||||
if (conn->llcp_req != conn->llcp_ack) {
|
||||
if (conn->llcp_version.req != conn->llcp_version.ack) {
|
||||
return BT_HCI_ERR_CMD_DISALLOWED;
|
||||
}
|
||||
|
||||
conn->llcp_type = LLCP_VERSION_EXCHANGE;
|
||||
conn->llcp_req++;
|
||||
conn->llcp_version.req++;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -12176,8 +12202,7 @@ u32_t ll_length_req_send(u16_t handle, u16_t tx_octets, u16_t tx_time)
|
|||
return BT_HCI_ERR_UNKNOWN_CONN_ID;
|
||||
}
|
||||
|
||||
if ((conn->llcp_req != conn->llcp_ack) ||
|
||||
(conn->llcp_length.req != conn->llcp_length.ack)) {
|
||||
if (conn->llcp_length.req != conn->llcp_length.ack) {
|
||||
return BT_HCI_ERR_CMD_DISALLOWED;
|
||||
}
|
||||
|
||||
|
@ -12257,8 +12282,7 @@ u8_t ll_phy_req_send(u16_t handle, u8_t tx, u8_t flags, u8_t rx)
|
|||
return BT_HCI_ERR_UNKNOWN_CONN_ID;
|
||||
}
|
||||
|
||||
if ((conn->llcp_req != conn->llcp_ack) ||
|
||||
(conn->llcp_phy.req != conn->llcp_phy.ack)) {
|
||||
if (conn->llcp_phy.req != conn->llcp_phy.ack) {
|
||||
return BT_HCI_ERR_CMD_DISALLOWED;
|
||||
}
|
||||
|
||||
|
|
|
@ -10,13 +10,16 @@ enum llcp {
|
|||
LLCP_CONN_UPD,
|
||||
LLCP_CHAN_MAP,
|
||||
|
||||
/*
|
||||
* LLCP_TERMINATE,
|
||||
* LLCP_FEATURE_EXCHANGE,
|
||||
* LLCP_VERSION_EXCHANGE,
|
||||
*/
|
||||
|
||||
#if defined(CONFIG_BT_CTLR_LE_ENC)
|
||||
LLCP_ENCRYPTION,
|
||||
#endif /* CONFIG_BT_CTLR_LE_ENC */
|
||||
|
||||
LLCP_FEATURE_EXCHANGE,
|
||||
LLCP_VERSION_EXCHANGE,
|
||||
/* LLCP_TERMINATE, */
|
||||
LLCP_CONNECTION_PARAM_REQ,
|
||||
|
||||
#if defined(CONFIG_BT_CTLR_LE_PING)
|
||||
|
@ -148,6 +151,7 @@ struct connection {
|
|||
u16_t *pdu_win_offset;
|
||||
u32_t ticks_anchor;
|
||||
} conn_upd;
|
||||
|
||||
struct {
|
||||
u8_t initiate;
|
||||
u8_t chm[5];
|
||||
|
@ -173,9 +177,15 @@ struct connection {
|
|||
#endif /* CONFIG_BT_CTLR_LE_ENC */
|
||||
} llcp;
|
||||
|
||||
u32_t llcp_features;
|
||||
struct {
|
||||
u8_t req;
|
||||
u8_t ack;
|
||||
u32_t features;
|
||||
} llcp_feature;
|
||||
|
||||
struct {
|
||||
u8_t req;
|
||||
u8_t ack;
|
||||
u8_t tx:1;
|
||||
u8_t rx:1;
|
||||
u8_t version_number;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue