From a95de4360c341511da2c6afb6ad1f0b84f2285cb Mon Sep 17 00:00:00 2001 From: Jaganath Kanakkassery Date: Fri, 25 Nov 2016 18:07:46 +0530 Subject: [PATCH] Bluetooth: RFCOMM: Disconnect session after last dlc disconnection As per the spec the station which disconnects last dlc should initiate session disconnection and then l2cap disconnection. < ACL Data TX: Handle 256 flags 0x00 dlen 8 Channel: 64 len 4 [PSM 3 mode 0] {chan 0} RFCOMM: Disconnect (DISC) (0x43) Address: 0x03 cr 1 dlci 0x00 Control: 0x53 poll/final 1 Length: 0 FCS: 0xfd > ACL Data RX: Handle 256 flags 0x02 dlen 8 Channel: 64 len 4 [PSM 3 mode 0] {chan 0} RFCOMM: Unnumbered Ack (UA) (0x63) Address: 0x01 cr 0 dlci 0x00 Control: 0x73 poll/final 1 Length: 0 FCS: 0xb6 Change-Id: I2d1d7d284995529d5d1522e0ca6082097db19bc2 Signed-off-by: Jaganath Kanakkassery --- subsys/bluetooth/host/rfcomm.c | 44 ++++++++++++++++++++++++++-------- 1 file changed, 34 insertions(+), 10 deletions(-) diff --git a/subsys/bluetooth/host/rfcomm.c b/subsys/bluetooth/host/rfcomm.c index 56a44da93f6..10e2aa89d39 100644 --- a/subsys/bluetooth/host/rfcomm.c +++ b/subsys/bluetooth/host/rfcomm.c @@ -804,23 +804,46 @@ static int rfcomm_dlc_start(struct bt_rfcomm_dlc *dlc) return 0; } +static void rfcomm_session_disconnect(struct bt_rfcomm_session *session) +{ + if (session->dlcs) { + return; + } + + session->state = BT_RFCOMM_STATE_DISCONNECTING; + rfcomm_send_disc(session, 0); +} + static void rfcomm_handle_ua(struct bt_rfcomm_session *session, uint8_t dlci) { struct bt_rfcomm_dlc *dlc, *tmp; + int err; if (!dlci) { - if (session->state != BT_RFCOMM_STATE_CONNECTING) { - return; - } - session->state = BT_RFCOMM_STATE_CONNECTED; - for (dlc = session->dlcs; dlc; dlc = tmp) { - tmp = dlc->_next; - if (dlc->role == BT_RFCOMM_ROLE_INITIATOR && - dlc->state == BT_RFCOMM_STATE_INIT) { - if (rfcomm_dlc_start(dlc) < 0) { - rfcomm_dlc_drop(dlc); + switch (session->state) { + case BT_RFCOMM_STATE_CONNECTING: + session->state = BT_RFCOMM_STATE_CONNECTED; + for (dlc = session->dlcs; dlc; dlc = tmp) { + tmp = dlc->_next; + if (dlc->role == BT_RFCOMM_ROLE_INITIATOR && + dlc->state == BT_RFCOMM_STATE_INIT) { + if (rfcomm_dlc_start(dlc) < 0) { + rfcomm_dlc_drop(dlc); + } } } + /* Disconnect session if there is no dlcs left */ + rfcomm_session_disconnect(session); + break; + case BT_RFCOMM_STATE_DISCONNECTING: + session->state = BT_RFCOMM_STATE_DISCONNECTED; + err = bt_l2cap_chan_disconnect(&session->br_chan.chan); + if (err < 0) { + session->state = BT_RFCOMM_STATE_IDLE; + } + break; + default: + break; } } else { dlc = rfcomm_dlcs_lookup_dlci(session->dlcs, dlci); @@ -834,6 +857,7 @@ static void rfcomm_handle_ua(struct bt_rfcomm_session *session, uint8_t dlci) break; case BT_RFCOMM_STATE_DISCONNECTING: rfcomm_dlc_drop(dlc); + rfcomm_session_disconnect(session); break; default: break;