Bluetooth: controller: Fix assert on different transaction collision

Updated the implementation of Connection Update Procedure to
not assert when peer master violates the Bluetooth
Specification v5.0 Vol.6 Part B Section 5.3 Procedure
Collisions. Instead disconnect the link with reason
Different Transaction Collision (0x2A).

Certain phones in the market perform Connection Update
Procedure and do not correctly handle remote initiated
colliding PHY update procedures. They try to perform both
the transactions involving an instant simultaneously
violating the Bluetooth Specifications.

Implementation in Zephyr is updated to gracefully handle
the violating remote master device, and not fatally assert
in the local device.

Relates to commit 8b3fd6963c ("Bluetooth: controller: Fix
assert on different transaction collision")

Signed-off-by: Vinayak Kariappa Chettimada <vich@nordicsemi.no>
This commit is contained in:
Vinayak Kariappa Chettimada 2018-08-17 10:12:28 +02:00 committed by Johan Hedberg
commit 94d7669cf3

View file

@ -293,7 +293,7 @@ static void pdu_node_tx_release(u16_t handle,
struct radio_pdu_node_tx *node_tx);
static void connection_release(struct connection *conn);
static void terminate_ind_rx_enqueue(struct connection *conn, u8_t reason);
static u32_t conn_update(struct connection *conn, struct pdu_data *pdu_data_rx);
static u8_t conn_update(struct connection *conn, struct pdu_data *pdu_data_rx);
#if defined(CONFIG_BT_CTLR_XTAL_ADVANCED) && \
defined(CONFIG_BT_CTLR_SCHED_ADVANCED)
@ -2427,19 +2427,24 @@ isr_rx_conn_pkt_ctrl(struct radio_pdu_node_rx *node_rx, u8_t *rx_enqueue)
pdu_data_rx = (void *)node_rx->pdu_data;
switch (pdu_data_rx->llctrl.opcode) {
case PDU_DATA_LLCTRL_TYPE_CONN_UPDATE_IND:
{
u8_t err;
if (!_radio.conn_curr->role ||
!pdu_len_cmp(PDU_DATA_LLCTRL_TYPE_CONN_UPDATE_IND,
pdu_data_rx->len)) {
goto isr_rx_conn_unknown_rsp_send;
}
if (conn_update(_radio.conn_curr, pdu_data_rx) == 0) {
err = conn_update(_radio.conn_curr, pdu_data_rx);
if (err) {
_radio.conn_curr->llcp_terminate.reason_peer = err;
} else {
/* conn param req procedure, if any, is complete */
_radio.conn_curr->procedure_expire = 0;
} else {
_radio.conn_curr->llcp_terminate.reason_peer = 0x28;
}
break;
}
break;
case PDU_DATA_LLCTRL_TYPE_CHAN_MAP_IND:
if (!_radio.conn_curr->role ||
@ -9151,14 +9156,17 @@ static void terminate_ind_rx_enqueue(struct connection *conn, u8_t reason)
packet_rx_callback();
}
static u32_t conn_update(struct connection *conn, struct pdu_data *pdu_data_rx)
static u8_t conn_update(struct connection *conn, struct pdu_data *pdu_data_rx)
{
if (((pdu_data_rx->llctrl.conn_update_ind.instant -
conn->event_counter) & 0xFFFF) > 0x7FFF) {
return 1;
return 0x28;
}
LL_ASSERT(conn->llcp_req == conn->llcp_ack);
/* different transaction collision */
if (conn->llcp_req != conn->llcp_ack) {
return 0x2a;
}
/* set mutex, if only not already set. As a master the mutex shall
* be set, but a slave we accept it as new 'set' of mutex.