From cc36c44824bdd49d5b1ad22e10b290aad31e98db Mon Sep 17 00:00:00 2001 From: chengkai Date: Tue, 5 Dec 2023 20:24:10 +0800 Subject: [PATCH] bluetooth:add btslip driver add btslip driver. Signed-off-by: chengkai --- drivers/wireless/bluetooth/Kconfig | 20 + drivers/wireless/bluetooth/Make.defs | 4 + drivers/wireless/bluetooth/bt_slip.c | 1044 ++++++++++++++++++++ include/nuttx/wireless/bluetooth/bt_slip.h | 49 + 4 files changed, 1117 insertions(+) create mode 100644 drivers/wireless/bluetooth/bt_slip.c create mode 100644 include/nuttx/wireless/bluetooth/bt_slip.h diff --git a/drivers/wireless/bluetooth/Kconfig b/drivers/wireless/bluetooth/Kconfig index e1af5d6cf30..5c07b46856c 100644 --- a/drivers/wireless/bluetooth/Kconfig +++ b/drivers/wireless/bluetooth/Kconfig @@ -85,4 +85,24 @@ config BLUETOOTH_RPMSG depends on EXPERIMENTAL && RPTUN default n +config BLUETOOTH_SLIP + bool "Bluetooth SLIP support" + default n + +if BLUETOOTH_SLIP + +config BLUETOOTH_SLIP_TXBUFSIZE + int "BT H5 UART TX Buffer Size" + default 2048 + ---help--- + H5 UART TX Buffer Size. Default: 2048 + +config BLUETOOTH_SLIP_TXWIN + int "BT H5 Uart TX Window Size" + default 4 + ---help--- + H5 UART TX Window Size. Default: 4 + +endif # BLUETOOTH_SLIP + endif # DRIVERS_BLUETOOTH diff --git a/drivers/wireless/bluetooth/Make.defs b/drivers/wireless/bluetooth/Make.defs index 963a172347e..a5bc196bb84 100644 --- a/drivers/wireless/bluetooth/Make.defs +++ b/drivers/wireless/bluetooth/Make.defs @@ -56,6 +56,10 @@ ifeq ($(CONFIG_BLUETOOTH_RPMSG),y) CSRCS += bt_rpmsghci.c endif +ifeq ($(CONFIG_BLUETOOTH_SLIP),y) +CSRCS += bt_slip.c +endif + # Include common Bluetooth driver build support DEPPATH += --dep-path wireless$(DELIM)bluetooth diff --git a/drivers/wireless/bluetooth/bt_slip.c b/drivers/wireless/bluetooth/bt_slip.c new file mode 100644 index 00000000000..688668edff2 --- /dev/null +++ b/drivers/wireless/bluetooth/bt_slip.c @@ -0,0 +1,1044 @@ +/**************************************************************************** + * drivers/wireless/bluetooth/bt_slip.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define HCI_3WIRE_ACK_PKT 0x00 +#define HCI_COMMAND_PKT 0x01 +#define HCI_ACLDATA_PKT 0x02 +#define HCI_SCODATA_PKT 0x03 +#define HCI_EVENT_PKT 0x04 +#define HCI_ISODATA_PKT 0x05 +#define HCI_3WIRE_LINK_PKT 0x0f +#define HCI_VENDOR_PKT 0xff + +#define SLIP_DELIMITER 0xc0 +#define SLIP_ESC 0xdb +#define SLIP_ESC_DELIM 0xdc +#define SLIP_ESC_ESC 0xdd + +#define BT_SLIP_HEADER_LEN 4 +#define BT_SLIP_CHECKSUM_LEN 2 +#define BT_SLIP_RTX_TIMEOUT MSEC2TICK (150) /* 150ms */ + +#define BT_SLIP_GET_SEQ(header) ((header)[0] & 0x07) +#define BT_SLIP_GET_ACK(header) (((header)[0] >> 3) & 0x07) +#define BT_SLIP_GET_CRC(header) (((header)[0] >> 6) & 0x01) +#define BT_SLIP_GET_PKT_TYPE(header) ((header)[1] & 0x0f) +#define BT_SLIP_GET_LEN(header) ((((header)[1] >> 4) & 0x0f) + ((header)[2] << 4)) + +#define BT_SLIP_SET_SEQ(header, seq) ((header)[0] |= (seq)) +#define BT_SLIP_SET_ACK(header, ack) ((header)[0] |= (ack) << 3) +#define BT_SLIP_SET_DIC(header) ((header)[0] |= 0x40) +#define BT_SLIP_SET_RELIABLE(header) ((header)[0] |= (1 << 7)) +#define BT_SLIP_SET_TYPE(header, type) ((header)[1] |= (type)) +#define BT_SLIP_SET_LEN(header, len) \ + (((header)[1] |= ((len) & 0x0f) << 4), ((header)[2] |= (len) >> 4)) +#define BT_SLIP_SET_HDRCHECKSUM(header) \ + ((header)[3] = ~((header[0] + header[1] + header[2]) & 0xff)) +#define BT_SLIP_SEQ_INCR(seq) ((seq) = (((seq) + 1) & 0x07)) + +#define BT_SLIP_IS_RELIABLE(header) (((header)[0] >> 7) & 0x01) +#define BT_SLIP_IS_HDRVALIDED(header) \ + (((header[0] + header[1] + header[2] + header[3]) & 0xff) == 0xff) + +#define BT_SLIP_GET_TXWIN(payload) (payload[2] & 0x07) +#define BT_SLIP_IS_DIC(payload) ((payload[2] & 0x10) == 0x10) + +#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof(arr[0])) + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct unack_pool_s +{ + size_t start; + size_t end; + size_t size; + + uint8_t buf[CONFIG_BLUETOOTH_SLIP_TXWIN][CONFIG_BLUETOOTH_SLIP_TXBUFSIZE]; +}; + +struct unack_frame_s +{ + enum bt_buf_type_e type; + size_t pktlen; + uint8_t data[1]; +}; + +struct sliphci_s +{ + FAR struct bt_driver_s dev; + FAR struct bt_driver_s *drv; + + mutex_t sliplock; + mutex_t unacklock; + sem_t sem; + + struct work_s retxworker; + struct unack_pool_s unackpool; + + enum + { + BT_SLIP_UNINITIALIZED, + BT_SLIP_INITIALIZED, + BT_SLIP_ACTIVE, + } linkstate; + + bool dipresent; /* Data integrity check */ + bool nonblock; /* Send hci in noblock mode */ + + uint8_t rxack; /* Last ack number received */ + uint8_t txseq; /* Next seq number to send */ + uint8_t txack; /* Next ack number to send */ + uint8_t txwin; /* Sliding window size */ +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static int bt_slip_open(FAR struct bt_driver_s *dev); + +static int bt_slip_send(FAR struct bt_driver_s *dev, enum bt_buf_type_e type, + FAR void *data, size_t len); + +static int bt_slip_ioctl(FAR struct bt_driver_s *dev, int cmd, + unsigned long arg); + +static void bt_slip_close(FAR struct bt_driver_s *dev); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static const uint8_t g_sync_req[] = + { + 0x01, 0x7e + }; + +static const uint8_t g_sync_rsp[] = + { + 0x02, 0x7d + }; + +static const uint8_t g_conf_req[] = + { + 0x03, 0xfc, 0x10 | CONFIG_BLUETOOTH_SLIP_TXWIN + }; + +static const uint8_t g_conf_rsp[] = + { + 0x04, 0x7b + }; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static uint8_t bt_slip_bit8_reverse(uint8_t byte) +{ + static const uint8_t table[256] = + { + 0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0, 0x10, 0x90, 0x50, 0xd0, + 0x30, 0xb0, 0x70, 0xf0, 0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8, + 0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8, 0x04, 0x84, 0x44, 0xc4, + 0x24, 0xa4, 0x64, 0xe4, 0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4, + 0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec, 0x1c, 0x9c, 0x5c, 0xdc, + 0x3c, 0xbc, 0x7c, 0xfc, 0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2, + 0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2, 0x0a, 0x8a, 0x4a, 0xca, + 0x2a, 0xaa, 0x6a, 0xea, 0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa, + 0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6, 0x16, 0x96, 0x56, 0xd6, + 0x36, 0xb6, 0x76, 0xf6, 0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee, + 0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe, 0x01, 0x81, 0x41, 0xc1, + 0x21, 0xa1, 0x61, 0xe1, 0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1, + 0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9, 0x19, 0x99, 0x59, 0xd9, + 0x39, 0xb9, 0x79, 0xf9, 0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5, + 0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5, 0x0d, 0x8d, 0x4d, 0xcd, + 0x2d, 0xad, 0x6d, 0xed, 0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd, + 0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3, 0x13, 0x93, 0x53, 0xd3, + 0x33, 0xb3, 0x73, 0xf3, 0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb, + 0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb, 0x07, 0x87, 0x47, 0xc7, + 0x27, 0xa7, 0x67, 0xe7, 0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7, + 0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef, 0x1f, 0x9f, 0x5f, 0xdf, + 0x3f, 0xbf, 0x7f, 0xff + }; + + return table[byte]; +} + +static uint16_t bt_slip_bit16_reverse(uint16_t x) +{ + return (bt_slip_bit8_reverse(x & 0xff) << 8) + | bt_slip_bit8_reverse(x >> 8); +} + +static uint16_t bt_slip_get_crc(FAR uint8_t *packet) +{ + return packet[1] + (packet[0] << 8); +} + +static void bt_slip_unack_init(FAR struct sliphci_s *priv) +{ + FAR struct unack_pool_s *pool = &priv->unackpool; + + nxmutex_lock(&priv->unacklock); + pool->start = 0; + pool->end = 0; + pool->size = 0; + nxmutex_unlock(&priv->unacklock); +} + +static FAR void *bt_slip_unack_ctor(FAR struct sliphci_s *priv) +{ + FAR struct unack_pool_s *pool = &priv->unackpool; + FAR void *buf; + + nxmutex_lock(&priv->unacklock); + buf = pool->buf[pool->start++]; + pool->start %= CONFIG_BLUETOOTH_SLIP_TXWIN; + pool->size++; + nxmutex_unlock(&priv->unacklock); + + return buf; +} + +static FAR void *bt_slip_unack_dtor(FAR struct sliphci_s *priv) +{ + FAR struct unack_pool_s *pool = &priv->unackpool; + FAR void *buf; + + nxmutex_lock(&priv->unacklock); + buf = pool->buf[pool->end++]; + pool->end %= CONFIG_BLUETOOTH_SLIP_TXWIN; + pool->size--; + nxmutex_unlock(&priv->unacklock); + + return buf; +} + +static size_t bt_slip_unack_size(FAR struct sliphci_s *priv) +{ + FAR struct unack_pool_s *pool = &priv->unackpool; + size_t size; + + nxmutex_lock(&priv->unacklock); + size = pool->size; + nxmutex_unlock(&priv->unacklock); + + return size; +} + +static void bt_slip_unack_cleanup(FAR struct sliphci_s *priv) +{ + bt_slip_unack_init(priv); +} + +static FAR uint8_t *bt_slip_unslip_byte(FAR uint8_t *packet, + FAR uint8_t *byte) +{ + uint8_t offset = 0; + + if (packet[0] != SLIP_ESC) + { + *byte = packet[0]; + return packet; + } + + switch (packet[1]) + { + case SLIP_ESC_DELIM: + *byte = SLIP_DELIMITER; + offset = 1; + break; + case SLIP_ESC_ESC: + *byte = SLIP_ESC; + offset = 1; + break; + default: + wlerr("err: invalid escape byte %x\n", *byte); + return NULL; + } + + packet += offset; + return packet; +} + +static uint8_t *bt_slip_slip_byte(FAR uint8_t *packet, FAR uint8_t byte) +{ + uint8_t offset = 1; + + switch (byte) + { + case SLIP_DELIMITER: + packet[0] = SLIP_ESC; + packet[1] = SLIP_ESC_DELIM; + offset = 2; + break; + case SLIP_ESC: + packet[0] = SLIP_ESC; + packet[1] = SLIP_ESC_ESC; + offset = 2; + default: + packet[0] = byte; + break; + } + + packet += offset; + return packet; +} + +static void bt_slip_retx_work(FAR void *arg) +{ + FAR struct sliphci_s *priv = arg; + FAR struct unack_frame_s *frame; + size_t size; + + if (priv->linkstate != BT_SLIP_ACTIVE) + { + wlerr("err: linkstate:%d not active", priv->linkstate); + return; + } + + size = bt_slip_unack_size(priv); + while (size > 0) + { + frame = (FAR struct unack_frame_s *)bt_slip_unack_dtor(priv); + bt_slip_send(&priv->dev, frame->type, frame->data, frame->pktlen); + size--; + } +} + +static bool bt_slip_reliable_packet(uint8_t type) +{ + switch (type) + { + case HCI_COMMAND_PKT: + case HCI_ACLDATA_PKT: + case HCI_EVENT_PKT: + case HCI_ISODATA_PKT: + return true; + default: + return false; + } +} + +static int bt_slip_send_packet(FAR struct sliphci_s *priv, uint8_t type, + FAR const uint8_t *payload, size_t len) +{ + uint8_t packet[CONFIG_BLUETOOTH_SLIP_TXBUFSIZE]; + uint8_t header[BT_SLIP_HEADER_LEN]; + FAR uint8_t *pend; + uint16_t checksum = 0; + int index; + + memset(header, 0, sizeof(header)); + if (bt_slip_reliable_packet(type)) + { + BT_SLIP_SET_RELIABLE(header); + BT_SLIP_SET_SEQ(header, priv->txseq); + BT_SLIP_SEQ_INCR(priv->txseq); + } + + BT_SLIP_SET_ACK(header, priv->txack); + BT_SLIP_SET_TYPE(header, type); + BT_SLIP_SET_LEN(header, len); + + if (priv->dipresent) + { + BT_SLIP_SET_DIC(header); + } + + /* Set head checksum */ + + BT_SLIP_SET_HDRCHECKSUM(header); + pend = packet; + + /* Put SLIP start */ + + *pend++ = SLIP_DELIMITER; + + /* Put h5 header */ + + if (priv->dipresent) + { + checksum = crc16ccittpart(header, BT_SLIP_HEADER_LEN, 0xffff); + } + + for (index = 0; index < BT_SLIP_HEADER_LEN; index++) + { + pend = bt_slip_slip_byte(pend, header[index]); + } + + /* Put h5 payload */ + + if (priv->dipresent) + { + checksum = crc16ccittpart(payload, len, checksum); + } + + for (index = 0; index < len; index++) + { + pend = bt_slip_slip_byte(pend, payload[index]); + } + + /* Put crc */ + + if (priv->dipresent) + { + checksum = bt_slip_bit16_reverse(checksum); + pend = bt_slip_slip_byte(pend, checksum >> 8); + pend = bt_slip_slip_byte(pend, checksum & 0xff); + } + + /* Put SLIP end */ + + *pend++ = SLIP_DELIMITER; + + wlinfo("tx t:%d l:%d s:%d a:%d\n", BT_SLIP_GET_PKT_TYPE(header), + BT_SLIP_GET_LEN(header), BT_SLIP_GET_SEQ(header), + BT_SLIP_GET_ACK(header)); + + return priv->drv->send(priv->drv, type, packet, pend - packet); +} + +static void bt_slip_send_link_control(FAR struct sliphci_s *priv, + FAR const void *data, size_t len) +{ + bt_slip_send_packet(priv, HCI_3WIRE_LINK_PKT, data, len); +} + +static void bt_slip_send_ack(FAR void *arg) +{ + FAR struct sliphci_s *priv = arg; + + if (priv->linkstate != BT_SLIP_ACTIVE) + { + wlerr("err: linkstate:%d not active", priv->linkstate); + return; + } + + bt_slip_send_packet(priv, HCI_3WIRE_ACK_PKT, NULL, 0); +} + +static int bt_slip_link_packet_hanlde(FAR struct sliphci_s *priv, + FAR const uint8_t *header, + size_t len) +{ + FAR const uint8_t *payload = header + BT_SLIP_HEADER_LEN; + int ret = len; + + if (memcmp(payload, g_sync_req, ARRAY_SIZE(g_sync_req)) == 0) + { + bt_slip_send_link_control(priv, g_sync_rsp, ARRAY_SIZE(g_sync_rsp)); + } + else if (memcmp(payload, g_sync_rsp, ARRAY_SIZE(g_sync_rsp)) == 0) + { + wlinfo("h5: initialized"); + priv->linkstate = BT_SLIP_INITIALIZED; + bt_slip_send_link_control(priv, g_conf_req, ARRAY_SIZE(g_conf_req)); + } + else if (memcmp(payload, g_conf_req, ARRAY_SIZE(g_conf_req) - 1) == 0) + { + bt_slip_send_link_control(priv, g_conf_rsp, ARRAY_SIZE(g_conf_rsp)); + bt_slip_send_link_control(priv, g_conf_req, ARRAY_SIZE(g_conf_req)); + } + else if (memcmp(payload, g_conf_rsp, ARRAY_SIZE(g_conf_rsp)) == 0) + { + if (BT_SLIP_GET_LEN(header) > 2) + { + priv->txwin = BT_SLIP_GET_TXWIN(payload); + priv->dipresent = BT_SLIP_IS_DIC(payload); + wlinfo("h5 txwin:%d, dipresent:%d", priv->txwin, priv->dipresent); + } + + if (priv->txwin > CONFIG_BLUETOOTH_SLIP_TXWIN) + { + wlerr("err: txwin(%d) overflow(%d)", priv->txwin, + CONFIG_BLUETOOTH_SLIP_TXWIN); + return ret; + } + + if (priv->linkstate == BT_SLIP_ACTIVE) + { + return ret; + } + + wlinfo("h5: active"); + priv->linkstate = BT_SLIP_ACTIVE; + nxsem_post(&priv->sem); + } + else + { + ret = -EINVAL; + } + + return ret; +} + +static void bt_slip_unack_handle(FAR struct sliphci_s *priv) +{ + size_t to_remove; + uint8_t seq; + int semcount = 0; + + to_remove = bt_slip_unack_size(priv); + if (to_remove == 0) + { + return; + } + + seq = priv->txseq; + + do + { + if (priv->rxack == seq) + { + break; + } + + seq = (seq - 1) & 0x07; + } + while (--to_remove > 0); + + if (seq != priv->rxack) + { + wlerr("err: seq:%d != rxack:%d", seq, priv->rxack); + return; + } + + while (to_remove > 0) + { + bt_slip_unack_dtor(priv); + + /* When in blocked hci write mode, we needs to notifiy bt_slip_send + * if it was blocked by full tx window. + */ + + if ((bt_slip_unack_size(priv) == priv->txwin - 1) && !priv->nonblock) + { + nxsem_get_value(&priv->sem, &semcount); + if (semcount < 0) + { + nxsem_post(&priv->sem); + } + } + + to_remove--; + } + + to_remove = bt_slip_unack_size(priv); + + if (!to_remove) + { + work_cancel(HPWORK, &priv->retxworker); + } +} + +static int bt_slip_hci_packet_handle(FAR struct bt_driver_s *dev, + uint8_t type, FAR void *data, + size_t len) +{ + enum bt_buf_type_e var; + + if (len < 1) + { + return -EINVAL; + } + + switch (type) + { + case HCI_EVENT_PKT: + var = BT_EVT; + break; + case HCI_ACLDATA_PKT: + var = BT_ACL_IN; + break; + case HCI_ISODATA_PKT: + var = BT_ISO_IN; + break; + default: + return -EINVAL; + } + + return bt_netdev_receive(dev, var, data, len); +} + +static int bt_slip_packet_receive(FAR struct sliphci_s *priv, + FAR uint8_t *header, size_t len) +{ + FAR uint8_t *payload = header + BT_SLIP_HEADER_LEN; + int ret; + + if (BT_SLIP_IS_RELIABLE(header)) + { + BT_SLIP_SEQ_INCR(priv->txack); + bt_slip_send_ack(priv); + } + + priv->rxack = BT_SLIP_GET_ACK(header); + bt_slip_unack_handle(priv); + + switch (BT_SLIP_GET_PKT_TYPE(header)) + { + case HCI_EVENT_PKT: + case HCI_ACLDATA_PKT: + case HCI_ISODATA_PKT: + { + ret = bt_slip_hci_packet_handle(&priv->dev, + BT_SLIP_GET_PKT_TYPE (header), + payload, BT_SLIP_GET_LEN (header)); + } + break; + case HCI_3WIRE_LINK_PKT: + { + ret = bt_slip_link_packet_hanlde(priv, header, len); + } + break; + default: + { + ret = -ENAVAIL; + } + break; + } + + return ret; +} + +static int bt_slip_open(FAR struct bt_driver_s *dev) +{ + FAR struct sliphci_s *priv; + FAR struct bt_driver_s *drv; + int ret; + + DEBUGASSERT(dev != NULL); + + priv = (FAR struct sliphci_s *)dev; + drv = priv->drv; + + ret = drv->open(drv); + if (ret < 0) + { + return ret; + } + + bt_slip_unack_init(priv); + bt_slip_send_link_control(priv, g_sync_req, sizeof(g_sync_req)); + + ret = nxsem_tickwait_uninterruptible(&priv->sem, SEC2TICK(3)); + if (ret == -ETIMEDOUT) + { + wlerr("err: bluetooth driver open timeout"); + goto err; + } + + return ret; + +err: + bt_slip_close(dev); + return ret; +} + +static int bt_slip_send(FAR struct bt_driver_s *dev, + enum bt_buf_type_e type, + FAR void *data, size_t len) +{ + FAR struct sliphci_s *priv; + FAR struct unack_frame_s *frame; + int ret; + uint8_t var; + + DEBUGASSERT(dev != NULL); + + if (data == NULL) + { + return -EINVAL; + } + + priv = (FAR struct sliphci_s *)dev; + + nxmutex_lock(&priv->sliplock); + if (bt_slip_unack_size(priv) >= priv->txwin) + { + bt_slip_send_ack(priv); + if (priv->nonblock) + { + ret = -EAGAIN; + goto end; + } + else + { + nxmutex_unlock(&priv->sliplock); + nxsem_wait_uninterruptible(&priv->sem); + nxmutex_lock(&priv->sliplock); + } + } + + switch (type) + { + case BT_CMD: + var = HCI_COMMAND_PKT; + break; + case BT_ACL_OUT: + var = HCI_ACLDATA_PKT; + break; + case BT_ISO_OUT: + var = HCI_ISODATA_PKT; + break; + default: + ret = -EINVAL; + goto end; + } + + ret = bt_slip_send_packet(priv, var, data, len); + if (ret < 0) + { + goto end; + } + + frame = (FAR struct unack_frame_s *)bt_slip_unack_ctor(priv); + frame->type = type; + frame->pktlen = len; + memcpy(frame->data, data, len); + work_queue(HPWORK, &priv->retxworker, bt_slip_retx_work, + priv, BT_SLIP_RTX_TIMEOUT); + +end: + nxmutex_unlock(&priv->sliplock); + return ret < 0 ? ret : len; +} + +/* Note: bt_slip_receive could only handle completed slip packets with + * len bytes data. Avoid adding a memory copy of the data, it won't + * handle this case here. you need to combine multiple incomplete slip + * packets to completed slip packets from bt vendor driver. + */ + +static int bt_slip_receive(FAR struct bt_driver_s *drv, + enum bt_buf_type_e type, + FAR void *data, size_t len) +{ + FAR struct sliphci_s *priv = drv->priv; + FAR uint8_t *packet; + FAR uint8_t *cursor; + FAR uint8_t *header; + uint8_t byte; + uint16_t checksum; + size_t remaining; + uint8_t state; + enum + { + PACKET_START, + PACKET_HEADER, + PACKET_PAYLOAD, + PACKET_DICHECK, + PACKET_END, + }; + + nxmutex_lock(&priv->sliplock); + for (packet = data, cursor = data, header = data, state = PACKET_START; + packet < (FAR uint8_t *)data + len; packet++) + { + switch (state) + { + case PACKET_START: + { + if (*packet == SLIP_DELIMITER) + { + state = PACKET_HEADER; + remaining = BT_SLIP_HEADER_LEN; + } + } + break; + case PACKET_HEADER: + { + if (*packet == SLIP_DELIMITER) + { + break; + } + + packet = bt_slip_unslip_byte(packet, &byte); + if (!packet) + { + state = PACKET_START; + break; + } + + *cursor++ = byte; + remaining--; + + if (remaining) + { + break; + } + + /* A packet from bt maybe consists of two H5 hci frame. + */ + + header = cursor - BT_SLIP_HEADER_LEN; + + wlinfo("rx t:%d l:%d s:%d a:%d", BT_SLIP_GET_PKT_TYPE(header), + BT_SLIP_GET_LEN(header), BT_SLIP_GET_SEQ(header), + BT_SLIP_GET_ACK(header)); + + if (!BT_SLIP_IS_HDRVALIDED(header)) + { + wlerr("err: invalid header checksum"); + state = PACKET_START; + break; + } + + if (BT_SLIP_IS_RELIABLE(header) + && BT_SLIP_GET_SEQ(header) != priv->txack) + { + wlerr("err: out of order packet arrived(%u != %u)", + BT_SLIP_GET_SEQ(header), priv->txack); + state = PACKET_START; + break; + } + + switch (BT_SLIP_GET_PKT_TYPE(header)) + { + case HCI_EVENT_PKT: + case HCI_ACLDATA_PKT: + case HCI_ISODATA_PKT: + case HCI_3WIRE_LINK_PKT: + case HCI_3WIRE_ACK_PKT: + { + /* In DIC mode, Ack packet which has 0 byte paylod + * and has 2 byte data integrity check + */ + + if (BT_SLIP_GET_LEN(header)) + { + state = PACKET_PAYLOAD; + remaining = BT_SLIP_GET_LEN(header); + } + else if (BT_SLIP_GET_CRC(header)) + { + state = PACKET_DICHECK; + remaining = BT_SLIP_CHECKSUM_LEN; + } + else + { + state = PACKET_END; + remaining = 0; + } + } + break; + default: + { + wlerr("err: packet type:%u", BT_SLIP_GET_PKT_TYPE(header)); + state = PACKET_START; + } + break; + } + } + break; + case PACKET_PAYLOAD: + { + packet = bt_slip_unslip_byte(packet, &byte); + if (!packet) + { + state = PACKET_START; + break; + } + + *cursor++ = byte; + remaining--; + + if (remaining) + { + break; + } + + if (BT_SLIP_GET_CRC(header)) + { + state = PACKET_DICHECK; + remaining = BT_SLIP_CHECKSUM_LEN; + } + else + { + state = PACKET_END; + } + } + break; + case PACKET_DICHECK: + { + packet = bt_slip_unslip_byte(packet, &byte); + if (!packet) + { + state = PACKET_START; + break; + } + + *cursor++ = byte; + remaining--; + + if (remaining) + { + break; + } + + /* Remove 2 bytes crc payload, then caculate packect + * checksum with packet header and body. + */ + + cursor -= BT_SLIP_CHECKSUM_LEN; + checksum = crc16ccittpart(header, cursor - header, 0xffff); + + if (bt_slip_bit16_reverse(checksum) != bt_slip_get_crc(cursor)) + { + wlerr("err: checksum(0x%04x) not match(0x%04x)", checksum, + bt_slip_get_crc(cursor)); + state = PACKET_START; + break; + } + + state = PACKET_END; + } + break; + case PACKET_END: + { + if (*packet != SLIP_DELIMITER) + { + wlerr("err: miss slip end"); + state = PACKET_START; + break; + } + + bt_slip_packet_receive(priv, header, cursor - header); + state = PACKET_START; + } + break; + } + } + + nxmutex_unlock(&priv->sliplock); + return packet - (FAR uint8_t *)data; +} + +static int bt_slip_ioctl(FAR struct bt_driver_s *dev, int cmd, + unsigned long arg) +{ + FAR struct sliphci_s *priv; + FAR struct bt_driver_s *drv; + int ret; + + DEBUGASSERT(dev != NULL); + + priv = (FAR struct sliphci_s *)dev; + drv = priv->drv; + + switch (cmd) + { + case FIONBIO: + { + FAR int *nonblock = (FAR int *)(uintptr_t)arg; + if (nonblock && *nonblock) + { + priv->nonblock = true; + } + else + { + priv->nonblock = false; + } + } + + ret = OK; + break; + default: + ret = drv->ioctl(drv, cmd, arg); + break; + } + + return ret; +} + +static void bt_slip_close(FAR struct bt_driver_s *dev) +{ + FAR struct sliphci_s *priv; + FAR struct bt_driver_s *drv; + + DEBUGASSERT(dev != NULL); + + priv = (FAR struct sliphci_s *)dev; + drv = priv->drv; + + drv->close(drv); + priv->linkstate = BT_SLIP_UNINITIALIZED; + bt_slip_unack_cleanup(priv); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +FAR struct bt_driver_s *bt_slip_register(FAR struct bt_driver_s *drv) +{ + FAR struct sliphci_s *priv; + + DEBUGASSERT(drv != NULL); + + priv = kmm_zalloc(sizeof(struct sliphci_s)); + if (!priv) + { + return NULL; + } + + priv->dev.open = bt_slip_open; + priv->dev.send = bt_slip_send; + priv->dev.ioctl = bt_slip_ioctl; + priv->dev.close = bt_slip_close; + + priv->drv = drv; + drv->priv = priv; + drv->receive = bt_slip_receive; + + nxmutex_init(&priv->sliplock); + nxmutex_init(&priv->unacklock); + nxsem_init(&priv->sem, 0, 0); + + return &priv->dev; +} diff --git a/include/nuttx/wireless/bluetooth/bt_slip.h b/include/nuttx/wireless/bluetooth/bt_slip.h new file mode 100644 index 00000000000..b73c1304a04 --- /dev/null +++ b/include/nuttx/wireless/bluetooth/bt_slip.h @@ -0,0 +1,49 @@ +/**************************************************************************** + * include/nuttx/wireless/bluetooth/bt_slip.h + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +#ifndef __INCLUDE_NUTTX_WIRELESS_BLUETOOTH_BT_SLIP_H +#define __INCLUDE_NUTTX_WIRELESS_BLUETOOTH_BT_SLIP_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include + +/**************************************************************************** + * Name: bt_slip_register + * + * Description: + * SLIP-HCI initialize function, the h5 hci protocol would call + * this function. + * + * Parameters: + * drv - SLIP-HCI low bt driver + * + * Returned Values: + * OK on success; A negated errno value is returned on any failure. + * + ****************************************************************************/ + +FAR struct bt_driver_s *bt_slip_register(FAR struct bt_driver_s *drv); + +#endif /* __INCLUDE_NUTTX_WIRELESS_BLUETOOTH_BT_SLIPHCI_H */