Commit d6222fb0 authored by John W. Linville's avatar John W. Linville

Merge branch 'master' of git://github.com/padovan/bluetooth-next

parents 76ed94be c510eae3
......@@ -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);
......
......@@ -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,
......
......@@ -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;
......
......@@ -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)
......
......@@ -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);
......
......@@ -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);
}
......
......@@ -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 @@ static inline void hci_le_conn_complete_evt(struct hci_dev *hdev, struct sk_buff
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);
......
......@@ -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);
......
......@@ -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 @@ static inline int l2cap_data_channel_iframe(struct l2cap_chan *chan, u16 rx_cont
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;
......
......@@ -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) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment