incubator-nuttx/drivers/wireless/bluetooth/bt_bridge.c

676 lines
18 KiB
C

/****************************************************************************
* drivers/wireless/bluetooth/bt_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 <debug.h>
#include <string.h>
#include <nuttx/irq.h>
#include <nuttx/kmalloc.h>
#include <nuttx/atomic.h>
#include <nuttx/net/snoop.h>
#include <nuttx/wireless/bluetooth/bt_bridge.h>
#include <nuttx/wireless/bluetooth/bt_driver.h>
#include <nuttx/wireless/bluetooth/bt_ioctl.h>
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
#define BT_FILTER_CONN_COUNT 16
#define BT_FILTER_OPCODE_COUNT 16
#define BT_FILTER_TYPE_BT 0
#define BT_FILTER_TYPE_BLE 1
#define BT_FILTER_TYPE_COUNT 2
#define BT_FILTER_CMD_RESET 0
#define BT_FILTER_CMD_COUNT 1
/****************************************************************************
* Private Types
****************************************************************************/
struct bt_filter_s
{
int type;
uint16_t opcode[BT_FILTER_OPCODE_COUNT];
uint16_t handle[BT_FILTER_CONN_COUNT];
};
struct bt_bridge_device_s
{
struct bt_driver_s driver;
struct bt_filter_s filter;
FAR struct bt_bridge_s *bridge;
};
struct bt_bridge_s
{
struct bt_bridge_device_s device[BT_FILTER_TYPE_COUNT];
FAR struct bt_driver_s *driver;
#ifdef CONFIG_BLUETOOTH_BRIDGE_BTSNOOP
FAR struct snoop_s *snoop;
#endif /* CONFIG_BLUETOOTH_BRIDGE_BTSNOOP */
atomic_uint refs;
bool dispatched[BT_FILTER_CMD_COUNT];
};
/****************************************************************************
* Private Data
****************************************************************************/
static const uint8_t g_bt_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_filter_ble_evt_table[] =
{
BT_HCI_EVT_LE_META_EVENT,
};
/****************************************************************************
* Private Functions
****************************************************************************/
static bool bt_filter_set(FAR uint16_t *array, int size, uint16_t old,
uint16_t new)
{
int i;
for (i = 0; i < size; i++)
{
if (array[i] == old)
{
array[i] = new;
return true;
}
}
return false;
}
static bool bt_filter_ogf_is_valid(uint8_t ogf)
{
switch (ogf)
{
case BT_OGF_BASEBAND:
case BT_OGF_LINK_CTRL:
case BT_OGF_INFO:
case BT_OGF_LE:
case BT_OGF_VS_RTK:
return true;
default:
return false;
}
}
static bool bt_filter_set_handle(FAR uint16_t *handle, int size,
uint16_t old, uint16_t new)
{
return bt_filter_set(handle, size, old, new);
}
static bool bt_filter_alloc_handle(FAR struct bt_filter_s *filter,
uint16_t handle)
{
return bt_filter_set_handle(filter->handle, BT_FILTER_CONN_COUNT,
0, handle);
}
static bool bt_filter_free_handle(FAR struct bt_filter_s *filter,
uint16_t handle)
{
return bt_filter_set_handle(filter->handle, BT_FILTER_CONN_COUNT,
handle, 0);
}
static bool bt_filter_has_handle(FAR struct bt_filter_s *filter,
uint16_t handle)
{
return bt_filter_set_handle(filter->handle, BT_FILTER_CONN_COUNT,
handle, handle);
}
static bool bt_filter_set_opcode(FAR uint16_t *opcode, int size,
uint16_t old, uint16_t new)
{
return bt_filter_set(opcode, size, old, new);
}
static bool bt_filter_alloc_opcode(FAR struct bt_filter_s *filter,
uint16_t opcode)
{
return bt_filter_set_opcode(filter->opcode, BT_FILTER_OPCODE_COUNT,
0, opcode);
}
static bool bt_filter_free_opcode(FAR struct bt_filter_s *filter,
uint16_t opcode)
{
return bt_filter_set_opcode(filter->opcode, BT_FILTER_OPCODE_COUNT,
opcode, 0);
}
static bool bt_filter_can_recv(FAR struct bt_filter_s *filter,
enum bt_buf_type_e type,
FAR const uint8_t *buffer, size_t buflen)
{
FAR const uint8_t *evt_table;
int size;
int i;
if (type == BT_EVT)
{
if (filter->type == BT_FILTER_TYPE_BLE)
{
evt_table = g_bt_filter_bt_evt_table;
size = sizeof(g_bt_filter_bt_evt_table);
}
else
{
evt_table = g_bt_filter_ble_evt_table;
size = sizeof(g_bt_filter_ble_evt_table);
}
for (i = 0; i < size; i++)
{
if (buffer[0] == evt_table[i])
{
return false;
}
}
switch (buffer[0])
{
case BT_HCI_EVT_LE_META_EVENT:
if (buffer[2] == BT_HCI_EVT_LE_CONN_COMPLETE)
{
FAR struct bt_hci_evt_le_conn_complete_s *evt;
evt = (FAR void *)&buffer[3];
if (evt->status == 0)
{
return bt_filter_alloc_handle(filter, evt->handle);
}
}
else if (buffer[2] == BT_HCI_EVT_LE_ENH_CONN_COMPLETE)
{
FAR struct bt_hci_evt_le_enh_conn_complete_s *evt;
evt = (FAR void *)&buffer[3];
if (evt->status == 0)
{
return bt_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[2];
if (evt->status == 0)
{
return bt_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[2];
return bt_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[2];
return bt_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[2];
return bt_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[2];
return bt_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[2];
return bt_filter_free_handle(filter, evt->handle);
}
break;
case BT_HCI_EVT_CMD_COMPLETE:
{
FAR struct hci_evt_cmd_complete_s *evt;
uint8_t ogf;
evt = (FAR void *)&buffer[2];
ogf = evt->opcode >> 10;
if (bt_filter_ogf_is_valid(ogf))
{
return bt_filter_free_opcode(filter, evt->opcode);
}
else if (BT_OGF_LINK_POLICY == ogf || BT_OGF_STATUS == ogf)
{
if (filter->type == BT_FILTER_TYPE_BLE)
{
return false;
}
}
}
break;
case BT_HCI_EVT_CMD_STATUS:
{
FAR struct bt_hci_evt_cmd_status_s *stat;
uint8_t ogf;
stat = (FAR void *)&buffer[2];
ogf = stat->opcode >> 10;
if (bt_filter_ogf_is_valid(ogf))
{
return bt_filter_free_opcode(filter, stat->opcode);
}
if (BT_OGF_LINK_POLICY == ogf || BT_OGF_STATUS == ogf)
{
if (filter->type == BT_FILTER_TYPE_BLE)
{
return false;
}
}
}
break;
}
}
else if (type == BT_ACL_IN)
{
FAR struct bt_hci_acl_hdr_s *acl = (FAR void *)&buffer[0];
/* When in HCI ACL Data packets, connection handle is
* the first 3 octets of the packet
*/
return bt_filter_has_handle(filter, acl->handle & 0x0fff);
}
return true;
}
static bool bt_filter_can_send(FAR struct bt_filter_s *filter,
enum bt_buf_type_e type,
FAR uint8_t *buffer, size_t buflen)
{
uint16_t opcode;
uint8_t ogf;
int i;
if (type == BT_CMD)
{
opcode = (uint16_t)buffer[1] << 8 | (uint16_t)buffer[0];
ogf = buffer[1] >> 2;
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[3], 0xff, 7);
buffer[10] = 0x3f;
}
break;
}
if (bt_filter_ogf_is_valid(ogf))
{
if (!bt_filter_alloc_opcode(filter, opcode))
{
nerr("Unable to set opcode 0x%04x.\n", opcode);
for (i = 0; i < BT_FILTER_OPCODE_COUNT; i++)
{
nerr("PENDING opcode: %04x.\n", filter->opcode[i]);
}
memset(filter->opcode, 0, sizeof(filter->opcode));
bt_filter_alloc_opcode(filter, opcode);
}
}
}
return true;
}
static int bt_bridge_open(FAR struct bt_driver_s *drv)
{
FAR struct bt_bridge_device_s *device =
(FAR struct bt_bridge_device_s *)drv;
FAR struct bt_bridge_s *bridge = device->bridge;
FAR struct bt_driver_s *driver = bridge->driver;
if (atomic_fetch_add(&bridge->refs, 1) == 0)
{
int ret = driver->open(driver);
if (ret < 0)
{
atomic_fetch_sub(&bridge->refs, 1);
}
return ret;
};
return OK;
}
static int bt_bridge_send_reset_response(FAR struct bt_driver_s *driver)
{
uint8_t reset_response[6];
reset_response[0] = BT_HCI_EVT_CMD_COMPLETE;
reset_response[1] = 0x04;
reset_response[2] = 0x01;
reset_response[3] = BT_HCI_OP_RESET & 0xff;
reset_response[4] = (BT_HCI_OP_RESET >> 8) & 0xff;
reset_response[5] = BT_HCI_SUCCESS;
return bt_netdev_receive(driver, BT_EVT, reset_response,
sizeof(reset_response));
}
static bool bt_bridge_filter_command(FAR struct bt_bridge_s *bridge,
FAR struct bt_driver_s *driver,
FAR uint8_t *data)
{
uint16_t opcode = BT_LE162HOST(((uint16_t *)data)[0]);
switch (opcode)
{
case BT_HCI_OP_RESET:
if (bridge->dispatched[BT_FILTER_CMD_RESET])
{
bt_bridge_send_reset_response(driver);
return true;
}
bridge->dispatched[BT_FILTER_CMD_RESET] = true;
break;
default:
break;
}
return false;
}
static int bt_bridge_send(FAR struct bt_driver_s *drv,
enum bt_buf_type_e type,
FAR void *data, size_t len)
{
FAR struct bt_bridge_device_s *device =
(FAR struct bt_bridge_device_s *)drv;
FAR struct bt_bridge_s *bridge = device->bridge;
FAR struct bt_driver_s *driver = bridge->driver;
irqstate_t flags;
flags = enter_critical_section();
if (bt_bridge_filter_command(bridge, drv, data))
{
leave_critical_section(flags);
return len;
}
if (bt_filter_can_send(&device->filter, type, data, len))
{
leave_critical_section(flags);
#ifdef CONFIG_BLUETOOTH_BRIDGE_BTSNOOP
snoop_dump(bridge->snoop, data - drv->head_reserve,
len + drv->head_reserve, 0,
SNOOP_DIRECTION_FLAG_SENT);
#endif /* CONFIG_BLUETOOTH_BRIDGE_BTSNOOP */
return driver->send(driver, type, data, len);
}
leave_critical_section(flags);
return 0;
}
static void bt_bridge_close(FAR struct bt_driver_s *drv)
{
FAR struct bt_bridge_device_s *device =
(FAR struct bt_bridge_device_s *)drv;
FAR struct bt_bridge_s *bridge = device->bridge;
FAR struct bt_driver_s *driver = bridge->driver;
if (atomic_fetch_sub(&bridge->refs, 1) == 1)
{
driver->close(driver);
};
}
static int bt_bridge_receive(FAR struct bt_driver_s *drv,
enum bt_buf_type_e type,
FAR void *data, size_t len)
{
FAR struct bt_bridge_s *bridge = (FAR struct bt_bridge_s *)drv->priv;
FAR struct bt_bridge_device_s *device;
FAR struct bt_driver_s *driver;
irqstate_t flags;
int i;
flags = enter_critical_section();
for (i = 0; i < BT_FILTER_TYPE_COUNT; i++)
{
device = &bridge->device[i];
driver = &device->driver;
if (bt_filter_can_recv(&device->filter, type, data, len))
{
int ret;
leave_critical_section(flags);
ret = bt_netdev_receive(driver, type, data, len);
#ifdef CONFIG_BLUETOOTH_BRIDGE_BTSNOOP
snoop_dump(bridge->snoop, data - drv->head_reserve,
len + drv->head_reserve, 0,
SNOOP_DIRECTION_FLAG_RECV);
#endif /* CONFIG_BLUETOOTH_BRIDGE_BTSNOOP */
return ret;
}
}
leave_critical_section(flags);
return 0;
}
static int bt_bridge_ioctl(FAR struct bt_driver_s *drv, int cmd,
unsigned long arg)
{
FAR struct bt_bridge_device_s *device =
(FAR struct bt_bridge_device_s *)drv;
FAR struct bt_bridge_s *bridge = device->bridge;
FAR struct bt_driver_s *driver = bridge->driver;
int ret;
switch (cmd)
{
#ifdef CONFIG_BLUETOOTH_BRIDGE_BTSNOOP
case SIOCBTSNOOPOPEN:
{
FAR const char *filename = (FAR const char *)((uintptr_t)arg);
FAR struct snoop_s *snoop = (FAR struct snoop_s *)
kmm_zalloc(sizeof(struct snoop_s));
if (!snoop)
{
return -ENOMEM;
}
ret = snoop_open(snoop, filename, SNOOP_DATALINK_HCI_UART, true);
if (ret == OK)
{
bridge->snoop = snoop;
}
else
{
kmm_free(snoop);
}
break;
}
case SIOCBTSNOOPCLOSE:
{
FAR struct snoop_s *snoop = bridge->snoop;
bridge->snoop = NULL;
ret = snoop_close(snoop);
kmm_free(snoop);
break;
}
#endif /* CONFIG_BLUETOOTH_BRIDGE_BTSNOOP */
/* hci ioctl operation */
default:
{
if (driver->ioctl)
{
ret = driver->ioctl(driver, cmd, arg);
}
else
{
ret = -ENOTTY;
}
}
}
return ret;
}
static void bt_device_init(FAR struct bt_bridge_s *bridge,
FAR struct bt_driver_s **drv, uint8_t type)
{
FAR struct bt_bridge_device_s *device = &bridge->device[type];
FAR struct bt_driver_s *driver = &device->driver;
device->bridge = bridge;
device->filter.type = type;
*drv = &device->driver;
driver->head_reserve = bridge->driver->head_reserve;
driver->open = bt_bridge_open;
driver->send = bt_bridge_send;
driver->close = bt_bridge_close;
driver->ioctl = bt_bridge_ioctl;
}
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: bt_bridge_register
*
* Description:
* Register the Bluetooth BT/BLE dual mode bridge driver
*
****************************************************************************/
int bt_bridge_register(FAR struct bt_driver_s *hcidrv,
FAR struct bt_driver_s **btdrv,
FAR struct bt_driver_s **bledrv)
{
FAR struct bt_bridge_s *bridge;
if (!hcidrv || !btdrv || !bledrv)
{
return -EINVAL;
}
bridge = kmm_zalloc(sizeof(struct bt_bridge_s));
if (!bridge)
{
return -ENOMEM;
}
bridge->driver = hcidrv;
hcidrv->receive = bt_bridge_receive;
hcidrv->priv = bridge;
bt_device_init(bridge, btdrv, BT_FILTER_TYPE_BT);
bt_device_init(bridge, bledrv, BT_FILTER_TYPE_BLE);
return OK;
}