Преглед на файлове

📃 docs: Add HCI Transport.md

添加了各家协议栈HCI与HCI传输层之间的接口。
Jackistang преди 4 години
родител
ревизия
c6d9b39d2f
променени са 4 файла, в които са добавени 138 реда и са изтрити 15 реда
  1. 21 0
      docs/BTStack.md
  2. 93 0
      docs/HCI_Tramsport.md
  3. 5 5
      src/hci_transport_h4.h
  4. 19 10
      src/hci_transport_uart_linux.c

+ 21 - 0
docs/BTStack.md

@@ -0,0 +1,21 @@
+
+
+BTStack 通过自定义 HCI 事件 `HCI_EVENT_TRANSPORT_PACKET_SENT` 通知 HCI 当前数据已发送完毕,可以再次发送。完整的事件字节流为:`0x04 0x6E 0x00`,参考代码:
+
+> hci_transport_h4.c -> hci_transport_h4_block_send() : 328 行
+
+在 hci.c 中可以看见该事件用于 BTStack 内部使用:
+
+```C
+case HCI_EVENT_TRANSPORT_PACKET_SENT:
+    // release packet buffer only for asynchronous transport and if there are not further fragements
+    if (hci_transport_synchronous()) {
+        log_error("Synchronous HCI Transport shouldn't send HCI_EVENT_TRANSPORT_PACKET_SENT");
+        return; // instead of break: to avoid re-entering hci_run()
+    }
+    hci_stack->acl_fragmentation_tx_active = 0;
+    if (hci_stack->acl_fragmentation_total_size) break;
+    hci_release_packet_buffer();
+    break;
+```
+

+ 93 - 0
docs/HCI_Tramsport.md

@@ -52,3 +52,96 @@ bit errors, overrun errors, burst errors.
 
 SLIP 将 0xC0 放置在数据报的起始处和末尾处,并将数据报内部的 0xC0 转义成 0xDB 0xDC,将数据报内部的 0xDB 转义成 0xDB 0xDD 。
 
+
+
+## 接口
+
+**BTStack:**
+
+```C
+typedef struct {
+    const char * name;
+    void   (*init) (const void *transport_config);
+    int    (*open)(void);
+    int    (*close)(void);
+    void   (*register_packet_handler)(void (*handler)(uint8_t packet_type, uint8_t *packet, uint16_t size));
+        /**
+     * support async transport layers, e.g. IRQ driven without buffers
+     */
+    int    (*can_send_packet_now)(uint8_t packet_type);
+    int    (*send_packet)(uint8_t packet_type, uint8_t *packet, int size);
+    int    (*set_baudrate)(uint32_t baudrate);
+    /**
+     * extension for UART H5 on CSR: reset BCSP/H5 Link
+     */
+    void   (*reset_link)(void);
+    /**
+     * extension for USB transport implementations: config SCO connections
+     */
+    void   (*set_sco_config)(uint16_t voice_setting, int num_connections);
+} hci_transport_t;
+```
+
+用 `init()` 初始化传输层,`open()` 和 `close()` 分别打开关闭传输层。`send_packet()` 发送 HCI 包,`can_send_packet_now()` 用于支持异步传输,例如我调用 `send_packet()` 将 HCI 包放到 DMA 缓冲区等待传输,传输时调用 `can_send_packet_now()` 返回 false,这样就可以避免破坏 DMA 缓冲区,造成数据传输混乱,当传输完成后,再调用 `can_send_packet_now()` 返回 true,表示可以继续传输。`register_packet_handler()` 函数注册接收回调函数,传输层还支持直接修改波特率 `set_baudrate()` 。
+
+H5 接口额外支持 `reset_link()`,USB 额外接口支持 `set_sco_config()` 。
+
+
+
+**Zephyr:**
+
+```C
+struct bt_hci_driver {
+	const char *name;
+	enum bt_hci_driver_bus bus;
+	uint32_t quirks;
+	int (*open)(void);
+	int (*send)(struct net_buf *buf);
+};
+```
+
+`open()` 用于打开 HCI Transport,并创建线程接收数据,`send()` 用于发送数据(中断发送),在创建的线程里接收数据后调用 `bt_recv()` 将数据通过消息队列传递给 HCI 线程。
+
+表驱动处理事件。
+
+
+
+**NimBLE:**
+
+Host HCI Transport 接口:
+
+```C
+int ble_hci_trans_hs_cmd_tx(uint8_t *cmd);
+int ble_hci_trans_hs_acl_tx(struct os_mbuf *om);
+void ble_hci_trans_cfg_hs(ble_hci_trans_rx_cmd_fn *cmd_cb,
+                          void *cmd_arg,
+                          ble_hci_trans_rx_acl_fn *acl_cb,
+                          void *acl_arg);
+
+uint8_t *ble_hci_trans_buf_alloc(int type);
+void ble_hci_trans_buf_free(uint8_t *buf);
+int ble_hci_trans_set_acl_free_cb(os_mempool_put_fn *cb, void *arg);
+
+int ble_hci_trans_reset(void);
+```
+
+该接口在 `ble_hci_trans.h` 文件里。 
+
+由于 NimBLE 同时支持 Controller 和 Host,上述只列出了与 Host 相关的 HCI Transport 接口。
+
+- `ble_hci_trans_hs_cmd_tx()` 函数用于发送 HCI 命令,`cmd` 内存是由 `ble_hci_trans_buf_alloc()` 申请的,并且该函数负责释放这片空间?
+- `ble_hci_trans_hs_acl_tx()` 函数用于发送 ACL 数据,数据格式为 NimBLE 定义的 `struct os_mbuf` 。
+- `ble_hci_trans_cfg_hs()` 注册 Event 事件接收回调函数,和 ACL 数据接收回调函数。
+
+- `ble_hci_trans_buf_alloc()` 根据参数 type 分配相应的内存空间,自定义的。
+- `ble_hci_trans_buf_free()` 释放分配的内存空间。
+- `ble_hci_trans_set_acl_free_cb()` 注册 ACL 数据释放的回调函数,(不太懂)
+- `ble_hci_trans_reset()` 复位传输层。
+
+
+
+从目前调研结果可知,共有两种类型的接口:
+
+1. 类似 BTStack、NimBLE 这种,提供通用的 HCI Transport 供实现,移植目标较为明确。
+2. 类似 Zephyr 这种,提供了一些 HCI Transport 接口,需要我们在收发包的时候调用这些接口,对接该类协议栈需要先研究其 HCI Transport 如何实现,较为麻烦。
+

