mirror of
https://github.com/torvalds/linux.git
synced 2024-11-02 18:21:49 +00:00
Merge branch 'master' of git://github.com/padovan/bluetooth-next
This commit is contained in:
commit
d6222fb0d6
@ -60,6 +60,9 @@ static struct usb_device_id btusb_table[] = {
|
|||||||
/* Generic Bluetooth USB device */
|
/* Generic Bluetooth USB device */
|
||||||
{ USB_DEVICE_INFO(0xe0, 0x01, 0x01) },
|
{ USB_DEVICE_INFO(0xe0, 0x01, 0x01) },
|
||||||
|
|
||||||
|
/* Broadcom SoftSailing reporting vendor specific */
|
||||||
|
{ USB_DEVICE(0x05ac, 0x21e1) },
|
||||||
|
|
||||||
/* Apple MacBookPro 7,1 */
|
/* Apple MacBookPro 7,1 */
|
||||||
{ USB_DEVICE(0x05ac, 0x8213) },
|
{ USB_DEVICE(0x05ac, 0x8213) },
|
||||||
|
|
||||||
@ -708,8 +711,7 @@ static int btusb_send_frame(struct sk_buff *skb)
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case HCI_ACLDATA_PKT:
|
case HCI_ACLDATA_PKT:
|
||||||
if (!data->bulk_tx_ep || (hdev->conn_hash.acl_num < 1 &&
|
if (!data->bulk_tx_ep)
|
||||||
hdev->conn_hash.le_num < 1))
|
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
|
|
||||||
urb = usb_alloc_urb(0, GFP_ATOMIC);
|
urb = usb_alloc_urb(0, GFP_ATOMIC);
|
||||||
|
@ -354,8 +354,8 @@ struct l2cap_chan {
|
|||||||
__u8 retry_count;
|
__u8 retry_count;
|
||||||
__u8 num_acked;
|
__u8 num_acked;
|
||||||
__u16 sdu_len;
|
__u16 sdu_len;
|
||||||
__u16 partial_sdu_len;
|
|
||||||
struct sk_buff *sdu;
|
struct sk_buff *sdu;
|
||||||
|
struct sk_buff *sdu_last_frag;
|
||||||
|
|
||||||
__u8 remote_tx_win;
|
__u8 remote_tx_win;
|
||||||
__u8 remote_max_tx;
|
__u8 remote_max_tx;
|
||||||
@ -448,7 +448,6 @@ enum {
|
|||||||
#define L2CAP_CONF_MAX_CONF_RSP 2
|
#define L2CAP_CONF_MAX_CONF_RSP 2
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
CONN_SAR_SDU,
|
|
||||||
CONN_SREJ_SENT,
|
CONN_SREJ_SENT,
|
||||||
CONN_WAIT_F,
|
CONN_WAIT_F,
|
||||||
CONN_SREJ_ACT,
|
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);
|
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);
|
skb_queue_head(&sk->sk_receive_queue, skb);
|
||||||
if (!copied)
|
if (!copied)
|
||||||
copied = -EFAULT;
|
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);
|
sock_recv_ts_and_drops(msg, sk, skb);
|
||||||
|
|
||||||
if (!(flags & MSG_PEEK)) {
|
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) {
|
if (skb->len) {
|
||||||
skb_queue_head(&sk->sk_receive_queue, skb);
|
skb_queue_head(&sk->sk_receive_queue, skb);
|
||||||
break;
|
break;
|
||||||
|
@ -492,7 +492,10 @@ static int bnep_session(void *arg)
|
|||||||
/* RX */
|
/* RX */
|
||||||
while ((skb = skb_dequeue(&sk->sk_receive_queue))) {
|
while ((skb = skb_dequeue(&sk->sk_receive_queue))) {
|
||||||
skb_orphan(skb);
|
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)
|
if (sk->sk_state != BT_CONNECTED)
|
||||||
|
@ -302,7 +302,10 @@ static int cmtp_session(void *arg)
|
|||||||
|
|
||||||
while ((skb = skb_dequeue(&sk->sk_receive_queue))) {
|
while ((skb = skb_dequeue(&sk->sk_receive_queue))) {
|
||||||
skb_orphan(skb);
|
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);
|
cmtp_process_transmit(session);
|
||||||
|
@ -56,15 +56,15 @@ static void hci_le_connect(struct hci_conn *conn)
|
|||||||
conn->sec_level = BT_SECURITY_LOW;
|
conn->sec_level = BT_SECURITY_LOW;
|
||||||
|
|
||||||
memset(&cp, 0, sizeof(cp));
|
memset(&cp, 0, sizeof(cp));
|
||||||
cp.scan_interval = cpu_to_le16(0x0004);
|
cp.scan_interval = cpu_to_le16(0x0060);
|
||||||
cp.scan_window = cpu_to_le16(0x0004);
|
cp.scan_window = cpu_to_le16(0x0030);
|
||||||
bacpy(&cp.peer_addr, &conn->dst);
|
bacpy(&cp.peer_addr, &conn->dst);
|
||||||
cp.peer_addr_type = conn->dst_type;
|
cp.peer_addr_type = conn->dst_type;
|
||||||
cp.conn_interval_min = cpu_to_le16(0x0008);
|
cp.conn_interval_min = cpu_to_le16(0x0028);
|
||||||
cp.conn_interval_max = cpu_to_le16(0x0100);
|
cp.conn_interval_max = cpu_to_le16(0x0038);
|
||||||
cp.supervision_timeout = cpu_to_le16(0x0064);
|
cp.supervision_timeout = cpu_to_le16(0x002a);
|
||||||
cp.min_ce_len = cpu_to_le16(0x0001);
|
cp.min_ce_len = cpu_to_le16(0x0000);
|
||||||
cp.max_ce_len = cpu_to_le16(0x0001);
|
cp.max_ce_len = cpu_to_le16(0x0000);
|
||||||
|
|
||||||
hci_send_cmd(hdev, HCI_OP_LE_CREATE_CONN, sizeof(cp), &cp);
|
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);
|
hci_dev_lock(hdev);
|
||||||
|
|
||||||
conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &ev->bdaddr);
|
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);
|
hci_conn_hold(conn);
|
||||||
conn->disc_timeout = HCI_PAIRING_TIMEOUT;
|
conn->disc_timeout = HCI_PAIRING_TIMEOUT;
|
||||||
hci_conn_put(conn);
|
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);
|
mgmt_pin_code_request(hdev->id, &ev->bdaddr, secure);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unlock:
|
||||||
hci_dev_unlock(hdev);
|
hci_dev_unlock(hdev);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2834,19 +2838,17 @@ unlock:
|
|||||||
static inline void hci_le_adv_report_evt(struct hci_dev *hdev,
|
static inline void hci_le_adv_report_evt(struct hci_dev *hdev,
|
||||||
struct sk_buff *skb)
|
struct sk_buff *skb)
|
||||||
{
|
{
|
||||||
struct hci_ev_le_advertising_info *ev;
|
u8 num_reports = skb->data[0];
|
||||||
u8 num_reports;
|
void *ptr = &skb->data[1];
|
||||||
|
|
||||||
num_reports = skb->data[0];
|
|
||||||
ev = (void *) &skb->data[1];
|
|
||||||
|
|
||||||
hci_dev_lock(hdev);
|
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);
|
hci_add_adv_entry(hdev, ev);
|
||||||
|
|
||||||
|
ptr += sizeof(*ev) + ev->length + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
hci_dev_unlock(hdev);
|
hci_dev_unlock(hdev);
|
||||||
|
@ -716,12 +716,18 @@ static int hidp_session(void *arg)
|
|||||||
|
|
||||||
while ((skb = skb_dequeue(&ctrl_sk->sk_receive_queue))) {
|
while ((skb = skb_dequeue(&ctrl_sk->sk_receive_queue))) {
|
||||||
skb_orphan(skb);
|
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))) {
|
while ((skb = skb_dequeue(&intr_sk->sk_receive_queue))) {
|
||||||
skb_orphan(skb);
|
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);
|
hidp_process_transmit(session);
|
||||||
|
@ -1245,7 +1245,7 @@ static void l2cap_drop_acked_frames(struct l2cap_chan *chan)
|
|||||||
__clear_retrans_timer(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;
|
struct hci_conn *hcon = chan->conn->hcon;
|
||||||
u16 flags;
|
u16 flags;
|
||||||
@ -1261,7 +1261,7 @@ void l2cap_do_send(struct l2cap_chan *chan, struct sk_buff *skb)
|
|||||||
hci_send_acl(hcon, skb, flags);
|
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;
|
struct sk_buff *skb;
|
||||||
u16 control, fcs;
|
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);
|
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;
|
struct sk_buff *skb, *tx_skb;
|
||||||
u16 control, fcs;
|
u16 control, fcs;
|
||||||
@ -1465,7 +1465,7 @@ static inline int l2cap_skbuff_fromiovec(struct sock *sk, struct msghdr *msg, in
|
|||||||
return sent;
|
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 sock *sk = chan->sk;
|
||||||
struct l2cap_conn *conn = chan->conn;
|
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;
|
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 sock *sk = chan->sk;
|
||||||
struct l2cap_conn *conn = chan->conn;
|
struct l2cap_conn *conn = chan->conn;
|
||||||
@ -1572,7 +1572,7 @@ static struct sk_buff *l2cap_create_iframe_pdu(struct l2cap_chan *chan,
|
|||||||
return skb;
|
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 *skb;
|
||||||
struct sk_buff_head sar_queue;
|
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;
|
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;
|
/* skb->len reflects data in skb as well as all fragments
|
||||||
int err;
|
* 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) {
|
switch (control & L2CAP_CTRL_SAR) {
|
||||||
case L2CAP_SDU_UNSEGMENTED:
|
case L2CAP_SDU_UNSEGMENTED:
|
||||||
if (test_bit(CONN_SAR_SDU, &chan->conn_state))
|
if (chan->sdu)
|
||||||
goto drop;
|
break;
|
||||||
|
|
||||||
return chan->ops->recv(chan->data, skb);
|
err = chan->ops->recv(chan->data, skb);
|
||||||
|
break;
|
||||||
|
|
||||||
case L2CAP_SDU_START:
|
case L2CAP_SDU_START:
|
||||||
if (test_bit(CONN_SAR_SDU, &chan->conn_state))
|
if (chan->sdu)
|
||||||
goto drop;
|
break;
|
||||||
|
|
||||||
chan->sdu_len = get_unaligned_le16(skb->data);
|
chan->sdu_len = get_unaligned_le16(skb->data);
|
||||||
|
|
||||||
if (chan->sdu_len > chan->imtu)
|
|
||||||
goto disconnect;
|
|
||||||
|
|
||||||
chan->sdu = bt_skb_alloc(chan->sdu_len, GFP_ATOMIC);
|
|
||||||
if (!chan->sdu)
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
/* 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);
|
skb_pull(skb, 2);
|
||||||
|
|
||||||
memcpy(skb_put(chan->sdu, skb->len), skb->data, skb->len);
|
if (chan->sdu_len > chan->imtu) {
|
||||||
|
err = -EMSGSIZE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
set_bit(CONN_SAR_SDU, &chan->conn_state);
|
if (skb->len >= chan->sdu_len)
|
||||||
chan->partial_sdu_len = skb->len;
|
break;
|
||||||
|
|
||||||
|
chan->sdu = skb;
|
||||||
|
chan->sdu_last_frag = skb;
|
||||||
|
|
||||||
|
skb = NULL;
|
||||||
|
err = 0;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case L2CAP_SDU_CONTINUE:
|
case L2CAP_SDU_CONTINUE:
|
||||||
if (!test_bit(CONN_SAR_SDU, &chan->conn_state))
|
|
||||||
goto disconnect;
|
|
||||||
|
|
||||||
if (!chan->sdu)
|
if (!chan->sdu)
|
||||||
goto disconnect;
|
break;
|
||||||
|
|
||||||
chan->partial_sdu_len += skb->len;
|
append_skb_frag(chan->sdu, skb,
|
||||||
if (chan->partial_sdu_len > chan->sdu_len)
|
&chan->sdu_last_frag);
|
||||||
goto drop;
|
skb = NULL;
|
||||||
|
|
||||||
memcpy(skb_put(chan->sdu, skb->len), skb->data, skb->len);
|
if (chan->sdu->len >= chan->sdu_len)
|
||||||
|
break;
|
||||||
|
|
||||||
|
err = 0;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case L2CAP_SDU_END:
|
case L2CAP_SDU_END:
|
||||||
if (!test_bit(CONN_SAR_SDU, &chan->conn_state))
|
|
||||||
goto disconnect;
|
|
||||||
|
|
||||||
if (!chan->sdu)
|
if (!chan->sdu)
|
||||||
goto disconnect;
|
break;
|
||||||
|
|
||||||
chan->partial_sdu_len += skb->len;
|
append_skb_frag(chan->sdu, skb,
|
||||||
|
&chan->sdu_last_frag);
|
||||||
|
skb = NULL;
|
||||||
|
|
||||||
if (chan->partial_sdu_len > chan->imtu)
|
if (chan->sdu->len != chan->sdu_len)
|
||||||
goto drop;
|
break;
|
||||||
|
|
||||||
if (chan->partial_sdu_len != chan->sdu_len)
|
err = chan->ops->recv(chan->data, chan->sdu);
|
||||||
goto drop;
|
|
||||||
|
|
||||||
memcpy(skb_put(chan->sdu, skb->len), skb->data, skb->len);
|
if (!err) {
|
||||||
|
/* Reassembly complete */
|
||||||
_skb = skb_clone(chan->sdu, GFP_ATOMIC);
|
chan->sdu = NULL;
|
||||||
if (!_skb) {
|
chan->sdu_last_frag = NULL;
|
||||||
return -ENOMEM;
|
chan->sdu_len = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
err = chan->ops->recv(chan->data, _skb);
|
|
||||||
if (err < 0) {
|
|
||||||
kfree_skb(_skb);
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
clear_bit(CONN_SAR_SDU, &chan->conn_state);
|
|
||||||
|
|
||||||
kfree_skb(chan->sdu);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
kfree_skb(skb);
|
if (err) {
|
||||||
return 0;
|
kfree_skb(skb);
|
||||||
|
kfree_skb(chan->sdu);
|
||||||
|
chan->sdu = NULL;
|
||||||
|
chan->sdu_last_frag = NULL;
|
||||||
|
chan->sdu_len = 0;
|
||||||
|
}
|
||||||
|
|
||||||
drop:
|
return err;
|
||||||
kfree_skb(chan->sdu);
|
|
||||||
chan->sdu = NULL;
|
|
||||||
|
|
||||||
disconnect:
|
|
||||||
l2cap_send_disconn_req(chan->conn, chan, ECONNRESET);
|
|
||||||
kfree_skb(skb);
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void l2cap_ertm_enter_local_busy(struct l2cap_chan *chan)
|
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)
|
static void l2cap_check_srej_gap(struct l2cap_chan *chan, u8 tx_seq)
|
||||||
{
|
{
|
||||||
struct sk_buff *skb;
|
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);
|
skb = skb_dequeue(&chan->srej_q);
|
||||||
control = bt_cb(skb)->sar << L2CAP_CTRL_SAR_SHIFT;
|
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) {
|
if (err < 0) {
|
||||||
l2cap_send_disconn_req(chan->conn, chan, ECONNRESET);
|
l2cap_send_disconn_req(chan->conn, chan, ECONNRESET);
|
||||||
@ -3544,7 +3453,7 @@ expected:
|
|||||||
return 0;
|
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;
|
chan->buffer_seq = (chan->buffer_seq + 1) % 64;
|
||||||
if (err < 0) {
|
if (err < 0) {
|
||||||
l2cap_send_disconn_req(chan->conn, chan, ECONNRESET);
|
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);
|
tx_seq = __get_txseq(control);
|
||||||
|
|
||||||
if (chan->expected_tx_seq == tx_seq)
|
if (chan->expected_tx_seq != tx_seq) {
|
||||||
chan->expected_tx_seq = (chan->expected_tx_seq + 1) % 64;
|
/* Frame(s) missing - must discard partial SDU */
|
||||||
else
|
kfree_skb(chan->sdu);
|
||||||
chan->expected_tx_seq = (tx_seq + 1) % 64;
|
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;
|
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. */
|
/* Get data directly from socket receive queue without copying it. */
|
||||||
while ((skb = skb_dequeue(&sk->sk_receive_queue))) {
|
while ((skb = skb_dequeue(&sk->sk_receive_queue))) {
|
||||||
skb_orphan(skb);
|
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) {
|
if (sk->sk_state == BT_CLOSED) {
|
||||||
|
Loading…
Reference in New Issue
Block a user