|
@@ -54,6 +54,7 @@
|
|
#define VERSION "2.14"
|
|
#define VERSION "2.14"
|
|
|
|
|
|
static int enable_ertm = 0;
|
|
static int enable_ertm = 0;
|
|
|
|
+static int max_transmit = L2CAP_DEFAULT_MAX_TX;
|
|
|
|
|
|
static u32 l2cap_feat_mask = L2CAP_FEAT_FIXED_CHAN;
|
|
static u32 l2cap_feat_mask = L2CAP_FEAT_FIXED_CHAN;
|
|
static u8 l2cap_fixed_chan[8] = { 0x02, };
|
|
static u8 l2cap_fixed_chan[8] = { 0x02, };
|
|
@@ -373,6 +374,8 @@ static inline int l2cap_send_rr_or_rnr(struct l2cap_pinfo *pi, u16 control)
|
|
else
|
|
else
|
|
control |= L2CAP_SUPER_RCV_READY;
|
|
control |= L2CAP_SUPER_RCV_READY;
|
|
|
|
|
|
|
|
+ control |= pi->buffer_seq << L2CAP_CTRL_REQSEQ_SHIFT;
|
|
|
|
+
|
|
return l2cap_send_sframe(pi, control);
|
|
return l2cap_send_sframe(pi, control);
|
|
}
|
|
}
|
|
|
|
|
|
@@ -1333,7 +1336,7 @@ static int l2cap_retransmit_frame(struct sock *sk, u8 tx_seq)
|
|
tx_skb = skb_clone(skb, GFP_ATOMIC);
|
|
tx_skb = skb_clone(skb, GFP_ATOMIC);
|
|
bt_cb(skb)->retries++;
|
|
bt_cb(skb)->retries++;
|
|
control = get_unaligned_le16(tx_skb->data + L2CAP_HDR_SIZE);
|
|
control = get_unaligned_le16(tx_skb->data + L2CAP_HDR_SIZE);
|
|
- control |= (pi->req_seq << L2CAP_CTRL_REQSEQ_SHIFT)
|
|
|
|
|
|
+ control |= (pi->buffer_seq << L2CAP_CTRL_REQSEQ_SHIFT)
|
|
| (tx_seq << L2CAP_CTRL_TXSEQ_SHIFT);
|
|
| (tx_seq << L2CAP_CTRL_TXSEQ_SHIFT);
|
|
put_unaligned_le16(control, tx_skb->data + L2CAP_HDR_SIZE);
|
|
put_unaligned_le16(control, tx_skb->data + L2CAP_HDR_SIZE);
|
|
|
|
|
|
@@ -1375,7 +1378,7 @@ static int l2cap_ertm_send(struct sock *sk)
|
|
bt_cb(skb)->retries++;
|
|
bt_cb(skb)->retries++;
|
|
|
|
|
|
control = get_unaligned_le16(tx_skb->data + L2CAP_HDR_SIZE);
|
|
control = get_unaligned_le16(tx_skb->data + L2CAP_HDR_SIZE);
|
|
- control |= (pi->req_seq << L2CAP_CTRL_REQSEQ_SHIFT)
|
|
|
|
|
|
+ control |= (pi->buffer_seq << L2CAP_CTRL_REQSEQ_SHIFT)
|
|
| (pi->next_tx_seq << L2CAP_CTRL_TXSEQ_SHIFT);
|
|
| (pi->next_tx_seq << L2CAP_CTRL_TXSEQ_SHIFT);
|
|
put_unaligned_le16(control, tx_skb->data + L2CAP_HDR_SIZE);
|
|
put_unaligned_le16(control, tx_skb->data + L2CAP_HDR_SIZE);
|
|
|
|
|
|
@@ -2173,6 +2176,21 @@ static void l2cap_add_conf_opt(void **ptr, u8 type, u8 len, unsigned long val)
|
|
*ptr += L2CAP_CONF_OPT_SIZE + len;
|
|
*ptr += L2CAP_CONF_OPT_SIZE + len;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+static inline void l2cap_ertm_init(struct sock *sk)
|
|
|
|
+{
|
|
|
|
+ l2cap_pi(sk)->expected_ack_seq = 0;
|
|
|
|
+ l2cap_pi(sk)->unacked_frames = 0;
|
|
|
|
+ l2cap_pi(sk)->buffer_seq = 0;
|
|
|
|
+ l2cap_pi(sk)->num_to_ack = 0;
|
|
|
|
+
|
|
|
|
+ setup_timer(&l2cap_pi(sk)->retrans_timer,
|
|
|
|
+ l2cap_retrans_timeout, (unsigned long) sk);
|
|
|
|
+ setup_timer(&l2cap_pi(sk)->monitor_timer,
|
|
|
|
+ l2cap_monitor_timeout, (unsigned long) sk);
|
|
|
|
+
|
|
|
|
+ __skb_queue_head_init(SREJ_QUEUE(sk));
|
|
|
|
+}
|
|
|
|
+
|
|
static int l2cap_mode_supported(__u8 mode, __u32 feat_mask)
|
|
static int l2cap_mode_supported(__u8 mode, __u32 feat_mask)
|
|
{
|
|
{
|
|
u32 local_feat_mask = l2cap_feat_mask;
|
|
u32 local_feat_mask = l2cap_feat_mask;
|
|
@@ -2236,7 +2254,7 @@ done:
|
|
case L2CAP_MODE_ERTM:
|
|
case L2CAP_MODE_ERTM:
|
|
rfc.mode = L2CAP_MODE_ERTM;
|
|
rfc.mode = L2CAP_MODE_ERTM;
|
|
rfc.txwin_size = L2CAP_DEFAULT_TX_WINDOW;
|
|
rfc.txwin_size = L2CAP_DEFAULT_TX_WINDOW;
|
|
- rfc.max_transmit = L2CAP_DEFAULT_MAX_TX;
|
|
|
|
|
|
+ rfc.max_transmit = max_transmit;
|
|
rfc.retrans_timeout = 0;
|
|
rfc.retrans_timeout = 0;
|
|
rfc.monitor_timeout = 0;
|
|
rfc.monitor_timeout = 0;
|
|
rfc.max_pdu_size = cpu_to_le16(L2CAP_DEFAULT_MAX_PDU_SIZE);
|
|
rfc.max_pdu_size = cpu_to_le16(L2CAP_DEFAULT_MAX_PDU_SIZE);
|
|
@@ -2761,17 +2779,13 @@ static inline int l2cap_config_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr
|
|
l2cap_pi(sk)->fcs = L2CAP_FCS_CRC16;
|
|
l2cap_pi(sk)->fcs = L2CAP_FCS_CRC16;
|
|
|
|
|
|
sk->sk_state = BT_CONNECTED;
|
|
sk->sk_state = BT_CONNECTED;
|
|
- l2cap_pi(sk)->next_tx_seq = 0;
|
|
|
|
- l2cap_pi(sk)->expected_ack_seq = 0;
|
|
|
|
- l2cap_pi(sk)->unacked_frames = 0;
|
|
|
|
-
|
|
|
|
- setup_timer(&l2cap_pi(sk)->retrans_timer,
|
|
|
|
- l2cap_retrans_timeout, (unsigned long) sk);
|
|
|
|
- setup_timer(&l2cap_pi(sk)->monitor_timer,
|
|
|
|
- l2cap_monitor_timeout, (unsigned long) sk);
|
|
|
|
|
|
|
|
|
|
+ l2cap_pi(sk)->next_tx_seq = 0;
|
|
|
|
+ l2cap_pi(sk)->expected_tx_seq = 0;
|
|
__skb_queue_head_init(TX_QUEUE(sk));
|
|
__skb_queue_head_init(TX_QUEUE(sk));
|
|
- __skb_queue_head_init(SREJ_QUEUE(sk));
|
|
|
|
|
|
+ if (l2cap_pi(sk)->mode == L2CAP_MODE_ERTM)
|
|
|
|
+ l2cap_ertm_init(sk);
|
|
|
|
+
|
|
l2cap_chan_ready(sk);
|
|
l2cap_chan_ready(sk);
|
|
goto unlock;
|
|
goto unlock;
|
|
}
|
|
}
|
|
@@ -2850,11 +2864,12 @@ static inline int l2cap_config_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hdr
|
|
l2cap_pi(sk)->fcs = L2CAP_FCS_CRC16;
|
|
l2cap_pi(sk)->fcs = L2CAP_FCS_CRC16;
|
|
|
|
|
|
sk->sk_state = BT_CONNECTED;
|
|
sk->sk_state = BT_CONNECTED;
|
|
|
|
+ l2cap_pi(sk)->next_tx_seq = 0;
|
|
l2cap_pi(sk)->expected_tx_seq = 0;
|
|
l2cap_pi(sk)->expected_tx_seq = 0;
|
|
- l2cap_pi(sk)->buffer_seq = 0;
|
|
|
|
- l2cap_pi(sk)->num_to_ack = 0;
|
|
|
|
__skb_queue_head_init(TX_QUEUE(sk));
|
|
__skb_queue_head_init(TX_QUEUE(sk));
|
|
- __skb_queue_head_init(SREJ_QUEUE(sk));
|
|
|
|
|
|
+ if (l2cap_pi(sk)->mode == L2CAP_MODE_ERTM)
|
|
|
|
+ l2cap_ertm_init(sk);
|
|
|
|
+
|
|
l2cap_chan_ready(sk);
|
|
l2cap_chan_ready(sk);
|
|
}
|
|
}
|
|
|
|
|
|
@@ -2886,9 +2901,12 @@ static inline int l2cap_disconnect_req(struct l2cap_conn *conn, struct l2cap_cmd
|
|
sk->sk_shutdown = SHUTDOWN_MASK;
|
|
sk->sk_shutdown = SHUTDOWN_MASK;
|
|
|
|
|
|
skb_queue_purge(TX_QUEUE(sk));
|
|
skb_queue_purge(TX_QUEUE(sk));
|
|
- skb_queue_purge(SREJ_QUEUE(sk));
|
|
|
|
- del_timer(&l2cap_pi(sk)->retrans_timer);
|
|
|
|
- del_timer(&l2cap_pi(sk)->monitor_timer);
|
|
|
|
|
|
+
|
|
|
|
+ if (l2cap_pi(sk)->mode == L2CAP_MODE_ERTM) {
|
|
|
|
+ skb_queue_purge(SREJ_QUEUE(sk));
|
|
|
|
+ del_timer(&l2cap_pi(sk)->retrans_timer);
|
|
|
|
+ del_timer(&l2cap_pi(sk)->monitor_timer);
|
|
|
|
+ }
|
|
|
|
|
|
l2cap_chan_del(sk, ECONNRESET);
|
|
l2cap_chan_del(sk, ECONNRESET);
|
|
bh_unlock_sock(sk);
|
|
bh_unlock_sock(sk);
|
|
@@ -2913,9 +2931,12 @@ static inline int l2cap_disconnect_rsp(struct l2cap_conn *conn, struct l2cap_cmd
|
|
return 0;
|
|
return 0;
|
|
|
|
|
|
skb_queue_purge(TX_QUEUE(sk));
|
|
skb_queue_purge(TX_QUEUE(sk));
|
|
- skb_queue_purge(SREJ_QUEUE(sk));
|
|
|
|
- del_timer(&l2cap_pi(sk)->retrans_timer);
|
|
|
|
- del_timer(&l2cap_pi(sk)->monitor_timer);
|
|
|
|
|
|
+
|
|
|
|
+ if (l2cap_pi(sk)->mode == L2CAP_MODE_ERTM) {
|
|
|
|
+ skb_queue_purge(SREJ_QUEUE(sk));
|
|
|
|
+ del_timer(&l2cap_pi(sk)->retrans_timer);
|
|
|
|
+ del_timer(&l2cap_pi(sk)->monitor_timer);
|
|
|
|
+ }
|
|
|
|
|
|
l2cap_chan_del(sk, 0);
|
|
l2cap_chan_del(sk, 0);
|
|
bh_unlock_sock(sk);
|
|
bh_unlock_sock(sk);
|
|
@@ -3280,12 +3301,16 @@ static inline int l2cap_data_channel_iframe(struct sock *sk, u16 rx_control, str
|
|
{
|
|
{
|
|
struct l2cap_pinfo *pi = l2cap_pi(sk);
|
|
struct l2cap_pinfo *pi = l2cap_pi(sk);
|
|
u8 tx_seq = __get_txseq(rx_control);
|
|
u8 tx_seq = __get_txseq(rx_control);
|
|
|
|
+ u8 req_seq = __get_reqseq(rx_control);
|
|
u16 tx_control = 0;
|
|
u16 tx_control = 0;
|
|
u8 sar = rx_control >> L2CAP_CTRL_SAR_SHIFT;
|
|
u8 sar = rx_control >> L2CAP_CTRL_SAR_SHIFT;
|
|
int err = 0;
|
|
int err = 0;
|
|
|
|
|
|
BT_DBG("sk %p rx_control 0x%4.4x len %d", sk, rx_control, skb->len);
|
|
BT_DBG("sk %p rx_control 0x%4.4x len %d", sk, rx_control, skb->len);
|
|
|
|
|
|
|
|
+ pi->expected_ack_seq = req_seq;
|
|
|
|
+ l2cap_drop_acked_frames(sk);
|
|
|
|
+
|
|
if (tx_seq == pi->expected_tx_seq)
|
|
if (tx_seq == pi->expected_tx_seq)
|
|
goto expected;
|
|
goto expected;
|
|
|
|
|
|
@@ -3340,6 +3365,16 @@ expected:
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ if (rx_control & L2CAP_CTRL_FINAL) {
|
|
|
|
+ if (pi->conn_state & L2CAP_CONN_REJ_ACT)
|
|
|
|
+ pi->conn_state &= ~L2CAP_CONN_REJ_ACT;
|
|
|
|
+ else {
|
|
|
|
+ sk->sk_send_head = TX_QUEUE(sk)->next;
|
|
|
|
+ pi->next_tx_seq = pi->expected_ack_seq;
|
|
|
|
+ l2cap_ertm_send(sk);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
pi->buffer_seq = (pi->buffer_seq + 1) % 64;
|
|
pi->buffer_seq = (pi->buffer_seq + 1) % 64;
|
|
|
|
|
|
err = l2cap_sar_reassembly_sdu(sk, skb, rx_control);
|
|
err = l2cap_sar_reassembly_sdu(sk, skb, rx_control);
|
|
@@ -3376,6 +3411,14 @@ static inline int l2cap_data_channel_sframe(struct sock *sk, u16 rx_control, str
|
|
pi->expected_ack_seq = tx_seq;
|
|
pi->expected_ack_seq = tx_seq;
|
|
l2cap_drop_acked_frames(sk);
|
|
l2cap_drop_acked_frames(sk);
|
|
|
|
|
|
|
|
+ if (pi->conn_state & L2CAP_CONN_REJ_ACT)
|
|
|
|
+ pi->conn_state &= ~L2CAP_CONN_REJ_ACT;
|
|
|
|
+ else {
|
|
|
|
+ sk->sk_send_head = TX_QUEUE(sk)->next;
|
|
|
|
+ pi->next_tx_seq = pi->expected_ack_seq;
|
|
|
|
+ l2cap_ertm_send(sk);
|
|
|
|
+ }
|
|
|
|
+
|
|
if (!(pi->conn_state & L2CAP_CONN_WAIT_F))
|
|
if (!(pi->conn_state & L2CAP_CONN_WAIT_F))
|
|
break;
|
|
break;
|
|
|
|
|
|
@@ -3403,10 +3446,24 @@ static inline int l2cap_data_channel_sframe(struct sock *sk, u16 rx_control, str
|
|
pi->expected_ack_seq = __get_reqseq(rx_control);
|
|
pi->expected_ack_seq = __get_reqseq(rx_control);
|
|
l2cap_drop_acked_frames(sk);
|
|
l2cap_drop_acked_frames(sk);
|
|
|
|
|
|
- sk->sk_send_head = TX_QUEUE(sk)->next;
|
|
|
|
- pi->next_tx_seq = pi->expected_ack_seq;
|
|
|
|
|
|
+ if (rx_control & L2CAP_CTRL_FINAL) {
|
|
|
|
+ if (pi->conn_state & L2CAP_CONN_REJ_ACT)
|
|
|
|
+ pi->conn_state &= ~L2CAP_CONN_REJ_ACT;
|
|
|
|
+ else {
|
|
|
|
+ sk->sk_send_head = TX_QUEUE(sk)->next;
|
|
|
|
+ pi->next_tx_seq = pi->expected_ack_seq;
|
|
|
|
+ l2cap_ertm_send(sk);
|
|
|
|
+ }
|
|
|
|
+ } else {
|
|
|
|
+ sk->sk_send_head = TX_QUEUE(sk)->next;
|
|
|
|
+ pi->next_tx_seq = pi->expected_ack_seq;
|
|
|
|
+ l2cap_ertm_send(sk);
|
|
|
|
|
|
- l2cap_ertm_send(sk);
|
|
|
|
|
|
+ if (pi->conn_state & L2CAP_CONN_WAIT_F) {
|
|
|
|
+ pi->srej_save_reqseq = tx_seq;
|
|
|
|
+ pi->conn_state |= L2CAP_CONN_REJ_ACT;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
|
|
break;
|
|
break;
|
|
|
|
|
|
@@ -3425,7 +3482,7 @@ static inline int l2cap_data_channel_sframe(struct sock *sk, u16 rx_control, str
|
|
} else if (rx_control & L2CAP_CTRL_FINAL) {
|
|
} else if (rx_control & L2CAP_CTRL_FINAL) {
|
|
if ((pi->conn_state & L2CAP_CONN_SREJ_ACT) &&
|
|
if ((pi->conn_state & L2CAP_CONN_SREJ_ACT) &&
|
|
pi->srej_save_reqseq == tx_seq)
|
|
pi->srej_save_reqseq == tx_seq)
|
|
- pi->srej_save_reqseq &= ~L2CAP_CONN_SREJ_ACT;
|
|
|
|
|
|
+ pi->conn_state &= ~L2CAP_CONN_SREJ_ACT;
|
|
else
|
|
else
|
|
l2cap_retransmit_frame(sk, tx_seq);
|
|
l2cap_retransmit_frame(sk, tx_seq);
|
|
}
|
|
}
|
|
@@ -4004,6 +4061,9 @@ module_exit(l2cap_exit);
|
|
module_param(enable_ertm, bool, 0644);
|
|
module_param(enable_ertm, bool, 0644);
|
|
MODULE_PARM_DESC(enable_ertm, "Enable enhanced retransmission mode");
|
|
MODULE_PARM_DESC(enable_ertm, "Enable enhanced retransmission mode");
|
|
|
|
|
|
|
|
+module_param(max_transmit, uint, 0644);
|
|
|
|
+MODULE_PARM_DESC(max_transmit, "Max transmit value (default = 3)");
|
|
|
|
+
|
|
MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
|
|
MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
|
|
MODULE_DESCRIPTION("Bluetooth L2CAP ver " VERSION);
|
|
MODULE_DESCRIPTION("Bluetooth L2CAP ver " VERSION);
|
|
MODULE_VERSION(VERSION);
|
|
MODULE_VERSION(VERSION);
|