+ 5 - 5
src/hci_transport_h4.h

@@ -8,17 +8,17 @@
 extern "C" {
 #endif
 
-typedef enum {
+enum {
     HCI_TRANSPORT_H4_COMMAND = 1,
     HCI_TRANSPORT_H4_ACL,
     HCI_TRANSPORT_H4_SYNC,
     HCI_TRANSPORT_H4_EVENT,
     HCI_TRANSPORT_H4_ISO,
-} HCI_TRANSPORT_H4_PACKAGE_TYPE;
+};
 
-int rt_hci_transport_h4_send(HCI_TRANSPORT_H4_PACKAGE_TYPE type, uint8_t *buf, size_t size);
-
-void rt_hci_transport_h4_register_callback(void (*package_callback)(uint8_t type, uint8_t *buf, size_t size));
+extern int rt_hci_transport_h4_send(int type, uint8_t *buf, size_t size);
+extern void rt_hci_transport_h4_register_callback(void (*package_callback)(uint8_t type, uint8_t *buf, size_t size));
+extern void rt_hci_transport_h4_loop(void);
 
 
 #ifdef __cplusplus

+ 19 - 10
src/hci_transport_uart_linux.c

@@ -15,8 +15,9 @@ enum {
     UART_TYPE_INIT,
 };
 
-static int set_baudrate(struct termios * toptions, uint32_t baudrate)
+static int set_baudrate(struct termios * toptions, void *args)
 {
+    uint32_t baudrate = *(uint32_t *)args;
     printf("h4_set_baudrate %u\n", baudrate);
 
     speed_t brate = baudrate; // let you override switch below if needed
@@ -75,8 +76,10 @@ static int set_baudrate(struct termios * toptions, uint32_t baudrate)
     return 0;
 }
 
-static int set_parity(struct termios * toptions, int parity)
+static int set_parity(struct termios * toptions, void *args)
 {
+    int parity = *(int *)args;
+
     switch (parity){
         case UART_PARITY_NONE:
             toptions->c_cflag &= ~PARENB;
@@ -95,8 +98,10 @@ static int set_parity(struct termios * toptions, int parity)
     return 0;
 }
 
-static int set_stopbit(struct termios * toptions, int stopbit)
+static int set_stopbit(struct termios * toptions, void *args)
 {
+    int stopbit = *(int *)args;
+
     switch (stopbit) {
     case UART_STOPBIT_1_BIT:
         toptions->c_cflag &= ~CSTOPB;
@@ -111,8 +116,10 @@ static int set_stopbit(struct termios * toptions, int stopbit)
     return 0;
 }
 
-static int set_databit(struct termios * toptions, int databit)
+static int set_databit(struct termios * toptions, void *args)
 {
+    int databit = *(int *)args;
+
     toptions->c_cflag &= ~CSIZE;
     switch (databit) {
     case UART_DATABIT_5_BIT:
@@ -133,8 +140,10 @@ static int set_databit(struct termios * toptions, int databit)
     }
 }
 
-static int set_flowcontrol(struct termios * toptions, bool flowcontrol)
+static int set_flowcontrol(struct termios * toptions, void *args)
 {
+    bool flowcontrol = *(bool *)args;
+
     if (flowcontrol) {
         // with flow control
         toptions->c_cflag |= CRTSCTS;
@@ -159,19 +168,19 @@ static int hci_transport_uart_set_params(int type, void *params)
     case UART_TYPE_INIT:
         toptions.c_cflag |= CREAD;
     case UART_TYPE_BAUDRATE:
-        set_baudrate(&toptions, *(uint32_t *)params);
+        set_baudrate(&toptions, params);
         break;
     case UART_TYPE_DATABIT:
-        set_databit(&toptions, *(int *)params);
+        set_databit(&toptions, params);
         break;
     case UART_TYPE_STOPBIT:
-        set_stopbit(&toptions, *(int *)params);
+        set_stopbit(&toptions, params);
         break;
     case UART_TYPE_PARITY:
-        set_parity(&toptions, *(int *)params);
+        set_parity(&toptions, params);
         break;
     case UART_TYPE_FLOWCONTROL:
-        set_flowcontrol(&toptions, *(bool *)params);
+        set_flowcontrol(&toptions, params);
         break;
     default:
         break;