aboutsummaryrefslogtreecommitdiffstats
path: root/net/bluetooth/l2cap.c
diff options
context:
space:
mode:
authorGustavo F. Padovan <padovan@profusion.mobi>2010-05-01 15:15:44 -0400
committerMarcel Holtmann <marcel@holtmann.org>2010-05-10 03:28:52 -0400
commit18778a63ddc83bc89bda3b119fb02eb121512a66 (patch)
tree18834b5fcb2a95eef41c03afe7744ebe0040078e /net/bluetooth/l2cap.c
parentf11d676da4059c7888efca810ab300b931736a26 (diff)
Bluetooth: Implement missing parts of the Invalid Frame Detection
There is a plenty of situation where ERTM shall close the channel, this commit treats the cases regarding Invalid Frame Detection. It create one reassembly SDU function for ERTM and other for Streaming Mode to make the Invalid Frame Detection handling less complex. Signed-off-by: Gustavo F. Padovan <padovan@profusion.mobi> Reviewed-by: João Paulo Rechi Vita <jprvita@profusion.mobi> Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
Diffstat (limited to 'net/bluetooth/l2cap.c')
-rw-r--r--net/bluetooth/l2cap.c119
1 files changed, 112 insertions, 7 deletions
diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c
index 1c35c328181d..cfd672419315 100644
--- a/net/bluetooth/l2cap.c
+++ b/net/bluetooth/l2cap.c
@@ -3329,12 +3329,111 @@ static void l2cap_add_to_srej_queue(struct sock *sk, struct sk_buff *skb, u8 tx_
3329 __skb_queue_tail(SREJ_QUEUE(sk), skb); 3329 __skb_queue_tail(SREJ_QUEUE(sk), skb);
3330} 3330}
3331 3331
3332static int l2cap_sar_reassembly_sdu(struct sock *sk, struct sk_buff *skb, u16 control) 3332static int l2cap_ertm_reassembly_sdu(struct sock *sk, struct sk_buff *skb, u16 control)
3333{
3334 struct l2cap_pinfo *pi = l2cap_pi(sk);
3335 struct sk_buff *_skb;
3336 int err = 0;
3337
3338 switch (control & L2CAP_CTRL_SAR) {
3339 case L2CAP_SDU_UNSEGMENTED:
3340 if (pi->conn_state & L2CAP_CONN_SAR_SDU)
3341 goto drop;
3342
3343 err = sock_queue_rcv_skb(sk, skb);
3344 if (!err)
3345 return err;
3346
3347 break;
3348
3349 case L2CAP_SDU_START:
3350 if (pi->conn_state & L2CAP_CONN_SAR_SDU)
3351 goto drop;
3352
3353 pi->sdu_len = get_unaligned_le16(skb->data);
3354 skb_pull(skb, 2);
3355
3356 if (pi->sdu_len > pi->imtu)
3357 goto disconnect;
3358
3359 pi->sdu = bt_skb_alloc(pi->sdu_len, GFP_ATOMIC);
3360 if (!pi->sdu) {
3361 err = -ENOMEM;
3362 break;
3363 }
3364
3365 memcpy(skb_put(pi->sdu, skb->len), skb->data, skb->len);
3366
3367 pi->conn_state |= L2CAP_CONN_SAR_SDU;
3368 pi->partial_sdu_len = skb->len;
3369 break;
3370
3371 case L2CAP_SDU_CONTINUE:
3372 if (!(pi->conn_state & L2CAP_CONN_SAR_SDU))
3373 goto disconnect;
3374
3375 if (!pi->sdu)
3376 goto disconnect;
3377
3378 memcpy(skb_put(pi->sdu, skb->len), skb->data, skb->len);
3379
3380 pi->partial_sdu_len += skb->len;
3381 if (pi->partial_sdu_len > pi->sdu_len)
3382 goto drop;
3383
3384 break;
3385
3386 case L2CAP_SDU_END:
3387 if (!(pi->conn_state & L2CAP_CONN_SAR_SDU))
3388 goto disconnect;
3389
3390 if (!pi->sdu)
3391 goto disconnect;
3392
3393 memcpy(skb_put(pi->sdu, skb->len), skb->data, skb->len);
3394
3395 pi->conn_state &= ~L2CAP_CONN_SAR_SDU;
3396 pi->partial_sdu_len += skb->len;
3397
3398 if (pi->partial_sdu_len > pi->imtu)
3399 goto drop;
3400
3401 if (pi->partial_sdu_len != pi->sdu_len)
3402 goto drop;
3403
3404 _skb = skb_clone(pi->sdu, GFP_ATOMIC);
3405 err = sock_queue_rcv_skb(sk, _skb);
3406 if (err < 0)
3407 kfree_skb(_skb);
3408
3409 kfree_skb(pi->sdu);
3410 break;
3411 }
3412
3413 kfree_skb(skb);
3414 return err;
3415
3416drop:
3417 kfree_skb(pi->sdu);
3418 pi->sdu = NULL;
3419
3420disconnect:
3421 l2cap_send_disconn_req(pi->conn, sk);
3422 kfree_skb(skb);
3423 return 0;
3424}
3425
3426static int l2cap_streaming_reassembly_sdu(struct sock *sk, struct sk_buff *skb, u16 control)
3333{ 3427{
3334 struct l2cap_pinfo *pi = l2cap_pi(sk); 3428 struct l2cap_pinfo *pi = l2cap_pi(sk);
3335 struct sk_buff *_skb; 3429 struct sk_buff *_skb;
3336 int err = -EINVAL; 3430 int err = -EINVAL;
3337 3431
3432 /*
3433 * TODO: We have to notify the userland if some data is lost with the
3434 * Streaming Mode.
3435 */
3436
3338 switch (control & L2CAP_CTRL_SAR) { 3437 switch (control & L2CAP_CTRL_SAR) {
3339 case L2CAP_SDU_UNSEGMENTED: 3438 case L2CAP_SDU_UNSEGMENTED:
3340 if (pi->conn_state & L2CAP_CONN_SAR_SDU) { 3439 if (pi->conn_state & L2CAP_CONN_SAR_SDU) {
@@ -3429,7 +3528,7 @@ static void l2cap_check_srej_gap(struct sock *sk, u8 tx_seq)
3429 3528
3430 skb = skb_dequeue(SREJ_QUEUE(sk)); 3529 skb = skb_dequeue(SREJ_QUEUE(sk));
3431 control = bt_cb(skb)->sar << L2CAP_CTRL_SAR_SHIFT; 3530 control = bt_cb(skb)->sar << L2CAP_CTRL_SAR_SHIFT;
3432 l2cap_sar_reassembly_sdu(sk, skb, control); 3531 l2cap_ertm_reassembly_sdu(sk, skb, control);
3433 l2cap_pi(sk)->buffer_seq_srej = 3532 l2cap_pi(sk)->buffer_seq_srej =
3434 (l2cap_pi(sk)->buffer_seq_srej + 1) % 64; 3533 (l2cap_pi(sk)->buffer_seq_srej + 1) % 64;
3435 tx_seq++; 3534 tx_seq++;
@@ -3566,7 +3665,7 @@ expected:
3566 3665
3567 pi->buffer_seq = (pi->buffer_seq + 1) % 64; 3666 pi->buffer_seq = (pi->buffer_seq + 1) % 64;
3568 3667
3569 err = l2cap_sar_reassembly_sdu(sk, skb, rx_control); 3668 err = l2cap_ertm_reassembly_sdu(sk, skb, rx_control);
3570 if (err < 0) 3669 if (err < 0)
3571 return err; 3670 return err;
3572 3671
@@ -3790,8 +3889,10 @@ static inline int l2cap_data_channel(struct l2cap_conn *conn, u16 cid, struct sk
3790 * Receiver will miss it and start proper recovery 3889 * Receiver will miss it and start proper recovery
3791 * procedures and ask retransmission. 3890 * procedures and ask retransmission.
3792 */ 3891 */
3793 if (len > pi->mps) 3892 if (len > pi->mps) {
3893 l2cap_send_disconn_req(pi->conn, sk);
3794 goto drop; 3894 goto drop;
3895 }
3795 3896
3796 if (l2cap_check_fcs(pi, skb)) 3897 if (l2cap_check_fcs(pi, skb))
3797 goto drop; 3898 goto drop;
@@ -3813,13 +3914,17 @@ static inline int l2cap_data_channel(struct l2cap_conn *conn, u16 cid, struct sk
3813 } 3914 }
3814 3915
3815 if (__is_iframe(control)) { 3916 if (__is_iframe(control)) {
3816 if (len < 4) 3917 if (len < 4) {
3918 l2cap_send_disconn_req(pi->conn, sk);
3817 goto drop; 3919 goto drop;
3920 }
3818 3921
3819 l2cap_data_channel_iframe(sk, control, skb); 3922 l2cap_data_channel_iframe(sk, control, skb);
3820 } else { 3923 } else {
3821 if (len != 0) 3924 if (len != 0) {
3925 l2cap_send_disconn_req(pi->conn, sk);
3822 goto drop; 3926 goto drop;
3927 }
3823 3928
3824 l2cap_data_channel_sframe(sk, control, skb); 3929 l2cap_data_channel_sframe(sk, control, skb);
3825 } 3930 }
@@ -3850,7 +3955,7 @@ static inline int l2cap_data_channel(struct l2cap_conn *conn, u16 cid, struct sk
3850 else 3955 else
3851 pi->expected_tx_seq = (tx_seq + 1) % 64; 3956 pi->expected_tx_seq = (tx_seq + 1) % 64;
3852 3957
3853 l2cap_sar_reassembly_sdu(sk, skb, control); 3958 l2cap_streaming_reassembly_sdu(sk, skb, control);
3854 3959
3855 goto done; 3960 goto done;
3856 3961