Bluetooth: Fix ACL MTU issue
[pandora-kernel.git] / net / bluetooth / l2cap.c
index 43e17f7..99cf177 100644 (file)
@@ -1002,7 +1002,8 @@ static int l2cap_sock_connect(struct socket *sock, struct sockaddr *addr, int al
 
        BT_DBG("sk %p", sk);
 
-       if (!addr || addr->sa_family != AF_BLUETOOTH)
+       if (!addr || alen < sizeof(addr->sa_family) ||
+           addr->sa_family != AF_BLUETOOTH)
                return -EINVAL;
 
        memset(&la, 0, sizeof(la));
@@ -1146,7 +1147,7 @@ static int l2cap_sock_accept(struct socket *sock, struct socket *newsock, int fl
        BT_DBG("sk %p timeo %ld", sk, timeo);
 
        /* Wait for an incoming connection. (wake-one). */
-       add_wait_queue_exclusive(sk->sk_sleep, &wait);
+       add_wait_queue_exclusive(sk_sleep(sk), &wait);
        while (!(nsk = bt_accept_dequeue(sk, newsock))) {
                set_current_state(TASK_INTERRUPTIBLE);
                if (!timeo) {
@@ -1169,7 +1170,7 @@ static int l2cap_sock_accept(struct socket *sock, struct socket *newsock, int fl
                }
        }
        set_current_state(TASK_RUNNING);
-       remove_wait_queue(sk->sk_sleep, &wait);
+       remove_wait_queue(sk_sleep(sk), &wait);
 
        if (err)
                goto done;
@@ -1610,11 +1611,6 @@ static int l2cap_sock_sendmsg(struct kiocb *iocb, struct socket *sock, struct ms
        if (msg->msg_flags & MSG_OOB)
                return -EOPNOTSUPP;
 
-       /* Check outgoing MTU */
-       if (sk->sk_type == SOCK_SEQPACKET && pi->mode == L2CAP_MODE_BASIC &&
-           len > pi->omtu)
-               return -EINVAL;
-
        lock_sock(sk);
 
        if (sk->sk_state != BT_CONNECTED) {
@@ -1625,12 +1621,21 @@ static int l2cap_sock_sendmsg(struct kiocb *iocb, struct socket *sock, struct ms
        /* Connectionless channel */
        if (sk->sk_type == SOCK_DGRAM) {
                skb = l2cap_create_connless_pdu(sk, msg, len);
-               err = l2cap_do_send(sk, skb);
+               if (IS_ERR(skb))
+                       err = PTR_ERR(skb);
+               else
+                       err = l2cap_do_send(sk, skb);
                goto done;
        }
 
        switch (pi->mode) {
        case L2CAP_MODE_BASIC:
+               /* Check outgoing MTU */
+               if (len > pi->omtu) {
+                       err = -EINVAL;
+                       goto done;
+               }
+
                /* Create a basic PDU */
                skb = l2cap_create_basic_pdu(sk, msg, len);
                if (IS_ERR(skb)) {
@@ -2262,6 +2267,8 @@ done:
                rfc.retrans_timeout = 0;
                rfc.monitor_timeout = 0;
                rfc.max_pdu_size    = cpu_to_le16(L2CAP_DEFAULT_MAX_PDU_SIZE);
+               if (L2CAP_DEFAULT_MAX_PDU_SIZE > pi->conn->mtu - 10)
+                       rfc.max_pdu_size = pi->conn->mtu - 10;
 
                l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC,
                                        sizeof(rfc), (unsigned long) &rfc);
@@ -2283,6 +2290,8 @@ done:
                rfc.retrans_timeout = 0;
                rfc.monitor_timeout = 0;
                rfc.max_pdu_size    = cpu_to_le16(L2CAP_DEFAULT_MAX_PDU_SIZE);
+               if (L2CAP_DEFAULT_MAX_PDU_SIZE > pi->conn->mtu - 10)
+                       rfc.max_pdu_size = pi->conn->mtu - 10;
 
                l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC,
                                        sizeof(rfc), (unsigned long) &rfc);
@@ -2832,6 +2841,11 @@ static inline int l2cap_config_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hdr
                        int len = cmd->len - sizeof(*rsp);
                        char req[64];
 
+                       if (len > sizeof(req) - sizeof(struct l2cap_conf_req)) {
+                               l2cap_send_disconn_req(conn, sk);
+                               goto done;
+                       }
+
                        /* throw out any old stored conf requests */
                        result = L2CAP_CONF_SUCCESS;
                        len = l2cap_parse_conf_rsp(sk, rsp->data,
@@ -3512,6 +3526,7 @@ static inline int l2cap_data_channel_sframe(struct sock *sk, u16 rx_control, str
                break;
        }
 
+       kfree_skb(skb);
        return 0;
 }
 
@@ -3600,7 +3615,7 @@ static inline int l2cap_data_channel(struct l2cap_conn *conn, u16 cid, struct sk
                if (pi->expected_tx_seq == tx_seq)
                        pi->expected_tx_seq = (pi->expected_tx_seq + 1) % 64;
                else
-                       pi->expected_tx_seq = tx_seq + 1;
+                       pi->expected_tx_seq = (tx_seq + 1) % 64;
 
                l2cap_sar_reassembly_sdu(sk, skb, control);