]> Pileus Git - ~andy/linux/blobdiff - net/bluetooth/l2cap.c
Bluetooth: Acknowledge L2CAP packets when receiving RR-frames (F-bit=1)
[~andy/linux] / net / bluetooth / l2cap.c
index 70aff921db8c0475e4a83d31c8d18b27652c271d..0a36c61c011fc7c1d4c774053f5a0696b89a215c 100644 (file)
@@ -1192,6 +1192,7 @@ static void l2cap_monitor_timeout(unsigned long arg)
        struct sock *sk = (void *) arg;
        u16 control;
 
+       bh_lock_sock(sk);
        if (l2cap_pi(sk)->retry_count >= l2cap_pi(sk)->remote_max_tx) {
                l2cap_send_disconn_req(l2cap_pi(sk)->conn, sk);
                return;
@@ -1203,6 +1204,7 @@ static void l2cap_monitor_timeout(unsigned long arg)
        control = L2CAP_CTRL_POLL;
        control |= L2CAP_SUPER_RCV_READY;
        l2cap_send_sframe(l2cap_pi(sk), control);
+       bh_unlock_sock(sk);
 }
 
 static void l2cap_retrans_timeout(unsigned long arg)
@@ -1210,6 +1212,7 @@ static void l2cap_retrans_timeout(unsigned long arg)
        struct sock *sk = (void *) arg;
        u16 control;
 
+       bh_lock_sock(sk);
        l2cap_pi(sk)->retry_count = 1;
        __mod_monitor_timer();
 
@@ -1218,6 +1221,7 @@ static void l2cap_retrans_timeout(unsigned long arg)
        control = L2CAP_CTRL_POLL;
        control |= L2CAP_SUPER_RCV_READY;
        l2cap_send_sframe(l2cap_pi(sk), control);
+       bh_unlock_sock(sk);
 }
 
 static void l2cap_drop_acked_frames(struct sock *sk)
@@ -2920,7 +2924,7 @@ static inline int l2cap_information_req(struct l2cap_conn *conn, struct l2cap_cm
                if (enable_ertm)
                        feat_mask |= L2CAP_FEAT_ERTM | L2CAP_FEAT_STREAMING
                                                         | L2CAP_FEAT_FCS;
-               put_unaligned(cpu_to_le32(feat_mask), (__le32 *) rsp->data);
+               put_unaligned_le32(feat_mask, rsp->data);
                l2cap_send_cmd(conn, cmd->ident,
                                        L2CAP_INFO_RSP, sizeof(buf), buf);
        } else if (type == L2CAP_IT_FIXED_CHAN) {
@@ -3241,6 +3245,10 @@ static void l2cap_send_srejframe(struct sock *sk, u8 tx_seq)
        while (tx_seq != pi->expected_tx_seq) {
                control = L2CAP_SUPER_SELECT_REJECT;
                control |= pi->expected_tx_seq << L2CAP_CTRL_REQSEQ_SHIFT;
+               if (pi->conn_state & L2CAP_CONN_SEND_PBIT) {
+                       control |= L2CAP_CTRL_POLL;
+                       pi->conn_state &= ~L2CAP_CONN_SEND_PBIT;
+               }
                l2cap_send_sframe(pi, control);
 
                new = kzalloc(sizeof(struct srej_list), GFP_ATOMIC);
@@ -3300,6 +3308,8 @@ static inline int l2cap_data_channel_iframe(struct sock *sk, u16 rx_control, str
                __skb_queue_head_init(SREJ_QUEUE(sk));
                l2cap_add_to_srej_queue(sk, skb, tx_seq, sar);
 
+               pi->conn_state |= L2CAP_CONN_SEND_PBIT;
+
                l2cap_send_srejframe(sk, tx_seq);
        }
        return 0;
@@ -3338,9 +3348,13 @@ static inline int l2cap_data_channel_sframe(struct sock *sk, u16 rx_control, str
        case L2CAP_SUPER_RCV_READY:
                if (rx_control & L2CAP_CTRL_POLL) {
                        u16 control = L2CAP_CTRL_FINAL;
-                       control |= L2CAP_SUPER_RCV_READY;
+                       control |= L2CAP_SUPER_RCV_READY |
+                               (pi->buffer_seq << L2CAP_CTRL_REQSEQ_SHIFT);
                        l2cap_send_sframe(l2cap_pi(sk), control);
                } else if (rx_control & L2CAP_CTRL_FINAL) {
+                       pi->expected_ack_seq = tx_seq;
+                       l2cap_drop_acked_frames(sk);
+
                        if (!(pi->conn_state & L2CAP_CONN_WAIT_F))
                                break;
 
@@ -3370,7 +3384,29 @@ static inline int l2cap_data_channel_sframe(struct sock *sk, u16 rx_control, str
                break;
 
        case L2CAP_SUPER_SELECT_REJECT:
-               l2cap_retransmit_frame(sk, tx_seq);
+               if (rx_control & L2CAP_CTRL_POLL) {
+                       l2cap_retransmit_frame(sk, tx_seq);
+                       pi->expected_ack_seq = tx_seq;
+                       l2cap_drop_acked_frames(sk);
+                       l2cap_ertm_send(sk);
+                       if (pi->conn_state & L2CAP_CONN_WAIT_F) {
+                               pi->srej_save_reqseq = tx_seq;
+                               pi->conn_state |= L2CAP_CONN_SREJ_ACT;
+                       }
+               } else if (rx_control & L2CAP_CTRL_FINAL) {
+                       if ((pi->conn_state & L2CAP_CONN_SREJ_ACT) &&
+                                       pi->srej_save_reqseq == tx_seq)
+                               pi->srej_save_reqseq &= ~L2CAP_CONN_SREJ_ACT;
+                       else
+                               l2cap_retransmit_frame(sk, tx_seq);
+               }
+               else {
+                       l2cap_retransmit_frame(sk, tx_seq);
+                       if (pi->conn_state & L2CAP_CONN_WAIT_F) {
+                               pi->srej_save_reqseq = tx_seq;
+                               pi->conn_state |= L2CAP_CONN_SREJ_ACT;
+                       }
+               }
                break;
 
        case L2CAP_SUPER_RCV_NOT_READY:
@@ -3540,7 +3576,7 @@ static void l2cap_recv_frame(struct l2cap_conn *conn, struct sk_buff *skb)
                break;
 
        case L2CAP_CID_CONN_LESS:
-               psm = get_unaligned((__le16 *) skb->data);
+               psm = get_unaligned_le16(skb->data);
                skb_pull(skb, 2);
                l2cap_conless_channel(conn, psm, skb);
                break;