Hi,
this is the v2 of the implementation of BCSP link establishment on the kernel side.
This patch was tested using hciattach from bluez version 5.23 that was modified to remove
the link establisment userland functions.
The BCSP endpoint will automatically handle connection switching from state SHY to CURIOUS
then GARULOUS.
Note: once the link is established, I cannot set the interface up using hciconfig. The problem is
in the HCI code that I do not completely understand. I get a timeout doing the __hci_init() call.
This might have to do with the way I test: 2 usb uarts connected together and both attached using hciattach.
In this setup, there is no one to answer __hci_req_sync().
Any comment is welcome, and I will keep investigating on my end to try to have this clear in my head.
Best regards.
v2:
- PATCH 1/2: fixes coding style issues
- PATCH 1/2: removes hack to notify upper layer of connection reset
- PATCH 1/2: uses hci_reset_dev for reset of the connection
- PATCH 2/2: adds the bcsp tx window value as a module parameter on top of the Kconfig entry.
- PATCH 2/2: longer description for the tx window parameter
Tristan Lelong (2):
hci_bcsp: Add LE protocol on kernel side
bcsp: Change tx window size in configuration
drivers/bluetooth/Kconfig | 18 ++++
drivers/bluetooth/hci_bcsp.c | 216 +++++++++++++++++++++++++++++++++++--------
2 files changed, 194 insertions(+), 40 deletions(-)
--
2.1.3
This patch make the TX window size configurable using the kernel configuration interface
or a module parameter: txwin.
The default value used is the previous value: 4
Signed-off-by: Tristan Lelong <[email protected]>
---
drivers/bluetooth/Kconfig | 18 ++++++++++++++++++
drivers/bluetooth/hci_bcsp.c | 10 ++++++----
2 files changed, 24 insertions(+), 4 deletions(-)
diff --git a/drivers/bluetooth/Kconfig b/drivers/bluetooth/Kconfig
index 364f080..3204ac6 100644
--- a/drivers/bluetooth/Kconfig
+++ b/drivers/bluetooth/Kconfig
@@ -59,6 +59,24 @@ config BT_HCIUART_BCSP
Say Y here to compile support for HCI BCSP protocol.
+config BT_HCIUART_BCSP_WINSIZE
+ int "BCSP reliable packet TX window size"
+ default 4
+ depends on BT_HCIUART_BCSP
+ help
+ The BCSP stack handles packet retransmission and acknowledgment.
+ For this it is tagging every packet with a sequence id coded
+ on 3 bits, and uses uses a ack to tell the other endpoint what is
+ the next expected packet. As soon as the number of non-ack'd
+ messages is equal to the TX window, the stack will put all
+ following packets on hold until the other endpoint sends an ack.
+
+ This parameter defines the size of the transmit window size,
+ thus, the number of consecutive messages that can be sent
+ without being acked.
+
+ This can also be configured at runtime using txwin module param.
+
config BT_HCIUART_ATH3K
bool "Atheros AR300x serial support"
depends on BT_HCIUART
diff --git a/drivers/bluetooth/hci_bcsp.c b/drivers/bluetooth/hci_bcsp.c
index 2685ab9..7d9aa1d 100644
--- a/drivers/bluetooth/hci_bcsp.c
+++ b/drivers/bluetooth/hci_bcsp.c
@@ -54,14 +54,13 @@
static bool txcrc = 1;
static bool hciextn = 1;
+static char txwin = CONFIG_BT_HCIUART_BCSP_WINSIZE;
static const u8 conf_pkt[4] = { 0xad, 0xef, 0xac, 0xed };
static const u8 conf_rsp_pkt[4] = { 0xde, 0xad, 0xd0, 0xd0 };
static const u8 sync_pkt[4] = { 0xda, 0xdc, 0xed, 0xed };
static const u8 sync_rsp_pkt[4] = { 0xac, 0xaf, 0xef, 0xee };
-#define BCSP_TXWINSIZE 4
-
#define BCSP_PKT_LEN(data) ((data[1] >> 4) + (data[2]))
#define BCSP_LE_PKT_LEN 0x04
@@ -335,7 +334,7 @@ static struct sk_buff *bcsp_dequeue(struct hci_uart *hu)
spin_lock_irqsave_nested(&bcsp->unack.lock, flags, SINGLE_DEPTH_NESTING);
- if (bcsp->unack.qlen < BCSP_TXWINSIZE) {
+ if (bcsp->unack.qlen < txwin) {
skb = skb_dequeue(&bcsp->rel);
if (skb != NULL) {
struct sk_buff *nskb = bcsp_prepare_pkt(bcsp, skb->data, skb->len,
@@ -428,7 +427,7 @@ static void bcsp_pkt_cull(struct hci_uart *hu)
/* Some reliable packets might be queued in ack queue,
* but not sent since the tx window was full.
* try to process them now. */
- if (bcsp->unack.qlen < BCSP_TXWINSIZE)
+ if (bcsp->unack.qlen < txwin)
if (skb_queue_len(&bcsp->rel))
hci_uart_tx_wakeup(hu);
@@ -901,3 +900,6 @@ MODULE_PARM_DESC(txcrc, "Transmit CRC with every BCSP packet");
module_param(hciextn, bool, 0644);
MODULE_PARM_DESC(hciextn, "Convert HCI Extensions into BCSP packets");
+
+module_param(txwin, byte, 0644);
+MODULE_PARM_DESC(txwin, "Change TX window size.");
--
2.1.3
Add the link establishment protocol handling in the kernel:
- le_state enum to handle state machine
- bcsp_timed_event to send LE messages
- bcsp_handle_le_pkt to answer endpoint LE messages
- various tests to muzzle BCSP link before GARULOUS
Signed-off-by: Tristan Lelong <[email protected]>
---
drivers/bluetooth/hci_bcsp.c | 210 +++++++++++++++++++++++++++++++++++--------
1 file changed, 172 insertions(+), 38 deletions(-)
diff --git a/drivers/bluetooth/hci_bcsp.c b/drivers/bluetooth/hci_bcsp.c
index 21cc45b..2685ab9 100644
--- a/drivers/bluetooth/hci_bcsp.c
+++ b/drivers/bluetooth/hci_bcsp.c
@@ -47,13 +47,24 @@
#include "hci_uart.h"
-#define VERSION "0.3"
+#define VERSION "0.4"
+
+#define LE_POLLING (HZ/10)
+#define TX_POLLING (HZ/4)
static bool txcrc = 1;
static bool hciextn = 1;
+static const u8 conf_pkt[4] = { 0xad, 0xef, 0xac, 0xed };
+static const u8 conf_rsp_pkt[4] = { 0xde, 0xad, 0xd0, 0xd0 };
+static const u8 sync_pkt[4] = { 0xda, 0xdc, 0xed, 0xed };
+static const u8 sync_rsp_pkt[4] = { 0xac, 0xaf, 0xef, 0xee };
+
#define BCSP_TXWINSIZE 4
+#define BCSP_PKT_LEN(data) ((data[1] >> 4) + (data[2]))
+#define BCSP_LE_PKT_LEN 0x04
+
#define BCSP_ACK_PKT 0x05
#define BCSP_LE_PKT 0x06
@@ -69,6 +80,12 @@ struct bcsp_struct {
struct timer_list tbcsp;
enum {
+ BCSP_SHY = 0,
+ BCSP_CURIOUS,
+ BCSP_GARULOUS
+ } le_state;
+
+ enum {
BCSP_W4_PKT_DELIMITER,
BCSP_W4_PKT_START,
BCSP_W4_BCSP_HDR,
@@ -184,6 +201,9 @@ static struct sk_buff *bcsp_prepare_pkt(struct bcsp_struct *bcsp, u8 *data,
u16 BCSP_CRC_INIT(bcsp_txmsg_crc);
int rel, i;
+ if (bcsp->le_state != BCSP_GARULOUS && pkt_type != BCSP_LE_PKT)
+ return NULL;
+
switch (pkt_type) {
case HCI_ACLDATA_PKT:
chan = 6; /* BCSP ACL channel */
@@ -299,10 +319,16 @@ static struct sk_buff *bcsp_dequeue(struct hci_uart *hu)
return nskb;
} else {
skb_queue_head(&bcsp->unrel, skb);
+ if (bcsp->le_state == BCSP_GARULOUS)
+ return NULL;
BT_ERR("Could not dequeue pkt because alloc_skb failed");
}
}
+ /* If BCSP is not connected yet, there is no need to go further */
+ if (bcsp->le_state != BCSP_GARULOUS)
+ return NULL;
+
/* Now, try to send a reliable pkt. We can only send a
reliable packet if the number of packets sent but not yet ack'ed
is < than the winsize */
@@ -316,11 +342,18 @@ static struct sk_buff *bcsp_dequeue(struct hci_uart *hu)
bt_cb(skb)->pkt_type);
if (nskb) {
__skb_queue_tail(&bcsp->unack, skb);
- mod_timer(&bcsp->tbcsp, jiffies + HZ / 4);
+
+ if (bcsp->le_state == BCSP_GARULOUS)
+ /* only re-arm retransmit timer
+ * if the BCSP link is connected */
+ mod_timer(&bcsp->tbcsp,
+ jiffies + HZ / 4);
spin_unlock_irqrestore(&bcsp->unack.lock, flags);
return nskb;
} else {
skb_queue_head(&bcsp->rel, skb);
+ if (bcsp->le_state != BCSP_GARULOUS)
+ return NULL;
BT_ERR("Could not dequeue pkt because alloc_skb failed");
}
}
@@ -350,8 +383,9 @@ static int bcsp_flush(struct hci_uart *hu)
}
/* Remove ack'ed packets */
-static void bcsp_pkt_cull(struct bcsp_struct *bcsp)
+static void bcsp_pkt_cull(struct hci_uart *hu)
{
+ struct bcsp_struct *bcsp = hu->priv;
struct sk_buff *skb, *tmp;
unsigned long flags;
int i, pkts_to_be_removed;
@@ -386,43 +420,111 @@ static void bcsp_pkt_cull(struct bcsp_struct *bcsp)
kfree_skb(skb);
}
- if (skb_queue_empty(&bcsp->unack))
+ if (skb_queue_empty(&bcsp->unack) && bcsp->le_state == BCSP_GARULOUS)
del_timer(&bcsp->tbcsp);
spin_unlock_irqrestore(&bcsp->unack.lock, flags);
+ /* Some reliable packets might be queued in ack queue,
+ * but not sent since the tx window was full.
+ * try to process them now. */
+ if (bcsp->unack.qlen < BCSP_TXWINSIZE)
+ if (skb_queue_len(&bcsp->rel))
+ hci_uart_tx_wakeup(hu);
+
if (i != pkts_to_be_removed)
BT_ERR("Removed only %u out of %u pkts", i, pkts_to_be_removed);
}
-/* Handle BCSP link-establishment packets. When we
- detect a "sync" packet, symptom that the BT module has reset,
- we do nothing :) (yet) */
+static void bcsp_internal_reset(struct hci_uart *hu)
+{
+ struct bcsp_struct *bcsp = hu->priv;
+
+ /* reset the bcsp stack counters */
+ bcsp->rxseq_txack = 0;
+ bcsp->rxack = 0;
+ bcsp->message_crc = 0;
+ bcsp->txack_req = 0;
+ bcsp->msgq_txseq = 0;
+
+ /* reset state machine */
+ bcsp->le_state = BCSP_SHY;
+ bcsp->rx_state = BCSP_W4_PKT_DELIMITER;
+ bcsp->rx_esc_state = BCSP_ESCSTATE_NOESC;
+
+ /* empty all the queues */
+ skb_queue_purge(&bcsp->unack);
+ skb_queue_purge(&bcsp->rel);
+ skb_queue_purge(&bcsp->unrel);
+
+ /* tell upper layer about the reset */
+ hci_reset_dev(hu->hdev);
+
+ bcsp->tbcsp.expires = jiffies + LE_POLLING;
+ add_timer(&bcsp->tbcsp);
+}
+
+/* Handle BCSP link-establishment packets.
+ */
static void bcsp_handle_le_pkt(struct hci_uart *hu)
{
+ struct sk_buff *nskb;
struct bcsp_struct *bcsp = hu->priv;
- u8 conf_pkt[4] = { 0xad, 0xef, 0xac, 0xed };
- u8 conf_rsp_pkt[4] = { 0xde, 0xad, 0xd0, 0xd0 };
- u8 sync_pkt[4] = { 0xda, 0xdc, 0xed, 0xed };
-
- /* spot "conf" pkts and reply with a "conf rsp" pkt */
- if (bcsp->rx_skb->data[1] >> 4 == 4 && bcsp->rx_skb->data[2] == 0 &&
- !memcmp(&bcsp->rx_skb->data[4], conf_pkt, 4)) {
- struct sk_buff *nskb = alloc_skb(4, GFP_ATOMIC);
-
- BT_DBG("Found a LE conf pkt");
- if (!nskb)
- return;
- memcpy(skb_put(nskb, 4), conf_rsp_pkt, 4);
- bt_cb(nskb)->pkt_type = BCSP_LE_PKT;
-
- skb_queue_head(&bcsp->unrel, nskb);
- hci_uart_tx_wakeup(hu);
- }
- /* Spot "sync" pkts. If we find one...disaster! */
- else if (bcsp->rx_skb->data[1] >> 4 == 4 && bcsp->rx_skb->data[2] == 0 &&
- !memcmp(&bcsp->rx_skb->data[4], sync_pkt, 4)) {
- BT_ERR("Found a LE sync pkt, card has reset");
+
+ if (BCSP_PKT_LEN(bcsp->rx_skb->data) == BCSP_LE_PKT_LEN) {
+ if (!memcmp(&bcsp->rx_skb->data[4], conf_pkt, 4)) {
+ /* spot "conf" pkts and reply with a "conf rsp" pkt */
+ if (bcsp->le_state == BCSP_SHY)
+ return;
+
+ BT_DBG("Found a LE conf pkt");
+ nskb = alloc_skb(4, GFP_ATOMIC);
+ if (!nskb)
+ return;
+ memcpy(skb_put(nskb, 4), conf_rsp_pkt, 4);
+ bt_cb(nskb)->pkt_type = BCSP_LE_PKT;
+
+ skb_queue_head(&bcsp->unrel, nskb);
+ hci_uart_tx_wakeup(hu);
+ } else if (!memcmp(&bcsp->rx_skb->data[4], sync_pkt, 4)) {
+ /* Spot "sync" pkts and reply with a "sync rsp" pkt */
+ if (bcsp->le_state == BCSP_GARULOUS)
+ /* we force the connection state to reset */
+ bcsp_internal_reset(hu);
+
+ BT_INFO("BCSP: hci%d LE sync pkt, performing reset",
+ hu->hdev->id);
+
+ nskb = alloc_skb(4, GFP_ATOMIC);
+ if (!nskb)
+ return;
+ memcpy(skb_put(nskb, 4), sync_rsp_pkt, 4);
+ bt_cb(nskb)->pkt_type = BCSP_LE_PKT;
+
+ skb_queue_head(&bcsp->unrel, nskb);
+ hci_uart_tx_wakeup(hu);
+ } else if (!memcmp(&bcsp->rx_skb->data[4], conf_rsp_pkt, 4)) {
+ /* Spot "conf rsp" pkts and update state machine */
+ if (bcsp->le_state != BCSP_CURIOUS) {
+ BT_DBG("BCSP: hci%d ingoring conf_resp packet",
+ hu->hdev->id);
+ return;
+ }
+
+ BT_INFO("BCSP: hci%d connected", hu->hdev->id);
+ bcsp->le_state = BCSP_GARULOUS;
+ } else if (!memcmp(&bcsp->rx_skb->data[4], sync_rsp_pkt, 4)) {
+ /* Spot "sync rsp" pkts and update state machine */
+ if (bcsp->le_state != BCSP_SHY) {
+ BT_DBG("BCSP: hci%d ignoring sync_resp packet",
+ hu->hdev->id);
+ return;
+ }
+
+ BT_DBG("Found a LE sync rsp pkt");
+ bcsp->le_state = BCSP_CURIOUS;
+ }
+
}
}
@@ -493,7 +595,7 @@ static void bcsp_complete_rx_pkt(struct hci_uart *hu)
bcsp->rxack = (bcsp->rx_skb->data[0] >> 3) & 0x07;
BT_DBG("Request for pkt %u from card", bcsp->rxack);
- bcsp_pkt_cull(bcsp);
+ bcsp_pkt_cull(hu);
if ((bcsp->rx_skb->data[1] & 0x0f) == 6 &&
bcsp->rx_skb->data[0] & 0x80) {
bt_cb(bcsp->rx_skb)->pkt_type = HCI_ACLDATA_PKT;
@@ -537,11 +639,13 @@ static void bcsp_complete_rx_pkt(struct hci_uart *hu)
}
} else
kfree_skb(bcsp->rx_skb);
- } else {
+ } else if (bcsp->le_state == BCSP_GARULOUS) {
/* Pull out BCSP hdr */
skb_pull(bcsp->rx_skb, 4);
hci_recv_frame(hu->hdev, bcsp->rx_skb);
+ } else {
+ kfree_skb(bcsp->rx_skb);
}
bcsp->rx_state = BCSP_W4_PKT_DELIMITER;
@@ -676,16 +780,42 @@ static void bcsp_timed_event(unsigned long arg)
struct sk_buff *skb;
unsigned long flags;
- BT_DBG("hu %p retransmitting %u pkts", hu, bcsp->unack.qlen);
+ switch (bcsp->le_state) {
+ case BCSP_GARULOUS:
+ BT_DBG("hu %p retransmitting %u pkts", hu, bcsp->unack.qlen);
- spin_lock_irqsave_nested(&bcsp->unack.lock, flags, SINGLE_DEPTH_NESTING);
+ spin_lock_irqsave_nested(&bcsp->unack.lock, flags,
+ SINGLE_DEPTH_NESTING);
- while ((skb = __skb_dequeue_tail(&bcsp->unack)) != NULL) {
- bcsp->msgq_txseq = (bcsp->msgq_txseq - 1) & 0x07;
- skb_queue_head(&bcsp->rel, skb);
- }
+ while ((skb = __skb_dequeue_tail(&bcsp->unack)) != NULL) {
+ bcsp->msgq_txseq = (bcsp->msgq_txseq - 1) & 0x07;
+ skb_queue_head(&bcsp->rel, skb);
+ }
- spin_unlock_irqrestore(&bcsp->unack.lock, flags);
+ spin_unlock_irqrestore(&bcsp->unack.lock, flags);
+ break;
+ case BCSP_CURIOUS:
+ skb = alloc_skb(4, GFP_ATOMIC);
+ memcpy(skb_put(skb, 4), conf_pkt, 4);
+ bt_cb(skb)->pkt_type = BCSP_LE_PKT;
+ skb_queue_head(&bcsp->unrel, skb);
+
+ bcsp->tbcsp.expires += LE_POLLING;
+ add_timer(&bcsp->tbcsp);
+ break;
+ default:
+ BT_INFO("Internal state unknown. Assuming not connected.");
+ bcsp->le_state = BCSP_SHY;
+ case BCSP_SHY:
+ skb = alloc_skb(4, GFP_ATOMIC);
+ memcpy(skb_put(skb, 4), sync_pkt, 4);
+ bt_cb(skb)->pkt_type = BCSP_LE_PKT;
+ skb_queue_head(&bcsp->unrel, skb);
+
+ bcsp->tbcsp.expires += LE_POLLING;
+ add_timer(&bcsp->tbcsp);
+ break;
+ }
hci_uart_tx_wakeup(hu);
}
@@ -708,12 +838,16 @@ static int bcsp_open(struct hci_uart *hu)
init_timer(&bcsp->tbcsp);
bcsp->tbcsp.function = bcsp_timed_event;
bcsp->tbcsp.data = (u_long) hu;
+ bcsp->tbcsp.expires = jiffies + LE_POLLING;
bcsp->rx_state = BCSP_W4_PKT_DELIMITER;
+ bcsp->le_state = BCSP_SHY;
if (txcrc)
bcsp->use_crc = 1;
+ add_timer(&bcsp->tbcsp);
+
return 0;
}
--
2.1.3