Bluetooth: Fix timeout configuration for AMP channels

When doing L2CAP configuration, use the correct retransmit and monitor
timeouts when configuring (or reconfiguring) AMP channels. The host
stack needs to use the timeouts read from incoming configuration
responses, and write appropriate timeouts when sending configuration
responses.

Change-Id: I1271ade93dbfee58a1cac115280704a327e901b2
Signed-off-by: Mat Martineau <mathewm@codeaurora.org>
This commit is contained in:
Mat Martineau
2011-12-12 10:45:31 -08:00
committed by Stephen Boyd
parent a0bfd58531
commit b3d0386b2f
3 changed files with 82 additions and 82 deletions

View File

@@ -294,8 +294,6 @@ struct l2cap_conf_ext_fs {
struct l2cap_conf_prm { struct l2cap_conf_prm {
__u8 fcs; __u8 fcs;
__le16 retrans_timeout;
__le16 monitor_timeout;
__le32 flush_to; __le32 flush_to;
}; };

View File

@@ -3212,6 +3212,44 @@ done:
return chan; return chan;
} }
static void l2cap_get_ertm_timeouts(struct l2cap_conf_rfc *rfc,
struct l2cap_pinfo *pi)
{
if (pi->amp_id && pi->ampcon) {
u64 ertm_to = pi->ampcon->hdev->amp_be_flush_to;
/* Class 1 devices have must have ERTM timeouts
* exceeding the Link Supervision Timeout. The
* default Link Supervision Timeout for AMP
* controllers is 10 seconds.
*
* Class 1 devices use 0xffffffff for their
* best-effort flush timeout, so the clamping logic
* will result in a timeout that meets the above
* requirement. ERTM timeouts are 16-bit values, so
* the maximum timeout is 65.535 seconds.
*/
/* Convert timeout to milliseconds and round */
ertm_to = div_u64(ertm_to + 999, 1000);
/* This is the recommended formula for class 2 devices
* that start ERTM timers when packets are sent to the
* controller.
*/
ertm_to = 3 * ertm_to + 500;
if (ertm_to > 0xffff)
ertm_to = 0xffff;
rfc->retrans_timeout = cpu_to_le16((u16) ertm_to);
rfc->monitor_timeout = rfc->retrans_timeout;
} else {
rfc->retrans_timeout = cpu_to_le16(L2CAP_DEFAULT_RETRANS_TO);
rfc->monitor_timeout = cpu_to_le16(L2CAP_DEFAULT_MONITOR_TO);
}
}
int l2cap_build_conf_req(struct sock *sk, void *data) int l2cap_build_conf_req(struct sock *sk, void *data)
{ {
struct l2cap_pinfo *pi = l2cap_pi(sk); struct l2cap_pinfo *pi = l2cap_pi(sk);
@@ -3263,9 +3301,9 @@ done:
else else
rfc.txwin_size = pi->tx_win; rfc.txwin_size = pi->tx_win;
rfc.max_transmit = pi->max_tx; rfc.max_transmit = pi->max_tx;
rfc.retrans_timeout = cpu_to_le16(L2CAP_DEFAULT_RETRANS_TO);
rfc.monitor_timeout = cpu_to_le16(L2CAP_DEFAULT_MONITOR_TO);
rfc.max_pdu_size = cpu_to_le16(L2CAP_DEFAULT_MAX_PDU_SIZE); rfc.max_pdu_size = cpu_to_le16(L2CAP_DEFAULT_MAX_PDU_SIZE);
l2cap_get_ertm_timeouts(&rfc, pi);
if (L2CAP_DEFAULT_MAX_PDU_SIZE > pi->imtu) if (L2CAP_DEFAULT_MAX_PDU_SIZE > pi->imtu)
rfc.max_pdu_size = cpu_to_le16(pi->imtu); rfc.max_pdu_size = cpu_to_le16(pi->imtu);
@@ -3337,30 +3375,16 @@ static int l2cap_build_amp_reconf_req(struct sock *sk, void *data)
struct l2cap_conf_req *req = data; struct l2cap_conf_req *req = data;
struct l2cap_conf_rfc rfc = { .mode = pi->mode }; struct l2cap_conf_rfc rfc = { .mode = pi->mode };
void *ptr = req->data; void *ptr = req->data;
u32 be_flush_to;
BT_DBG("sk %p", sk); BT_DBG("sk %p", sk);
/* convert to milliseconds, round up */
be_flush_to = (pi->conn->hcon->hdev->amp_be_flush_to + 999) / 1000;
switch (pi->mode) { switch (pi->mode) {
case L2CAP_MODE_ERTM: case L2CAP_MODE_ERTM:
rfc.mode = L2CAP_MODE_ERTM; rfc.mode = L2CAP_MODE_ERTM;
rfc.txwin_size = pi->tx_win; rfc.txwin_size = pi->tx_win;
rfc.max_transmit = pi->max_tx; rfc.max_transmit = pi->max_tx;
if (pi->amp_move_id) {
rfc.retrans_timeout =
cpu_to_le16((3 * be_flush_to) + 500);
rfc.monitor_timeout =
cpu_to_le16((3 * be_flush_to) + 500);
} else {
rfc.retrans_timeout =
cpu_to_le16(L2CAP_DEFAULT_RETRANS_TO);
rfc.monitor_timeout =
cpu_to_le16(L2CAP_DEFAULT_MONITOR_TO);
}
rfc.max_pdu_size = cpu_to_le16(L2CAP_DEFAULT_MAX_PDU_SIZE); rfc.max_pdu_size = cpu_to_le16(L2CAP_DEFAULT_MAX_PDU_SIZE);
l2cap_get_ertm_timeouts(&rfc, pi);
if (L2CAP_DEFAULT_MAX_PDU_SIZE > pi->imtu) if (L2CAP_DEFAULT_MAX_PDU_SIZE > pi->imtu)
rfc.max_pdu_size = cpu_to_le16(pi->imtu); rfc.max_pdu_size = cpu_to_le16(pi->imtu);
@@ -3374,16 +3398,15 @@ static int l2cap_build_amp_reconf_req(struct sock *sk, void *data)
(unsigned long) &rfc); (unsigned long) &rfc);
if (pi->conn->feat_mask & L2CAP_FEAT_FCS) { if (pi->conn->feat_mask & L2CAP_FEAT_FCS) {
/* TODO assign fcs for br/edr based on socket config option */ /* TODO assign fcs for br/edr based on socket config option */
if (pi->amp_move_id) /* FCS is not used with AMP because it is redundant - lower
* layers already include a checksum. */
if (pi->amp_id)
pi->local_conf.fcs = L2CAP_FCS_NONE; pi->local_conf.fcs = L2CAP_FCS_NONE;
else else
pi->local_conf.fcs = L2CAP_FCS_CRC16; pi->local_conf.fcs = L2CAP_FCS_CRC16;
l2cap_add_conf_opt(&ptr, L2CAP_CONF_FCS, 1, l2cap_add_conf_opt(&ptr, L2CAP_CONF_FCS, 1, pi->local_conf.fcs);
pi->local_conf.fcs);
pi->fcs = pi->local_conf.fcs | pi->remote_conf.fcs; pi->fcs = pi->local_conf.fcs | pi->remote_conf.fcs;
} }
@@ -3546,15 +3569,9 @@ done:
case L2CAP_MODE_ERTM: case L2CAP_MODE_ERTM:
if (!(pi->conf_state & L2CAP_CONF_EXT_WIN_RECV)) if (!(pi->conf_state & L2CAP_CONF_EXT_WIN_RECV))
pi->remote_tx_win = rfc.txwin_size; pi->remote_tx_win = rfc.txwin_size;
pi->remote_max_tx = rfc.max_transmit; pi->remote_max_tx = rfc.max_transmit;
pi->remote_mps = le16_to_cpu(rfc.max_pdu_size); pi->remote_mps = le16_to_cpu(rfc.max_pdu_size);
l2cap_get_ertm_timeouts(&rfc, pi);
rfc.retrans_timeout =
cpu_to_le16(L2CAP_DEFAULT_RETRANS_TO);
rfc.monitor_timeout =
cpu_to_le16(L2CAP_DEFAULT_MONITOR_TO);
pi->conf_state |= L2CAP_CONF_MODE_DONE; pi->conf_state |= L2CAP_CONF_MODE_DONE;
@@ -3660,9 +3677,6 @@ static int l2cap_parse_amp_move_reconf_req(struct sock *sk, void *data)
case L2CAP_CONF_RFC: case L2CAP_CONF_RFC:
if (olen == sizeof(rfc)) if (olen == sizeof(rfc))
memcpy(&rfc, (void *) val, olen); memcpy(&rfc, (void *) val, olen);
if (pi->mode != rfc.mode ||
rfc.mode == L2CAP_MODE_BASIC)
result = L2CAP_CONF_UNACCEPT;
break; break;
case L2CAP_CONF_FCS: case L2CAP_CONF_FCS:
@@ -3698,6 +3712,9 @@ static int l2cap_parse_amp_move_reconf_req(struct sock *sk, void *data)
BT_DBG("result 0x%2.2x cur mode 0x%2.2x req mode 0x%2.2x", BT_DBG("result 0x%2.2x cur mode 0x%2.2x req mode 0x%2.2x",
result, pi->mode, rfc.mode); result, pi->mode, rfc.mode);
if (pi->mode != rfc.mode || rfc.mode == L2CAP_MODE_BASIC)
result = L2CAP_CONF_UNACCEPT;
if (result == L2CAP_CONF_SUCCESS) { if (result == L2CAP_CONF_SUCCESS) {
/* Configure output options and let the other side know /* Configure output options and let the other side know
* which ones we don't like. */ * which ones we don't like. */
@@ -3717,38 +3734,26 @@ static int l2cap_parse_amp_move_reconf_req(struct sock *sk, void *data)
pi->remote_tx_win); pi->remote_tx_win);
} }
if (rfc.mode == L2CAP_MODE_ERTM) { pi->remote_mps = rfc.max_pdu_size;
pi->remote_conf.retrans_timeout =
le16_to_cpu(rfc.retrans_timeout);
pi->remote_conf.monitor_timeout =
le16_to_cpu(rfc.monitor_timeout);
BT_DBG("remote conf monitor timeout %d", if (rfc.mode == L2CAP_MODE_ERTM) {
pi->remote_conf.monitor_timeout); l2cap_get_ertm_timeouts(&rfc, pi);
} else {
rfc.retrans_timeout = 0;
rfc.monitor_timeout = 0;
}
l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC, l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC,
sizeof(rfc), (unsigned long) &rfc); sizeof(rfc), (unsigned long) &rfc);
} }
}
if (result != L2CAP_CONF_SUCCESS) if (result != L2CAP_CONF_SUCCESS)
goto done; goto done;
pi->fcs = pi->remote_conf.fcs | pi->local_conf.fcs ; pi->fcs = pi->remote_conf.fcs | pi->local_conf.fcs;
if (pi->rx_state == L2CAP_ERTM_RX_STATE_WAIT_F_FLAG) { if (pi->rx_state == L2CAP_ERTM_RX_STATE_WAIT_F_FLAG)
pi->flush_to = pi->remote_conf.flush_to; pi->flush_to = pi->remote_conf.flush_to;
pi->retrans_timeout = pi->remote_conf.retrans_timeout;
if (pi->amp_move_id)
pi->monitor_timeout = pi->remote_conf.monitor_timeout;
else
pi->monitor_timeout = L2CAP_DEFAULT_MONITOR_TO;
BT_DBG("mode %d monitor timeout %d",
pi->mode, pi->monitor_timeout);
}
done: done:
rsp->scid = cpu_to_le16(pi->dcid); rsp->scid = cpu_to_le16(pi->dcid);
@@ -3972,34 +3977,41 @@ static int l2cap_amp_move_reconf_rsp(struct sock *sk, void *rsp, int len,
if (type == L2CAP_CONF_RFC) { if (type == L2CAP_CONF_RFC) {
if (olen == sizeof(rfc)) if (olen == sizeof(rfc))
memcpy(&rfc, (void *)val, olen); memcpy(&rfc, (void *)val, olen);
if (rfc.mode != pi->mode &&
rfc.mode != L2CAP_MODE_ERTM) { if (rfc.mode != pi->mode) {
err = -ECONNREFUSED; l2cap_send_disconn_req(pi->conn, sk,
ECONNRESET);
return -ECONNRESET;
}
goto done; goto done;
} }
break;
}
} }
} }
BT_ERR("Expected RFC option was missing, using existing values");
rfc.mode = pi->mode;
rfc.retrans_timeout = cpu_to_le16(pi->retrans_timeout);
rfc.monitor_timeout = cpu_to_le16(pi->monitor_timeout);
done: done:
l2cap_ertm_stop_ack_timer(pi); l2cap_ertm_stop_ack_timer(pi);
l2cap_ertm_stop_retrans_timer(pi); l2cap_ertm_stop_retrans_timer(pi);
l2cap_ertm_stop_monitor_timer(pi); l2cap_ertm_stop_monitor_timer(pi);
pi->mps = le16_to_cpu(rfc.max_pdu_size);
if (pi->mode == L2CAP_MODE_ERTM) {
pi->retrans_timeout = le16_to_cpu(rfc.retrans_timeout);
pi->monitor_timeout = le16_to_cpu(rfc.monitor_timeout);
}
if (l2cap_pi(sk)->reconf_state == L2CAP_RECONF_ACC) { if (l2cap_pi(sk)->reconf_state == L2CAP_RECONF_ACC) {
l2cap_pi(sk)->reconf_state = L2CAP_RECONF_NONE; l2cap_pi(sk)->reconf_state = L2CAP_RECONF_NONE;
/* Respond to poll */ /* Respond to poll */
err = l2cap_answer_move_poll(sk); err = l2cap_answer_move_poll(sk);
} else if (l2cap_pi(sk)->reconf_state == L2CAP_RECONF_INT) { } else if (l2cap_pi(sk)->reconf_state == L2CAP_RECONF_INT) {
/* If moving to BR/EDR, use default timeout defined by
* the spec */
if (pi->amp_move_id == 0)
pi->monitor_timeout = L2CAP_DEFAULT_MONITOR_TO;
if (pi->mode == L2CAP_MODE_ERTM) { if (pi->mode == L2CAP_MODE_ERTM) {
l2cap_ertm_tx(sk, NULL, NULL, l2cap_ertm_tx(sk, NULL, NULL,
L2CAP_ERTM_EVENT_EXPLICIT_POLL); L2CAP_ERTM_EVENT_EXPLICIT_POLL);

View File

@@ -1,6 +1,6 @@
/* /*
BlueZ - Bluetooth protocol stack for Linux BlueZ - Bluetooth protocol stack for Linux
Copyright (C) 2000-2001 Qualcomm Incorporated Copyright (c) 2000-2001, 2011-2012 Code Aurora Forum. All rights reserved.
Copyright (C) 2009-2010 Gustavo F. Padovan <gustavo@padovan.org> Copyright (C) 2009-2010 Gustavo F. Padovan <gustavo@padovan.org>
Copyright (C) 2010 Google Inc. Copyright (C) 2010 Google Inc.
@@ -1123,8 +1123,6 @@ static void l2cap_sock_destruct(struct sock *sk)
static void set_default_config(struct l2cap_conf_prm *conf_prm) static void set_default_config(struct l2cap_conf_prm *conf_prm)
{ {
conf_prm->fcs = L2CAP_FCS_CRC16; conf_prm->fcs = L2CAP_FCS_CRC16;
conf_prm->retrans_timeout = 0;
conf_prm->monitor_timeout = 0;
conf_prm->flush_to = L2CAP_DEFAULT_FLUSH_TO; conf_prm->flush_to = L2CAP_DEFAULT_FLUSH_TO;
} }
@@ -1186,14 +1184,6 @@ void l2cap_sock_init(struct sock *sk, struct sock *parent)
pi->extended_control = 0; pi->extended_control = 0;
pi->local_conf.fcs = pi->fcs; pi->local_conf.fcs = pi->fcs;
if (pi->mode == L2CAP_MODE_BASIC) {
pi->local_conf.retrans_timeout = 0;
pi->local_conf.monitor_timeout = 0;
} else {
pi->local_conf.retrans_timeout = L2CAP_DEFAULT_RETRANS_TO;
pi->local_conf.monitor_timeout = L2CAP_DEFAULT_MONITOR_TO;
}
pi->local_conf.flush_to = pi->flush_to; pi->local_conf.flush_to = pi->flush_to;
set_default_config(&pi->remote_conf); set_default_config(&pi->remote_conf);