diff options
author | John W. Linville <linville@tuxdriver.com> | 2011-10-04 20:06:47 +0200 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2011-10-04 20:06:47 +0200 |
commit | d6222fb0d669307a49e2a96aad86c156a9bb9551 (patch) | |
tree | 99c821e5823999b99e7fd49fb840d18cbb89b238 | |
parent | ath9k_hw: set pci_express capability true for AR9480 (diff) | |
parent | btusb: add device entry for Broadcom SoftSailing (diff) | |
download | linux-d6222fb0d669307a49e2a96aad86c156a9bb9551.tar.xz linux-d6222fb0d669307a49e2a96aad86c156a9bb9551.zip |
Merge branch 'master' of git://github.com/padovan/bluetooth-next
-rw-r--r-- | drivers/bluetooth/btusb.c | 6 | ||||
-rw-r--r-- | include/net/bluetooth/l2cap.h | 3 | ||||
-rw-r--r-- | net/bluetooth/af_bluetooth.c | 30 | ||||
-rw-r--r-- | net/bluetooth/bnep/core.c | 5 | ||||
-rw-r--r-- | net/bluetooth/cmtp/core.c | 5 | ||||
-rw-r--r-- | net/bluetooth/hci_conn.c | 14 | ||||
-rw-r--r-- | net/bluetooth/hci_event.c | 20 | ||||
-rw-r--r-- | net/bluetooth/hidp/core.c | 10 | ||||
-rw-r--r-- | net/bluetooth/l2cap_core.c | 255 | ||||
-rw-r--r-- | net/bluetooth/rfcomm/core.c | 5 |
10 files changed, 157 insertions, 196 deletions
diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index 9cbac6b445e1..675246a6f7ef 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -60,6 +60,9 @@ static struct usb_device_id btusb_table[] = { /* Generic Bluetooth USB device */ { USB_DEVICE_INFO(0xe0, 0x01, 0x01) }, + /* Broadcom SoftSailing reporting vendor specific */ + { USB_DEVICE(0x05ac, 0x21e1) }, + /* Apple MacBookPro 7,1 */ { USB_DEVICE(0x05ac, 0x8213) }, @@ -708,8 +711,7 @@ static int btusb_send_frame(struct sk_buff *skb) break; case HCI_ACLDATA_PKT: - if (!data->bulk_tx_ep || (hdev->conn_hash.acl_num < 1 && - hdev->conn_hash.le_num < 1)) + if (!data->bulk_tx_ep) return -ENODEV; urb = usb_alloc_urb(0, GFP_ATOMIC); diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index 7f878b9d5642..ab90ae0970a6 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -354,8 +354,8 @@ struct l2cap_chan { __u8 retry_count; __u8 num_acked; __u16 sdu_len; - __u16 partial_sdu_len; struct sk_buff *sdu; + struct sk_buff *sdu_last_frag; __u8 remote_tx_win; __u8 remote_max_tx; @@ -448,7 +448,6 @@ enum { #define L2CAP_CONF_MAX_CONF_RSP 2 enum { - CONN_SAR_SDU, CONN_SREJ_SENT, CONN_WAIT_F, CONN_SREJ_ACT, diff --git a/net/bluetooth/af_bluetooth.c b/net/bluetooth/af_bluetooth.c index 117e0d161780..062124cd89cf 100644 --- a/net/bluetooth/af_bluetooth.c +++ b/net/bluetooth/af_bluetooth.c @@ -349,7 +349,7 @@ int bt_sock_stream_recvmsg(struct kiocb *iocb, struct socket *sock, } chunk = min_t(unsigned int, skb->len, size); - if (memcpy_toiovec(msg->msg_iov, skb->data, chunk)) { + if (skb_copy_datagram_iovec(skb, 0, msg->msg_iov, chunk)) { skb_queue_head(&sk->sk_receive_queue, skb); if (!copied) copied = -EFAULT; @@ -361,7 +361,33 @@ int bt_sock_stream_recvmsg(struct kiocb *iocb, struct socket *sock, sock_recv_ts_and_drops(msg, sk, skb); if (!(flags & MSG_PEEK)) { - skb_pull(skb, chunk); + int skb_len = skb_headlen(skb); + + if (chunk <= skb_len) { + __skb_pull(skb, chunk); + } else { + struct sk_buff *frag; + + __skb_pull(skb, skb_len); + chunk -= skb_len; + + skb_walk_frags(skb, frag) { + if (chunk <= frag->len) { + /* Pulling partial data */ + skb->len -= chunk; + skb->data_len -= chunk; + __skb_pull(frag, chunk); + break; + } else if (frag->len) { + /* Pulling all frag data */ + chunk -= frag->len; + skb->len -= frag->len; + skb->data_len -= frag->len; + __skb_pull(frag, frag->len); + } + } + } + if (skb->len) { skb_queue_head(&sk->sk_receive_queue, skb); break; diff --git a/net/bluetooth/bnep/core.c b/net/bluetooth/bnep/core.c index d9edfe8bf9d6..91bcd3a961ec 100644 --- a/net/bluetooth/bnep/core.c +++ b/net/bluetooth/bnep/core.c @@ -492,7 +492,10 @@ static int bnep_session(void *arg) /* RX */ while ((skb = skb_dequeue(&sk->sk_receive_queue))) { skb_orphan(skb); - bnep_rx_frame(s, skb); + if (!skb_linearize(skb)) + bnep_rx_frame(s, skb); + else + kfree_skb(skb); } if (sk->sk_state != BT_CONNECTED) diff --git a/net/bluetooth/cmtp/core.c b/net/bluetooth/cmtp/core.c index 521baa4fe835..7d00ddf9e9dc 100644 --- a/net/bluetooth/cmtp/core.c +++ b/net/bluetooth/cmtp/core.c @@ -302,7 +302,10 @@ static int cmtp_session(void *arg) while ((skb = skb_dequeue(&sk->sk_receive_queue))) { skb_orphan(skb); - cmtp_recv_frame(session, skb); + if (!skb_linearize(skb)) + cmtp_recv_frame(session, skb); + else + kfree_skb(skb); } cmtp_process_transmit(session); diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index c2df7bf1d374..c1c597e3e198 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -56,15 +56,15 @@ static void hci_le_connect(struct hci_conn *conn) conn->sec_level = BT_SECURITY_LOW; memset(&cp, 0, sizeof(cp)); - cp.scan_interval = cpu_to_le16(0x0004); - cp.scan_window = cpu_to_le16(0x0004); + cp.scan_interval = cpu_to_le16(0x0060); + cp.scan_window = cpu_to_le16(0x0030); bacpy(&cp.peer_addr, &conn->dst); cp.peer_addr_type = conn->dst_type; - cp.conn_interval_min = cpu_to_le16(0x0008); - cp.conn_interval_max = cpu_to_le16(0x0100); - cp.supervision_timeout = cpu_to_le16(0x0064); - cp.min_ce_len = cpu_to_le16(0x0001); - cp.max_ce_len = cpu_to_le16(0x0001); + cp.conn_interval_min = cpu_to_le16(0x0028); + cp.conn_interval_max = cpu_to_le16(0x0038); + cp.supervision_timeout = cpu_to_le16(0x002a); + cp.min_ce_len = cpu_to_le16(0x0000); + cp.max_ce_len = cpu_to_le16(0x0000); hci_send_cmd(hdev, HCI_OP_LE_CREATE_CONN, sizeof(cp), &cp); } diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 35083f2aa2ea..d7d96b6b1f0d 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -2174,7 +2174,10 @@ static inline void hci_pin_code_request_evt(struct hci_dev *hdev, struct sk_buff hci_dev_lock(hdev); conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &ev->bdaddr); - if (conn && conn->state == BT_CONNECTED) { + if (!conn) + goto unlock; + + if (conn->state == BT_CONNECTED) { hci_conn_hold(conn); conn->disc_timeout = HCI_PAIRING_TIMEOUT; hci_conn_put(conn); @@ -2194,6 +2197,7 @@ static inline void hci_pin_code_request_evt(struct hci_dev *hdev, struct sk_buff mgmt_pin_code_request(hdev->id, &ev->bdaddr, secure); } +unlock: hci_dev_unlock(hdev); } @@ -2834,19 +2838,17 @@ unlock: static inline void hci_le_adv_report_evt(struct hci_dev *hdev, struct sk_buff *skb) { - struct hci_ev_le_advertising_info *ev; - u8 num_reports; - - num_reports = skb->data[0]; - ev = (void *) &skb->data[1]; + u8 num_reports = skb->data[0]; + void *ptr = &skb->data[1]; hci_dev_lock(hdev); - hci_add_adv_entry(hdev, ev); + while (num_reports--) { + struct hci_ev_le_advertising_info *ev = ptr; - while (--num_reports) { - ev = (void *) (ev->data + ev->length + 1); hci_add_adv_entry(hdev, ev); + + ptr += sizeof(*ev) + ev->length + 1; } hci_dev_unlock(hdev); diff --git a/net/bluetooth/hidp/core.c b/net/bluetooth/hidp/core.c index b83979c548b2..075a3e920caf 100644 --- a/net/bluetooth/hidp/core.c +++ b/net/bluetooth/hidp/core.c @@ -716,12 +716,18 @@ static int hidp_session(void *arg) while ((skb = skb_dequeue(&ctrl_sk->sk_receive_queue))) { skb_orphan(skb); - hidp_recv_ctrl_frame(session, skb); + if (!skb_linearize(skb)) + hidp_recv_ctrl_frame(session, skb); + else + kfree_skb(skb); } while ((skb = skb_dequeue(&intr_sk->sk_receive_queue))) { skb_orphan(skb); - hidp_recv_intr_frame(session, skb); + if (!skb_linearize(skb)) + hidp_recv_intr_frame(session, skb); + else + kfree_skb(skb); } hidp_process_transmit(session); diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 1611b3544bb1..8cd12917733b 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -1245,7 +1245,7 @@ static void l2cap_drop_acked_frames(struct l2cap_chan *chan) __clear_retrans_timer(chan); } -void l2cap_do_send(struct l2cap_chan *chan, struct sk_buff *skb) +static void l2cap_do_send(struct l2cap_chan *chan, struct sk_buff *skb) { struct hci_conn *hcon = chan->conn->hcon; u16 flags; @@ -1261,7 +1261,7 @@ void l2cap_do_send(struct l2cap_chan *chan, struct sk_buff *skb) hci_send_acl(hcon, skb, flags); } -void l2cap_streaming_send(struct l2cap_chan *chan) +static void l2cap_streaming_send(struct l2cap_chan *chan) { struct sk_buff *skb; u16 control, fcs; @@ -1327,7 +1327,7 @@ static void l2cap_retransmit_one_frame(struct l2cap_chan *chan, u8 tx_seq) l2cap_do_send(chan, tx_skb); } -int l2cap_ertm_send(struct l2cap_chan *chan) +static int l2cap_ertm_send(struct l2cap_chan *chan) { struct sk_buff *skb, *tx_skb; u16 control, fcs; @@ -1465,7 +1465,7 @@ static inline int l2cap_skbuff_fromiovec(struct sock *sk, struct msghdr *msg, in return sent; } -struct sk_buff *l2cap_create_connless_pdu(struct l2cap_chan *chan, struct msghdr *msg, size_t len) +static struct sk_buff *l2cap_create_connless_pdu(struct l2cap_chan *chan, struct msghdr *msg, size_t len) { struct sock *sk = chan->sk; struct l2cap_conn *conn = chan->conn; @@ -1495,7 +1495,7 @@ struct sk_buff *l2cap_create_connless_pdu(struct l2cap_chan *chan, struct msghdr return skb; } -struct sk_buff *l2cap_create_basic_pdu(struct l2cap_chan *chan, struct msghdr *msg, size_t len) +static struct sk_buff *l2cap_create_basic_pdu(struct l2cap_chan *chan, struct msghdr *msg, size_t len) { struct sock *sk = chan->sk; struct l2cap_conn *conn = chan->conn; @@ -1572,7 +1572,7 @@ static struct sk_buff *l2cap_create_iframe_pdu(struct l2cap_chan *chan, return skb; } -int l2cap_sar_segment_sdu(struct l2cap_chan *chan, struct msghdr *msg, size_t len) +static int l2cap_sar_segment_sdu(struct l2cap_chan *chan, struct msghdr *msg, size_t len) { struct sk_buff *skb; struct sk_buff_head sar_queue; @@ -3128,102 +3128,104 @@ static int l2cap_add_to_srej_queue(struct l2cap_chan *chan, struct sk_buff *skb, return 0; } -static int l2cap_ertm_reassembly_sdu(struct l2cap_chan *chan, struct sk_buff *skb, u16 control) +static void append_skb_frag(struct sk_buff *skb, + struct sk_buff *new_frag, struct sk_buff **last_frag) { - struct sk_buff *_skb; - int err; + /* skb->len reflects data in skb as well as all fragments + * skb->data_len reflects only data in fragments + */ + if (!skb_has_frag_list(skb)) + skb_shinfo(skb)->frag_list = new_frag; + + new_frag->next = NULL; + + (*last_frag)->next = new_frag; + *last_frag = new_frag; + + skb->len += new_frag->len; + skb->data_len += new_frag->len; + skb->truesize += new_frag->truesize; +} + +static int l2cap_reassemble_sdu(struct l2cap_chan *chan, struct sk_buff *skb, u16 control) +{ + int err = -EINVAL; switch (control & L2CAP_CTRL_SAR) { case L2CAP_SDU_UNSEGMENTED: - if (test_bit(CONN_SAR_SDU, &chan->conn_state)) - goto drop; + if (chan->sdu) + break; - return chan->ops->recv(chan->data, skb); + err = chan->ops->recv(chan->data, skb); + break; case L2CAP_SDU_START: - if (test_bit(CONN_SAR_SDU, &chan->conn_state)) - goto drop; + if (chan->sdu) + break; chan->sdu_len = get_unaligned_le16(skb->data); + skb_pull(skb, 2); - if (chan->sdu_len > chan->imtu) - goto disconnect; - - chan->sdu = bt_skb_alloc(chan->sdu_len, GFP_ATOMIC); - if (!chan->sdu) - return -ENOMEM; + if (chan->sdu_len > chan->imtu) { + err = -EMSGSIZE; + break; + } - /* pull sdu_len bytes only after alloc, because of Local Busy - * condition we have to be sure that this will be executed - * only once, i.e., when alloc does not fail */ - skb_pull(skb, 2); + if (skb->len >= chan->sdu_len) + break; - memcpy(skb_put(chan->sdu, skb->len), skb->data, skb->len); + chan->sdu = skb; + chan->sdu_last_frag = skb; - set_bit(CONN_SAR_SDU, &chan->conn_state); - chan->partial_sdu_len = skb->len; + skb = NULL; + err = 0; break; case L2CAP_SDU_CONTINUE: - if (!test_bit(CONN_SAR_SDU, &chan->conn_state)) - goto disconnect; - if (!chan->sdu) - goto disconnect; + break; - chan->partial_sdu_len += skb->len; - if (chan->partial_sdu_len > chan->sdu_len) - goto drop; + append_skb_frag(chan->sdu, skb, + &chan->sdu_last_frag); + skb = NULL; - memcpy(skb_put(chan->sdu, skb->len), skb->data, skb->len); + if (chan->sdu->len >= chan->sdu_len) + break; + err = 0; break; case L2CAP_SDU_END: - if (!test_bit(CONN_SAR_SDU, &chan->conn_state)) - goto disconnect; - if (!chan->sdu) - goto disconnect; - - chan->partial_sdu_len += skb->len; - - if (chan->partial_sdu_len > chan->imtu) - goto drop; + break; - if (chan->partial_sdu_len != chan->sdu_len) - goto drop; + append_skb_frag(chan->sdu, skb, + &chan->sdu_last_frag); + skb = NULL; - memcpy(skb_put(chan->sdu, skb->len), skb->data, skb->len); + if (chan->sdu->len != chan->sdu_len) + break; - _skb = skb_clone(chan->sdu, GFP_ATOMIC); - if (!_skb) { - return -ENOMEM; - } + err = chan->ops->recv(chan->data, chan->sdu); - err = chan->ops->recv(chan->data, _skb); - if (err < 0) { - kfree_skb(_skb); - return err; + if (!err) { + /* Reassembly complete */ + chan->sdu = NULL; + chan->sdu_last_frag = NULL; + chan->sdu_len = 0; } - - clear_bit(CONN_SAR_SDU, &chan->conn_state); - - kfree_skb(chan->sdu); break; } - kfree_skb(skb); - return 0; - -drop: - kfree_skb(chan->sdu); - chan->sdu = NULL; + if (err) { + kfree_skb(skb); + kfree_skb(chan->sdu); + chan->sdu = NULL; + chan->sdu_last_frag = NULL; + chan->sdu_len = 0; + } -disconnect: - l2cap_send_disconn_req(chan->conn, chan, ECONNRESET); - kfree_skb(skb); - return 0; + return err; } static void l2cap_ertm_enter_local_busy(struct l2cap_chan *chan) @@ -3277,99 +3279,6 @@ void l2cap_chan_busy(struct l2cap_chan *chan, int busy) } } -static int l2cap_streaming_reassembly_sdu(struct l2cap_chan *chan, struct sk_buff *skb, u16 control) -{ - struct sk_buff *_skb; - int err = -EINVAL; - - /* - * TODO: We have to notify the userland if some data is lost with the - * Streaming Mode. - */ - - switch (control & L2CAP_CTRL_SAR) { - case L2CAP_SDU_UNSEGMENTED: - if (test_bit(CONN_SAR_SDU, &chan->conn_state)) { - kfree_skb(chan->sdu); - break; - } - - err = chan->ops->recv(chan->data, skb); - if (!err) - return 0; - - break; - - case L2CAP_SDU_START: - if (test_bit(CONN_SAR_SDU, &chan->conn_state)) { - kfree_skb(chan->sdu); - break; - } - - chan->sdu_len = get_unaligned_le16(skb->data); - skb_pull(skb, 2); - - if (chan->sdu_len > chan->imtu) { - err = -EMSGSIZE; - break; - } - - chan->sdu = bt_skb_alloc(chan->sdu_len, GFP_ATOMIC); - if (!chan->sdu) { - err = -ENOMEM; - break; - } - - memcpy(skb_put(chan->sdu, skb->len), skb->data, skb->len); - - set_bit(CONN_SAR_SDU, &chan->conn_state); - chan->partial_sdu_len = skb->len; - err = 0; - break; - - case L2CAP_SDU_CONTINUE: - if (!test_bit(CONN_SAR_SDU, &chan->conn_state)) - break; - - memcpy(skb_put(chan->sdu, skb->len), skb->data, skb->len); - - chan->partial_sdu_len += skb->len; - if (chan->partial_sdu_len > chan->sdu_len) - kfree_skb(chan->sdu); - else - err = 0; - - break; - - case L2CAP_SDU_END: - if (!test_bit(CONN_SAR_SDU, &chan->conn_state)) - break; - - memcpy(skb_put(chan->sdu, skb->len), skb->data, skb->len); - - clear_bit(CONN_SAR_SDU, &chan->conn_state); - chan->partial_sdu_len += skb->len; - - if (chan->partial_sdu_len > chan->imtu) - goto drop; - - if (chan->partial_sdu_len == chan->sdu_len) { - _skb = skb_clone(chan->sdu, GFP_ATOMIC); - err = chan->ops->recv(chan->data, _skb); - if (err < 0) - kfree_skb(_skb); - } - err = 0; - -drop: - kfree_skb(chan->sdu); - break; - } - - kfree_skb(skb); - return err; -} - static void l2cap_check_srej_gap(struct l2cap_chan *chan, u8 tx_seq) { struct sk_buff *skb; @@ -3384,7 +3293,7 @@ static void l2cap_check_srej_gap(struct l2cap_chan *chan, u8 tx_seq) skb = skb_dequeue(&chan->srej_q); control = bt_cb(skb)->sar << L2CAP_CTRL_SAR_SHIFT; - err = l2cap_ertm_reassembly_sdu(chan, skb, control); + err = l2cap_reassemble_sdu(chan, skb, control); if (err < 0) { l2cap_send_disconn_req(chan->conn, chan, ECONNRESET); @@ -3544,7 +3453,7 @@ expected: return 0; } - err = l2cap_ertm_reassembly_sdu(chan, skb, rx_control); + err = l2cap_reassemble_sdu(chan, skb, rx_control); chan->buffer_seq = (chan->buffer_seq + 1) % 64; if (err < 0) { l2cap_send_disconn_req(chan->conn, chan, ECONNRESET); @@ -3860,12 +3769,20 @@ static inline int l2cap_data_channel(struct l2cap_conn *conn, u16 cid, struct sk tx_seq = __get_txseq(control); - if (chan->expected_tx_seq == tx_seq) - chan->expected_tx_seq = (chan->expected_tx_seq + 1) % 64; - else - chan->expected_tx_seq = (tx_seq + 1) % 64; + if (chan->expected_tx_seq != tx_seq) { + /* Frame(s) missing - must discard partial SDU */ + kfree_skb(chan->sdu); + chan->sdu = NULL; + chan->sdu_last_frag = NULL; + chan->sdu_len = 0; - l2cap_streaming_reassembly_sdu(chan, skb, control); + /* TODO: Notify userland of missing data */ + } + + chan->expected_tx_seq = (tx_seq + 1) % 64; + + if (l2cap_reassemble_sdu(chan, skb, control) == -EMSGSIZE) + l2cap_send_disconn_req(chan->conn, chan, ECONNRESET); goto done; diff --git a/net/bluetooth/rfcomm/core.c b/net/bluetooth/rfcomm/core.c index 5ba3f6df665c..38b618c96de6 100644 --- a/net/bluetooth/rfcomm/core.c +++ b/net/bluetooth/rfcomm/core.c @@ -1853,7 +1853,10 @@ static inline void rfcomm_process_rx(struct rfcomm_session *s) /* Get data directly from socket receive queue without copying it. */ while ((skb = skb_dequeue(&sk->sk_receive_queue))) { skb_orphan(skb); - rfcomm_recv_frame(s, skb); + if (!skb_linearize(skb)) + rfcomm_recv_frame(s, skb); + else + kfree_skb(skb); } if (sk->sk_state == BT_CLOSED) { |