diff options
author | Mat Martineau <mathewm@codeaurora.org> | 2012-10-23 18:24:15 -0400 |
---|---|---|
committer | Gustavo Padovan <gustavo.padovan@collabora.co.uk> | 2012-10-23 22:21:41 -0400 |
commit | 1500109bbc6cc42ec6c8445f1cf04d25fa54a57b (patch) | |
tree | b9cc56660249c438ed56e3b284642bc230f72ff4 | |
parent | 5b155ef960202b20a5cae43b9e675f4326e2375c (diff) |
Bluetooth: Add logical link confirm
The logical link confirm callback is executed when the AMP controller
completes its logical link setup. During a channel move, a newly
formed logical link allows a move responder to send a move channel
response. A move initiator will send a move channel confirm. A
failed logical link will end the channel move and send an appropriate
response or confirm command indicating a failure.
If the channel is being created on an AMP controller, L2CAP
configuration is completed after the logical link is set up.
Signed-off-by: Mat Martineau <mathewm@codeaurora.org>
Acked-by: Marcel Holtmann <marcel@holtmann.org>
Signed-off-by: Gustavo Padovan <gustavo.padovan@collabora.co.uk>
-rw-r--r-- | net/bluetooth/l2cap_core.c | 134 |
1 files changed, 123 insertions, 11 deletions
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 2277ed504283..4d240c23e9dc 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c | |||
@@ -3787,6 +3787,7 @@ static inline int l2cap_config_req(struct l2cap_conn *conn, | |||
3787 | goto unlock; | 3787 | goto unlock; |
3788 | } | 3788 | } |
3789 | 3789 | ||
3790 | chan->ident = cmd->ident; | ||
3790 | l2cap_send_cmd(conn, cmd->ident, L2CAP_CONF_RSP, len, rsp); | 3791 | l2cap_send_cmd(conn, cmd->ident, L2CAP_CONF_RSP, len, rsp); |
3791 | chan->num_conf_rsp++; | 3792 | chan->num_conf_rsp++; |
3792 | 3793 | ||
@@ -4186,17 +4187,17 @@ static int l2cap_create_channel_req(struct l2cap_conn *conn, | |||
4186 | return 0; | 4187 | return 0; |
4187 | } | 4188 | } |
4188 | 4189 | ||
4189 | static void l2cap_send_move_chan_rsp(struct l2cap_conn *conn, u8 ident, | 4190 | static void l2cap_send_move_chan_rsp(struct l2cap_chan *chan, u16 result) |
4190 | u16 icid, u16 result) | ||
4191 | { | 4191 | { |
4192 | struct l2cap_move_chan_rsp rsp; | 4192 | struct l2cap_move_chan_rsp rsp; |
4193 | 4193 | ||
4194 | BT_DBG("icid 0x%4.4x, result 0x%4.4x", icid, result); | 4194 | BT_DBG("chan %p, result 0x%4.4x", chan, result); |
4195 | 4195 | ||
4196 | rsp.icid = cpu_to_le16(icid); | 4196 | rsp.icid = cpu_to_le16(chan->dcid); |
4197 | rsp.result = cpu_to_le16(result); | 4197 | rsp.result = cpu_to_le16(result); |
4198 | 4198 | ||
4199 | l2cap_send_cmd(conn, ident, L2CAP_MOVE_CHAN_RSP, sizeof(rsp), &rsp); | 4199 | l2cap_send_cmd(chan->conn, chan->ident, L2CAP_MOVE_CHAN_RSP, |
4200 | sizeof(rsp), &rsp); | ||
4200 | } | 4201 | } |
4201 | 4202 | ||
4202 | static void l2cap_send_move_chan_cfm(struct l2cap_chan *chan, u16 result) | 4203 | static void l2cap_send_move_chan_cfm(struct l2cap_chan *chan, u16 result) |
@@ -4248,11 +4249,118 @@ static void __release_logical_link(struct l2cap_chan *chan) | |||
4248 | /* Placeholder - release the logical link */ | 4249 | /* Placeholder - release the logical link */ |
4249 | } | 4250 | } |
4250 | 4251 | ||
4252 | static void l2cap_logical_fail(struct l2cap_chan *chan) | ||
4253 | { | ||
4254 | /* Logical link setup failed */ | ||
4255 | if (chan->state != BT_CONNECTED) { | ||
4256 | /* Create channel failure, disconnect */ | ||
4257 | l2cap_send_disconn_req(chan->conn, chan, ECONNRESET); | ||
4258 | return; | ||
4259 | } | ||
4260 | |||
4261 | switch (chan->move_role) { | ||
4262 | case L2CAP_MOVE_ROLE_RESPONDER: | ||
4263 | l2cap_move_done(chan); | ||
4264 | l2cap_send_move_chan_rsp(chan, L2CAP_MR_NOT_SUPP); | ||
4265 | break; | ||
4266 | case L2CAP_MOVE_ROLE_INITIATOR: | ||
4267 | if (chan->move_state == L2CAP_MOVE_WAIT_LOGICAL_COMP || | ||
4268 | chan->move_state == L2CAP_MOVE_WAIT_LOGICAL_CFM) { | ||
4269 | /* Remote has only sent pending or | ||
4270 | * success responses, clean up | ||
4271 | */ | ||
4272 | l2cap_move_done(chan); | ||
4273 | } | ||
4274 | |||
4275 | /* Other amp move states imply that the move | ||
4276 | * has already aborted | ||
4277 | */ | ||
4278 | l2cap_send_move_chan_cfm(chan, L2CAP_MC_UNCONFIRMED); | ||
4279 | break; | ||
4280 | } | ||
4281 | } | ||
4282 | |||
4283 | static void l2cap_logical_finish_create(struct l2cap_chan *chan, | ||
4284 | struct hci_chan *hchan) | ||
4285 | { | ||
4286 | struct l2cap_conf_rsp rsp; | ||
4287 | u8 code; | ||
4288 | |||
4289 | chan->hs_hcon = hchan->conn; | ||
4290 | chan->hs_hcon->l2cap_data = chan->conn; | ||
4291 | |||
4292 | code = l2cap_build_conf_rsp(chan, &rsp, | ||
4293 | L2CAP_CONF_SUCCESS, 0); | ||
4294 | l2cap_send_cmd(chan->conn, chan->ident, L2CAP_CONF_RSP, code, | ||
4295 | &rsp); | ||
4296 | set_bit(CONF_OUTPUT_DONE, &chan->conf_state); | ||
4297 | |||
4298 | if (test_bit(CONF_INPUT_DONE, &chan->conf_state)) { | ||
4299 | int err = 0; | ||
4300 | |||
4301 | set_default_fcs(chan); | ||
4302 | |||
4303 | err = l2cap_ertm_init(chan); | ||
4304 | if (err < 0) | ||
4305 | l2cap_send_disconn_req(chan->conn, chan, -err); | ||
4306 | else | ||
4307 | l2cap_chan_ready(chan); | ||
4308 | } | ||
4309 | } | ||
4310 | |||
4311 | static void l2cap_logical_finish_move(struct l2cap_chan *chan, | ||
4312 | struct hci_chan *hchan) | ||
4313 | { | ||
4314 | chan->hs_hcon = hchan->conn; | ||
4315 | chan->hs_hcon->l2cap_data = chan->conn; | ||
4316 | |||
4317 | BT_DBG("move_state %d", chan->move_state); | ||
4318 | |||
4319 | switch (chan->move_state) { | ||
4320 | case L2CAP_MOVE_WAIT_LOGICAL_COMP: | ||
4321 | /* Move confirm will be sent after a success | ||
4322 | * response is received | ||
4323 | */ | ||
4324 | chan->move_state = L2CAP_MOVE_WAIT_RSP_SUCCESS; | ||
4325 | break; | ||
4326 | case L2CAP_MOVE_WAIT_LOGICAL_CFM: | ||
4327 | if (test_bit(CONN_LOCAL_BUSY, &chan->conn_state)) { | ||
4328 | chan->move_state = L2CAP_MOVE_WAIT_LOCAL_BUSY; | ||
4329 | } else if (chan->move_role == L2CAP_MOVE_ROLE_INITIATOR) { | ||
4330 | chan->move_state = L2CAP_MOVE_WAIT_CONFIRM_RSP; | ||
4331 | l2cap_send_move_chan_cfm(chan, L2CAP_MC_CONFIRMED); | ||
4332 | } else if (chan->move_role == L2CAP_MOVE_ROLE_RESPONDER) { | ||
4333 | chan->move_state = L2CAP_MOVE_WAIT_CONFIRM; | ||
4334 | l2cap_send_move_chan_rsp(chan, L2CAP_MR_SUCCESS); | ||
4335 | } | ||
4336 | break; | ||
4337 | default: | ||
4338 | /* Move was not in expected state, free the channel */ | ||
4339 | __release_logical_link(chan); | ||
4340 | |||
4341 | chan->move_state = L2CAP_MOVE_STABLE; | ||
4342 | } | ||
4343 | } | ||
4344 | |||
4345 | /* Call with chan locked */ | ||
4251 | static void l2cap_logical_cfm(struct l2cap_chan *chan, struct hci_chan *hchan, | 4346 | static void l2cap_logical_cfm(struct l2cap_chan *chan, struct hci_chan *hchan, |
4252 | u8 status) | 4347 | u8 status) |
4253 | { | 4348 | { |
4254 | /* Placeholder */ | 4349 | BT_DBG("chan %p, hchan %p, status %d", chan, hchan, status); |
4255 | return; | 4350 | |
4351 | if (status) { | ||
4352 | l2cap_logical_fail(chan); | ||
4353 | __release_logical_link(chan); | ||
4354 | return; | ||
4355 | } | ||
4356 | |||
4357 | if (chan->state != BT_CONNECTED) { | ||
4358 | /* Ignore logical link if channel is on BR/EDR */ | ||
4359 | if (chan->local_amp_id) | ||
4360 | l2cap_logical_finish_create(chan, hchan); | ||
4361 | } else { | ||
4362 | l2cap_logical_finish_move(chan, hchan); | ||
4363 | } | ||
4256 | } | 4364 | } |
4257 | 4365 | ||
4258 | static inline int l2cap_move_channel_req(struct l2cap_conn *conn, | 4366 | static inline int l2cap_move_channel_req(struct l2cap_conn *conn, |
@@ -4260,6 +4368,7 @@ static inline int l2cap_move_channel_req(struct l2cap_conn *conn, | |||
4260 | u16 cmd_len, void *data) | 4368 | u16 cmd_len, void *data) |
4261 | { | 4369 | { |
4262 | struct l2cap_move_chan_req *req = data; | 4370 | struct l2cap_move_chan_req *req = data; |
4371 | struct l2cap_move_chan_rsp rsp; | ||
4263 | struct l2cap_chan *chan; | 4372 | struct l2cap_chan *chan; |
4264 | u16 icid = 0; | 4373 | u16 icid = 0; |
4265 | u16 result = L2CAP_MR_NOT_ALLOWED; | 4374 | u16 result = L2CAP_MR_NOT_ALLOWED; |
@@ -4276,11 +4385,15 @@ static inline int l2cap_move_channel_req(struct l2cap_conn *conn, | |||
4276 | 4385 | ||
4277 | chan = l2cap_get_chan_by_dcid(conn, icid); | 4386 | chan = l2cap_get_chan_by_dcid(conn, icid); |
4278 | if (!chan) { | 4387 | if (!chan) { |
4279 | l2cap_send_move_chan_rsp(conn, cmd->ident, icid, | 4388 | rsp.icid = cpu_to_le16(icid); |
4280 | L2CAP_MR_NOT_ALLOWED); | 4389 | rsp.result = __constant_cpu_to_le16(L2CAP_MR_NOT_ALLOWED); |
4390 | l2cap_send_cmd(conn, cmd->ident, L2CAP_MOVE_CHAN_RSP, | ||
4391 | sizeof(rsp), &rsp); | ||
4281 | return 0; | 4392 | return 0; |
4282 | } | 4393 | } |
4283 | 4394 | ||
4395 | chan->ident = cmd->ident; | ||
4396 | |||
4284 | if (chan->scid < L2CAP_CID_DYN_START || | 4397 | if (chan->scid < L2CAP_CID_DYN_START || |
4285 | chan->chan_policy == BT_CHANNEL_POLICY_BREDR_ONLY || | 4398 | chan->chan_policy == BT_CHANNEL_POLICY_BREDR_ONLY || |
4286 | (chan->mode != L2CAP_MODE_ERTM && | 4399 | (chan->mode != L2CAP_MODE_ERTM && |
@@ -4319,7 +4432,6 @@ static inline int l2cap_move_channel_req(struct l2cap_conn *conn, | |||
4319 | goto send_move_response; | 4432 | goto send_move_response; |
4320 | } | 4433 | } |
4321 | 4434 | ||
4322 | chan->ident = cmd->ident; | ||
4323 | chan->move_role = L2CAP_MOVE_ROLE_RESPONDER; | 4435 | chan->move_role = L2CAP_MOVE_ROLE_RESPONDER; |
4324 | l2cap_move_setup(chan); | 4436 | l2cap_move_setup(chan); |
4325 | chan->move_id = req->dest_amp_id; | 4437 | chan->move_id = req->dest_amp_id; |
@@ -4342,7 +4454,7 @@ static inline int l2cap_move_channel_req(struct l2cap_conn *conn, | |||
4342 | } | 4454 | } |
4343 | 4455 | ||
4344 | send_move_response: | 4456 | send_move_response: |
4345 | l2cap_send_move_chan_rsp(conn, cmd->ident, icid, result); | 4457 | l2cap_send_move_chan_rsp(chan, result); |
4346 | 4458 | ||
4347 | l2cap_chan_unlock(chan); | 4459 | l2cap_chan_unlock(chan); |
4348 | 4460 | ||