summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJaganath Kanakkassery <jaganathx.kanakkassery@intel.com>2017-01-17 22:16:19 +0530
committerJohan Hedberg <johan.hedberg@intel.com>2017-01-28 08:43:41 +0200
commit91f9c9cf1cfd98daee949553bc423eab4f52c5d3 (patch)
treefd2269d114d135c8d151bd0dbb6e14de18135e0b
parent17ea79e733a5b84527b4331903adec97ecf49d1d (diff)
Bluetooth: RFCOMM: Implement MSC Flow Control
This is mainly for backward compatibility with 1.0b devices and for spec compliance. CFC is mandatory post 1.0b spec where in MSC FC shall not be used. FC bit in MSC is used to manage the flow control. If FC is 1 then the device is unable to accept frames. Implementation is done by reusing "tx_credit" as a binary semaphore wherein it will be blocked if MSC is recieved with FC bit 1 and unblocked if FC bit is 0. Once tx thread is scheduled then semaphore should be always available until all the buf in queue is sent. Change-Id: I91181668ec0f46ff0b02905dd97e4503fc1fa7a7 Signed-off-by: Jaganath Kanakkassery <jaganathx.kanakkassery@intel.com>
-rw-r--r--include/bluetooth/rfcomm.h2
-rw-r--r--subsys/bluetooth/host/rfcomm.c43
-rw-r--r--subsys/bluetooth/host/rfcomm_internal.h2
3 files changed, 38 insertions, 9 deletions
diff --git a/include/bluetooth/rfcomm.h b/include/bluetooth/rfcomm.h
index d54f57434..3da79574a 100644
--- a/include/bluetooth/rfcomm.h
+++ b/include/bluetooth/rfcomm.h
@@ -80,7 +80,7 @@ struct bt_rfcomm_dlc {
/* Queue for outgoing data */
struct k_fifo tx_queue;
- /** TX credits */
+ /* TX credits, Reuse as a binary sem for MSC FC if CFC is not enabled */
struct k_sem tx_credits;
struct bt_rfcomm_session *session;
diff --git a/subsys/bluetooth/host/rfcomm.c b/subsys/bluetooth/host/rfcomm.c
index d6752c667..c2893cd2d 100644
--- a/subsys/bluetooth/host/rfcomm.c
+++ b/subsys/bluetooth/host/rfcomm.c
@@ -451,7 +451,6 @@ static void rfcomm_dlc_init(struct bt_rfcomm_dlc *dlc,
dlc->rx_credit = RFCOMM_DEFAULT_CREDIT;
dlc->state = BT_RFCOMM_STATE_INIT;
dlc->role = role;
- k_sem_init(&dlc->tx_credits, 0, UINT32_MAX);
k_delayed_work_init(&dlc->rtx_work, rfcomm_dlc_rtx_timeout);
/* Start a conn timer which includes auth as well */
@@ -517,20 +516,23 @@ static void rfcomm_check_fc(struct bt_rfcomm_dlc *dlc)
{
BT_DBG("%p", dlc);
+ BT_DBG("Wait for credits or MSC FC %p", dlc);
+ /* Wait for credits or MSC FC */
+ k_sem_take(&dlc->tx_credits, K_FOREVER);
+
if (dlc->session->cfc == BT_RFCOMM_CFC_SUPPORTED) {
- BT_DBG("Wait for credits %p", dlc);
- /* Wait for credits */
- k_sem_take(&dlc->tx_credits, K_FOREVER);
return;
}
k_sem_take(&dlc->session->fc, K_FOREVER);
- /* Give the sem immediately so that sem will be available for all
+ /* Give the sems immediately so that sem will be available for all
* the bufs in the queue. It will be blocked only once all the bufs
- * are sent (which will preempt this thread) and FCOFF is received.
+ * are sent (which will preempt this thread) and FCOFF / FC bit
+ * with 1, is received.
*/
k_sem_give(&dlc->session->fc);
+ k_sem_give(&dlc->tx_credits);
}
static void rfcomm_dlc_tx_thread(void *p1, void *p2, void *p3)
@@ -748,6 +750,8 @@ static void rfcomm_dlc_connected(struct bt_rfcomm_dlc *dlc)
if (dlc->session->cfc == BT_RFCOMM_CFC_NOT_SUPPORTED) {
BT_DBG("CFC not supported %p", dlc);
rfcomm_send_fcon(dlc->session, BT_RFCOMM_MSG_CMD_CR);
+ /* Use tx_credits as binary sem for MSC FC */
+ k_sem_init(&dlc->tx_credits, 0, 1);
}
/* Cancel conn timer */
@@ -1058,9 +1062,30 @@ static void rfcomm_handle_msc(struct bt_rfcomm_session *session,
return;
}
- if (cr == BT_RFCOMM_MSG_CMD_CR) {
- rfcomm_send_msc(dlc, BT_RFCOMM_MSG_RESP_CR, msc->v24_signal);
+ if (cr == BT_RFCOMM_MSG_RESP_CR) {
+ return;
+ }
+
+ if (dlc->session->cfc == BT_RFCOMM_CFC_NOT_SUPPORTED) {
+ /* Only FC bit affects the flow on RFCOMM level */
+ if (BT_RFCOMM_GET_FC(msc->v24_signal)) {
+ /* If FC bit is 1 the device is unable to accept frames.
+ * Take the semaphore with timeout K_NO_WAIT so that
+ * dlc thread will be blocked when it tries sem_take
+ * before sending the data. K_NO_WAIT timeout will make
+ * sure that RX thread will not be blocked while taking
+ * the semaphore.
+ */
+ k_sem_take(&dlc->tx_credits, K_NO_WAIT);
+ } else {
+ /* Give the sem so that it will unblock the waiting dlc
+ * thread in sem_take().
+ */
+ k_sem_give(&dlc->tx_credits);
+ }
}
+
+ rfcomm_send_msc(dlc, BT_RFCOMM_MSG_RESP_CR, msc->v24_signal);
}
static void rfcomm_handle_rls(struct bt_rfcomm_session *session,
@@ -1165,6 +1190,7 @@ static void rfcomm_handle_pn(struct bt_rfcomm_session *session,
if (session->cfc == BT_RFCOMM_CFC_UNKNOWN) {
session->cfc = BT_RFCOMM_CFC_SUPPORTED;
}
+ k_sem_init(&dlc->tx_credits, 0, UINT32_MAX);
rfcomm_dlc_tx_give_credits(dlc, pn->credits);
} else {
session->cfc = BT_RFCOMM_CFC_NOT_SUPPORTED;
@@ -1194,6 +1220,7 @@ static void rfcomm_handle_pn(struct bt_rfcomm_session *session,
if (session->cfc == BT_RFCOMM_CFC_UNKNOWN) {
session->cfc = BT_RFCOMM_CFC_SUPPORTED;
}
+ k_sem_init(&dlc->tx_credits, 0, UINT32_MAX);
rfcomm_dlc_tx_give_credits(dlc, pn->credits);
} else {
session->cfc = BT_RFCOMM_CFC_NOT_SUPPORTED;
diff --git a/subsys/bluetooth/host/rfcomm_internal.h b/subsys/bluetooth/host/rfcomm_internal.h
index 850453040..c5b9e6187 100644
--- a/subsys/bluetooth/host/rfcomm_internal.h
+++ b/subsys/bluetooth/host/rfcomm_internal.h
@@ -120,6 +120,8 @@ struct bt_rfcomm_rpn {
/* DV = 1 IC = 0 RTR = 1 RTC = 1 FC = 0 EXT = 0 */
#define BT_RFCOMM_DEFAULT_V24_SIG 0x8d
+#define BT_RFCOMM_GET_FC(v24_signal) (((v24_signal) & 0x02) >> 1)
+
#define BT_RFCOMM_SIG_MIN_MTU 23
#define BT_RFCOMM_SIG_MAX_MTU 32767