diff options
author | Mat Martineau <mathewm@codeaurora.org> | 2012-10-23 18:24:17 -0400 |
---|---|---|
committer | Gustavo Padovan <gustavo.padovan@collabora.co.uk> | 2012-10-23 22:24:16 -0400 |
commit | 8eb200bd2f1c772dcb7f108f690ef03b054be04e (patch) | |
tree | 440eba222989ad20dbeedd3324ab4f7f39b8871e /net/bluetooth/l2cap_core.c | |
parent | 3fd71a0a438aa5bd43f52f3feec24a4cb3b799d3 (diff) |
Bluetooth: Handle physical link completion
Several different actions may be taken when an AMP physical link
becomes available. A channel being created on an AMP controller must
continue the connection process. A channel being moved needs to
either send a move request or a move response. A failed physical link
will revert to using a BR/EDR controller if possible.
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>
Diffstat (limited to 'net/bluetooth/l2cap_core.c')
-rw-r--r-- | net/bluetooth/l2cap_core.c | 164 |
1 files changed, 164 insertions, 0 deletions
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 7663a1e6f1cc..898529d102f2 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c | |||
@@ -1016,6 +1016,19 @@ void l2cap_send_conn_req(struct l2cap_chan *chan) | |||
1016 | l2cap_send_cmd(conn, chan->ident, L2CAP_CONN_REQ, sizeof(req), &req); | 1016 | l2cap_send_cmd(conn, chan->ident, L2CAP_CONN_REQ, sizeof(req), &req); |
1017 | } | 1017 | } |
1018 | 1018 | ||
1019 | static void l2cap_send_create_chan_req(struct l2cap_chan *chan, u8 amp_id) | ||
1020 | { | ||
1021 | struct l2cap_create_chan_req req; | ||
1022 | req.scid = cpu_to_le16(chan->scid); | ||
1023 | req.psm = chan->psm; | ||
1024 | req.amp_id = amp_id; | ||
1025 | |||
1026 | chan->ident = l2cap_get_ident(chan->conn); | ||
1027 | |||
1028 | l2cap_send_cmd(chan->conn, chan->ident, L2CAP_CREATE_CHAN_REQ, | ||
1029 | sizeof(req), &req); | ||
1030 | } | ||
1031 | |||
1019 | static void l2cap_move_setup(struct l2cap_chan *chan) | 1032 | static void l2cap_move_setup(struct l2cap_chan *chan) |
1020 | { | 1033 | { |
1021 | struct sk_buff *skb; | 1034 | struct sk_buff *skb; |
@@ -4187,6 +4200,25 @@ static int l2cap_create_channel_req(struct l2cap_conn *conn, | |||
4187 | return 0; | 4200 | return 0; |
4188 | } | 4201 | } |
4189 | 4202 | ||
4203 | static void l2cap_send_move_chan_req(struct l2cap_chan *chan, u8 dest_amp_id) | ||
4204 | { | ||
4205 | struct l2cap_move_chan_req req; | ||
4206 | u8 ident; | ||
4207 | |||
4208 | BT_DBG("chan %p, dest_amp_id %d", chan, dest_amp_id); | ||
4209 | |||
4210 | ident = l2cap_get_ident(chan->conn); | ||
4211 | chan->ident = ident; | ||
4212 | |||
4213 | req.icid = cpu_to_le16(chan->scid); | ||
4214 | req.dest_amp_id = dest_amp_id; | ||
4215 | |||
4216 | l2cap_send_cmd(chan->conn, ident, L2CAP_MOVE_CHAN_REQ, sizeof(req), | ||
4217 | &req); | ||
4218 | |||
4219 | __set_chan_timer(chan, L2CAP_MOVE_TIMEOUT); | ||
4220 | } | ||
4221 | |||
4190 | static void l2cap_send_move_chan_rsp(struct l2cap_chan *chan, u16 result) | 4222 | static void l2cap_send_move_chan_rsp(struct l2cap_chan *chan, u16 result) |
4191 | { | 4223 | { |
4192 | struct l2cap_move_chan_rsp rsp; | 4224 | struct l2cap_move_chan_rsp rsp; |
@@ -4363,6 +4395,138 @@ static void l2cap_logical_cfm(struct l2cap_chan *chan, struct hci_chan *hchan, | |||
4363 | } | 4395 | } |
4364 | } | 4396 | } |
4365 | 4397 | ||
4398 | static void l2cap_do_create(struct l2cap_chan *chan, int result, | ||
4399 | u8 local_amp_id, u8 remote_amp_id) | ||
4400 | { | ||
4401 | if (!test_bit(CONF_CONNECT_PEND, &chan->conf_state)) { | ||
4402 | struct l2cap_conn_rsp rsp; | ||
4403 | char buf[128]; | ||
4404 | rsp.scid = cpu_to_le16(chan->dcid); | ||
4405 | rsp.dcid = cpu_to_le16(chan->scid); | ||
4406 | |||
4407 | /* Incoming channel on AMP */ | ||
4408 | if (result == L2CAP_CR_SUCCESS) { | ||
4409 | /* Send successful response */ | ||
4410 | rsp.result = cpu_to_le16(L2CAP_CR_SUCCESS); | ||
4411 | rsp.status = cpu_to_le16(L2CAP_CS_NO_INFO); | ||
4412 | } else { | ||
4413 | /* Send negative response */ | ||
4414 | rsp.result = cpu_to_le16(L2CAP_CR_NO_MEM); | ||
4415 | rsp.status = cpu_to_le16(L2CAP_CS_NO_INFO); | ||
4416 | } | ||
4417 | |||
4418 | l2cap_send_cmd(chan->conn, chan->ident, L2CAP_CREATE_CHAN_RSP, | ||
4419 | sizeof(rsp), &rsp); | ||
4420 | |||
4421 | if (result == L2CAP_CR_SUCCESS) { | ||
4422 | __l2cap_state_change(chan, BT_CONFIG); | ||
4423 | set_bit(CONF_REQ_SENT, &chan->conf_state); | ||
4424 | l2cap_send_cmd(chan->conn, l2cap_get_ident(chan->conn), | ||
4425 | L2CAP_CONF_REQ, | ||
4426 | l2cap_build_conf_req(chan, buf), buf); | ||
4427 | chan->num_conf_req++; | ||
4428 | } | ||
4429 | } else { | ||
4430 | /* Outgoing channel on AMP */ | ||
4431 | if (result == L2CAP_CR_SUCCESS) { | ||
4432 | chan->local_amp_id = local_amp_id; | ||
4433 | l2cap_send_create_chan_req(chan, remote_amp_id); | ||
4434 | } else { | ||
4435 | /* Revert to BR/EDR connect */ | ||
4436 | l2cap_send_conn_req(chan); | ||
4437 | } | ||
4438 | } | ||
4439 | } | ||
4440 | |||
4441 | static void l2cap_do_move_initiate(struct l2cap_chan *chan, u8 local_amp_id, | ||
4442 | u8 remote_amp_id) | ||
4443 | { | ||
4444 | l2cap_move_setup(chan); | ||
4445 | chan->move_id = local_amp_id; | ||
4446 | chan->move_state = L2CAP_MOVE_WAIT_RSP; | ||
4447 | |||
4448 | l2cap_send_move_chan_req(chan, remote_amp_id); | ||
4449 | } | ||
4450 | |||
4451 | static void l2cap_do_move_respond(struct l2cap_chan *chan, int result) | ||
4452 | { | ||
4453 | struct hci_chan *hchan = NULL; | ||
4454 | |||
4455 | /* Placeholder - get hci_chan for logical link */ | ||
4456 | |||
4457 | if (hchan) { | ||
4458 | if (hchan->state == BT_CONNECTED) { | ||
4459 | /* Logical link is ready to go */ | ||
4460 | chan->hs_hcon = hchan->conn; | ||
4461 | chan->hs_hcon->l2cap_data = chan->conn; | ||
4462 | chan->move_state = L2CAP_MOVE_WAIT_CONFIRM; | ||
4463 | l2cap_send_move_chan_rsp(chan, L2CAP_MR_SUCCESS); | ||
4464 | |||
4465 | l2cap_logical_cfm(chan, hchan, L2CAP_MR_SUCCESS); | ||
4466 | } else { | ||
4467 | /* Wait for logical link to be ready */ | ||
4468 | chan->move_state = L2CAP_MOVE_WAIT_LOGICAL_CFM; | ||
4469 | } | ||
4470 | } else { | ||
4471 | /* Logical link not available */ | ||
4472 | l2cap_send_move_chan_rsp(chan, L2CAP_MR_NOT_ALLOWED); | ||
4473 | } | ||
4474 | } | ||
4475 | |||
4476 | static void l2cap_do_move_cancel(struct l2cap_chan *chan, int result) | ||
4477 | { | ||
4478 | if (chan->move_role == L2CAP_MOVE_ROLE_RESPONDER) { | ||
4479 | u8 rsp_result; | ||
4480 | if (result == -EINVAL) | ||
4481 | rsp_result = L2CAP_MR_BAD_ID; | ||
4482 | else | ||
4483 | rsp_result = L2CAP_MR_NOT_ALLOWED; | ||
4484 | |||
4485 | l2cap_send_move_chan_rsp(chan, rsp_result); | ||
4486 | } | ||
4487 | |||
4488 | chan->move_role = L2CAP_MOVE_ROLE_NONE; | ||
4489 | chan->move_state = L2CAP_MOVE_STABLE; | ||
4490 | |||
4491 | /* Restart data transmission */ | ||
4492 | l2cap_ertm_send(chan); | ||
4493 | } | ||
4494 | |||
4495 | void l2cap_physical_cfm(struct l2cap_chan *chan, int result, u8 local_amp_id, | ||
4496 | u8 remote_amp_id) | ||
4497 | { | ||
4498 | BT_DBG("chan %p, result %d, local_amp_id %d, remote_amp_id %d", | ||
4499 | chan, result, local_amp_id, remote_amp_id); | ||
4500 | |||
4501 | l2cap_chan_lock(chan); | ||
4502 | |||
4503 | if (chan->state == BT_DISCONN || chan->state == BT_CLOSED) { | ||
4504 | l2cap_chan_unlock(chan); | ||
4505 | return; | ||
4506 | } | ||
4507 | |||
4508 | if (chan->state != BT_CONNECTED) { | ||
4509 | l2cap_do_create(chan, result, local_amp_id, remote_amp_id); | ||
4510 | } else if (result != L2CAP_MR_SUCCESS) { | ||
4511 | l2cap_do_move_cancel(chan, result); | ||
4512 | } else { | ||
4513 | switch (chan->move_role) { | ||
4514 | case L2CAP_MOVE_ROLE_INITIATOR: | ||
4515 | l2cap_do_move_initiate(chan, local_amp_id, | ||
4516 | remote_amp_id); | ||
4517 | break; | ||
4518 | case L2CAP_MOVE_ROLE_RESPONDER: | ||
4519 | l2cap_do_move_respond(chan, result); | ||
4520 | break; | ||
4521 | default: | ||
4522 | l2cap_do_move_cancel(chan, result); | ||
4523 | break; | ||
4524 | } | ||
4525 | } | ||
4526 | |||
4527 | l2cap_chan_unlock(chan); | ||
4528 | } | ||
4529 | |||
4366 | static inline int l2cap_move_channel_req(struct l2cap_conn *conn, | 4530 | static inline int l2cap_move_channel_req(struct l2cap_conn *conn, |
4367 | struct l2cap_cmd_hdr *cmd, | 4531 | struct l2cap_cmd_hdr *cmd, |
4368 | u16 cmd_len, void *data) | 4532 | u16 cmd_len, void *data) |