.. | .. |
---|
710 | 710 | } |
---|
711 | 711 | EXPORT_SYMBOL_GPL(l2cap_chan_del); |
---|
712 | 712 | |
---|
| 713 | +static void __l2cap_chan_list_id(struct l2cap_conn *conn, u16 id, |
---|
| 714 | + l2cap_chan_func_t func, void *data) |
---|
| 715 | +{ |
---|
| 716 | + struct l2cap_chan *chan, *l; |
---|
| 717 | + |
---|
| 718 | + list_for_each_entry_safe(chan, l, &conn->chan_l, list) { |
---|
| 719 | + if (chan->ident == id) |
---|
| 720 | + func(chan, data); |
---|
| 721 | + } |
---|
| 722 | +} |
---|
| 723 | + |
---|
713 | 724 | static void __l2cap_chan_list(struct l2cap_conn *conn, l2cap_chan_func_t func, |
---|
714 | 725 | void *data) |
---|
715 | 726 | { |
---|
.. | .. |
---|
777 | 788 | |
---|
778 | 789 | static void l2cap_chan_ecred_connect_reject(struct l2cap_chan *chan) |
---|
779 | 790 | { |
---|
780 | | - struct l2cap_conn *conn = chan->conn; |
---|
781 | | - struct l2cap_ecred_conn_rsp rsp; |
---|
782 | | - u16 result; |
---|
783 | | - |
---|
784 | | - if (test_bit(FLAG_DEFER_SETUP, &chan->flags)) |
---|
785 | | - result = L2CAP_CR_LE_AUTHORIZATION; |
---|
786 | | - else |
---|
787 | | - result = L2CAP_CR_LE_BAD_PSM; |
---|
788 | | - |
---|
789 | 791 | l2cap_state_change(chan, BT_DISCONN); |
---|
790 | 792 | |
---|
791 | | - memset(&rsp, 0, sizeof(rsp)); |
---|
792 | | - |
---|
793 | | - rsp.result = cpu_to_le16(result); |
---|
794 | | - |
---|
795 | | - l2cap_send_cmd(conn, chan->ident, L2CAP_LE_CONN_RSP, sizeof(rsp), |
---|
796 | | - &rsp); |
---|
| 793 | + __l2cap_ecred_conn_rsp_defer(chan); |
---|
797 | 794 | } |
---|
798 | 795 | |
---|
799 | 796 | static void l2cap_chan_connect_reject(struct l2cap_chan *chan) |
---|
.. | .. |
---|
848 | 845 | break; |
---|
849 | 846 | case L2CAP_MODE_EXT_FLOWCTL: |
---|
850 | 847 | l2cap_chan_ecred_connect_reject(chan); |
---|
851 | | - break; |
---|
| 848 | + return; |
---|
852 | 849 | } |
---|
853 | 850 | } |
---|
854 | 851 | } |
---|
.. | .. |
---|
2679 | 2676 | if (IS_ERR(skb)) |
---|
2680 | 2677 | return PTR_ERR(skb); |
---|
2681 | 2678 | |
---|
2682 | | - /* Channel lock is released before requesting new skb and then |
---|
2683 | | - * reacquired thus we need to recheck channel state. |
---|
2684 | | - */ |
---|
2685 | | - if (chan->state != BT_CONNECTED) { |
---|
2686 | | - kfree_skb(skb); |
---|
2687 | | - return -ENOTCONN; |
---|
2688 | | - } |
---|
2689 | | - |
---|
2690 | 2679 | l2cap_do_send(chan, skb); |
---|
2691 | 2680 | return len; |
---|
2692 | 2681 | } |
---|
.. | .. |
---|
2731 | 2720 | if (IS_ERR(skb)) |
---|
2732 | 2721 | return PTR_ERR(skb); |
---|
2733 | 2722 | |
---|
2734 | | - /* Channel lock is released before requesting new skb and then |
---|
2735 | | - * reacquired thus we need to recheck channel state. |
---|
2736 | | - */ |
---|
2737 | | - if (chan->state != BT_CONNECTED) { |
---|
2738 | | - kfree_skb(skb); |
---|
2739 | | - return -ENOTCONN; |
---|
2740 | | - } |
---|
2741 | | - |
---|
2742 | 2723 | l2cap_do_send(chan, skb); |
---|
2743 | 2724 | err = len; |
---|
2744 | 2725 | break; |
---|
.. | .. |
---|
2758 | 2739 | * allocation. |
---|
2759 | 2740 | */ |
---|
2760 | 2741 | err = l2cap_segment_sdu(chan, &seg_queue, msg, len); |
---|
2761 | | - |
---|
2762 | | - /* The channel could have been closed while segmenting, |
---|
2763 | | - * check that it is still connected. |
---|
2764 | | - */ |
---|
2765 | | - if (chan->state != BT_CONNECTED) { |
---|
2766 | | - __skb_queue_purge(&seg_queue); |
---|
2767 | | - err = -ENOTCONN; |
---|
2768 | | - } |
---|
2769 | 2742 | |
---|
2770 | 2743 | if (err) |
---|
2771 | 2744 | break; |
---|
.. | .. |
---|
3958 | 3931 | &rsp); |
---|
3959 | 3932 | } |
---|
3960 | 3933 | |
---|
3961 | | -void __l2cap_ecred_conn_rsp_defer(struct l2cap_chan *chan) |
---|
| 3934 | +static void l2cap_ecred_list_defer(struct l2cap_chan *chan, void *data) |
---|
3962 | 3935 | { |
---|
3963 | | - struct { |
---|
3964 | | - struct l2cap_ecred_conn_rsp rsp; |
---|
3965 | | - __le16 dcid[5]; |
---|
3966 | | - } __packed pdu; |
---|
3967 | | - struct l2cap_conn *conn = chan->conn; |
---|
3968 | | - u16 ident = chan->ident; |
---|
3969 | | - int i = 0; |
---|
| 3936 | + int *result = data; |
---|
3970 | 3937 | |
---|
3971 | | - if (!ident) |
---|
| 3938 | + if (*result || test_bit(FLAG_ECRED_CONN_REQ_SENT, &chan->flags)) |
---|
3972 | 3939 | return; |
---|
3973 | 3940 | |
---|
3974 | | - BT_DBG("chan %p ident %d", chan, ident); |
---|
3975 | | - |
---|
3976 | | - pdu.rsp.mtu = cpu_to_le16(chan->imtu); |
---|
3977 | | - pdu.rsp.mps = cpu_to_le16(chan->mps); |
---|
3978 | | - pdu.rsp.credits = cpu_to_le16(chan->rx_credits); |
---|
3979 | | - pdu.rsp.result = cpu_to_le16(L2CAP_CR_LE_SUCCESS); |
---|
3980 | | - |
---|
3981 | | - mutex_lock(&conn->chan_lock); |
---|
3982 | | - |
---|
3983 | | - list_for_each_entry(chan, &conn->chan_l, list) { |
---|
3984 | | - if (chan->ident != ident) |
---|
3985 | | - continue; |
---|
3986 | | - |
---|
3987 | | - /* Reset ident so only one response is sent */ |
---|
3988 | | - chan->ident = 0; |
---|
3989 | | - |
---|
3990 | | - /* Include all channels pending with the same ident */ |
---|
3991 | | - pdu.dcid[i++] = cpu_to_le16(chan->scid); |
---|
| 3941 | + switch (chan->state) { |
---|
| 3942 | + case BT_CONNECT2: |
---|
| 3943 | + /* If channel still pending accept add to result */ |
---|
| 3944 | + (*result)++; |
---|
| 3945 | + return; |
---|
| 3946 | + case BT_CONNECTED: |
---|
| 3947 | + return; |
---|
| 3948 | + default: |
---|
| 3949 | + /* If not connected or pending accept it has been refused */ |
---|
| 3950 | + *result = -ECONNREFUSED; |
---|
| 3951 | + return; |
---|
3992 | 3952 | } |
---|
| 3953 | +} |
---|
3993 | 3954 | |
---|
3994 | | - mutex_unlock(&conn->chan_lock); |
---|
| 3955 | +struct l2cap_ecred_rsp_data { |
---|
| 3956 | + struct { |
---|
| 3957 | + struct l2cap_ecred_conn_rsp rsp; |
---|
| 3958 | + __le16 scid[L2CAP_ECRED_MAX_CID]; |
---|
| 3959 | + } __packed pdu; |
---|
| 3960 | + int count; |
---|
| 3961 | +}; |
---|
3995 | 3962 | |
---|
3996 | | - l2cap_send_cmd(conn, ident, L2CAP_ECRED_CONN_RSP, |
---|
3997 | | - sizeof(pdu.rsp) + i * sizeof(__le16), &pdu); |
---|
| 3963 | +static void l2cap_ecred_rsp_defer(struct l2cap_chan *chan, void *data) |
---|
| 3964 | +{ |
---|
| 3965 | + struct l2cap_ecred_rsp_data *rsp = data; |
---|
| 3966 | + |
---|
| 3967 | + if (test_bit(FLAG_ECRED_CONN_REQ_SENT, &chan->flags)) |
---|
| 3968 | + return; |
---|
| 3969 | + |
---|
| 3970 | + /* Reset ident so only one response is sent */ |
---|
| 3971 | + chan->ident = 0; |
---|
| 3972 | + |
---|
| 3973 | + /* Include all channels pending with the same ident */ |
---|
| 3974 | + if (!rsp->pdu.rsp.result) |
---|
| 3975 | + rsp->pdu.rsp.dcid[rsp->count++] = cpu_to_le16(chan->scid); |
---|
| 3976 | + else |
---|
| 3977 | + l2cap_chan_del(chan, ECONNRESET); |
---|
| 3978 | +} |
---|
| 3979 | + |
---|
| 3980 | +void __l2cap_ecred_conn_rsp_defer(struct l2cap_chan *chan) |
---|
| 3981 | +{ |
---|
| 3982 | + struct l2cap_conn *conn = chan->conn; |
---|
| 3983 | + struct l2cap_ecred_rsp_data data; |
---|
| 3984 | + u16 id = chan->ident; |
---|
| 3985 | + int result = 0; |
---|
| 3986 | + |
---|
| 3987 | + if (!id) |
---|
| 3988 | + return; |
---|
| 3989 | + |
---|
| 3990 | + BT_DBG("chan %p id %d", chan, id); |
---|
| 3991 | + |
---|
| 3992 | + memset(&data, 0, sizeof(data)); |
---|
| 3993 | + |
---|
| 3994 | + data.pdu.rsp.mtu = cpu_to_le16(chan->imtu); |
---|
| 3995 | + data.pdu.rsp.mps = cpu_to_le16(chan->mps); |
---|
| 3996 | + data.pdu.rsp.credits = cpu_to_le16(chan->rx_credits); |
---|
| 3997 | + data.pdu.rsp.result = cpu_to_le16(L2CAP_CR_LE_SUCCESS); |
---|
| 3998 | + |
---|
| 3999 | + /* Verify that all channels are ready */ |
---|
| 4000 | + __l2cap_chan_list_id(conn, id, l2cap_ecred_list_defer, &result); |
---|
| 4001 | + |
---|
| 4002 | + if (result > 0) |
---|
| 4003 | + return; |
---|
| 4004 | + |
---|
| 4005 | + if (result < 0) |
---|
| 4006 | + data.pdu.rsp.result = cpu_to_le16(L2CAP_CR_LE_AUTHORIZATION); |
---|
| 4007 | + |
---|
| 4008 | + /* Build response */ |
---|
| 4009 | + __l2cap_chan_list_id(conn, id, l2cap_ecred_rsp_defer, &data); |
---|
| 4010 | + |
---|
| 4011 | + l2cap_send_cmd(conn, id, L2CAP_ECRED_CONN_RSP, |
---|
| 4012 | + sizeof(data.pdu.rsp) + (data.count * sizeof(__le16)), |
---|
| 4013 | + &data.pdu); |
---|
3998 | 4014 | } |
---|
3999 | 4015 | |
---|
4000 | 4016 | void __l2cap_connect_rsp_defer(struct l2cap_chan *chan) |
---|
.. | .. |
---|
4287 | 4303 | result = __le16_to_cpu(rsp->result); |
---|
4288 | 4304 | status = __le16_to_cpu(rsp->status); |
---|
4289 | 4305 | |
---|
| 4306 | + if (result == L2CAP_CR_SUCCESS && (dcid < L2CAP_CID_DYN_START || |
---|
| 4307 | + dcid > L2CAP_CID_DYN_END)) |
---|
| 4308 | + return -EPROTO; |
---|
| 4309 | + |
---|
4290 | 4310 | BT_DBG("dcid 0x%4.4x scid 0x%4.4x result 0x%2.2x status 0x%2.2x", |
---|
4291 | 4311 | dcid, scid, result, status); |
---|
4292 | 4312 | |
---|
.. | .. |
---|
4318 | 4338 | |
---|
4319 | 4339 | switch (result) { |
---|
4320 | 4340 | case L2CAP_CR_SUCCESS: |
---|
| 4341 | + if (__l2cap_get_chan_by_dcid(conn, dcid)) { |
---|
| 4342 | + err = -EBADSLT; |
---|
| 4343 | + break; |
---|
| 4344 | + } |
---|
| 4345 | + |
---|
4321 | 4346 | l2cap_state_change(chan, BT_CONFIG); |
---|
4322 | 4347 | chan->ident = 0; |
---|
4323 | 4348 | chan->dcid = dcid; |
---|
.. | .. |
---|
4631 | 4656 | |
---|
4632 | 4657 | BT_DBG("scid 0x%4.4x dcid 0x%4.4x", scid, dcid); |
---|
4633 | 4658 | |
---|
4634 | | - mutex_lock(&conn->chan_lock); |
---|
4635 | | - |
---|
4636 | | - chan = __l2cap_get_chan_by_scid(conn, dcid); |
---|
| 4659 | + chan = l2cap_get_chan_by_scid(conn, dcid); |
---|
4637 | 4660 | if (!chan) { |
---|
4638 | | - mutex_unlock(&conn->chan_lock); |
---|
4639 | 4661 | cmd_reject_invalid_cid(conn, cmd->ident, dcid, scid); |
---|
4640 | 4662 | return 0; |
---|
4641 | 4663 | } |
---|
4642 | | - |
---|
4643 | | - l2cap_chan_hold(chan); |
---|
4644 | | - l2cap_chan_lock(chan); |
---|
4645 | 4664 | |
---|
4646 | 4665 | rsp.dcid = cpu_to_le16(chan->scid); |
---|
4647 | 4666 | rsp.scid = cpu_to_le16(chan->dcid); |
---|
.. | .. |
---|
4649 | 4668 | |
---|
4650 | 4669 | chan->ops->set_shutdown(chan); |
---|
4651 | 4670 | |
---|
| 4671 | + l2cap_chan_unlock(chan); |
---|
| 4672 | + mutex_lock(&conn->chan_lock); |
---|
| 4673 | + l2cap_chan_lock(chan); |
---|
4652 | 4674 | l2cap_chan_del(chan, ECONNRESET); |
---|
| 4675 | + mutex_unlock(&conn->chan_lock); |
---|
4653 | 4676 | |
---|
4654 | 4677 | chan->ops->close(chan); |
---|
4655 | 4678 | |
---|
4656 | 4679 | l2cap_chan_unlock(chan); |
---|
4657 | 4680 | l2cap_chan_put(chan); |
---|
4658 | | - |
---|
4659 | | - mutex_unlock(&conn->chan_lock); |
---|
4660 | 4681 | |
---|
4661 | 4682 | return 0; |
---|
4662 | 4683 | } |
---|
.. | .. |
---|
4677 | 4698 | |
---|
4678 | 4699 | BT_DBG("dcid 0x%4.4x scid 0x%4.4x", dcid, scid); |
---|
4679 | 4700 | |
---|
4680 | | - mutex_lock(&conn->chan_lock); |
---|
4681 | | - |
---|
4682 | | - chan = __l2cap_get_chan_by_scid(conn, scid); |
---|
| 4701 | + chan = l2cap_get_chan_by_scid(conn, scid); |
---|
4683 | 4702 | if (!chan) { |
---|
4684 | | - mutex_unlock(&conn->chan_lock); |
---|
4685 | 4703 | return 0; |
---|
4686 | 4704 | } |
---|
4687 | | - |
---|
4688 | | - l2cap_chan_hold(chan); |
---|
4689 | | - l2cap_chan_lock(chan); |
---|
4690 | 4705 | |
---|
4691 | 4706 | if (chan->state != BT_DISCONN) { |
---|
4692 | 4707 | l2cap_chan_unlock(chan); |
---|
4693 | 4708 | l2cap_chan_put(chan); |
---|
4694 | | - mutex_unlock(&conn->chan_lock); |
---|
4695 | 4709 | return 0; |
---|
4696 | 4710 | } |
---|
4697 | 4711 | |
---|
| 4712 | + l2cap_chan_unlock(chan); |
---|
| 4713 | + mutex_lock(&conn->chan_lock); |
---|
| 4714 | + l2cap_chan_lock(chan); |
---|
4698 | 4715 | l2cap_chan_del(chan, 0); |
---|
| 4716 | + mutex_unlock(&conn->chan_lock); |
---|
4699 | 4717 | |
---|
4700 | 4718 | chan->ops->close(chan); |
---|
4701 | 4719 | |
---|
4702 | 4720 | l2cap_chan_unlock(chan); |
---|
4703 | 4721 | l2cap_chan_put(chan); |
---|
4704 | | - |
---|
4705 | | - mutex_unlock(&conn->chan_lock); |
---|
4706 | 4722 | |
---|
4707 | 4723 | return 0; |
---|
4708 | 4724 | } |
---|
.. | .. |
---|
5976 | 5992 | struct l2cap_ecred_conn_req *req = (void *) data; |
---|
5977 | 5993 | struct { |
---|
5978 | 5994 | struct l2cap_ecred_conn_rsp rsp; |
---|
5979 | | - __le16 dcid[5]; |
---|
| 5995 | + __le16 dcid[L2CAP_ECRED_MAX_CID]; |
---|
5980 | 5996 | } __packed pdu; |
---|
5981 | 5997 | struct l2cap_chan *chan, *pchan; |
---|
5982 | 5998 | u16 mtu, mps; |
---|
.. | .. |
---|
5989 | 6005 | return -EINVAL; |
---|
5990 | 6006 | |
---|
5991 | 6007 | if (cmd_len < sizeof(*req) || (cmd_len - sizeof(*req)) % sizeof(u16)) { |
---|
| 6008 | + result = L2CAP_CR_LE_INVALID_PARAMS; |
---|
| 6009 | + goto response; |
---|
| 6010 | + } |
---|
| 6011 | + |
---|
| 6012 | + cmd_len -= sizeof(*req); |
---|
| 6013 | + num_scid = cmd_len / sizeof(u16); |
---|
| 6014 | + |
---|
| 6015 | + if (num_scid > ARRAY_SIZE(pdu.dcid)) { |
---|
5992 | 6016 | result = L2CAP_CR_LE_INVALID_PARAMS; |
---|
5993 | 6017 | goto response; |
---|
5994 | 6018 | } |
---|
.. | .. |
---|
6037 | 6061 | } |
---|
6038 | 6062 | |
---|
6039 | 6063 | result = L2CAP_CR_LE_SUCCESS; |
---|
6040 | | - cmd_len -= sizeof(*req); |
---|
6041 | | - num_scid = cmd_len / sizeof(u16); |
---|
6042 | 6064 | |
---|
6043 | 6065 | for (i = 0; i < num_scid; i++) { |
---|
6044 | 6066 | u16 scid = __le16_to_cpu(req->scid[i]); |
---|
.. | .. |
---|
6091 | 6113 | __set_chan_timer(chan, chan->ops->get_sndtimeo(chan)); |
---|
6092 | 6114 | |
---|
6093 | 6115 | chan->ident = cmd->ident; |
---|
| 6116 | + chan->mode = L2CAP_MODE_EXT_FLOWCTL; |
---|
6094 | 6117 | |
---|
6095 | 6118 | if (test_bit(FLAG_DEFER_SETUP, &chan->flags)) { |
---|
6096 | 6119 | l2cap_state_change(chan, BT_CONNECT2); |
---|
.. | .. |
---|
6347 | 6370 | if (!chan) |
---|
6348 | 6371 | goto done; |
---|
6349 | 6372 | |
---|
| 6373 | + chan = l2cap_chan_hold_unless_zero(chan); |
---|
| 6374 | + if (!chan) |
---|
| 6375 | + goto done; |
---|
| 6376 | + |
---|
6350 | 6377 | l2cap_chan_lock(chan); |
---|
6351 | 6378 | l2cap_chan_del(chan, ECONNREFUSED); |
---|
6352 | 6379 | l2cap_chan_unlock(chan); |
---|
| 6380 | + l2cap_chan_put(chan); |
---|
6353 | 6381 | |
---|
6354 | 6382 | done: |
---|
6355 | 6383 | mutex_unlock(&conn->chan_lock); |
---|