wireless/bluetooth: add BT/BLE dual mode bridge driver

Change-Id: I692228fda27df1a4249f11474ff50a1049becf4b
Signed-off-by: chao.an <anchao@xiaomi.com>
This commit is contained in:
chao.an
2021-01-10 19:55:16 +08:00
parent d4947c38eb
commit 96267fe88c
7 changed files with 821 additions and 0 deletions
+12
View File
@@ -38,6 +38,7 @@
#include <nuttx/timers/oneshot.h>
#include <nuttx/wireless/pktradio.h>
#include <nuttx/wireless/bluetooth/bt_driver.h>
#include <nuttx/wireless/bluetooth/bt_uart_bridge.h>
#include <nuttx/wireless/bluetooth/bt_null.h>
#include <nuttx/wireless/ieee802154/ieee802154_loopback.h>
#include <nuttx/i2c/i2c_master.h>
@@ -354,6 +355,17 @@ int sim_bringup(void)
}
#endif
#ifdef CONFIG_BLUETOOTH_UART_BRIDGE
/* Register the Bluetooth BT/BLE dual mode bridge driver */
ret = bt_uart_bridge_register("/dev/ttyHCI0",
"/dev/ttyBT", "/dev/ttyBLE");
if (ret < 0)
{
syslog(LOG_ERR, "ERROR: bt_uart_bridge_register() failed: %d\n", ret);
}
#endif
#ifdef CONFIG_SIM_I2CBUS
/* Initialize the i2c master bus device */
+7
View File
@@ -57,6 +57,13 @@ config BLUETOOTH_UART_DUMP
endif # BLUETOOTH_UART
config BLUETOOTH_UART_BRIDGE
bool "Bluetooth BT/BLE Dual Mode UART Bridge Driver"
select MM_CIRCBUF
default n
---help---
Enable Bluetooth BT/BLE Dual Mode UART Bridge Driver.
config BLUETOOTH_NULL
bool "NULL Bluetooth device"
default n
+5
View File
@@ -55,6 +55,11 @@ CSRCS += bt_uart_bcm4343x.c
endif
endif
ifeq ($(CONFIG_BLUETOOTH_UART_BRIDGE),y)
CSRCS += bt_uart_bridge.c
CSRCS += bt_uart_filter.c
endif
ifeq ($(CONFIG_BLUETOOTH_NULL),y)
CSRCS += bt_null.c
endif
+433
View File
@@ -0,0 +1,433 @@
/****************************************************************************
* drivers/wireless/bluetooth/bt_uart_bridge.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 <fcntl.h>
#include <poll.h>
#include <nuttx/fs/fs.h>
#include <nuttx/kmalloc.h>
#include <nuttx/semaphore.h>
#include <nuttx/mm/circbuf.h>
#include <nuttx/wireless/bluetooth/bt_hci.h>
#include <nuttx/wireless/bluetooth/bt_uart.h>
#include "bt_uart_filter.h"
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
#define HCI_RECVBUF_SIZE 2048
#define HCI_SENDBUF_SIZE 1024
#define HCI_NPOLLWAITERS 2
/****************************************************************************
* Private Types
****************************************************************************/
union bt_hci_hdr_u
{
struct bt_hci_cmd_hdr_s cmd;
struct bt_hci_acl_hdr_s acl;
struct bt_hci_iso_hdr_s iso;
struct bt_hci_evt_hdr_s evt;
};
struct bt_uart_bridge_device_s
{
FAR struct bt_uart_bridge_s *bridge;
struct bt_uart_filter_s filter;
struct circbuf_s recvbuf;
sem_t recvlock;
uint8_t sendbuf[HCI_SENDBUF_SIZE];
size_t sendlen;
};
struct bt_uart_bridge_s
{
struct bt_uart_bridge_device_s device[BT_UART_FILTER_TYPE_COUNT];
sem_t recvlock;
sem_t sendlock;
struct file filep;
uint8_t tmpbuf[HCI_RECVBUF_SIZE];
};
/****************************************************************************
* Private Function Prototypes
****************************************************************************/
static int bt_uart_bridge_open (FAR struct file *filep);
static int bt_uart_bridge_close (FAR struct file *filep);
static ssize_t bt_uart_bridge_read (FAR struct file *filep,
FAR char *buffer, size_t buflen);
static ssize_t bt_uart_bridge_write (FAR struct file *filep,
FAR const char *buffer, size_t buflen);
static int bt_uart_bridge_ioctl (FAR struct file *filep,
int cmd, unsigned long arg);
static int bt_uart_bridge_poll (FAR struct file *filep,
FAR struct pollfd *fds, bool setup);
/****************************************************************************
* Private Data
****************************************************************************/
static const struct file_operations g_hcibridge_ops =
{
.open = bt_uart_bridge_open,
.close = bt_uart_bridge_close,
.read = bt_uart_bridge_read,
.write = bt_uart_bridge_write,
.ioctl = bt_uart_bridge_ioctl,
.poll = bt_uart_bridge_poll
};
/****************************************************************************
* Private Functions
****************************************************************************/
static ssize_t
bt_uart_circbuf_read(FAR struct bt_uart_bridge_device_s *device,
FAR char *buffer, size_t buflen)
{
int ret;
ret = nxsem_wait_uninterruptible(&device->recvlock);
if (ret < 0)
{
return ret;
}
ret = circbuf_read(&device->recvbuf, buffer, buflen);
nxsem_post(&device->recvlock);
return ret;
}
static ssize_t
bt_uart_circbuf_write(FAR struct bt_uart_bridge_device_s *device,
FAR char *buffer, size_t buflen)
{
int ret;
ret = nxsem_wait_uninterruptible(&device->recvlock);
if (ret < 0)
{
return ret;
}
if (circbuf_space(&device->recvbuf) >= buflen)
{
ret = circbuf_write(&device->recvbuf, buffer, buflen);
}
nxsem_post(&device->recvlock);
return ret;
}
static int bt_uart_bridge_open(FAR struct file *filep)
{
FAR struct inode *inode = filep->f_inode;
FAR struct bt_uart_bridge_device_s *device = inode->i_private;
if (filep->f_inode->i_crefs == 0)
{
device->sendlen = 0;
circbuf_reset(&device->recvbuf);
}
return OK;
}
static int bt_uart_bridge_close(FAR struct file *filep)
{
return OK;
}
static ssize_t bt_uart_bridge_read(FAR struct file *filep,
FAR char *buffer, size_t buflen)
{
FAR struct inode *inode = filep->f_inode;
FAR struct bt_uart_bridge_device_s *device = inode->i_private;
FAR struct bt_uart_bridge_s *bridge = device->bridge;
FAR struct bt_uart_bridge_device_s *iterator;
int recvlen;
int ret;
int i;
ret = bt_uart_circbuf_read(device, buffer, buflen);
if (ret != 0)
{
return ret;
}
ret = nxsem_wait_uninterruptible(&bridge->recvlock);
if (ret < 0)
{
return ret;
}
while (circbuf_is_empty(&device->recvbuf))
{
recvlen = file_read(&bridge->filep,
bridge->tmpbuf, HCI_RECVBUF_SIZE);
if (recvlen < 0)
{
nxsem_post(&bridge->recvlock);
return recvlen;
}
for (i = 0; i < BT_UART_FILTER_TYPE_COUNT; i++)
{
iterator = &bridge->device[i];
if (bt_uart_filter_forward_recv(&iterator->filter,
bridge->tmpbuf, recvlen))
{
bt_uart_circbuf_write(iterator, bridge->tmpbuf, recvlen);
}
}
}
nxsem_post(&bridge->recvlock);
return bt_uart_circbuf_read(device, buffer, buflen);
}
static ssize_t bt_uart_bridge_write(FAR struct file *filep,
FAR const char *buffer, size_t buflen)
{
FAR struct inode *inode = filep->f_inode;
FAR struct bt_uart_bridge_device_s *device = inode->i_private;
FAR struct bt_uart_bridge_s *bridge = device->bridge;
FAR union bt_hci_hdr_u *hdr;
size_t pktlen;
size_t hdrlen;
int ret;
int i;
ret = nxsem_wait_uninterruptible(&bridge->sendlock);
if (ret < 0)
{
return ret;
}
if (device->sendlen + buflen > HCI_SENDBUF_SIZE)
{
ret = -EINVAL;
goto err;
}
memcpy(device->sendbuf + device->sendlen, buffer, buflen);
device->sendlen += buflen;
hdr = (FAR union bt_hci_hdr_u *)(device->sendbuf + 1);
while (1)
{
switch (device->sendbuf[0])
{
case H4_CMD:
hdrlen = sizeof(struct bt_hci_cmd_hdr_s);
pktlen = hdr->cmd.param_len;
break;
case H4_ACL:
hdrlen = sizeof(struct bt_hci_acl_hdr_s);
pktlen = hdr->acl.len;
break;
case H4_ISO:
hdrlen = sizeof(struct bt_hci_iso_hdr_s);
pktlen = hdr->iso.len;
break;
default:
ret = -EINVAL;
goto err;
}
/* Reassembly is incomplete ? */
hdrlen += H4_HEADER_SIZE;
if (device->sendlen < hdrlen)
{
goto out;
}
pktlen += hdrlen;
if (device->sendlen < pktlen)
{
goto out;
}
/* Got the full packet, check and send out */
if (bt_uart_filter_forward_send(&device->filter,
device->sendbuf, pktlen))
{
ret = file_write(&bridge->filep, device->sendbuf, pktlen);
if (ret < 0)
{
goto err;
}
}
device->sendlen -= pktlen;
if (device->sendlen == 0)
{
goto out;
}
memmove(device->sendbuf, device->sendbuf + pktlen, device->sendlen);
}
err:
device->sendlen = 0;
out:
nxsem_post(&bridge->sendlock);
return ret < 0 ? ret : buflen;
}
static int bt_uart_bridge_ioctl(FAR struct file *filep,
int cmd, unsigned long arg)
{
FAR struct inode *inode = filep->f_inode;
FAR struct bt_uart_bridge_device_s *device = inode->i_private;
FAR struct bt_uart_bridge_s *bridge = device->bridge;
return file_ioctl(&bridge->filep, cmd, arg);
}
static int bt_uart_bridge_poll(FAR struct file *filep,
FAR struct pollfd *fds, bool setup)
{
FAR struct inode *inode = filep->f_inode;
FAR struct bt_uart_bridge_device_s *device = inode->i_private;
FAR struct bt_uart_bridge_s *bridge = device->bridge;
int ret;
ret = file_poll(&bridge->filep, fds, setup);
if (setup && ret >= 0 && !circbuf_is_empty(&device->recvbuf))
{
fds->revents |= POLLIN;
}
return ret;
}
/****************************************************************************
* Public Functions
****************************************************************************/
int bt_uart_bridge_register(const char *hciname,
const char *btname, const char *blename)
{
FAR struct bt_uart_bridge_device_s *device;
FAR struct bt_uart_bridge_s *bridge;
int i = 0;
int ret;
FAR const char *bridges[] =
{
btname,
blename
};
if (!hciname || (!btname && !blename))
{
return -EINVAL;
}
bridge = (FAR struct bt_uart_bridge_s *)
kmm_zalloc(sizeof(struct bt_uart_bridge_s));
if (bridge == NULL)
{
return -ENOMEM;
}
nxsem_init(&bridge->recvlock, 0, 1);
nxsem_init(&bridge->sendlock, 0, 1);
ret = file_open(&bridge->filep, hciname, O_RDWR);
if (ret < 0)
{
goto err_file;
}
for (i = 0; i < BT_UART_FILTER_TYPE_COUNT; i++)
{
if (!bridges[i])
{
continue;
}
device = &bridge->device[i];
device->bridge = bridge;
bt_uart_filter_init(&device->filter, i);
nxsem_init(&device->recvlock, 0, 1);
ret = circbuf_init(&device->recvbuf, NULL, HCI_RECVBUF_SIZE);
if (ret < 0)
{
goto err_circbuf;
}
ret = register_driver(bridges[i], &g_hcibridge_ops, 0666, device);
if (ret < 0)
{
goto err_device;
}
}
return OK;
for (; i >= 0; i--)
{
if (!bridges[i])
{
continue;
}
device = &bridge->device[i];
unregister_driver(bridges[i]);
err_device:
circbuf_uninit(&device->recvbuf);
err_circbuf:
nxsem_destroy(&device->recvlock);
}
file_close(&bridge->filep);
err_file:
nxsem_destroy(&bridge->recvlock);
nxsem_destroy(&bridge->sendlock);
kmm_free(bridge);
return ret;
}
+265
View File
@@ -0,0 +1,265 @@
/****************************************************************************
* drivers/wireless/bluetooth/bt_uart_filter.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 <string.h>
#include <nuttx/wireless/bluetooth/bt_hci.h>
#include <nuttx/wireless/bluetooth/bt_uart.h>
#include "bt_uart_filter.h"
/****************************************************************************
* Private Data
****************************************************************************/
static const uint8_t g_bt_uart_filter_bt_evt_table[] =
{
BT_HCI_EVT_INQUIRY_COMPLETE,
BT_HCI_EVT_CONN_COMPLETE,
BT_HCI_EVT_CONN_REQUEST,
BT_HCI_EVT_AUTH_COMPLETE,
BT_HCI_EVT_REMOTE_NAME_REQ_COMPLETE,
BT_HCI_EVT_REMOTE_FEATURES,
BT_HCI_EVT_ROLE_CHANGE,
BT_HCI_EVT_PIN_CODE_REQ,
BT_HCI_EVT_LINK_KEY_NOTIFY,
BT_HCI_EVT_LINK_KEY_REQ,
BT_HCI_EVT_INQUIRY_RESULT_WITH_RSSI,
BT_HCI_EVT_REMOTE_EXT_FEATURES,
BT_HCI_EVT_SYNC_CONN_COMPLETE,
BT_HCI_EVT_EXTENDED_INQUIRY_RESULT,
BT_HCI_EVT_IO_CAPA_RESP,
BT_HCI_EVT_IO_CAPA_REQ,
BT_HCI_EVT_SSP_COMPLETE,
BT_HCI_EVT_USER_CONFIRM_REQ,
BT_HCI_EVT_USER_PASSKEY_REQ,
BT_HCI_EVT_USER_PASSKEY_NOTIFY,
BT_HCI_EVT_PAGE_SCAN_REP_MODE_CHNG,
BT_HCI_EVT_CONN_PKT_TYPE_CHANGE,
BT_HCI_EVT_READ_CLOCK_OFF_COMP,
BT_HCI_EVT_MAX_SLOTS_CHANGED,
BT_HCI_EVT_MODE_CHANGE,
BT_HCI_EVT_QOS_SETUP_COMP,
BT_HCI_EVT_LINK_SUPER_TOUT_CHANGED,
};
static const uint8_t g_bt_uart_filter_ble_evt_table[] =
{
BT_HCI_EVT_LE_META_EVENT,
};
/****************************************************************************
* Private Functions
****************************************************************************/
static bool bt_uart_filter_set_handle(FAR struct bt_uart_filter_s *filter,
int old, int new)
{
int i;
for (i = 0; i < BT_UART_FILTER_CONN_COUNT; i++)
{
if (filter->handle[i] == old)
{
filter->handle[i] = new & 0xfff;
return true;
}
}
return false;
}
static bool bt_uart_filter_alloc_handle(FAR struct bt_uart_filter_s *filter,
int handle)
{
return bt_uart_filter_set_handle(filter, 0, handle);
}
static bool bt_uart_filter_free_handle(FAR struct bt_uart_filter_s *filter,
int handle)
{
return bt_uart_filter_set_handle(filter, handle, 0);
}
static bool bt_uart_filter_has_handle(FAR struct bt_uart_filter_s *filter,
int handle)
{
return bt_uart_filter_set_handle(filter, handle, handle);
}
/****************************************************************************
* Public Functions
****************************************************************************/
bool bt_uart_filter_forward_recv(FAR struct bt_uart_filter_s *filter,
FAR char *buffer, size_t buflen)
{
FAR const uint8_t *evt_table;
int size;
int i;
if (buffer[0] == H4_EVT)
{
if (filter->type == BT_UART_FILTER_TYPE_BLE)
{
evt_table = g_bt_uart_filter_bt_evt_table;
size = sizeof(g_bt_uart_filter_bt_evt_table);
}
else
{
evt_table = g_bt_uart_filter_ble_evt_table;
size = sizeof(g_bt_uart_filter_ble_evt_table);
}
for (i = 0; i < size; i++)
{
if (buffer[1] == evt_table[i])
{
return false;
}
}
switch (buffer[1])
{
case BT_HCI_EVT_LE_META_EVENT:
if (buffer[3] == BT_HCI_EVT_LE_CONN_COMPLETE)
{
FAR struct bt_hci_evt_le_conn_complete_s *evt;
evt = (FAR void *)&buffer[4];
if (evt->status == 0)
{
return bt_uart_filter_alloc_handle(filter, evt->handle);
}
}
else if (buffer[3] == BT_HCI_EVT_LE_ENH_CONN_COMPLETE)
{
FAR struct bt_hci_evt_le_enh_conn_complete_s *evt;
evt = (FAR void *)&buffer[4];
if (evt->status == 0)
{
return bt_uart_filter_alloc_handle(filter, evt->handle);
}
}
break;
case BT_HCI_EVT_CONN_COMPLETE:
{
FAR struct bt_hci_evt_conn_complete_s *evt;
evt = (FAR void *)&buffer[3];
if (evt->status == 0)
{
return bt_uart_filter_alloc_handle(filter, evt->handle);
}
}
break;
case BT_HCI_EVT_NUM_COMPLETED_PACKETS:
{
FAR struct bt_hci_evt_num_completed_packets_s *evt;
evt = (FAR void *)&buffer[3];
return bt_uart_filter_has_handle(filter, evt->h[0].handle);
}
break;
case BT_HCI_EVT_REMOTE_VERSION_INFO:
{
FAR struct bt_hci_evt_remote_version_info_s *evt;
evt = (FAR void *)&buffer[3];
return bt_uart_filter_has_handle(filter, evt->handle);
}
break;
case BT_HCI_EVT_ENCRYPT_CHANGE:
{
FAR struct bt_hci_evt_encrypt_change_s *evt;
evt = (FAR void *)&buffer[3];
return bt_uart_filter_has_handle(filter, evt->handle);
}
break;
case BT_HCI_EVT_ENCRYPT_KEY_REFRESH_COMPLETE:
{
FAR struct bt_hci_evt_encrypt_key_refresh_complete_s *evt;
evt = (FAR void *)&buffer[3];
return bt_uart_filter_has_handle(filter, evt->handle);
}
break;
case BT_HCI_EVT_DISCONN_COMPLETE:
{
FAR struct bt_hci_evt_disconn_complete_s *evt;
evt = (FAR void *)&buffer[3];
return bt_uart_filter_free_handle(filter, evt->handle);
}
break;
default:
break;
}
}
else if (buffer[0] == H4_ACL)
{
FAR struct bt_hci_acl_hdr_s *acl = (FAR void *)&buffer[1];
return bt_uart_filter_has_handle(filter, acl->handle);
}
return true;
}
bool bt_uart_filter_forward_send(FAR struct bt_uart_filter_s *filter,
FAR char *buffer, size_t buflen)
{
uint16_t opcode;
if (buffer[0] == H4_CMD)
{
opcode = buffer[2] << 8 | buffer[1];
switch (opcode)
{
case BT_HCI_OP_SET_EVENT_MASK:
{
/* Override the all event bits roughly to avoid the
* bt/ble host specific mask deliver to controller.
*/
memset(&buffer[4], 0xff, 7);
buffer[11] = 0x3f;
}
break;
default:
break;
}
}
return true;
}
void bt_uart_filter_init(FAR struct bt_uart_filter_s *filter, int type)
{
memset(filter->handle, 0, sizeof(filter->handle));
filter->type = type;
}
@@ -0,0 +1,60 @@
/****************************************************************************
* drivers/wireless/bluetooth/bt_uart_filter.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 __DRIVER_WIRELESS_BLUETOOTH_BT_UART_FILTER_H
#define __DRIVER_WIRELESS_BLUETOOTH_BT_UART_FILTER_H
/****************************************************************************
* Included Files
****************************************************************************/
#include <stdint.h>
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
#define BT_UART_FILTER_CONN_COUNT 4
#define BT_UART_FILTER_TYPE_BT 0
#define BT_UART_FILTER_TYPE_BLE 1
#define BT_UART_FILTER_TYPE_COUNT 2
/****************************************************************************
* Public Types
****************************************************************************/
struct bt_uart_filter_s
{
int type;
uint16_t handle[BT_UART_FILTER_CONN_COUNT];
};
/****************************************************************************
* Public Function Prototypes
****************************************************************************/
void bt_uart_filter_init(FAR struct bt_uart_filter_s *filter, int type);
bool bt_uart_filter_forward_send(FAR struct bt_uart_filter_s *filter,
FAR char *buffer, size_t buflen);
bool bt_uart_filter_forward_recv(FAR struct bt_uart_filter_s *filter,
FAR char *buffer, size_t buflen);
#endif /* __DRIVER_WIRELESS_BLUETOOTH_BT_UART_FILTER_H */
@@ -0,0 +1,39 @@
/****************************************************************************
* include/nuttx/wireless/bluetooth/bt_uart_bridge.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_UART_BRIDGE_H
#define __INCLUDE_NUTTX_WIRELESS_BLUETOOTH_BT_UART_BRIDGE_H
/****************************************************************************
* Public Function Prototypes
****************************************************************************/
/****************************************************************************
* Name: bt_uart_bridge_register
*
* Description:
* Register the Bluetooth BT/BLE dual mode UART bridge driver
*
****************************************************************************/
int bt_uart_bridge_register(FAR const char *hciname,
FAR const char *btname, FAR const char *blename);
#endif /* __INCLUDE_NUTTX_WIRELESS_BLUETOOTH_BT_UART_BRIDGE_H */