aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMat Martineau <mathewm@codeaurora.org>2012-10-23 18:24:10 -0400
committerGustavo Padovan <gustavo.padovan@collabora.co.uk>2012-10-23 22:09:15 -0400
commit02b0fbb92dbb0e3c50f1c955547444e3997c80e3 (patch)
treeae848f370d64143399cfe56add8c58c4c2577aca
parentb1a130b7d372c5ccc2001d4ee08928b5324f0a76 (diff)
Bluetooth: Channel move request handling
On receipt of a channel move request, the request must be validated based on the L2CAP mode, connection state, and controller capabilities. ERTM channels must have their state machines cleared and transmission paused while the channel move takes place. If the channel is being moved to an AMP controller then an AMP physical link must be prepared. Moving the channel back to BR/EDR proceeds immediately. Signed-off-by: Mat Martineau <mathewm@codeaurora.org> Acked-by: Marcel Holtmann <marcel@holtmann.org> Acked-by: Andrei Emeltchenko <andrei.emeltchenko@intel.com> Signed-off-by: Gustavo Padovan <gustavo.padovan@collabora.co.uk>
-rw-r--r--net/bluetooth/l2cap_core.c113
1 files changed, 112 insertions, 1 deletions
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index 03daae8ab269..24729f54bfd0 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -735,6 +735,12 @@ static void l2cap_send_cmd(struct l2cap_conn *conn, u8 ident, u8 code, u16 len,
735 hci_send_acl(conn->hchan, skb, flags); 735 hci_send_acl(conn->hchan, skb, flags);
736} 736}
737 737
738static bool __chan_is_moving(struct l2cap_chan *chan)
739{
740 return chan->move_state != L2CAP_MOVE_STABLE &&
741 chan->move_state != L2CAP_MOVE_WAIT_PREPARE;
742}
743
738static void l2cap_do_send(struct l2cap_chan *chan, struct sk_buff *skb) 744static void l2cap_do_send(struct l2cap_chan *chan, struct sk_buff *skb)
739{ 745{
740 struct hci_conn *hcon = chan->conn->hcon; 746 struct hci_conn *hcon = chan->conn->hcon;
@@ -996,6 +1002,41 @@ void l2cap_send_conn_req(struct l2cap_chan *chan)
996 l2cap_send_cmd(conn, chan->ident, L2CAP_CONN_REQ, sizeof(req), &req); 1002 l2cap_send_cmd(conn, chan->ident, L2CAP_CONN_REQ, sizeof(req), &req);
997} 1003}
998 1004
1005static void l2cap_move_setup(struct l2cap_chan *chan)
1006{
1007 struct sk_buff *skb;
1008
1009 BT_DBG("chan %p", chan);
1010
1011 if (chan->mode != L2CAP_MODE_ERTM)
1012 return;
1013
1014 __clear_retrans_timer(chan);
1015 __clear_monitor_timer(chan);
1016 __clear_ack_timer(chan);
1017
1018 chan->retry_count = 0;
1019 skb_queue_walk(&chan->tx_q, skb) {
1020 if (bt_cb(skb)->control.retries)
1021 bt_cb(skb)->control.retries = 1;
1022 else
1023 break;
1024 }
1025
1026 chan->expected_tx_seq = chan->buffer_seq;
1027
1028 clear_bit(CONN_REJ_ACT, &chan->conn_state);
1029 clear_bit(CONN_SREJ_ACT, &chan->conn_state);
1030 l2cap_seq_list_clear(&chan->retrans_list);
1031 l2cap_seq_list_clear(&chan->srej_list);
1032 skb_queue_purge(&chan->srej_q);
1033
1034 chan->tx_state = L2CAP_TX_STATE_XMIT;
1035 chan->rx_state = L2CAP_RX_STATE_MOVE;
1036
1037 set_bit(CONN_REMOTE_BUSY, &chan->conn_state);
1038}
1039
999static void l2cap_chan_ready(struct l2cap_chan *chan) 1040static void l2cap_chan_ready(struct l2cap_chan *chan)
1000{ 1041{
1001 /* This clears all conf flags, including CONF_NOT_COMPLETE */ 1042 /* This clears all conf flags, including CONF_NOT_COMPLETE */
@@ -4157,6 +4198,7 @@ static inline int l2cap_move_channel_req(struct l2cap_conn *conn,
4157 u16 cmd_len, void *data) 4198 u16 cmd_len, void *data)
4158{ 4199{
4159 struct l2cap_move_chan_req *req = data; 4200 struct l2cap_move_chan_req *req = data;
4201 struct l2cap_chan *chan;
4160 u16 icid = 0; 4202 u16 icid = 0;
4161 u16 result = L2CAP_MR_NOT_ALLOWED; 4203 u16 result = L2CAP_MR_NOT_ALLOWED;
4162 4204
@@ -4170,9 +4212,78 @@ static inline int l2cap_move_channel_req(struct l2cap_conn *conn,
4170 if (!enable_hs) 4212 if (!enable_hs)
4171 return -EINVAL; 4213 return -EINVAL;
4172 4214
4173 /* Placeholder: Always refuse */ 4215 chan = l2cap_get_chan_by_dcid(conn, icid);
4216 if (!chan) {
4217 l2cap_send_move_chan_rsp(conn, cmd->ident, icid,
4218 L2CAP_MR_NOT_ALLOWED);
4219 return 0;
4220 }
4221
4222 if (chan->scid < L2CAP_CID_DYN_START ||
4223 chan->chan_policy == BT_CHANNEL_POLICY_BREDR_ONLY ||
4224 (chan->mode != L2CAP_MODE_ERTM &&
4225 chan->mode != L2CAP_MODE_STREAMING)) {
4226 result = L2CAP_MR_NOT_ALLOWED;
4227 goto send_move_response;
4228 }
4229
4230 if (chan->local_amp_id == req->dest_amp_id) {
4231 result = L2CAP_MR_SAME_ID;
4232 goto send_move_response;
4233 }
4234
4235 if (req->dest_amp_id) {
4236 struct hci_dev *hdev;
4237 hdev = hci_dev_get(req->dest_amp_id);
4238 if (!hdev || hdev->dev_type != HCI_AMP ||
4239 !test_bit(HCI_UP, &hdev->flags)) {
4240 if (hdev)
4241 hci_dev_put(hdev);
4242
4243 result = L2CAP_MR_BAD_ID;
4244 goto send_move_response;
4245 }
4246 hci_dev_put(hdev);
4247 }
4248
4249 /* Detect a move collision. Only send a collision response
4250 * if this side has "lost", otherwise proceed with the move.
4251 * The winner has the larger bd_addr.
4252 */
4253 if ((__chan_is_moving(chan) ||
4254 chan->move_role != L2CAP_MOVE_ROLE_NONE) &&
4255 bacmp(conn->src, conn->dst) > 0) {
4256 result = L2CAP_MR_COLLISION;
4257 goto send_move_response;
4258 }
4259
4260 chan->ident = cmd->ident;
4261 chan->move_role = L2CAP_MOVE_ROLE_RESPONDER;
4262 l2cap_move_setup(chan);
4263 chan->move_id = req->dest_amp_id;
4264 icid = chan->dcid;
4265
4266 if (!req->dest_amp_id) {
4267 /* Moving to BR/EDR */
4268 if (test_bit(CONN_LOCAL_BUSY, &chan->conn_state)) {
4269 chan->move_state = L2CAP_MOVE_WAIT_LOCAL_BUSY;
4270 result = L2CAP_MR_PEND;
4271 } else {
4272 chan->move_state = L2CAP_MOVE_WAIT_CONFIRM;
4273 result = L2CAP_MR_SUCCESS;
4274 }
4275 } else {
4276 chan->move_state = L2CAP_MOVE_WAIT_PREPARE;
4277 /* Placeholder - uncomment when amp functions are available */
4278 /*amp_accept_physical(chan, req->dest_amp_id);*/
4279 result = L2CAP_MR_PEND;
4280 }
4281
4282send_move_response:
4174 l2cap_send_move_chan_rsp(conn, cmd->ident, icid, result); 4283 l2cap_send_move_chan_rsp(conn, cmd->ident, icid, result);
4175 4284
4285 l2cap_chan_unlock(chan);
4286
4176 return 0; 4287 return 0;
4177} 4288}
4178 4289