mirror of
https://github.com/espressif/esp-nimble.git
synced 2026-07-21 10:34:07 +00:00
apps/bttester: Copy files from Zephyr
This commit is contained in:
@@ -0,0 +1,57 @@
|
||||
Title: Bluetooth tester application
|
||||
|
||||
Description:
|
||||
|
||||
Tester application uses binary protocol to control Zephyr stack and is aimed at
|
||||
automated testing. It requires two serial ports to operate.
|
||||
The first serial is used by Bluetooth Testing Protocol (BTP) to drive Bluetooth
|
||||
stack. BTP commands and events are received and buffered for further processing
|
||||
over the same serial.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
Supported Profiles:
|
||||
|
||||
GAP, GATT, SM
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
Building and running on QEMU:
|
||||
|
||||
QEMU should have connection with the external host Bluetooth hardware.
|
||||
The btproxy tool from BlueZ can be used to give access to a Bluetooth controller
|
||||
attached to the Linux host OS:
|
||||
|
||||
$ sudo tools/btproxy -u
|
||||
Listening on /tmp/bt-server-bredr
|
||||
|
||||
/tmp/bt-server-bredr option is already set in Makefile through QEMU_EXTRA_FLAGS.
|
||||
|
||||
To build tester application for QEMU use BOARD=qemu_cortex_m3 and
|
||||
CONF_FILE=qemu.conf. After this qemu can be started through the "run"
|
||||
build target.
|
||||
|
||||
Note: Target board have to support enough UARTs for BTP and controller.
|
||||
We recommend using qemu_cortex_m3.
|
||||
|
||||
'bt-stack-tester' UNIX socket (previously set in Makefile) can be used for now
|
||||
to control tester application.
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
Building and running on Arduino 101:
|
||||
|
||||
Arduino 101 is equipped with Nordic nRF51 Bluetooth LE controller.
|
||||
Please refer to the Zephyr Project docs [1] to see how to build and flash the
|
||||
controller with the HCI Bluetooth LE firmware.
|
||||
|
||||
Next, build and flash tester application by employing the "flash" build
|
||||
target.
|
||||
|
||||
While running tester application on Arduino 101, serial converter, typically
|
||||
UART <-> USB is required by BTP to operate. Connect Arduino 101 Tx and Rx lines
|
||||
(0 and 1 ports on Arduino 101 board) through the UART converter to the host
|
||||
USB port.
|
||||
|
||||
Use serial client, e.g. PUTTY to communicate over the serial port
|
||||
(typically /dev/ttyUSBx) with the tester using BTP.
|
||||
|
||||
[1] https://www.zephyrproject.org/doc/boards/x86/arduino_101/doc/board.html#flashing-the-bluetooth-core
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,300 @@
|
||||
/* bttester.c - Bluetooth Tester */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2015-2016 Intel Corporation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <zephyr.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <zephyr/types.h>
|
||||
|
||||
#include <toolchain.h>
|
||||
#include <bluetooth/bluetooth.h>
|
||||
#include <misc/byteorder.h>
|
||||
#include <console/uart_pipe.h>
|
||||
|
||||
#include "bttester.h"
|
||||
|
||||
#define STACKSIZE 2048
|
||||
static K_THREAD_STACK_DEFINE(stack, STACKSIZE);
|
||||
static struct k_thread cmd_thread;
|
||||
|
||||
#define CMD_QUEUED 2
|
||||
struct btp_buf {
|
||||
u32_t _reserved;
|
||||
union {
|
||||
u8_t data[BTP_MTU];
|
||||
struct btp_hdr hdr;
|
||||
};
|
||||
};
|
||||
|
||||
static struct btp_buf cmd_buf[CMD_QUEUED];
|
||||
|
||||
static K_FIFO_DEFINE(cmds_queue);
|
||||
static K_FIFO_DEFINE(avail_queue);
|
||||
|
||||
static void supported_commands(u8_t *data, u16_t len)
|
||||
{
|
||||
u8_t buf[1];
|
||||
struct core_read_supported_commands_rp *rp = (void *) buf;
|
||||
|
||||
memset(buf, 0, sizeof(buf));
|
||||
|
||||
tester_set_bit(buf, CORE_READ_SUPPORTED_COMMANDS);
|
||||
tester_set_bit(buf, CORE_READ_SUPPORTED_SERVICES);
|
||||
tester_set_bit(buf, CORE_REGISTER_SERVICE);
|
||||
tester_set_bit(buf, CORE_UNREGISTER_SERVICE);
|
||||
|
||||
tester_send(BTP_SERVICE_ID_CORE, CORE_READ_SUPPORTED_COMMANDS,
|
||||
BTP_INDEX_NONE, (u8_t *) rp, sizeof(buf));
|
||||
}
|
||||
|
||||
static void supported_services(u8_t *data, u16_t len)
|
||||
{
|
||||
u8_t buf[1];
|
||||
struct core_read_supported_services_rp *rp = (void *) buf;
|
||||
|
||||
memset(buf, 0, sizeof(buf));
|
||||
|
||||
tester_set_bit(buf, BTP_SERVICE_ID_CORE);
|
||||
tester_set_bit(buf, BTP_SERVICE_ID_GAP);
|
||||
tester_set_bit(buf, BTP_SERVICE_ID_GATT);
|
||||
#if defined(CONFIG_BT_L2CAP_DYNAMIC_CHANNEL)
|
||||
tester_set_bit(buf, BTP_SERVICE_ID_L2CAP);
|
||||
#endif /* CONFIG_BT_L2CAP_DYNAMIC_CHANNEL */
|
||||
#if defined(CONFIG_BT_MESH)
|
||||
tester_set_bit(buf, BTP_SERVICE_ID_MESH);
|
||||
#endif /* CONFIG_BT_MESH */
|
||||
|
||||
tester_send(BTP_SERVICE_ID_CORE, CORE_READ_SUPPORTED_SERVICES,
|
||||
BTP_INDEX_NONE, (u8_t *) rp, sizeof(buf));
|
||||
}
|
||||
|
||||
static void register_service(u8_t *data, u16_t len)
|
||||
{
|
||||
struct core_register_service_cmd *cmd = (void *) data;
|
||||
u8_t status;
|
||||
|
||||
switch (cmd->id) {
|
||||
case BTP_SERVICE_ID_GAP:
|
||||
status = tester_init_gap();
|
||||
/* Rsp with success status will be handled by bt enable cb */
|
||||
if (status == BTP_STATUS_FAILED) {
|
||||
goto rsp;
|
||||
}
|
||||
return;
|
||||
case BTP_SERVICE_ID_GATT:
|
||||
status = tester_init_gatt();
|
||||
break;
|
||||
#if defined(CONFIG_BT_L2CAP_DYNAMIC_CHANNEL)
|
||||
case BTP_SERVICE_ID_L2CAP:
|
||||
status = tester_init_l2cap();
|
||||
#endif /* CONFIG_BT_L2CAP_DYNAMIC_CHANNEL */
|
||||
break;
|
||||
#if defined(CONFIG_BT_MESH)
|
||||
case BTP_SERVICE_ID_MESH:
|
||||
status = tester_init_mesh();
|
||||
break;
|
||||
#endif /* CONFIG_BT_MESH */
|
||||
default:
|
||||
status = BTP_STATUS_FAILED;
|
||||
break;
|
||||
}
|
||||
|
||||
rsp:
|
||||
tester_rsp(BTP_SERVICE_ID_CORE, CORE_REGISTER_SERVICE, BTP_INDEX_NONE,
|
||||
status);
|
||||
}
|
||||
|
||||
static void unregister_service(u8_t *data, u16_t len)
|
||||
{
|
||||
struct core_unregister_service_cmd *cmd = (void *) data;
|
||||
u8_t status;
|
||||
|
||||
switch (cmd->id) {
|
||||
case BTP_SERVICE_ID_GAP:
|
||||
status = tester_unregister_gap();
|
||||
break;
|
||||
case BTP_SERVICE_ID_GATT:
|
||||
status = tester_unregister_gatt();
|
||||
break;
|
||||
#if defined(CONFIG_BT_L2CAP_DYNAMIC_CHANNEL)
|
||||
case BTP_SERVICE_ID_L2CAP:
|
||||
status = tester_unregister_l2cap();
|
||||
break;
|
||||
#endif /* CONFIG_BT_L2CAP_DYNAMIC_CHANNEL */
|
||||
#if defined(CONFIG_BT_MESH)
|
||||
case BTP_SERVICE_ID_MESH:
|
||||
status = tester_unregister_mesh();
|
||||
break;
|
||||
#endif /* CONFIG_BT_MESH */
|
||||
default:
|
||||
status = BTP_STATUS_FAILED;
|
||||
break;
|
||||
}
|
||||
|
||||
tester_rsp(BTP_SERVICE_ID_CORE, CORE_UNREGISTER_SERVICE, BTP_INDEX_NONE,
|
||||
status);
|
||||
}
|
||||
|
||||
static void handle_core(u8_t opcode, u8_t index, u8_t *data,
|
||||
u16_t len)
|
||||
{
|
||||
if (index != BTP_INDEX_NONE) {
|
||||
tester_rsp(BTP_SERVICE_ID_CORE, opcode, index, BTP_STATUS_FAILED);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (opcode) {
|
||||
case CORE_READ_SUPPORTED_COMMANDS:
|
||||
supported_commands(data, len);
|
||||
return;
|
||||
case CORE_READ_SUPPORTED_SERVICES:
|
||||
supported_services(data, len);
|
||||
return;
|
||||
case CORE_REGISTER_SERVICE:
|
||||
register_service(data, len);
|
||||
return;
|
||||
case CORE_UNREGISTER_SERVICE:
|
||||
unregister_service(data, len);
|
||||
return;
|
||||
default:
|
||||
tester_rsp(BTP_SERVICE_ID_CORE, opcode, BTP_INDEX_NONE,
|
||||
BTP_STATUS_UNKNOWN_CMD);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void cmd_handler(void *p1, void *p2, void *p3)
|
||||
{
|
||||
while (1) {
|
||||
struct btp_buf *cmd;
|
||||
u16_t len;
|
||||
|
||||
cmd = k_fifo_get(&cmds_queue, K_FOREVER);
|
||||
|
||||
len = sys_le16_to_cpu(cmd->hdr.len);
|
||||
|
||||
/* TODO
|
||||
* verify if service is registered before calling handler
|
||||
*/
|
||||
|
||||
switch (cmd->hdr.service) {
|
||||
case BTP_SERVICE_ID_CORE:
|
||||
handle_core(cmd->hdr.opcode, cmd->hdr.index,
|
||||
cmd->hdr.data, len);
|
||||
break;
|
||||
case BTP_SERVICE_ID_GAP:
|
||||
tester_handle_gap(cmd->hdr.opcode, cmd->hdr.index,
|
||||
cmd->hdr.data, len);
|
||||
break;
|
||||
case BTP_SERVICE_ID_GATT:
|
||||
tester_handle_gatt(cmd->hdr.opcode, cmd->hdr.index,
|
||||
cmd->hdr.data, len);
|
||||
break;
|
||||
#if defined(CONFIG_BT_L2CAP_DYNAMIC_CHANNEL)
|
||||
case BTP_SERVICE_ID_L2CAP:
|
||||
tester_handle_l2cap(cmd->hdr.opcode, cmd->hdr.index,
|
||||
cmd->hdr.data, len);
|
||||
#endif /* CONFIG_BT_L2CAP_DYNAMIC_CHANNEL */
|
||||
break;
|
||||
#if defined(CONFIG_BT_MESH)
|
||||
case BTP_SERVICE_ID_MESH:
|
||||
tester_handle_mesh(cmd->hdr.opcode, cmd->hdr.index,
|
||||
cmd->hdr.data, len);
|
||||
break;
|
||||
#endif /* CONFIG_BT_MESH */
|
||||
default:
|
||||
tester_rsp(cmd->hdr.service, cmd->hdr.opcode,
|
||||
cmd->hdr.index, BTP_STATUS_FAILED);
|
||||
break;
|
||||
}
|
||||
|
||||
k_fifo_put(&avail_queue, cmd);
|
||||
}
|
||||
}
|
||||
|
||||
static u8_t *recv_cb(u8_t *buf, size_t *off)
|
||||
{
|
||||
struct btp_hdr *cmd = (void *) buf;
|
||||
struct btp_buf *new_buf;
|
||||
u16_t len;
|
||||
|
||||
if (*off < sizeof(*cmd)) {
|
||||
return buf;
|
||||
}
|
||||
|
||||
len = sys_le16_to_cpu(cmd->len);
|
||||
if (len > BTP_MTU - sizeof(*cmd)) {
|
||||
SYS_LOG_ERR("BT tester: invalid packet length");
|
||||
*off = 0;
|
||||
return buf;
|
||||
}
|
||||
|
||||
if (*off < sizeof(*cmd) + len) {
|
||||
return buf;
|
||||
}
|
||||
|
||||
new_buf = k_fifo_get(&avail_queue, K_NO_WAIT);
|
||||
if (!new_buf) {
|
||||
SYS_LOG_ERR("BT tester: RX overflow");
|
||||
*off = 0;
|
||||
return buf;
|
||||
}
|
||||
|
||||
k_fifo_put(&cmds_queue, CONTAINER_OF(buf, struct btp_buf, data));
|
||||
|
||||
*off = 0;
|
||||
return new_buf->data;
|
||||
}
|
||||
|
||||
void tester_init(void)
|
||||
{
|
||||
int i;
|
||||
struct btp_buf *buf;
|
||||
|
||||
for (i = 0; i < CMD_QUEUED; i++) {
|
||||
k_fifo_put(&avail_queue, &cmd_buf[i]);
|
||||
}
|
||||
|
||||
k_thread_create(&cmd_thread, stack, STACKSIZE, cmd_handler,
|
||||
NULL, NULL, NULL, K_PRIO_COOP(7), 0, K_NO_WAIT);
|
||||
|
||||
buf = k_fifo_get(&avail_queue, K_NO_WAIT);
|
||||
uart_pipe_register(buf->data, BTP_MTU, recv_cb);
|
||||
|
||||
tester_send(BTP_SERVICE_ID_CORE, CORE_EV_IUT_READY, BTP_INDEX_NONE,
|
||||
NULL, 0);
|
||||
}
|
||||
|
||||
void tester_send(u8_t service, u8_t opcode, u8_t index, u8_t *data,
|
||||
size_t len)
|
||||
{
|
||||
struct btp_hdr msg;
|
||||
|
||||
msg.service = service;
|
||||
msg.opcode = opcode;
|
||||
msg.index = index;
|
||||
msg.len = len;
|
||||
|
||||
uart_pipe_send((u8_t *)&msg, sizeof(msg));
|
||||
if (data && len) {
|
||||
uart_pipe_send(data, len);
|
||||
}
|
||||
}
|
||||
|
||||
void tester_rsp(u8_t service, u8_t opcode, u8_t index, u8_t status)
|
||||
{
|
||||
struct btp_status s;
|
||||
|
||||
if (status == BTP_STATUS_SUCCESS) {
|
||||
tester_send(service, opcode, index, NULL, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
s.code = status;
|
||||
tester_send(service, BTP_STATUS, index, (u8_t *) &s, sizeof(s));
|
||||
}
|
||||
@@ -0,0 +1,846 @@
|
||||
/* bttester.h - Bluetooth tester headers */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2015-2016 Intel Corporation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <misc/util.h>
|
||||
|
||||
#define BTP_MTU 1024
|
||||
#define BTP_DATA_MAX_SIZE (BTP_MTU - sizeof(struct btp_hdr))
|
||||
|
||||
#define BTP_INDEX_NONE 0xff
|
||||
|
||||
#define BTP_SERVICE_ID_CORE 0
|
||||
#define BTP_SERVICE_ID_GAP 1
|
||||
#define BTP_SERVICE_ID_GATT 2
|
||||
#define BTP_SERVICE_ID_L2CAP 3
|
||||
#define BTP_SERVICE_ID_MESH 4
|
||||
|
||||
#define BTP_STATUS_SUCCESS 0x00
|
||||
#define BTP_STATUS_FAILED 0x01
|
||||
#define BTP_STATUS_UNKNOWN_CMD 0x02
|
||||
#define BTP_STATUS_NOT_READY 0x03
|
||||
|
||||
#define SYS_LOG_LEVEL SYS_LOG_LEVEL_DEBUG
|
||||
#define SYS_LOG_DOMAIN "bttester"
|
||||
#include <logging/sys_log.h>
|
||||
|
||||
struct btp_hdr {
|
||||
u8_t service;
|
||||
u8_t opcode;
|
||||
u8_t index;
|
||||
u16_t len;
|
||||
u8_t data[0];
|
||||
} __packed;
|
||||
|
||||
#define BTP_STATUS 0x00
|
||||
struct btp_status {
|
||||
u8_t code;
|
||||
} __packed;
|
||||
|
||||
/* Core Service */
|
||||
#define CORE_READ_SUPPORTED_COMMANDS 0x01
|
||||
struct core_read_supported_commands_rp {
|
||||
u8_t data[0];
|
||||
} __packed;
|
||||
|
||||
#define CORE_READ_SUPPORTED_SERVICES 0x02
|
||||
struct core_read_supported_services_rp {
|
||||
u8_t data[0];
|
||||
} __packed;
|
||||
|
||||
#define CORE_REGISTER_SERVICE 0x03
|
||||
struct core_register_service_cmd {
|
||||
u8_t id;
|
||||
} __packed;
|
||||
|
||||
#define CORE_UNREGISTER_SERVICE 0x04
|
||||
struct core_unregister_service_cmd {
|
||||
u8_t id;
|
||||
} __packed;
|
||||
|
||||
/* events */
|
||||
#define CORE_EV_IUT_READY 0x80
|
||||
|
||||
/* GAP Service */
|
||||
/* commands */
|
||||
#define GAP_READ_SUPPORTED_COMMANDS 0x01
|
||||
struct gap_read_supported_commands_rp {
|
||||
u8_t data[0];
|
||||
} __packed;
|
||||
|
||||
#define GAP_READ_CONTROLLER_INDEX_LIST 0x02
|
||||
struct gap_read_controller_index_list_rp {
|
||||
u8_t num;
|
||||
u8_t index[0];
|
||||
} __packed;
|
||||
|
||||
#define GAP_SETTINGS_POWERED 0
|
||||
#define GAP_SETTINGS_CONNECTABLE 1
|
||||
#define GAP_SETTINGS_FAST_CONNECTABLE 2
|
||||
#define GAP_SETTINGS_DISCOVERABLE 3
|
||||
#define GAP_SETTINGS_BONDABLE 4
|
||||
#define GAP_SETTINGS_LINK_SEC_3 5
|
||||
#define GAP_SETTINGS_SSP 6
|
||||
#define GAP_SETTINGS_BREDR 7
|
||||
#define GAP_SETTINGS_HS 8
|
||||
#define GAP_SETTINGS_LE 9
|
||||
#define GAP_SETTINGS_ADVERTISING 10
|
||||
#define GAP_SETTINGS_SC 11
|
||||
#define GAP_SETTINGS_DEBUG_KEYS 12
|
||||
#define GAP_SETTINGS_PRIVACY 13
|
||||
#define GAP_SETTINGS_CONTROLLER_CONFIG 14
|
||||
#define GAP_SETTINGS_STATIC_ADDRESS 15
|
||||
|
||||
#define GAP_READ_CONTROLLER_INFO 0x03
|
||||
struct gap_read_controller_info_rp {
|
||||
u8_t address[6];
|
||||
u32_t supported_settings;
|
||||
u32_t current_settings;
|
||||
u8_t cod[3];
|
||||
u8_t name[249];
|
||||
u8_t short_name[11];
|
||||
} __packed;
|
||||
|
||||
#define GAP_RESET 0x04
|
||||
struct gap_reset_rp {
|
||||
u32_t current_settings;
|
||||
} __packed;
|
||||
|
||||
#define GAP_SET_POWERED 0x05
|
||||
struct gap_set_powered_cmd {
|
||||
u8_t powered;
|
||||
} __packed;
|
||||
struct gap_set_powered_rp {
|
||||
u32_t current_settings;
|
||||
} __packed;
|
||||
|
||||
#define GAP_SET_CONNECTABLE 0x06
|
||||
struct gap_set_connectable_cmd {
|
||||
u8_t connectable;
|
||||
} __packed;
|
||||
struct gap_set_connectable_rp {
|
||||
u32_t current_settings;
|
||||
} __packed;
|
||||
|
||||
#define GAP_SET_FAST_CONNECTABLE 0x07
|
||||
struct gap_set_fast_connectable_cmd {
|
||||
u8_t fast_connectable;
|
||||
} __packed;
|
||||
struct gap_set_fast_connectable_rp {
|
||||
u32_t current_settings;
|
||||
} __packed;
|
||||
|
||||
#define GAP_NON_DISCOVERABLE 0x00
|
||||
#define GAP_GENERAL_DISCOVERABLE 0x01
|
||||
#define GAP_LIMITED_DISCOVERABLE 0x02
|
||||
|
||||
#define GAP_SET_DISCOVERABLE 0x08
|
||||
struct gap_set_discoverable_cmd {
|
||||
u8_t discoverable;
|
||||
} __packed;
|
||||
struct gap_set_discoverable_rp {
|
||||
u32_t current_settings;
|
||||
} __packed;
|
||||
|
||||
#define GAP_SET_BONDABLE 0x09
|
||||
struct gap_set_bondable_cmd {
|
||||
u8_t gap_set_bondable_cmd;
|
||||
} __packed;
|
||||
struct gap_set_bondable_rp {
|
||||
u32_t current_settings;
|
||||
} __packed;
|
||||
|
||||
#define GAP_START_ADVERTISING 0x0a
|
||||
struct gap_start_advertising_cmd {
|
||||
u8_t adv_data_len;
|
||||
u8_t scan_rsp_len;
|
||||
u8_t adv_data[0];
|
||||
u8_t scan_rsp[0];
|
||||
} __packed;
|
||||
struct gap_start_advertising_rp {
|
||||
u32_t current_settings;
|
||||
} __packed;
|
||||
|
||||
#define GAP_STOP_ADVERTISING 0x0b
|
||||
struct gap_stop_advertising_rp {
|
||||
u32_t current_settings;
|
||||
} __packed;
|
||||
|
||||
#define GAP_DISCOVERY_FLAG_LE 0x01
|
||||
#define GAP_DISCOVERY_FLAG_BREDR 0x02
|
||||
#define GAP_DISCOVERY_FLAG_LIMITED 0x04
|
||||
#define GAP_DISCOVERY_FLAG_LE_ACTIVE_SCAN 0x08
|
||||
#define GAP_DISCOVERY_FLAG_LE_OBSERVE 0x10
|
||||
|
||||
#define GAP_START_DISCOVERY 0x0c
|
||||
struct gap_start_discovery_cmd {
|
||||
u8_t flags;
|
||||
} __packed;
|
||||
|
||||
#define GAP_STOP_DISCOVERY 0x0d
|
||||
|
||||
#define GAP_CONNECT 0x0e
|
||||
struct gap_connect_cmd {
|
||||
u8_t address_type;
|
||||
u8_t address[6];
|
||||
} __packed;
|
||||
|
||||
#define GAP_DISCONNECT 0x0f
|
||||
struct gap_disconnect_cmd {
|
||||
u8_t address_type;
|
||||
u8_t address[6];
|
||||
} __packed;
|
||||
|
||||
#define GAP_IO_CAP_DISPLAY_ONLY 0
|
||||
#define GAP_IO_CAP_DISPLAY_YESNO 1
|
||||
#define GAP_IO_CAP_KEYBOARD_ONLY 2
|
||||
#define GAP_IO_CAP_NO_INPUT_OUTPUT 3
|
||||
#define GAP_IO_CAP_KEYBOARD_DISPLAY 4
|
||||
|
||||
#define GAP_SET_IO_CAP 0x10
|
||||
struct gap_set_io_cap_cmd {
|
||||
u8_t io_cap;
|
||||
} __packed;
|
||||
|
||||
#define GAP_PAIR 0x11
|
||||
struct gap_pair_cmd {
|
||||
u8_t address_type;
|
||||
u8_t address[6];
|
||||
} __packed;
|
||||
|
||||
#define GAP_UNPAIR 0x12
|
||||
struct gap_unpair_cmd {
|
||||
u8_t address_type;
|
||||
u8_t address[6];
|
||||
} __packed;
|
||||
|
||||
#define GAP_PASSKEY_ENTRY 0x13
|
||||
struct gap_passkey_entry_cmd {
|
||||
u8_t address_type;
|
||||
u8_t address[6];
|
||||
u32_t passkey;
|
||||
} __packed;
|
||||
|
||||
#define GAP_PASSKEY_CONFIRM 0x14
|
||||
struct gap_passkey_confirm_cmd {
|
||||
u8_t address_type;
|
||||
u8_t address[6];
|
||||
u8_t match;
|
||||
} __packed;
|
||||
|
||||
/* events */
|
||||
#define GAP_EV_NEW_SETTINGS 0x80
|
||||
struct gap_new_settings_ev {
|
||||
u32_t current_settings;
|
||||
} __packed;
|
||||
|
||||
#define GAP_DEVICE_FOUND_FLAG_RSSI 0x01
|
||||
#define GAP_DEVICE_FOUND_FLAG_AD 0x02
|
||||
#define GAP_DEVICE_FOUND_FLAG_SD 0x04
|
||||
|
||||
#define GAP_EV_DEVICE_FOUND 0x81
|
||||
struct gap_device_found_ev {
|
||||
u8_t address_type;
|
||||
u8_t address[6];
|
||||
s8_t rssi;
|
||||
u8_t flags;
|
||||
u16_t eir_data_len;
|
||||
u8_t eir_data[0];
|
||||
} __packed;
|
||||
|
||||
#define GAP_EV_DEVICE_CONNECTED 0x82
|
||||
struct gap_device_connected_ev {
|
||||
u8_t address_type;
|
||||
u8_t address[6];
|
||||
} __packed;
|
||||
|
||||
#define GAP_EV_DEVICE_DISCONNECTED 0x83
|
||||
struct gap_device_disconnected_ev {
|
||||
u8_t address_type;
|
||||
u8_t address[6];
|
||||
} __packed;
|
||||
|
||||
#define GAP_EV_PASSKEY_DISPLAY 0x84
|
||||
struct gap_passkey_display_ev {
|
||||
u8_t address_type;
|
||||
u8_t address[6];
|
||||
u32_t passkey;
|
||||
} __packed;
|
||||
|
||||
#define GAP_EV_PASSKEY_ENTRY_REQ 0x85
|
||||
struct gap_passkey_entry_req_ev {
|
||||
u8_t address_type;
|
||||
u8_t address[6];
|
||||
} __packed;
|
||||
|
||||
#define GAP_EV_PASSKEY_CONFIRM_REQ 0x86
|
||||
struct gap_passkey_confirm_req_ev {
|
||||
u8_t address_type;
|
||||
u8_t address[6];
|
||||
u32_t passkey;
|
||||
} __packed;
|
||||
|
||||
#define GAP_EV_IDENTITY_RESOLVED 0x87
|
||||
struct gap_identity_resolved_ev {
|
||||
u8_t address_type;
|
||||
u8_t address[6];
|
||||
u8_t identity_address_type;
|
||||
u8_t identity_address[6];
|
||||
} __packed;
|
||||
|
||||
/* GATT Service */
|
||||
/* commands */
|
||||
#define GATT_READ_SUPPORTED_COMMANDS 0x01
|
||||
struct gatt_read_supported_commands_rp {
|
||||
u8_t data[0];
|
||||
} __packed;
|
||||
|
||||
#define GATT_SERVICE_PRIMARY 0x00
|
||||
#define GATT_SERVICE_SECONDARY 0x01
|
||||
|
||||
#define GATT_ADD_SERVICE 0x02
|
||||
struct gatt_add_service_cmd {
|
||||
u8_t type;
|
||||
u8_t uuid_length;
|
||||
u8_t uuid[0];
|
||||
} __packed;
|
||||
struct gatt_add_service_rp {
|
||||
u16_t svc_id;
|
||||
} __packed;
|
||||
|
||||
#define GATT_ADD_CHARACTERISTIC 0x03
|
||||
struct gatt_add_characteristic_cmd {
|
||||
u16_t svc_id;
|
||||
u8_t properties;
|
||||
u8_t permissions;
|
||||
u8_t uuid_length;
|
||||
u8_t uuid[0];
|
||||
} __packed;
|
||||
struct gatt_add_characteristic_rp {
|
||||
u16_t char_id;
|
||||
} __packed;
|
||||
|
||||
#define GATT_ADD_DESCRIPTOR 0x04
|
||||
struct gatt_add_descriptor_cmd {
|
||||
u16_t char_id;
|
||||
u8_t permissions;
|
||||
u8_t uuid_length;
|
||||
u8_t uuid[0];
|
||||
} __packed;
|
||||
struct gatt_add_descriptor_rp {
|
||||
u16_t desc_id;
|
||||
} __packed;
|
||||
|
||||
#define GATT_ADD_INCLUDED_SERVICE 0x05
|
||||
struct gatt_add_included_service_cmd {
|
||||
u16_t svc_id;
|
||||
} __packed;
|
||||
struct gatt_add_included_service_rp {
|
||||
u16_t included_service_id;
|
||||
} __packed;
|
||||
|
||||
#define GATT_SET_VALUE 0x06
|
||||
struct gatt_set_value_cmd {
|
||||
u16_t attr_id;
|
||||
u16_t len;
|
||||
u8_t value[0];
|
||||
} __packed;
|
||||
|
||||
#define GATT_START_SERVER 0x07
|
||||
struct gatt_start_server_rp {
|
||||
u16_t db_attr_off;
|
||||
u8_t db_attr_cnt;
|
||||
} __packed;
|
||||
|
||||
#define GATT_SET_ENC_KEY_SIZE 0x09
|
||||
struct gatt_set_enc_key_size_cmd {
|
||||
u16_t attr_id;
|
||||
u8_t key_size;
|
||||
} __packed;
|
||||
|
||||
/* Gatt Client */
|
||||
struct gatt_service {
|
||||
u16_t start_handle;
|
||||
u16_t end_handle;
|
||||
u8_t uuid_length;
|
||||
u8_t uuid[0];
|
||||
} __packed;
|
||||
|
||||
struct gatt_included {
|
||||
u16_t included_handle;
|
||||
struct gatt_service service;
|
||||
} __packed;
|
||||
|
||||
struct gatt_characteristic {
|
||||
u16_t characteristic_handle;
|
||||
u16_t value_handle;
|
||||
u8_t properties;
|
||||
u8_t uuid_length;
|
||||
u8_t uuid[0];
|
||||
} __packed;
|
||||
|
||||
struct gatt_descriptor {
|
||||
u16_t descriptor_handle;
|
||||
u8_t uuid_length;
|
||||
u8_t uuid[0];
|
||||
} __packed;
|
||||
|
||||
#define GATT_EXCHANGE_MTU 0x0a
|
||||
|
||||
#define GATT_DISC_PRIM_UUID 0x0c
|
||||
struct gatt_disc_prim_uuid_cmd {
|
||||
u8_t address_type;
|
||||
u8_t address[6];
|
||||
u8_t uuid_length;
|
||||
u8_t uuid[0];
|
||||
} __packed;
|
||||
struct gatt_disc_prim_uuid_rp {
|
||||
u8_t services_count;
|
||||
struct gatt_service services[0];
|
||||
} __packed;
|
||||
|
||||
#define GATT_FIND_INCLUDED 0x0d
|
||||
struct gatt_find_included_cmd {
|
||||
u8_t address_type;
|
||||
u8_t address[6];
|
||||
u16_t start_handle;
|
||||
u16_t end_handle;
|
||||
} __packed;
|
||||
struct gatt_find_included_rp {
|
||||
u8_t services_count;
|
||||
struct gatt_included included[0];
|
||||
} __packed;
|
||||
|
||||
#define GATT_DISC_ALL_CHRC 0x0e
|
||||
struct gatt_disc_all_chrc_cmd {
|
||||
u8_t address_type;
|
||||
u8_t address[6];
|
||||
u16_t start_handle;
|
||||
u16_t end_handle;
|
||||
} __packed;
|
||||
struct gatt_disc_chrc_rp {
|
||||
u8_t characteristics_count;
|
||||
struct gatt_characteristic characteristics[0];
|
||||
} __packed;
|
||||
|
||||
#define GATT_DISC_CHRC_UUID 0x0f
|
||||
struct gatt_disc_chrc_uuid_cmd {
|
||||
u8_t address_type;
|
||||
u8_t address[6];
|
||||
u16_t start_handle;
|
||||
u16_t end_handle;
|
||||
u8_t uuid_length;
|
||||
u8_t uuid[0];
|
||||
} __packed;
|
||||
|
||||
#define GATT_DISC_ALL_DESC 0x10
|
||||
struct gatt_disc_all_desc_cmd {
|
||||
u8_t address_type;
|
||||
u8_t address[6];
|
||||
u16_t start_handle;
|
||||
u16_t end_handle;
|
||||
} __packed;
|
||||
struct gatt_disc_all_desc_rp {
|
||||
u8_t descriptors_count;
|
||||
struct gatt_descriptor descriptors[0];
|
||||
} __packed;
|
||||
|
||||
#define GATT_READ 0x11
|
||||
struct gatt_read_cmd {
|
||||
u8_t address_type;
|
||||
u8_t address[6];
|
||||
u16_t handle;
|
||||
} __packed;
|
||||
struct gatt_read_rp {
|
||||
u8_t att_response;
|
||||
u16_t data_length;
|
||||
u8_t data[0];
|
||||
} __packed;
|
||||
|
||||
#define GATT_READ_LONG 0x13
|
||||
struct gatt_read_long_cmd {
|
||||
u8_t address_type;
|
||||
u8_t address[6];
|
||||
u16_t handle;
|
||||
u16_t offset;
|
||||
} __packed;
|
||||
|
||||
#define GATT_READ_MULTIPLE 0x14
|
||||
struct gatt_read_multiple_cmd {
|
||||
u8_t address_type;
|
||||
u8_t address[6];
|
||||
u8_t handles_count;
|
||||
u16_t handles[0];
|
||||
} __packed;
|
||||
|
||||
#define GATT_WRITE_WITHOUT_RSP 0x15
|
||||
struct gatt_write_without_rsp_cmd {
|
||||
u8_t address_type;
|
||||
u8_t address[6];
|
||||
u16_t handle;
|
||||
u16_t data_length;
|
||||
u8_t data[0];
|
||||
} __packed;
|
||||
|
||||
#define GATT_SIGNED_WRITE_WITHOUT_RSP 0x16
|
||||
struct gatt_signed_write_without_rsp_cmd {
|
||||
u8_t address_type;
|
||||
u8_t address[6];
|
||||
u16_t handle;
|
||||
u16_t data_length;
|
||||
u8_t data[0];
|
||||
} __packed;
|
||||
|
||||
#define GATT_WRITE 0x17
|
||||
struct gatt_write_cmd {
|
||||
u8_t address_type;
|
||||
u8_t address[6];
|
||||
u16_t handle;
|
||||
u16_t data_length;
|
||||
u8_t data[0];
|
||||
} __packed;
|
||||
|
||||
#define GATT_WRITE_LONG 0x18
|
||||
struct gatt_write_long_cmd {
|
||||
u8_t address_type;
|
||||
u8_t address[6];
|
||||
u16_t handle;
|
||||
u16_t offset;
|
||||
u16_t data_length;
|
||||
u8_t data[0];
|
||||
} __packed;
|
||||
|
||||
#define GATT_CFG_NOTIFY 0x1a
|
||||
#define GATT_CFG_INDICATE 0x1b
|
||||
struct gatt_cfg_notify_cmd {
|
||||
u8_t address_type;
|
||||
u8_t address[6];
|
||||
u8_t enable;
|
||||
u16_t ccc_handle;
|
||||
} __packed;
|
||||
|
||||
#define GATT_GET_ATTRIBUTES 0x1c
|
||||
struct gatt_get_attributes_cmd {
|
||||
u16_t start_handle;
|
||||
u16_t end_handle;
|
||||
u8_t type_length;
|
||||
u8_t type[0];
|
||||
} __packed;
|
||||
struct gatt_get_attributes_rp {
|
||||
u8_t attrs_count;
|
||||
u8_t attrs[0];
|
||||
} __packed;
|
||||
struct gatt_attr {
|
||||
u16_t handle;
|
||||
u8_t permission;
|
||||
u8_t type_length;
|
||||
u8_t type[0];
|
||||
} __packed;
|
||||
|
||||
#define GATT_GET_ATTRIBUTE_VALUE 0x1d
|
||||
struct gatt_get_attribute_value_cmd {
|
||||
u16_t handle;
|
||||
} __packed;
|
||||
struct gatt_get_attribute_value_rp {
|
||||
u8_t att_response;
|
||||
u16_t value_length;
|
||||
u8_t value[0];
|
||||
} __packed;
|
||||
|
||||
/* GATT events */
|
||||
#define GATT_EV_NOTIFICATION 0x80
|
||||
struct gatt_notification_ev {
|
||||
u8_t address_type;
|
||||
u8_t address[6];
|
||||
u8_t type;
|
||||
u16_t handle;
|
||||
u16_t data_length;
|
||||
u8_t data[0];
|
||||
} __packed;
|
||||
|
||||
#define GATT_EV_ATTR_VALUE_CHANGED 0x81
|
||||
struct gatt_attr_value_changed_ev {
|
||||
u16_t handle;
|
||||
u16_t data_length;
|
||||
u8_t data[0];
|
||||
} __packed;
|
||||
|
||||
static inline void tester_set_bit(u8_t *addr, unsigned int bit)
|
||||
{
|
||||
u8_t *p = addr + (bit / 8);
|
||||
|
||||
*p |= BIT(bit % 8);
|
||||
}
|
||||
|
||||
static inline u8_t tester_test_bit(const u8_t *addr, unsigned int bit)
|
||||
{
|
||||
const u8_t *p = addr + (bit / 8);
|
||||
|
||||
return *p & BIT(bit % 8);
|
||||
}
|
||||
|
||||
/* L2CAP Service */
|
||||
/* commands */
|
||||
#define L2CAP_READ_SUPPORTED_COMMANDS 0x01
|
||||
struct l2cap_read_supported_commands_rp {
|
||||
u8_t data[0];
|
||||
} __packed;
|
||||
|
||||
#define L2CAP_CONNECT 0x02
|
||||
struct l2cap_connect_cmd {
|
||||
u8_t address_type;
|
||||
u8_t address[6];
|
||||
u16_t psm;
|
||||
} __packed;
|
||||
|
||||
struct l2cap_connect_rp {
|
||||
u8_t chan_id;
|
||||
} __packed;
|
||||
|
||||
#define L2CAP_DISCONNECT 0x03
|
||||
struct l2cap_disconnect_cmd {
|
||||
u8_t chan_id;
|
||||
} __packed;
|
||||
|
||||
#define L2CAP_SEND_DATA 0x04
|
||||
struct l2cap_send_data_cmd {
|
||||
u8_t chan_id;
|
||||
u16_t data_len;
|
||||
u8_t data[];
|
||||
} __packed;
|
||||
|
||||
#define L2CAP_TRANSPORT_BREDR 0x00
|
||||
#define L2CAP_TRANSPORT_LE 0x01
|
||||
|
||||
#define L2CAP_LISTEN 0x05
|
||||
struct l2cap_listen_cmd {
|
||||
u16_t psm;
|
||||
u8_t transport;
|
||||
} __packed;
|
||||
|
||||
#define L2CAP_ACCEPT_CONNECTION 0x06
|
||||
struct l2cap_accept_connection_cmd {
|
||||
u8_t chan_id;
|
||||
u16_t result;
|
||||
} __packed;
|
||||
|
||||
/* events */
|
||||
#define L2CAP_EV_CONNECTION_REQ 0x80
|
||||
struct l2cap_connection_req_ev {
|
||||
u8_t chan_id;
|
||||
u16_t psm;
|
||||
u8_t address_type;
|
||||
u8_t address[6];
|
||||
} __packed;
|
||||
|
||||
#define L2CAP_EV_CONNECTED 0x81
|
||||
struct l2cap_connected_ev {
|
||||
u8_t chan_id;
|
||||
u16_t psm;
|
||||
u8_t address_type;
|
||||
u8_t address[6];
|
||||
} __packed;
|
||||
|
||||
#define L2CAP_EV_DISCONNECTED 0x82
|
||||
struct l2cap_disconnected_ev {
|
||||
u16_t result;
|
||||
u8_t chan_id;
|
||||
u16_t psm;
|
||||
u8_t address_type;
|
||||
u8_t address[6];
|
||||
} __packed;
|
||||
|
||||
#define L2CAP_EV_DATA_RECEIVED 0x83
|
||||
struct l2cap_data_received_ev {
|
||||
u8_t chan_id;
|
||||
u16_t data_length;
|
||||
u8_t data[0];
|
||||
} __packed;
|
||||
|
||||
/* MESH Service */
|
||||
/* commands */
|
||||
#define MESH_READ_SUPPORTED_COMMANDS 0x01
|
||||
struct mesh_read_supported_commands_rp {
|
||||
u8_t data[0];
|
||||
} __packed;
|
||||
|
||||
#define MESH_OUT_BLINK BIT(0)
|
||||
#define MESH_OUT_BEEP BIT(1)
|
||||
#define MESH_OUT_VIBRATE BIT(2)
|
||||
#define MESH_OUT_DISPLAY_NUMBER BIT(3)
|
||||
#define MESH_OUT_DISPLAY_STRING BIT(4)
|
||||
|
||||
#define MESH_IN_PUSH BIT(0)
|
||||
#define MESH_IN_TWIST BIT(1)
|
||||
#define MESH_IN_ENTER_NUMBER BIT(2)
|
||||
#define MESH_IN_ENTER_STRING BIT(3)
|
||||
|
||||
#define MESH_CONFIG_PROVISIONING 0x02
|
||||
struct mesh_config_provisioning_cmd {
|
||||
u8_t uuid[16];
|
||||
u8_t static_auth[16];
|
||||
u8_t out_size;
|
||||
u16_t out_actions;
|
||||
u8_t in_size;
|
||||
u16_t in_actions;
|
||||
} __packed;
|
||||
|
||||
#define MESH_PROVISION_NODE 0x03
|
||||
struct mesh_provision_node_cmd {
|
||||
u8_t net_key[16];
|
||||
u16_t net_key_idx;
|
||||
u8_t flags;
|
||||
u32_t iv_index;
|
||||
u32_t seq_num;
|
||||
u16_t addr;
|
||||
u8_t dev_key[16];
|
||||
} __packed;
|
||||
|
||||
#define MESH_INIT 0x04
|
||||
#define MESH_RESET 0x05
|
||||
#define MESH_INPUT_NUMBER 0x06
|
||||
struct mesh_input_number_cmd {
|
||||
u32_t number;
|
||||
} __packed;
|
||||
|
||||
#define MESH_INPUT_STRING 0x07
|
||||
struct mesh_input_string_cmd {
|
||||
u8_t string_len;
|
||||
u8_t string[0];
|
||||
} __packed;
|
||||
|
||||
#define MESH_IVU_TEST_MODE 0x08
|
||||
struct mesh_ivu_test_mode_cmd {
|
||||
u8_t enable;
|
||||
} __packed;
|
||||
|
||||
#define MESH_IVU_TOGGLE_STATE 0x09
|
||||
|
||||
#define MESH_NET_SEND 0x0a
|
||||
struct mesh_net_send_cmd {
|
||||
u8_t ttl;
|
||||
u16_t src;
|
||||
u16_t dst;
|
||||
u8_t payload_len;
|
||||
u8_t payload[0];
|
||||
} __packed;
|
||||
|
||||
#define MESH_HEALTH_GENERATE_FAULTS 0x0b
|
||||
struct mesh_health_generate_faults_rp {
|
||||
u8_t test_id;
|
||||
u8_t cur_faults_count;
|
||||
u8_t reg_faults_count;
|
||||
u8_t current_faults[0];
|
||||
u8_t registered_faults[0];
|
||||
} __packed;
|
||||
|
||||
#define MESH_HEALTH_CLEAR_FAULTS 0x0c
|
||||
|
||||
#define MESH_LPN 0x0d
|
||||
struct mesh_lpn_set_cmd {
|
||||
u8_t enable;
|
||||
} __packed;
|
||||
|
||||
#define MESH_LPN_POLL 0x0e
|
||||
|
||||
#define MESH_MODEL_SEND 0x0f
|
||||
struct mesh_model_send_cmd {
|
||||
u16_t src;
|
||||
u16_t dst;
|
||||
u8_t payload_len;
|
||||
u8_t payload[0];
|
||||
} __packed;
|
||||
|
||||
#define MESH_LPN_SUBSCRIBE 0x10
|
||||
struct mesh_lpn_subscribe_cmd {
|
||||
u16_t address;
|
||||
} __packed;
|
||||
|
||||
#define MESH_LPN_UNSUBSCRIBE 0x11
|
||||
struct mesh_lpn_unsubscribe_cmd {
|
||||
u16_t address;
|
||||
} __packed;
|
||||
|
||||
#define MESH_RPL_CLEAR 0x12
|
||||
#define MESH_PROXY_IDENTITY 0x13
|
||||
|
||||
/* events */
|
||||
#define MESH_EV_OUT_NUMBER_ACTION 0x80
|
||||
struct mesh_out_number_action_ev {
|
||||
u16_t action;
|
||||
u32_t number;
|
||||
} __packed;
|
||||
|
||||
#define MESH_EV_OUT_STRING_ACTION 0x81
|
||||
struct mesh_out_string_action_ev {
|
||||
u8_t string_len;
|
||||
u8_t string[0];
|
||||
} __packed;
|
||||
|
||||
#define MESH_EV_IN_ACTION 0x82
|
||||
struct mesh_in_action_ev {
|
||||
u16_t action;
|
||||
u8_t size;
|
||||
} __packed;
|
||||
|
||||
#define MESH_EV_PROVISIONED 0x83
|
||||
|
||||
#define MESH_PROV_BEARER_PB_ADV 0x00
|
||||
#define MESH_PROV_BEARER_PB_GATT 0x01
|
||||
#define MESH_EV_PROV_LINK_OPEN 0x84
|
||||
struct mesh_prov_link_open_ev {
|
||||
u8_t bearer;
|
||||
} __packed;
|
||||
|
||||
#define MESH_EV_PROV_LINK_CLOSED 0x85
|
||||
struct mesh_prov_link_closed_ev {
|
||||
u8_t bearer;
|
||||
} __packed;
|
||||
|
||||
#define MESH_EV_NET_RECV 0x86
|
||||
struct mesh_net_recv_ev {
|
||||
u8_t ttl;
|
||||
u8_t ctl;
|
||||
u16_t src;
|
||||
u16_t dst;
|
||||
u8_t payload_len;
|
||||
u8_t payload[0];
|
||||
} __packed;
|
||||
|
||||
#define MESH_EV_INVALID_BEARER 0x87
|
||||
struct mesh_invalid_bearer_ev {
|
||||
u8_t opcode;
|
||||
} __packed;
|
||||
|
||||
#define MESH_EV_INCOMP_TIMER_EXP 0x88
|
||||
|
||||
void tester_init(void);
|
||||
void tester_rsp(u8_t service, u8_t opcode, u8_t index, u8_t status);
|
||||
void tester_send(u8_t service, u8_t opcode, u8_t index, u8_t *data,
|
||||
size_t len);
|
||||
|
||||
u8_t tester_init_gap(void);
|
||||
u8_t tester_unregister_gap(void);
|
||||
void tester_handle_gap(u8_t opcode, u8_t index, u8_t *data,
|
||||
u16_t len);
|
||||
u8_t tester_init_gatt(void);
|
||||
u8_t tester_unregister_gatt(void);
|
||||
void tester_handle_gatt(u8_t opcode, u8_t index, u8_t *data,
|
||||
u16_t len);
|
||||
|
||||
#if defined(CONFIG_BT_L2CAP_DYNAMIC_CHANNEL)
|
||||
u8_t tester_init_l2cap(void);
|
||||
u8_t tester_unregister_l2cap(void);
|
||||
void tester_handle_l2cap(u8_t opcode, u8_t index, u8_t *data,
|
||||
u16_t len);
|
||||
#endif /* CONFIG_BT_L2CAP_DYNAMIC_CHANNEL */
|
||||
|
||||
#if defined(CONFIG_BT_MESH)
|
||||
u8_t tester_init_mesh(void);
|
||||
u8_t tester_unregister_mesh(void);
|
||||
void tester_handle_mesh(u8_t opcode, u8_t index, u8_t *data, u16_t len);
|
||||
#endif /* CONFIG_BT_MESH */
|
||||
@@ -0,0 +1,748 @@
|
||||
/* gap.c - Bluetooth GAP Tester */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2015-2016 Intel Corporation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <atomic.h>
|
||||
#include <zephyr/types.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <toolchain.h>
|
||||
#include <bluetooth/bluetooth.h>
|
||||
#include <bluetooth/conn.h>
|
||||
|
||||
#include <misc/byteorder.h>
|
||||
#include <net/buf.h>
|
||||
|
||||
#include "bttester.h"
|
||||
|
||||
#define CONTROLLER_INDEX 0
|
||||
#define CONTROLLER_NAME "btp_tester"
|
||||
|
||||
#define BT_LE_AD_DISCOV_MASK (BT_LE_AD_LIMITED | BT_LE_AD_GENERAL)
|
||||
#define ADV_BUF_LEN (sizeof(struct gap_device_found_ev) + 2 * 31)
|
||||
|
||||
static atomic_t current_settings;
|
||||
struct bt_conn_auth_cb cb;
|
||||
|
||||
static void le_connected(struct bt_conn *conn, u8_t err)
|
||||
{
|
||||
struct gap_device_connected_ev ev;
|
||||
const bt_addr_le_t *addr = bt_conn_get_dst(conn);
|
||||
|
||||
if (err) {
|
||||
return;
|
||||
}
|
||||
|
||||
memcpy(ev.address, addr->a.val, sizeof(ev.address));
|
||||
ev.address_type = addr->type;
|
||||
|
||||
tester_send(BTP_SERVICE_ID_GAP, GAP_EV_DEVICE_CONNECTED,
|
||||
CONTROLLER_INDEX, (u8_t *) &ev, sizeof(ev));
|
||||
}
|
||||
|
||||
static void le_disconnected(struct bt_conn *conn, u8_t reason)
|
||||
{
|
||||
struct gap_device_disconnected_ev ev;
|
||||
const bt_addr_le_t *addr = bt_conn_get_dst(conn);
|
||||
|
||||
memcpy(ev.address, addr->a.val, sizeof(ev.address));
|
||||
ev.address_type = addr->type;
|
||||
|
||||
tester_send(BTP_SERVICE_ID_GAP, GAP_EV_DEVICE_DISCONNECTED,
|
||||
CONTROLLER_INDEX, (u8_t *) &ev, sizeof(ev));
|
||||
}
|
||||
|
||||
static void le_identity_resolved(struct bt_conn *conn, const bt_addr_le_t *rpa,
|
||||
const bt_addr_le_t *identity)
|
||||
{
|
||||
struct gap_identity_resolved_ev ev;
|
||||
|
||||
ev.address_type = rpa->type;
|
||||
memcpy(ev.address, rpa->a.val, sizeof(ev.address));
|
||||
|
||||
ev.identity_address_type = identity->type;
|
||||
memcpy(ev.identity_address, identity->a.val,
|
||||
sizeof(ev.identity_address));
|
||||
|
||||
tester_send(BTP_SERVICE_ID_GAP, GAP_EV_IDENTITY_RESOLVED,
|
||||
CONTROLLER_INDEX, (u8_t *) &ev, sizeof(ev));
|
||||
}
|
||||
|
||||
static struct bt_conn_cb conn_callbacks = {
|
||||
.connected = le_connected,
|
||||
.disconnected = le_disconnected,
|
||||
.identity_resolved = le_identity_resolved,
|
||||
};
|
||||
|
||||
static void supported_commands(u8_t *data, u16_t len)
|
||||
{
|
||||
u8_t cmds[3];
|
||||
struct gap_read_supported_commands_rp *rp = (void *) &cmds;
|
||||
|
||||
memset(cmds, 0, sizeof(cmds));
|
||||
|
||||
tester_set_bit(cmds, GAP_READ_SUPPORTED_COMMANDS);
|
||||
tester_set_bit(cmds, GAP_READ_CONTROLLER_INDEX_LIST);
|
||||
tester_set_bit(cmds, GAP_READ_CONTROLLER_INFO);
|
||||
tester_set_bit(cmds, GAP_SET_CONNECTABLE);
|
||||
tester_set_bit(cmds, GAP_SET_DISCOVERABLE);
|
||||
tester_set_bit(cmds, GAP_START_ADVERTISING);
|
||||
tester_set_bit(cmds, GAP_STOP_ADVERTISING);
|
||||
tester_set_bit(cmds, GAP_START_DISCOVERY);
|
||||
tester_set_bit(cmds, GAP_STOP_DISCOVERY);
|
||||
tester_set_bit(cmds, GAP_CONNECT);
|
||||
tester_set_bit(cmds, GAP_DISCONNECT);
|
||||
tester_set_bit(cmds, GAP_SET_IO_CAP);
|
||||
tester_set_bit(cmds, GAP_PAIR);
|
||||
tester_set_bit(cmds, GAP_PASSKEY_ENTRY);
|
||||
|
||||
tester_send(BTP_SERVICE_ID_GAP, GAP_READ_SUPPORTED_COMMANDS,
|
||||
CONTROLLER_INDEX, (u8_t *) rp, sizeof(cmds));
|
||||
}
|
||||
|
||||
static void controller_index_list(u8_t *data, u16_t len)
|
||||
{
|
||||
struct gap_read_controller_index_list_rp *rp;
|
||||
u8_t buf[sizeof(*rp) + 1];
|
||||
|
||||
rp = (void *) buf;
|
||||
|
||||
rp->num = 1;
|
||||
rp->index[0] = CONTROLLER_INDEX;
|
||||
|
||||
tester_send(BTP_SERVICE_ID_GAP, GAP_READ_CONTROLLER_INDEX_LIST,
|
||||
BTP_INDEX_NONE, (u8_t *) rp, sizeof(buf));
|
||||
}
|
||||
|
||||
static void controller_info(u8_t *data, u16_t len)
|
||||
{
|
||||
struct gap_read_controller_info_rp rp;
|
||||
struct bt_le_oob oob;
|
||||
u32_t supported_settings;
|
||||
|
||||
memset(&rp, 0, sizeof(rp));
|
||||
|
||||
bt_le_oob_get_local(BT_ID_DEFAULT, &oob);
|
||||
memcpy(rp.address, &oob.addr.a, sizeof(bt_addr_t));
|
||||
/*
|
||||
* If privacy is used, the device uses random type address, otherwise
|
||||
* static random or public type address is used.
|
||||
*/
|
||||
#if !defined(CONFIG_BT_PRIVACY)
|
||||
if (oob.addr.type == BT_ADDR_LE_RANDOM) {
|
||||
atomic_set_bit(¤t_settings, GAP_SETTINGS_STATIC_ADDRESS);
|
||||
}
|
||||
#endif /* CONFIG_BT_PRIVACY */
|
||||
|
||||
supported_settings = BIT(GAP_SETTINGS_POWERED);
|
||||
supported_settings |= BIT(GAP_SETTINGS_CONNECTABLE);
|
||||
supported_settings |= BIT(GAP_SETTINGS_BONDABLE);
|
||||
supported_settings |= BIT(GAP_SETTINGS_LE);
|
||||
supported_settings |= BIT(GAP_SETTINGS_ADVERTISING);
|
||||
|
||||
rp.supported_settings = sys_cpu_to_le32(supported_settings);
|
||||
rp.current_settings = sys_cpu_to_le32(current_settings);
|
||||
|
||||
memcpy(rp.name, CONTROLLER_NAME, sizeof(CONTROLLER_NAME));
|
||||
|
||||
tester_send(BTP_SERVICE_ID_GAP, GAP_READ_CONTROLLER_INFO,
|
||||
CONTROLLER_INDEX, (u8_t *) &rp, sizeof(rp));
|
||||
}
|
||||
|
||||
static void set_connectable(u8_t *data, u16_t len)
|
||||
{
|
||||
const struct gap_set_connectable_cmd *cmd = (void *) data;
|
||||
struct gap_set_connectable_rp rp;
|
||||
|
||||
if (cmd->connectable) {
|
||||
atomic_set_bit(¤t_settings, GAP_SETTINGS_CONNECTABLE);
|
||||
} else {
|
||||
atomic_clear_bit(¤t_settings, GAP_SETTINGS_CONNECTABLE);
|
||||
}
|
||||
|
||||
rp.current_settings = sys_cpu_to_le32(current_settings);
|
||||
|
||||
tester_send(BTP_SERVICE_ID_GAP, GAP_SET_CONNECTABLE, CONTROLLER_INDEX,
|
||||
(u8_t *) &rp, sizeof(rp));
|
||||
}
|
||||
|
||||
static u8_t ad_flags = BT_LE_AD_NO_BREDR;
|
||||
static struct bt_data ad[10] = {
|
||||
BT_DATA(BT_DATA_FLAGS, &ad_flags, sizeof(ad_flags)),
|
||||
};
|
||||
static struct bt_data sd[10];
|
||||
|
||||
static void set_discoverable(u8_t *data, u16_t len)
|
||||
{
|
||||
const struct gap_set_discoverable_cmd *cmd = (void *) data;
|
||||
struct gap_set_discoverable_rp rp;
|
||||
|
||||
switch (cmd->discoverable) {
|
||||
case GAP_NON_DISCOVERABLE:
|
||||
ad_flags &= ~(BT_LE_AD_GENERAL | BT_LE_AD_LIMITED);
|
||||
atomic_clear_bit(¤t_settings, GAP_SETTINGS_DISCOVERABLE);
|
||||
break;
|
||||
case GAP_GENERAL_DISCOVERABLE:
|
||||
ad_flags &= ~BT_LE_AD_LIMITED;
|
||||
ad_flags |= BT_LE_AD_GENERAL;
|
||||
atomic_set_bit(¤t_settings, GAP_SETTINGS_DISCOVERABLE);
|
||||
break;
|
||||
case GAP_LIMITED_DISCOVERABLE:
|
||||
ad_flags &= ~BT_LE_AD_GENERAL;
|
||||
ad_flags |= BT_LE_AD_LIMITED;
|
||||
atomic_set_bit(¤t_settings, GAP_SETTINGS_DISCOVERABLE);
|
||||
break;
|
||||
default:
|
||||
tester_rsp(BTP_SERVICE_ID_GAP, GAP_SET_DISCOVERABLE,
|
||||
CONTROLLER_INDEX, BTP_STATUS_FAILED);
|
||||
return;
|
||||
}
|
||||
|
||||
rp.current_settings = sys_cpu_to_le32(current_settings);
|
||||
|
||||
tester_send(BTP_SERVICE_ID_GAP, GAP_SET_DISCOVERABLE, CONTROLLER_INDEX,
|
||||
(u8_t *) &rp, sizeof(rp));
|
||||
}
|
||||
|
||||
static void start_advertising(const u8_t *data, u16_t len)
|
||||
{
|
||||
const struct gap_start_advertising_cmd *cmd = (void *) data;
|
||||
struct gap_start_advertising_rp rp;
|
||||
u8_t adv_len, sd_len;
|
||||
bool adv_conn;
|
||||
int i;
|
||||
|
||||
for (i = 0, adv_len = 1; i < cmd->adv_data_len; adv_len++) {
|
||||
if (adv_len >= ARRAY_SIZE(ad)) {
|
||||
SYS_LOG_ERR("ad[] Out of memory");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ad[adv_len].type = cmd->adv_data[i++];
|
||||
ad[adv_len].data_len = cmd->adv_data[i++];
|
||||
ad[adv_len].data = &cmd->adv_data[i];
|
||||
i += ad[adv_len].data_len;
|
||||
}
|
||||
|
||||
for (i = 0, sd_len = 0; i < cmd->scan_rsp_len; sd_len++) {
|
||||
if (sd_len >= ARRAY_SIZE(sd)) {
|
||||
SYS_LOG_ERR("sd[] Out of memory");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
sd[sd_len].type = cmd->scan_rsp[i++];
|
||||
sd[sd_len].data_len = cmd->scan_rsp[i++];
|
||||
sd[sd_len].data = &cmd->scan_rsp[i];
|
||||
i += sd[sd_len].data_len;
|
||||
}
|
||||
|
||||
adv_conn = atomic_test_bit(¤t_settings, GAP_SETTINGS_CONNECTABLE);
|
||||
|
||||
/* BTP API don't allow to set empty scan response data. */
|
||||
if (bt_le_adv_start(adv_conn ? BT_LE_ADV_CONN : BT_LE_ADV_NCONN,
|
||||
ad, adv_len, sd_len ? sd : NULL, sd_len) < 0) {
|
||||
SYS_LOG_ERR("Failed to start advertising");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
atomic_set_bit(¤t_settings, GAP_SETTINGS_ADVERTISING);
|
||||
rp.current_settings = sys_cpu_to_le32(current_settings);
|
||||
|
||||
tester_send(BTP_SERVICE_ID_GAP, GAP_START_ADVERTISING, CONTROLLER_INDEX,
|
||||
(u8_t *) &rp, sizeof(rp));
|
||||
return;
|
||||
fail:
|
||||
tester_rsp(BTP_SERVICE_ID_GAP, GAP_START_ADVERTISING, CONTROLLER_INDEX,
|
||||
BTP_STATUS_FAILED);
|
||||
}
|
||||
|
||||
static void stop_advertising(const u8_t *data, u16_t len)
|
||||
{
|
||||
struct gap_stop_advertising_rp rp;
|
||||
|
||||
if (bt_le_adv_stop() < 0) {
|
||||
tester_rsp(BTP_SERVICE_ID_GAP, GAP_STOP_ADVERTISING,
|
||||
CONTROLLER_INDEX, BTP_STATUS_FAILED);
|
||||
return;
|
||||
}
|
||||
|
||||
atomic_clear_bit(¤t_settings, GAP_SETTINGS_ADVERTISING);
|
||||
rp.current_settings = sys_cpu_to_le32(current_settings);
|
||||
|
||||
tester_send(BTP_SERVICE_ID_GAP, GAP_STOP_ADVERTISING, CONTROLLER_INDEX,
|
||||
(u8_t *) &rp, sizeof(rp));
|
||||
}
|
||||
|
||||
static u8_t get_ad_flags(struct net_buf_simple *ad)
|
||||
{
|
||||
u8_t len, i;
|
||||
|
||||
/* Parse advertisement to get flags */
|
||||
for (i = 0; i < ad->len; i += len - 1) {
|
||||
len = ad->data[i++];
|
||||
if (!len) {
|
||||
break;
|
||||
}
|
||||
|
||||
/* Check if field length is correct */
|
||||
if (len > (ad->len - i) || (ad->len - i) < 1) {
|
||||
break;
|
||||
}
|
||||
|
||||
switch (ad->data[i++]) {
|
||||
case BT_DATA_FLAGS:
|
||||
return ad->data[i];
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static u8_t discovery_flags;
|
||||
static struct net_buf_simple *adv_buf = NET_BUF_SIMPLE(ADV_BUF_LEN);
|
||||
|
||||
static void store_adv(const bt_addr_le_t *addr, s8_t rssi,
|
||||
struct net_buf_simple *ad)
|
||||
{
|
||||
struct gap_device_found_ev *ev;
|
||||
|
||||
/* cleanup */
|
||||
net_buf_simple_init(adv_buf, 0);
|
||||
|
||||
ev = net_buf_simple_add(adv_buf, sizeof(*ev));
|
||||
|
||||
memcpy(ev->address, addr->a.val, sizeof(ev->address));
|
||||
ev->address_type = addr->type;
|
||||
ev->rssi = rssi;
|
||||
ev->flags = GAP_DEVICE_FOUND_FLAG_AD | GAP_DEVICE_FOUND_FLAG_RSSI;
|
||||
ev->eir_data_len = ad->len;
|
||||
memcpy(net_buf_simple_add(adv_buf, ad->len), ad->data, ad->len);
|
||||
}
|
||||
|
||||
static void device_found(const bt_addr_le_t *addr, s8_t rssi, u8_t evtype,
|
||||
struct net_buf_simple *ad)
|
||||
{
|
||||
/* if General/Limited Discovery - parse Advertising data to get flags */
|
||||
if (!(discovery_flags & GAP_DISCOVERY_FLAG_LE_OBSERVE) &&
|
||||
(evtype != BT_LE_ADV_SCAN_RSP)) {
|
||||
u8_t flags = get_ad_flags(ad);
|
||||
|
||||
/* ignore non-discoverable devices */
|
||||
if (!(flags & BT_LE_AD_DISCOV_MASK)) {
|
||||
SYS_LOG_DBG("Non discoverable, skipping");
|
||||
return;
|
||||
}
|
||||
|
||||
/* if Limited Discovery - ignore general discoverable devices */
|
||||
if ((discovery_flags & GAP_DISCOVERY_FLAG_LIMITED) &&
|
||||
!(flags & BT_LE_AD_LIMITED)) {
|
||||
SYS_LOG_DBG("General discoverable, skipping");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* attach Scan Response data */
|
||||
if (evtype == BT_LE_ADV_SCAN_RSP) {
|
||||
struct gap_device_found_ev *ev;
|
||||
bt_addr_le_t a;
|
||||
|
||||
/* skip if there is no pending advertisement */
|
||||
if (!adv_buf->len) {
|
||||
SYS_LOG_INF("No pending advertisement, skipping");
|
||||
return;
|
||||
}
|
||||
|
||||
ev = (void *) adv_buf->data;
|
||||
a.type = ev->address_type;
|
||||
memcpy(a.a.val, ev->address, sizeof(a.a.val));
|
||||
|
||||
/*
|
||||
* in general, the Scan Response comes right after the
|
||||
* Advertisement, but if not if send stored event and ignore
|
||||
* this one
|
||||
*/
|
||||
if (bt_addr_le_cmp(addr, &a)) {
|
||||
SYS_LOG_INF("Address does not match, skipping");
|
||||
goto done;
|
||||
}
|
||||
|
||||
ev->eir_data_len += ad->len;
|
||||
ev->flags |= GAP_DEVICE_FOUND_FLAG_SD;
|
||||
|
||||
memcpy(net_buf_simple_add(adv_buf, ad->len), ad->data, ad->len);
|
||||
|
||||
goto done;
|
||||
}
|
||||
|
||||
/*
|
||||
* if there is another pending advertisement, send it and store the
|
||||
* current one
|
||||
*/
|
||||
if (adv_buf->len) {
|
||||
tester_send(BTP_SERVICE_ID_GAP, GAP_EV_DEVICE_FOUND,
|
||||
CONTROLLER_INDEX, adv_buf->data, adv_buf->len);
|
||||
}
|
||||
|
||||
store_adv(addr, rssi, ad);
|
||||
|
||||
/* if Active Scan and scannable event - wait for Scan Response */
|
||||
if ((discovery_flags & GAP_DISCOVERY_FLAG_LE_ACTIVE_SCAN) &&
|
||||
(evtype == BT_LE_ADV_IND || evtype == BT_LE_ADV_SCAN_IND)) {
|
||||
SYS_LOG_DBG("Waiting for scan response");
|
||||
return;
|
||||
}
|
||||
done:
|
||||
tester_send(BTP_SERVICE_ID_GAP, GAP_EV_DEVICE_FOUND,
|
||||
CONTROLLER_INDEX, adv_buf->data, adv_buf->len);
|
||||
}
|
||||
|
||||
static void start_discovery(const u8_t *data, u16_t len)
|
||||
{
|
||||
const struct gap_start_discovery_cmd *cmd = (void *) data;
|
||||
u8_t status;
|
||||
|
||||
/* only LE scan is supported */
|
||||
if (cmd->flags & GAP_DISCOVERY_FLAG_BREDR) {
|
||||
status = BTP_STATUS_FAILED;
|
||||
goto reply;
|
||||
}
|
||||
|
||||
if (bt_le_scan_start(cmd->flags & GAP_DISCOVERY_FLAG_LE_ACTIVE_SCAN ?
|
||||
BT_LE_SCAN_ACTIVE : BT_LE_SCAN_PASSIVE,
|
||||
device_found) < 0) {
|
||||
status = BTP_STATUS_FAILED;
|
||||
goto reply;
|
||||
}
|
||||
|
||||
net_buf_simple_init(adv_buf, 0);
|
||||
discovery_flags = cmd->flags;
|
||||
|
||||
status = BTP_STATUS_SUCCESS;
|
||||
reply:
|
||||
tester_rsp(BTP_SERVICE_ID_GAP, GAP_START_DISCOVERY, CONTROLLER_INDEX,
|
||||
status);
|
||||
}
|
||||
|
||||
static void stop_discovery(const u8_t *data, u16_t len)
|
||||
{
|
||||
u8_t status = BTP_STATUS_SUCCESS;
|
||||
|
||||
if (bt_le_scan_stop() < 0) {
|
||||
status = BTP_STATUS_FAILED;
|
||||
}
|
||||
|
||||
tester_rsp(BTP_SERVICE_ID_GAP, GAP_STOP_DISCOVERY, CONTROLLER_INDEX,
|
||||
status);
|
||||
}
|
||||
|
||||
static void connect(const u8_t *data, u16_t len)
|
||||
{
|
||||
struct bt_conn *conn;
|
||||
u8_t status;
|
||||
|
||||
conn = bt_conn_create_le((bt_addr_le_t *) data,
|
||||
BT_LE_CONN_PARAM_DEFAULT);
|
||||
if (!conn) {
|
||||
status = BTP_STATUS_FAILED;
|
||||
goto rsp;
|
||||
}
|
||||
|
||||
bt_conn_unref(conn);
|
||||
status = BTP_STATUS_SUCCESS;
|
||||
|
||||
rsp:
|
||||
tester_rsp(BTP_SERVICE_ID_GAP, GAP_CONNECT, CONTROLLER_INDEX, status);
|
||||
}
|
||||
|
||||
static void disconnect(const u8_t *data, u16_t len)
|
||||
{
|
||||
struct bt_conn *conn;
|
||||
u8_t status;
|
||||
|
||||
conn = bt_conn_lookup_addr_le(BT_ID_DEFAULT, (bt_addr_le_t *)data);
|
||||
if (!conn) {
|
||||
status = BTP_STATUS_FAILED;
|
||||
goto rsp;
|
||||
}
|
||||
|
||||
if (bt_conn_disconnect(conn, BT_HCI_ERR_REMOTE_USER_TERM_CONN)) {
|
||||
status = BTP_STATUS_FAILED;
|
||||
} else {
|
||||
status = BTP_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
bt_conn_unref(conn);
|
||||
|
||||
rsp:
|
||||
tester_rsp(BTP_SERVICE_ID_GAP, GAP_DISCONNECT, CONTROLLER_INDEX,
|
||||
status);
|
||||
}
|
||||
|
||||
static void auth_passkey_display(struct bt_conn *conn, unsigned int passkey)
|
||||
{
|
||||
struct gap_passkey_display_ev ev;
|
||||
const bt_addr_le_t *addr = bt_conn_get_dst(conn);
|
||||
|
||||
memcpy(ev.address, addr->a.val, sizeof(ev.address));
|
||||
ev.address_type = addr->type;
|
||||
ev.passkey = sys_cpu_to_le32(passkey);
|
||||
|
||||
tester_send(BTP_SERVICE_ID_GAP, GAP_EV_PASSKEY_DISPLAY,
|
||||
CONTROLLER_INDEX, (u8_t *) &ev, sizeof(ev));
|
||||
}
|
||||
|
||||
static void auth_passkey_entry(struct bt_conn *conn)
|
||||
{
|
||||
struct gap_passkey_entry_req_ev ev;
|
||||
const bt_addr_le_t *addr = bt_conn_get_dst(conn);
|
||||
|
||||
memcpy(ev.address, addr->a.val, sizeof(ev.address));
|
||||
ev.address_type = addr->type;
|
||||
|
||||
tester_send(BTP_SERVICE_ID_GAP, GAP_EV_PASSKEY_ENTRY_REQ,
|
||||
CONTROLLER_INDEX, (u8_t *) &ev, sizeof(ev));
|
||||
}
|
||||
|
||||
static void auth_cancel(struct bt_conn *conn)
|
||||
{
|
||||
/* TODO */
|
||||
}
|
||||
|
||||
static void set_io_cap(const u8_t *data, u16_t len)
|
||||
{
|
||||
const struct gap_set_io_cap_cmd *cmd = (void *) data;
|
||||
u8_t status;
|
||||
|
||||
/* Reset io cap requirements */
|
||||
memset(&cb, 0, sizeof(cb));
|
||||
bt_conn_auth_cb_register(NULL);
|
||||
|
||||
switch (cmd->io_cap) {
|
||||
case GAP_IO_CAP_DISPLAY_ONLY:
|
||||
cb.cancel = auth_cancel;
|
||||
cb.passkey_display = auth_passkey_display;
|
||||
break;
|
||||
case GAP_IO_CAP_KEYBOARD_DISPLAY:
|
||||
cb.cancel = auth_cancel;
|
||||
cb.passkey_display = auth_passkey_display;
|
||||
cb.passkey_entry = auth_passkey_entry;
|
||||
break;
|
||||
case GAP_IO_CAP_NO_INPUT_OUTPUT:
|
||||
cb.cancel = auth_cancel;
|
||||
break;
|
||||
case GAP_IO_CAP_KEYBOARD_ONLY:
|
||||
cb.cancel = auth_cancel;
|
||||
cb.passkey_entry = auth_passkey_entry;
|
||||
break;
|
||||
case GAP_IO_CAP_DISPLAY_YESNO:
|
||||
default:
|
||||
status = BTP_STATUS_FAILED;
|
||||
goto rsp;
|
||||
}
|
||||
|
||||
if (bt_conn_auth_cb_register(&cb)) {
|
||||
status = BTP_STATUS_FAILED;
|
||||
goto rsp;
|
||||
}
|
||||
|
||||
status = BTP_STATUS_SUCCESS;
|
||||
|
||||
rsp:
|
||||
tester_rsp(BTP_SERVICE_ID_GAP, GAP_SET_IO_CAP, CONTROLLER_INDEX,
|
||||
status);
|
||||
}
|
||||
|
||||
static void pair(const u8_t *data, u16_t len)
|
||||
{
|
||||
struct bt_conn *conn;
|
||||
u8_t status;
|
||||
|
||||
conn = bt_conn_lookup_addr_le(BT_ID_DEFAULT, (bt_addr_le_t *)data);
|
||||
if (!conn) {
|
||||
status = BTP_STATUS_FAILED;
|
||||
goto rsp;
|
||||
}
|
||||
|
||||
if (bt_conn_security(conn, BT_SECURITY_MEDIUM)) {
|
||||
status = BTP_STATUS_FAILED;
|
||||
bt_conn_unref(conn);
|
||||
goto rsp;
|
||||
}
|
||||
|
||||
bt_conn_unref(conn);
|
||||
status = BTP_STATUS_SUCCESS;
|
||||
|
||||
rsp:
|
||||
tester_rsp(BTP_SERVICE_ID_GAP, GAP_PAIR, CONTROLLER_INDEX, status);
|
||||
}
|
||||
|
||||
static void unpair(const u8_t *data, u16_t len)
|
||||
{
|
||||
struct gap_unpair_cmd *cmd = (void *) data;
|
||||
struct bt_conn *conn;
|
||||
bt_addr_le_t addr;
|
||||
u8_t status;
|
||||
int err;
|
||||
|
||||
addr.type = cmd->address_type;
|
||||
memcpy(addr.a.val, cmd->address, sizeof(addr.a.val));
|
||||
|
||||
conn = bt_conn_lookup_addr_le(BT_ID_DEFAULT, &addr);
|
||||
if (!conn) {
|
||||
goto keys;
|
||||
}
|
||||
|
||||
err = bt_conn_disconnect(conn, BT_HCI_ERR_REMOTE_USER_TERM_CONN);
|
||||
|
||||
bt_conn_unref(conn);
|
||||
|
||||
if (err < 0) {
|
||||
status = BTP_STATUS_FAILED;
|
||||
goto rsp;
|
||||
}
|
||||
keys:
|
||||
err = bt_unpair(BT_ID_DEFAULT, &addr);
|
||||
|
||||
status = err < 0 ? BTP_STATUS_FAILED : BTP_STATUS_SUCCESS;
|
||||
rsp:
|
||||
tester_rsp(BTP_SERVICE_ID_GAP, GAP_UNPAIR, CONTROLLER_INDEX, status);
|
||||
}
|
||||
|
||||
static void passkey_entry(const u8_t *data, u16_t len)
|
||||
{
|
||||
const struct gap_passkey_entry_cmd *cmd = (void *) data;
|
||||
struct bt_conn *conn;
|
||||
u8_t status;
|
||||
|
||||
conn = bt_conn_lookup_addr_le(BT_ID_DEFAULT, (bt_addr_le_t *)data);
|
||||
if (!conn) {
|
||||
status = BTP_STATUS_FAILED;
|
||||
goto rsp;
|
||||
}
|
||||
|
||||
bt_conn_auth_passkey_entry(conn, sys_le32_to_cpu(cmd->passkey));
|
||||
|
||||
bt_conn_unref(conn);
|
||||
status = BTP_STATUS_SUCCESS;
|
||||
|
||||
rsp:
|
||||
tester_rsp(BTP_SERVICE_ID_GAP, GAP_PASSKEY_ENTRY, CONTROLLER_INDEX,
|
||||
status);
|
||||
}
|
||||
|
||||
void tester_handle_gap(u8_t opcode, u8_t index, u8_t *data,
|
||||
u16_t len)
|
||||
{
|
||||
switch (opcode) {
|
||||
case GAP_READ_SUPPORTED_COMMANDS:
|
||||
case GAP_READ_CONTROLLER_INDEX_LIST:
|
||||
if (index != BTP_INDEX_NONE){
|
||||
tester_rsp(BTP_SERVICE_ID_GAP, opcode, index,
|
||||
BTP_STATUS_FAILED);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (index != CONTROLLER_INDEX){
|
||||
tester_rsp(BTP_SERVICE_ID_GAP, opcode, index,
|
||||
BTP_STATUS_FAILED);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
switch (opcode) {
|
||||
case GAP_READ_SUPPORTED_COMMANDS:
|
||||
supported_commands(data, len);
|
||||
return;
|
||||
case GAP_READ_CONTROLLER_INDEX_LIST:
|
||||
controller_index_list(data, len);
|
||||
return;
|
||||
case GAP_READ_CONTROLLER_INFO:
|
||||
controller_info(data, len);
|
||||
return;
|
||||
case GAP_SET_CONNECTABLE:
|
||||
set_connectable(data, len);
|
||||
return;
|
||||
case GAP_SET_DISCOVERABLE:
|
||||
set_discoverable(data, len);
|
||||
return;
|
||||
case GAP_START_ADVERTISING:
|
||||
start_advertising(data, len);
|
||||
return;
|
||||
case GAP_STOP_ADVERTISING:
|
||||
stop_advertising(data, len);
|
||||
return;
|
||||
case GAP_START_DISCOVERY:
|
||||
start_discovery(data, len);
|
||||
return;
|
||||
case GAP_STOP_DISCOVERY:
|
||||
stop_discovery(data, len);
|
||||
return;
|
||||
case GAP_CONNECT:
|
||||
connect(data, len);
|
||||
return;
|
||||
case GAP_DISCONNECT:
|
||||
disconnect(data, len);
|
||||
return;
|
||||
case GAP_SET_IO_CAP:
|
||||
set_io_cap(data, len);
|
||||
return;
|
||||
case GAP_PAIR:
|
||||
pair(data, len);
|
||||
return;
|
||||
case GAP_UNPAIR:
|
||||
unpair(data, len);
|
||||
return;
|
||||
case GAP_PASSKEY_ENTRY:
|
||||
passkey_entry(data, len);
|
||||
return;
|
||||
default:
|
||||
tester_rsp(BTP_SERVICE_ID_GAP, opcode, index,
|
||||
BTP_STATUS_UNKNOWN_CMD);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void tester_init_gap_cb(int err)
|
||||
{
|
||||
if (err) {
|
||||
tester_rsp(BTP_SERVICE_ID_CORE, CORE_REGISTER_SERVICE,
|
||||
BTP_INDEX_NONE, BTP_STATUS_FAILED);
|
||||
return;
|
||||
}
|
||||
|
||||
atomic_clear(¤t_settings);
|
||||
atomic_set_bit(¤t_settings, GAP_SETTINGS_POWERED);
|
||||
atomic_set_bit(¤t_settings, GAP_SETTINGS_CONNECTABLE);
|
||||
atomic_set_bit(¤t_settings, GAP_SETTINGS_BONDABLE);
|
||||
atomic_set_bit(¤t_settings, GAP_SETTINGS_LE);
|
||||
#if defined(CONFIG_BT_PRIVACY)
|
||||
atomic_set_bit(¤t_settings, GAP_SETTINGS_PRIVACY);
|
||||
#endif /* CONFIG_BT_PRIVACY */
|
||||
|
||||
bt_conn_cb_register(&conn_callbacks);
|
||||
|
||||
tester_rsp(BTP_SERVICE_ID_CORE, CORE_REGISTER_SERVICE, BTP_INDEX_NONE,
|
||||
BTP_STATUS_SUCCESS);
|
||||
}
|
||||
|
||||
u8_t tester_init_gap(void)
|
||||
{
|
||||
if (bt_enable(tester_init_gap_cb) < 0) {
|
||||
return BTP_STATUS_FAILED;
|
||||
}
|
||||
|
||||
return BTP_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
u8_t tester_unregister_gap(void)
|
||||
{
|
||||
return BTP_STATUS_SUCCESS;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,357 @@
|
||||
/* l2cap.c - Bluetooth L2CAP Tester */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2016 Intel Corporation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <bluetooth/bluetooth.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <bluetooth/l2cap.h>
|
||||
#include <misc/byteorder.h>
|
||||
#include "bttester.h"
|
||||
|
||||
#define CONTROLLER_INDEX 0
|
||||
#define DATA_MTU 230
|
||||
#define CHANNELS 2
|
||||
#define SERVERS 1
|
||||
|
||||
NET_BUF_POOL_DEFINE(data_pool, 1, DATA_MTU, BT_BUF_USER_DATA_MIN, NULL);
|
||||
|
||||
static struct channel {
|
||||
u8_t chan_id; /* Internal number that identifies L2CAP channel. */
|
||||
struct bt_l2cap_le_chan le;
|
||||
} channels[CHANNELS];
|
||||
|
||||
/* TODO Extend to support multiple servers */
|
||||
static struct bt_l2cap_server servers[SERVERS];
|
||||
|
||||
static struct net_buf *alloc_buf_cb(struct bt_l2cap_chan *chan)
|
||||
{
|
||||
return net_buf_alloc(&data_pool, K_FOREVER);
|
||||
}
|
||||
|
||||
static u8_t recv_cb_buf[DATA_MTU + sizeof(struct l2cap_data_received_ev)];
|
||||
|
||||
static void recv_cb(struct bt_l2cap_chan *l2cap_chan, struct net_buf *buf)
|
||||
{
|
||||
struct l2cap_data_received_ev *ev = (void *) recv_cb_buf;
|
||||
struct channel *chan = CONTAINER_OF(l2cap_chan, struct channel, le);
|
||||
|
||||
ev->chan_id = chan->chan_id;
|
||||
ev->data_length = sys_cpu_to_le16(buf->len);
|
||||
memcpy(ev->data, buf->data, buf->len);
|
||||
|
||||
tester_send(BTP_SERVICE_ID_L2CAP, L2CAP_EV_DATA_RECEIVED,
|
||||
CONTROLLER_INDEX, recv_cb_buf, sizeof(*ev) + buf->len);
|
||||
}
|
||||
|
||||
static void connected_cb(struct bt_l2cap_chan *l2cap_chan)
|
||||
{
|
||||
struct l2cap_connected_ev ev;
|
||||
struct channel *chan = CONTAINER_OF(l2cap_chan, struct channel, le);
|
||||
struct bt_conn_info info;
|
||||
|
||||
ev.chan_id = chan->chan_id;
|
||||
/* TODO: ev.psm */
|
||||
if (!bt_conn_get_info(l2cap_chan->conn, &info)) {
|
||||
switch (info.type) {
|
||||
case BT_CONN_TYPE_LE:
|
||||
ev.address_type = info.le.dst->type;
|
||||
memcpy(ev.address, info.le.dst->a.val,
|
||||
sizeof(ev.address));
|
||||
break;
|
||||
case BT_CONN_TYPE_BR:
|
||||
memcpy(ev.address, info.br.dst->val,
|
||||
sizeof(ev.address));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
tester_send(BTP_SERVICE_ID_L2CAP, L2CAP_EV_CONNECTED, CONTROLLER_INDEX,
|
||||
(u8_t *) &ev, sizeof(ev));
|
||||
}
|
||||
|
||||
static void disconnected_cb(struct bt_l2cap_chan *l2cap_chan)
|
||||
{
|
||||
struct l2cap_disconnected_ev ev;
|
||||
struct channel *chan = CONTAINER_OF(l2cap_chan, struct channel, le);
|
||||
struct bt_conn_info info;
|
||||
|
||||
memset(&ev, 0, sizeof(struct l2cap_disconnected_ev));
|
||||
|
||||
/* TODO: ev.result */
|
||||
ev.chan_id = chan->chan_id;
|
||||
/* TODO: ev.psm */
|
||||
if (!bt_conn_get_info(l2cap_chan->conn, &info)) {
|
||||
switch (info.type) {
|
||||
case BT_CONN_TYPE_LE:
|
||||
ev.address_type = info.le.dst->type;
|
||||
memcpy(ev.address, info.le.dst->a.val,
|
||||
sizeof(ev.address));
|
||||
break;
|
||||
case BT_CONN_TYPE_BR:
|
||||
memcpy(ev.address, info.br.dst->val,
|
||||
sizeof(ev.address));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
tester_send(BTP_SERVICE_ID_L2CAP, L2CAP_EV_DISCONNECTED,
|
||||
CONTROLLER_INDEX, (u8_t *) &ev, sizeof(ev));
|
||||
}
|
||||
|
||||
static struct bt_l2cap_chan_ops l2cap_ops = {
|
||||
.alloc_buf = alloc_buf_cb,
|
||||
.recv = recv_cb,
|
||||
.connected = connected_cb,
|
||||
.disconnected = disconnected_cb,
|
||||
};
|
||||
|
||||
static struct channel *get_free_channel()
|
||||
{
|
||||
u8_t i;
|
||||
struct channel *chan;
|
||||
|
||||
for (i = 0; i < CHANNELS; i++) {
|
||||
if (channels[i].le.chan.state != BT_L2CAP_DISCONNECTED) {
|
||||
continue;
|
||||
}
|
||||
|
||||
chan = &channels[i];
|
||||
chan->chan_id = i;
|
||||
|
||||
return chan;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void connect(u8_t *data, u16_t len)
|
||||
{
|
||||
const struct l2cap_connect_cmd *cmd = (void *) data;
|
||||
struct l2cap_connect_rp rp;
|
||||
struct bt_conn *conn;
|
||||
struct channel *chan;
|
||||
int err;
|
||||
|
||||
conn = bt_conn_lookup_addr_le(BT_ID_DEFAULT, (bt_addr_le_t *)data);
|
||||
if (!conn) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
chan = get_free_channel();
|
||||
if (!chan) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
chan->le.chan.ops = &l2cap_ops;
|
||||
chan->le.rx.mtu = DATA_MTU;
|
||||
|
||||
err = bt_l2cap_chan_connect(conn, &chan->le.chan, cmd->psm);
|
||||
if (err < 0) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
rp.chan_id = chan->chan_id;
|
||||
|
||||
tester_send(BTP_SERVICE_ID_L2CAP, L2CAP_CONNECT, CONTROLLER_INDEX,
|
||||
(u8_t *) &rp, sizeof(rp));
|
||||
|
||||
return;
|
||||
|
||||
fail:
|
||||
tester_rsp(BTP_SERVICE_ID_L2CAP, L2CAP_CONNECT, CONTROLLER_INDEX,
|
||||
BTP_STATUS_FAILED);
|
||||
}
|
||||
|
||||
static void disconnect(u8_t *data, u16_t len)
|
||||
{
|
||||
const struct l2cap_disconnect_cmd *cmd = (void *) data;
|
||||
struct channel *chan = &channels[cmd->chan_id];
|
||||
u8_t status;
|
||||
int err;
|
||||
|
||||
err = bt_l2cap_chan_disconnect(&chan->le.chan);
|
||||
if (err) {
|
||||
status = BTP_STATUS_FAILED;
|
||||
goto rsp;
|
||||
}
|
||||
|
||||
status = BTP_STATUS_SUCCESS;
|
||||
|
||||
rsp:
|
||||
tester_rsp(BTP_SERVICE_ID_L2CAP, L2CAP_DISCONNECT, CONTROLLER_INDEX,
|
||||
status);
|
||||
}
|
||||
|
||||
static void send_data(u8_t *data, u16_t len)
|
||||
{
|
||||
const struct l2cap_send_data_cmd *cmd = (void *) data;
|
||||
struct channel *chan = &channels[cmd->chan_id];
|
||||
struct net_buf *buf;
|
||||
int ret;
|
||||
u16_t data_len = sys_le16_to_cpu(cmd->data_len);
|
||||
|
||||
/* FIXME: For now, fail if data length exceeds buffer length */
|
||||
if (data_len > DATA_MTU - BT_L2CAP_CHAN_SEND_RESERVE) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* FIXME: For now, fail if data length exceeds remote's L2CAP SDU */
|
||||
if (data_len > chan->le.tx.mtu) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
buf = net_buf_alloc(&data_pool, K_FOREVER);
|
||||
net_buf_reserve(buf, BT_L2CAP_CHAN_SEND_RESERVE);
|
||||
|
||||
net_buf_add_mem(buf, cmd->data, data_len);
|
||||
ret = bt_l2cap_chan_send(&chan->le.chan, buf);
|
||||
if (ret < 0) {
|
||||
SYS_LOG_ERR("Unable to send data: %d", -ret);
|
||||
net_buf_unref(buf);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
tester_rsp(BTP_SERVICE_ID_L2CAP, L2CAP_SEND_DATA, CONTROLLER_INDEX,
|
||||
BTP_STATUS_SUCCESS);
|
||||
return;
|
||||
|
||||
fail:
|
||||
tester_rsp(BTP_SERVICE_ID_L2CAP, L2CAP_SEND_DATA, CONTROLLER_INDEX,
|
||||
BTP_STATUS_FAILED);
|
||||
}
|
||||
|
||||
static struct bt_l2cap_server *get_free_server(void)
|
||||
{
|
||||
u8_t i;
|
||||
|
||||
for (i = 0; i < SERVERS ; i++) {
|
||||
if (servers[i].psm) {
|
||||
continue;
|
||||
}
|
||||
|
||||
return &servers[i];
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static bool is_free_psm(u16_t psm)
|
||||
{
|
||||
u8_t i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(servers); i++) {
|
||||
if (servers[i].psm == psm) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static int accept(struct bt_conn *conn, struct bt_l2cap_chan **l2cap_chan)
|
||||
{
|
||||
struct channel *chan;
|
||||
|
||||
chan = get_free_channel();
|
||||
if (!chan) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
chan->le.chan.ops = &l2cap_ops;
|
||||
chan->le.rx.mtu = DATA_MTU;
|
||||
|
||||
*l2cap_chan = &chan->le.chan;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void listen(u8_t *data, u16_t len)
|
||||
{
|
||||
const struct l2cap_listen_cmd *cmd = (void *) data;
|
||||
struct bt_l2cap_server *server;
|
||||
|
||||
/* TODO: Handle cmd->transport flag */
|
||||
|
||||
if (!is_free_psm(cmd->psm)) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
server = get_free_server();
|
||||
if (!server) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
server->accept = accept;
|
||||
server->psm = cmd->psm;
|
||||
|
||||
if (bt_l2cap_server_register(server) < 0) {
|
||||
server->psm = 0;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
tester_rsp(BTP_SERVICE_ID_L2CAP, L2CAP_LISTEN, CONTROLLER_INDEX,
|
||||
BTP_STATUS_SUCCESS);
|
||||
return;
|
||||
|
||||
fail:
|
||||
tester_rsp(BTP_SERVICE_ID_L2CAP, L2CAP_LISTEN, CONTROLLER_INDEX,
|
||||
BTP_STATUS_FAILED);
|
||||
}
|
||||
|
||||
static void supported_commands(u8_t *data, u16_t len)
|
||||
{
|
||||
u8_t cmds[1];
|
||||
struct l2cap_read_supported_commands_rp *rp = (void *) cmds;
|
||||
|
||||
memset(cmds, 0, sizeof(cmds));
|
||||
|
||||
tester_set_bit(cmds, L2CAP_READ_SUPPORTED_COMMANDS);
|
||||
tester_set_bit(cmds, L2CAP_CONNECT);
|
||||
tester_set_bit(cmds, L2CAP_DISCONNECT);
|
||||
tester_set_bit(cmds, L2CAP_LISTEN);
|
||||
tester_set_bit(cmds, L2CAP_SEND_DATA);
|
||||
|
||||
tester_send(BTP_SERVICE_ID_L2CAP, L2CAP_READ_SUPPORTED_COMMANDS,
|
||||
CONTROLLER_INDEX, (u8_t *) rp, sizeof(cmds));
|
||||
}
|
||||
|
||||
void tester_handle_l2cap(u8_t opcode, u8_t index, u8_t *data,
|
||||
u16_t len)
|
||||
{
|
||||
switch (opcode) {
|
||||
case L2CAP_READ_SUPPORTED_COMMANDS:
|
||||
supported_commands(data, len);
|
||||
return;
|
||||
case L2CAP_CONNECT:
|
||||
connect(data, len);
|
||||
return;
|
||||
case L2CAP_DISCONNECT:
|
||||
disconnect(data, len);
|
||||
return;
|
||||
case L2CAP_SEND_DATA:
|
||||
send_data(data, len);
|
||||
return;
|
||||
case L2CAP_LISTEN:
|
||||
listen(data, len);
|
||||
return;
|
||||
default:
|
||||
tester_rsp(BTP_SERVICE_ID_L2CAP, opcode, index,
|
||||
BTP_STATUS_UNKNOWN_CMD);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
u8_t tester_init_l2cap(void)
|
||||
{
|
||||
return BTP_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
u8_t tester_unregister_l2cap(void)
|
||||
{
|
||||
return BTP_STATUS_SUCCESS;
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
/* main.c - Application main entry point */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2015-2016 Intel Corporation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <zephyr.h>
|
||||
#include <zephyr/types.h>
|
||||
#include <toolchain.h>
|
||||
|
||||
#include "bttester.h"
|
||||
|
||||
void main(void)
|
||||
{
|
||||
tester_init();
|
||||
}
|
||||
@@ -0,0 +1,927 @@
|
||||
/* mesh.c - Bluetooth Mesh Tester */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2017 Intel Corporation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <bluetooth/bluetooth.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <bluetooth/mesh.h>
|
||||
#include <bluetooth/testing.h>
|
||||
#include <misc/byteorder.h>
|
||||
#include "bttester.h"
|
||||
|
||||
#define CONTROLLER_INDEX 0
|
||||
#define CID_LOCAL 0xffff
|
||||
|
||||
/* Health server data */
|
||||
#define CUR_FAULTS_MAX 4
|
||||
#define HEALTH_TEST_ID 0x00
|
||||
|
||||
static u8_t cur_faults[CUR_FAULTS_MAX];
|
||||
static u8_t reg_faults[CUR_FAULTS_MAX * 2];
|
||||
|
||||
/* Provision node data */
|
||||
static u8_t net_key[16];
|
||||
static u16_t net_key_idx;
|
||||
static u8_t flags;
|
||||
static u32_t iv_index;
|
||||
static u16_t addr;
|
||||
static u8_t dev_key[16];
|
||||
static u8_t input_size;
|
||||
|
||||
/* Configured provisioning data */
|
||||
static u8_t dev_uuid[16];
|
||||
static u8_t static_auth[16];
|
||||
|
||||
/* Vendor Model data */
|
||||
#define VND_MODEL_ID_1 0x1234
|
||||
|
||||
/* Model send data */
|
||||
#define MODEL_BOUNDS_MAX 2
|
||||
|
||||
static struct model_data {
|
||||
struct bt_mesh_model *model;
|
||||
u16_t addr;
|
||||
u16_t appkey_idx;
|
||||
} model_bound[MODEL_BOUNDS_MAX];
|
||||
|
||||
static struct {
|
||||
u16_t local;
|
||||
u16_t dst;
|
||||
u16_t net_idx;
|
||||
} net = {
|
||||
.local = BT_MESH_ADDR_UNASSIGNED,
|
||||
.dst = BT_MESH_ADDR_UNASSIGNED,
|
||||
};
|
||||
|
||||
static void supported_commands(u8_t *data, u16_t len)
|
||||
{
|
||||
struct net_buf_simple *buf = NET_BUF_SIMPLE(BTP_DATA_MAX_SIZE);
|
||||
|
||||
net_buf_simple_init(buf, 0);
|
||||
|
||||
/* 1st octet */
|
||||
memset(net_buf_simple_add(buf, 1), 0, 1);
|
||||
tester_set_bit(buf->data, MESH_READ_SUPPORTED_COMMANDS);
|
||||
tester_set_bit(buf->data, MESH_CONFIG_PROVISIONING);
|
||||
tester_set_bit(buf->data, MESH_PROVISION_NODE);
|
||||
tester_set_bit(buf->data, MESH_INIT);
|
||||
tester_set_bit(buf->data, MESH_RESET);
|
||||
tester_set_bit(buf->data, MESH_INPUT_NUMBER);
|
||||
tester_set_bit(buf->data, MESH_INPUT_STRING);
|
||||
/* 2nd octet */
|
||||
tester_set_bit(buf->data, MESH_IVU_TEST_MODE);
|
||||
tester_set_bit(buf->data, MESH_IVU_TOGGLE_STATE);
|
||||
tester_set_bit(buf->data, MESH_NET_SEND);
|
||||
tester_set_bit(buf->data, MESH_HEALTH_GENERATE_FAULTS);
|
||||
tester_set_bit(buf->data, MESH_HEALTH_CLEAR_FAULTS);
|
||||
tester_set_bit(buf->data, MESH_LPN);
|
||||
tester_set_bit(buf->data, MESH_LPN_POLL);
|
||||
tester_set_bit(buf->data, MESH_MODEL_SEND);
|
||||
/* 3rd octet */
|
||||
memset(net_buf_simple_add(buf, 1), 0, 1);
|
||||
#if defined(CONFIG_BT_TESTING)
|
||||
tester_set_bit(buf->data, MESH_LPN_SUBSCRIBE);
|
||||
tester_set_bit(buf->data, MESH_LPN_UNSUBSCRIBE);
|
||||
tester_set_bit(buf->data, MESH_RPL_CLEAR);
|
||||
#endif /* CONFIG_BT_TESTING */
|
||||
tester_set_bit(buf->data, MESH_PROXY_IDENTITY);
|
||||
|
||||
tester_send(BTP_SERVICE_ID_MESH, MESH_READ_SUPPORTED_COMMANDS,
|
||||
CONTROLLER_INDEX, buf->data, buf->len);
|
||||
}
|
||||
|
||||
static struct bt_mesh_cfg_srv cfg_srv = {
|
||||
.relay = BT_MESH_RELAY_ENABLED,
|
||||
.beacon = BT_MESH_BEACON_ENABLED,
|
||||
#if defined(CONFIG_BT_MESH_FRIEND)
|
||||
.frnd = BT_MESH_FRIEND_DISABLED,
|
||||
#else
|
||||
.frnd = BT_MESH_FRIEND_NOT_SUPPORTED,
|
||||
#endif
|
||||
#if defined(CONFIG_BT_MESH_GATT_PROXY)
|
||||
.gatt_proxy = BT_MESH_GATT_PROXY_ENABLED,
|
||||
#else
|
||||
.gatt_proxy = BT_MESH_GATT_PROXY_NOT_SUPPORTED,
|
||||
#endif
|
||||
.default_ttl = 7,
|
||||
|
||||
/* 3 transmissions with 20ms interval */
|
||||
.net_transmit = BT_MESH_TRANSMIT(2, 20),
|
||||
.relay_retransmit = BT_MESH_TRANSMIT(2, 20),
|
||||
};
|
||||
|
||||
static void get_faults(u8_t *faults, u8_t faults_size, u8_t *dst, u8_t *count)
|
||||
{
|
||||
u8_t i, limit = *count;
|
||||
|
||||
for (i = 0, *count = 0; i < faults_size && *count < limit; i++) {
|
||||
if (faults[i]) {
|
||||
*dst++ = faults[i];
|
||||
(*count)++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int fault_get_cur(struct bt_mesh_model *model, u8_t *test_id,
|
||||
u16_t *company_id, u8_t *faults, u8_t *fault_count)
|
||||
{
|
||||
SYS_LOG_DBG("");
|
||||
|
||||
*test_id = HEALTH_TEST_ID;
|
||||
*company_id = CID_LOCAL;
|
||||
|
||||
get_faults(cur_faults, sizeof(cur_faults), faults, fault_count);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fault_get_reg(struct bt_mesh_model *model, u16_t company_id,
|
||||
u8_t *test_id, u8_t *faults, u8_t *fault_count)
|
||||
{
|
||||
SYS_LOG_DBG("company_id 0x%04x", company_id);
|
||||
|
||||
if (company_id != CID_LOCAL) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
*test_id = HEALTH_TEST_ID;
|
||||
|
||||
get_faults(reg_faults, sizeof(reg_faults), faults, fault_count);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fault_clear(struct bt_mesh_model *model, uint16_t company_id)
|
||||
{
|
||||
SYS_LOG_DBG("company_id 0x%04x", company_id);
|
||||
|
||||
if (company_id != CID_LOCAL) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
memset(reg_faults, 0, sizeof(reg_faults));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fault_test(struct bt_mesh_model *model, uint8_t test_id,
|
||||
uint16_t company_id)
|
||||
{
|
||||
SYS_LOG_DBG("test_id 0x%02x company_id 0x%04x", test_id, company_id);
|
||||
|
||||
if (company_id != CID_LOCAL || test_id != HEALTH_TEST_ID) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct bt_mesh_health_srv_cb health_srv_cb = {
|
||||
.fault_get_cur = fault_get_cur,
|
||||
.fault_get_reg = fault_get_reg,
|
||||
.fault_clear = fault_clear,
|
||||
.fault_test = fault_test,
|
||||
};
|
||||
|
||||
static struct bt_mesh_health_srv health_srv = {
|
||||
.cb = &health_srv_cb,
|
||||
};
|
||||
|
||||
BT_MESH_HEALTH_PUB_DEFINE(health_pub, CUR_FAULTS_MAX);
|
||||
|
||||
static struct bt_mesh_cfg_cli cfg_cli = {
|
||||
};
|
||||
|
||||
void show_faults(u8_t test_id, u16_t cid, u8_t *faults, size_t fault_count)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
if (!fault_count) {
|
||||
SYS_LOG_DBG("Health Test ID 0x%02x Company ID 0x%04x: "
|
||||
"no faults", test_id, cid);
|
||||
return;
|
||||
}
|
||||
|
||||
SYS_LOG_DBG("Health Test ID 0x%02x Company ID 0x%04x Fault Count %zu: ",
|
||||
test_id, cid, fault_count);
|
||||
|
||||
for (i = 0; i < fault_count; i++) {
|
||||
SYS_LOG_DBG("0x%02x", faults[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static void health_current_status(struct bt_mesh_health_cli *cli, u16_t addr,
|
||||
u8_t test_id, u16_t cid, u8_t *faults,
|
||||
size_t fault_count)
|
||||
{
|
||||
SYS_LOG_DBG("Health Current Status from 0x%04x", addr);
|
||||
show_faults(test_id, cid, faults, fault_count);
|
||||
}
|
||||
|
||||
static struct bt_mesh_health_cli health_cli = {
|
||||
.current_status = health_current_status,
|
||||
};
|
||||
|
||||
static struct bt_mesh_model root_models[] = {
|
||||
BT_MESH_MODEL_CFG_SRV(&cfg_srv),
|
||||
BT_MESH_MODEL_CFG_CLI(&cfg_cli),
|
||||
BT_MESH_MODEL_HEALTH_SRV(&health_srv, &health_pub),
|
||||
BT_MESH_MODEL_HEALTH_CLI(&health_cli),
|
||||
};
|
||||
|
||||
static struct bt_mesh_model vnd_models[] = {
|
||||
BT_MESH_MODEL_VND(CID_LOCAL, VND_MODEL_ID_1, BT_MESH_MODEL_NO_OPS, NULL,
|
||||
NULL),
|
||||
};
|
||||
|
||||
static struct bt_mesh_elem elements[] = {
|
||||
BT_MESH_ELEM(0, root_models, vnd_models),
|
||||
};
|
||||
|
||||
static void link_open(bt_mesh_prov_bearer_t bearer)
|
||||
{
|
||||
struct mesh_prov_link_open_ev ev;
|
||||
|
||||
SYS_LOG_DBG("bearer 0x%02x", bearer);
|
||||
|
||||
switch (bearer) {
|
||||
case BT_MESH_PROV_ADV:
|
||||
ev.bearer = MESH_PROV_BEARER_PB_ADV;
|
||||
break;
|
||||
case BT_MESH_PROV_GATT:
|
||||
ev.bearer = MESH_PROV_BEARER_PB_GATT;
|
||||
break;
|
||||
default:
|
||||
SYS_LOG_ERR("Invalid bearer");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
tester_send(BTP_SERVICE_ID_MESH, MESH_EV_PROV_LINK_OPEN,
|
||||
CONTROLLER_INDEX, (u8_t *) &ev, sizeof(ev));
|
||||
}
|
||||
|
||||
static void link_close(bt_mesh_prov_bearer_t bearer)
|
||||
{
|
||||
struct mesh_prov_link_closed_ev ev;
|
||||
|
||||
SYS_LOG_DBG("bearer 0x%02x", bearer);
|
||||
|
||||
switch (bearer) {
|
||||
case BT_MESH_PROV_ADV:
|
||||
ev.bearer = MESH_PROV_BEARER_PB_ADV;
|
||||
break;
|
||||
case BT_MESH_PROV_GATT:
|
||||
ev.bearer = MESH_PROV_BEARER_PB_GATT;
|
||||
break;
|
||||
default:
|
||||
SYS_LOG_ERR("Invalid bearer");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
tester_send(BTP_SERVICE_ID_MESH, MESH_EV_PROV_LINK_CLOSED,
|
||||
CONTROLLER_INDEX, (u8_t *) &ev, sizeof(ev));
|
||||
}
|
||||
|
||||
static int output_number(bt_mesh_output_action_t action, u32_t number)
|
||||
{
|
||||
struct mesh_out_number_action_ev ev;
|
||||
|
||||
SYS_LOG_DBG("action 0x%04x number 0x%08x", action, number);
|
||||
|
||||
ev.action = sys_cpu_to_le16(action);
|
||||
ev.number = sys_cpu_to_le32(number);
|
||||
|
||||
tester_send(BTP_SERVICE_ID_MESH, MESH_EV_OUT_NUMBER_ACTION,
|
||||
CONTROLLER_INDEX, (u8_t *) &ev, sizeof(ev));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int output_string(const char *str)
|
||||
{
|
||||
struct mesh_out_string_action_ev *ev;
|
||||
struct net_buf_simple *buf = NET_BUF_SIMPLE(BTP_DATA_MAX_SIZE);
|
||||
|
||||
SYS_LOG_DBG("str %s", str);
|
||||
|
||||
net_buf_simple_init(buf, 0);
|
||||
|
||||
ev = net_buf_simple_add(buf, sizeof(*ev));
|
||||
ev->string_len = strlen(str);
|
||||
|
||||
net_buf_simple_add_mem(buf, str, ev->string_len);
|
||||
|
||||
tester_send(BTP_SERVICE_ID_MESH, MESH_EV_OUT_STRING_ACTION,
|
||||
CONTROLLER_INDEX, buf->data, buf->len);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int input(bt_mesh_input_action_t action, u8_t size)
|
||||
{
|
||||
struct mesh_in_action_ev ev;
|
||||
|
||||
SYS_LOG_DBG("action 0x%04x number 0x%02x", action, size);
|
||||
|
||||
input_size = size;
|
||||
|
||||
ev.action = sys_cpu_to_le16(action);
|
||||
ev.size = size;
|
||||
|
||||
tester_send(BTP_SERVICE_ID_MESH, MESH_EV_IN_ACTION, CONTROLLER_INDEX,
|
||||
(u8_t *) &ev, sizeof(ev));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void prov_complete(u16_t net_idx, u16_t addr)
|
||||
{
|
||||
SYS_LOG_DBG("net_idx 0x%04x addr 0x%04x", net_idx, addr);
|
||||
|
||||
net.net_idx = net_idx,
|
||||
net.local = addr;
|
||||
net.dst = addr;
|
||||
|
||||
tester_send(BTP_SERVICE_ID_MESH, MESH_EV_PROVISIONED, CONTROLLER_INDEX,
|
||||
NULL, 0);
|
||||
}
|
||||
|
||||
static void prov_reset(void)
|
||||
{
|
||||
SYS_LOG_DBG("");
|
||||
|
||||
bt_mesh_prov_enable(BT_MESH_PROV_ADV | BT_MESH_PROV_GATT);
|
||||
}
|
||||
|
||||
static const struct bt_mesh_comp comp = {
|
||||
.cid = CID_LOCAL,
|
||||
.elem = elements,
|
||||
.elem_count = ARRAY_SIZE(elements),
|
||||
};
|
||||
|
||||
static struct bt_mesh_prov prov = {
|
||||
.uuid = dev_uuid,
|
||||
.static_val = static_auth,
|
||||
.static_val_len = sizeof(static_auth),
|
||||
.output_number = output_number,
|
||||
.output_string = output_string,
|
||||
.input = input,
|
||||
.link_open = link_open,
|
||||
.link_close = link_close,
|
||||
.complete = prov_complete,
|
||||
.reset = prov_reset,
|
||||
};
|
||||
|
||||
static void config_prov(u8_t *data, u16_t len)
|
||||
{
|
||||
const struct mesh_config_provisioning_cmd *cmd = (void *) data;
|
||||
|
||||
SYS_LOG_DBG("");
|
||||
|
||||
memcpy(dev_uuid, cmd->uuid, sizeof(dev_uuid));
|
||||
memcpy(static_auth, cmd->static_auth, sizeof(static_auth));
|
||||
|
||||
prov.output_size = cmd->out_size;
|
||||
prov.output_actions = sys_le16_to_cpu(cmd->out_actions);
|
||||
prov.input_size = cmd->in_size;
|
||||
prov.input_actions = sys_le16_to_cpu(cmd->in_actions);
|
||||
|
||||
tester_rsp(BTP_SERVICE_ID_MESH, MESH_CONFIG_PROVISIONING,
|
||||
CONTROLLER_INDEX, BTP_STATUS_SUCCESS);
|
||||
}
|
||||
|
||||
static void provision_node(u8_t *data, u16_t len)
|
||||
{
|
||||
const struct mesh_provision_node_cmd *cmd = (void *) data;
|
||||
|
||||
SYS_LOG_DBG("");
|
||||
|
||||
memcpy(dev_key, cmd->dev_key, sizeof(dev_key));
|
||||
memcpy(net_key, cmd->net_key, sizeof(net_key));
|
||||
|
||||
addr = sys_le16_to_cpu(cmd->addr);
|
||||
flags = cmd->flags;
|
||||
iv_index = sys_le32_to_cpu(cmd->iv_index);
|
||||
net_key_idx = sys_le16_to_cpu(cmd->net_key_idx);
|
||||
|
||||
tester_rsp(BTP_SERVICE_ID_MESH, MESH_PROVISION_NODE,
|
||||
CONTROLLER_INDEX, BTP_STATUS_SUCCESS);
|
||||
}
|
||||
|
||||
static void init(u8_t *data, u16_t len)
|
||||
{
|
||||
u8_t status = BTP_STATUS_SUCCESS;
|
||||
int err;
|
||||
|
||||
SYS_LOG_DBG("");
|
||||
|
||||
err = bt_mesh_init(&prov, &comp);
|
||||
if (err) {
|
||||
status = BTP_STATUS_FAILED;
|
||||
|
||||
goto rsp;
|
||||
}
|
||||
|
||||
if (addr) {
|
||||
err = bt_mesh_provision(net_key, net_key_idx, flags, iv_index,
|
||||
addr, dev_key);
|
||||
if (err) {
|
||||
status = BTP_STATUS_FAILED;
|
||||
}
|
||||
} else {
|
||||
err = bt_mesh_prov_enable(BT_MESH_PROV_ADV | BT_MESH_PROV_GATT);
|
||||
if (err) {
|
||||
status = BTP_STATUS_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
/* Set device key for vendor model */
|
||||
vnd_models[0].keys[0] = BT_MESH_KEY_DEV;
|
||||
|
||||
rsp:
|
||||
tester_rsp(BTP_SERVICE_ID_MESH, MESH_INIT, CONTROLLER_INDEX,
|
||||
status);
|
||||
}
|
||||
|
||||
static void reset(u8_t *data, u16_t len)
|
||||
{
|
||||
SYS_LOG_DBG("");
|
||||
|
||||
bt_mesh_reset();
|
||||
|
||||
tester_rsp(BTP_SERVICE_ID_MESH, MESH_RESET, CONTROLLER_INDEX,
|
||||
BTP_STATUS_SUCCESS);
|
||||
}
|
||||
|
||||
static void input_number(u8_t *data, u16_t len)
|
||||
{
|
||||
const struct mesh_input_number_cmd *cmd = (void *) data;
|
||||
u8_t status = BTP_STATUS_SUCCESS;
|
||||
u32_t number;
|
||||
int err;
|
||||
|
||||
number = sys_le32_to_cpu(cmd->number);
|
||||
|
||||
SYS_LOG_DBG("number 0x%04x", number);
|
||||
|
||||
err = bt_mesh_input_number(number);
|
||||
if (err) {
|
||||
status = BTP_STATUS_FAILED;
|
||||
}
|
||||
|
||||
tester_rsp(BTP_SERVICE_ID_MESH, MESH_INPUT_NUMBER, CONTROLLER_INDEX,
|
||||
status);
|
||||
}
|
||||
|
||||
static void input_string(u8_t *data, u16_t len)
|
||||
{
|
||||
const struct mesh_input_string_cmd *cmd = (void *) data;
|
||||
u8_t status = BTP_STATUS_SUCCESS;
|
||||
u8_t str_auth[16];
|
||||
int err;
|
||||
|
||||
SYS_LOG_DBG("");
|
||||
|
||||
if (cmd->string_len > sizeof(str_auth)) {
|
||||
SYS_LOG_ERR("Too long input (%u chars required)", input_size);
|
||||
status = BTP_STATUS_FAILED;
|
||||
goto rsp;
|
||||
} else if (cmd->string_len < input_size) {
|
||||
SYS_LOG_ERR("Too short input (%u chars required)", input_size);
|
||||
status = BTP_STATUS_FAILED;
|
||||
goto rsp;
|
||||
}
|
||||
|
||||
strncpy(str_auth, cmd->string, cmd->string_len);
|
||||
|
||||
err = bt_mesh_input_string(str_auth);
|
||||
if (err) {
|
||||
status = BTP_STATUS_FAILED;
|
||||
}
|
||||
|
||||
rsp:
|
||||
tester_rsp(BTP_SERVICE_ID_MESH, MESH_INPUT_STRING, CONTROLLER_INDEX,
|
||||
status);
|
||||
}
|
||||
|
||||
static void ivu_test_mode(u8_t *data, u16_t len)
|
||||
{
|
||||
const struct mesh_ivu_test_mode_cmd *cmd = (void *) data;
|
||||
|
||||
SYS_LOG_DBG("enable 0x%02x", cmd->enable);
|
||||
|
||||
bt_mesh_iv_update_test(cmd->enable ? true : false);
|
||||
|
||||
tester_rsp(BTP_SERVICE_ID_MESH, MESH_IVU_TEST_MODE, CONTROLLER_INDEX,
|
||||
BTP_STATUS_SUCCESS);
|
||||
}
|
||||
|
||||
static void ivu_toggle_state(u8_t *data, u16_t len)
|
||||
{
|
||||
bool result;
|
||||
|
||||
SYS_LOG_DBG("");
|
||||
|
||||
result = bt_mesh_iv_update();
|
||||
if (!result) {
|
||||
SYS_LOG_ERR("Failed to toggle the IV Update state");
|
||||
}
|
||||
|
||||
tester_rsp(BTP_SERVICE_ID_MESH, MESH_IVU_TOGGLE_STATE, CONTROLLER_INDEX,
|
||||
result ? BTP_STATUS_SUCCESS : BTP_STATUS_FAILED);
|
||||
}
|
||||
|
||||
static void lpn(u8_t *data, u16_t len)
|
||||
{
|
||||
struct mesh_lpn_set_cmd *cmd = (void *) data;
|
||||
bool enable;
|
||||
int err;
|
||||
|
||||
SYS_LOG_DBG("enable 0x%02x", cmd->enable);
|
||||
|
||||
enable = cmd->enable ? true : false;
|
||||
err = bt_mesh_lpn_set(enable);
|
||||
if (err) {
|
||||
SYS_LOG_ERR("Failed to toggle LPN (err %d)", err);
|
||||
}
|
||||
|
||||
tester_rsp(BTP_SERVICE_ID_MESH, MESH_LPN, CONTROLLER_INDEX,
|
||||
err ? BTP_STATUS_FAILED : BTP_STATUS_SUCCESS);
|
||||
}
|
||||
|
||||
static void lpn_poll(u8_t *data, u16_t len)
|
||||
{
|
||||
int err;
|
||||
|
||||
SYS_LOG_DBG("");
|
||||
|
||||
err = bt_mesh_lpn_poll();
|
||||
if (err) {
|
||||
SYS_LOG_ERR("Failed to send poll msg (err %d)", err);
|
||||
}
|
||||
|
||||
tester_rsp(BTP_SERVICE_ID_MESH, MESH_LPN_POLL, CONTROLLER_INDEX,
|
||||
err ? BTP_STATUS_FAILED : BTP_STATUS_SUCCESS);
|
||||
}
|
||||
|
||||
static void net_send(u8_t *data, u16_t len)
|
||||
{
|
||||
struct mesh_net_send_cmd *cmd = (void *) data;
|
||||
NET_BUF_SIMPLE_DEFINE(msg, UINT8_MAX);
|
||||
struct bt_mesh_msg_ctx ctx = {
|
||||
.net_idx = net.net_idx,
|
||||
.app_idx = BT_MESH_KEY_DEV,
|
||||
.addr = sys_le16_to_cpu(cmd->dst),
|
||||
.send_ttl = cmd->ttl,
|
||||
};
|
||||
int err;
|
||||
|
||||
SYS_LOG_DBG("ttl 0x%02x dst 0x%04x payload_len %d", ctx.send_ttl,
|
||||
ctx.addr, cmd->payload_len);
|
||||
|
||||
net_buf_simple_add_mem(&msg, cmd->payload, cmd->payload_len);
|
||||
|
||||
err = bt_mesh_model_send(&vnd_models[0], &ctx, &msg, NULL, NULL);
|
||||
if (err) {
|
||||
SYS_LOG_ERR("Failed to send (err %d)", err);
|
||||
}
|
||||
|
||||
tester_rsp(BTP_SERVICE_ID_MESH, MESH_NET_SEND, CONTROLLER_INDEX,
|
||||
err ? BTP_STATUS_FAILED : BTP_STATUS_SUCCESS);
|
||||
}
|
||||
|
||||
static void health_generate_faults(u8_t *data, u16_t len)
|
||||
{
|
||||
struct mesh_health_generate_faults_rp *rp;
|
||||
NET_BUF_SIMPLE_DEFINE(buf, sizeof(*rp) + sizeof(cur_faults) +
|
||||
sizeof(reg_faults));
|
||||
u8_t some_faults[] = { 0x01, 0x02, 0x03, 0xff, 0x06 };
|
||||
u8_t cur_faults_count, reg_faults_count;
|
||||
|
||||
rp = net_buf_simple_add(&buf, sizeof(*rp));
|
||||
|
||||
cur_faults_count = min(sizeof(cur_faults), sizeof(some_faults));
|
||||
memcpy(cur_faults, some_faults, cur_faults_count);
|
||||
net_buf_simple_add_mem(&buf, cur_faults, cur_faults_count);
|
||||
rp->cur_faults_count = cur_faults_count;
|
||||
|
||||
reg_faults_count = min(sizeof(reg_faults), sizeof(some_faults));
|
||||
memcpy(reg_faults, some_faults, reg_faults_count);
|
||||
net_buf_simple_add_mem(&buf, reg_faults, reg_faults_count);
|
||||
rp->reg_faults_count = reg_faults_count;
|
||||
|
||||
bt_mesh_fault_update(&elements[0]);
|
||||
|
||||
tester_send(BTP_SERVICE_ID_MESH, MESH_HEALTH_GENERATE_FAULTS,
|
||||
CONTROLLER_INDEX, buf.data, buf.len);
|
||||
}
|
||||
|
||||
static void health_clear_faults(u8_t *data, u16_t len)
|
||||
{
|
||||
SYS_LOG_DBG("");
|
||||
|
||||
memset(cur_faults, 0, sizeof(cur_faults));
|
||||
memset(reg_faults, 0, sizeof(reg_faults));
|
||||
|
||||
bt_mesh_fault_update(&elements[0]);
|
||||
|
||||
tester_rsp(BTP_SERVICE_ID_MESH, MESH_HEALTH_CLEAR_FAULTS,
|
||||
CONTROLLER_INDEX, BTP_STATUS_SUCCESS);
|
||||
}
|
||||
|
||||
static void model_send(u8_t *data, u16_t len)
|
||||
{
|
||||
struct mesh_model_send_cmd *cmd = (void *) data;
|
||||
NET_BUF_SIMPLE_DEFINE(msg, UINT8_MAX);
|
||||
struct bt_mesh_msg_ctx ctx = {
|
||||
.net_idx = net.net_idx,
|
||||
.app_idx = BT_MESH_KEY_DEV,
|
||||
.addr = sys_le16_to_cpu(cmd->dst),
|
||||
.send_ttl = BT_MESH_TTL_DEFAULT,
|
||||
};
|
||||
struct bt_mesh_model *model = NULL;
|
||||
int err, i;
|
||||
u16_t src = sys_le16_to_cpu(cmd->src);
|
||||
|
||||
/* Lookup source address */
|
||||
for (i = 0; i < ARRAY_SIZE(model_bound); i++) {
|
||||
if (bt_mesh_model_elem(model_bound[i].model)->addr == src) {
|
||||
model = model_bound[i].model;
|
||||
ctx.app_idx = model_bound[i].appkey_idx;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!model) {
|
||||
SYS_LOG_ERR("Model not found");
|
||||
err = -EINVAL;
|
||||
|
||||
goto fail;
|
||||
}
|
||||
|
||||
SYS_LOG_DBG("src 0x%04x dst 0x%04x model %p payload_len %d", src,
|
||||
ctx.addr, model, cmd->payload_len);
|
||||
|
||||
net_buf_simple_add_mem(&msg, cmd->payload, cmd->payload_len);
|
||||
|
||||
err = bt_mesh_model_send(model, &ctx, &msg, NULL, NULL);
|
||||
if (err) {
|
||||
SYS_LOG_ERR("Failed to send (err %d)", err);
|
||||
}
|
||||
|
||||
fail:
|
||||
tester_rsp(BTP_SERVICE_ID_MESH, MESH_MODEL_SEND, CONTROLLER_INDEX,
|
||||
err ? BTP_STATUS_FAILED : BTP_STATUS_SUCCESS);
|
||||
}
|
||||
|
||||
#if defined(CONFIG_BT_TESTING)
|
||||
static void lpn_subscribe(u8_t *data, u16_t len)
|
||||
{
|
||||
struct mesh_lpn_subscribe_cmd *cmd = (void *) data;
|
||||
u16_t address = sys_le16_to_cpu(cmd->address);
|
||||
int err;
|
||||
|
||||
SYS_LOG_DBG("address 0x%04x", address);
|
||||
|
||||
err = bt_test_mesh_lpn_group_add(address);
|
||||
if (err) {
|
||||
SYS_LOG_ERR("Failed to subscribe (err %d)", err);
|
||||
}
|
||||
|
||||
tester_rsp(BTP_SERVICE_ID_MESH, MESH_LPN_SUBSCRIBE, CONTROLLER_INDEX,
|
||||
err ? BTP_STATUS_FAILED : BTP_STATUS_SUCCESS);
|
||||
}
|
||||
|
||||
static void lpn_unsubscribe(u8_t *data, u16_t len)
|
||||
{
|
||||
struct mesh_lpn_unsubscribe_cmd *cmd = (void *) data;
|
||||
u16_t address = sys_le16_to_cpu(cmd->address);
|
||||
int err;
|
||||
|
||||
SYS_LOG_DBG("address 0x%04x", address);
|
||||
|
||||
err = bt_test_mesh_lpn_group_remove(&address, 1);
|
||||
if (err) {
|
||||
SYS_LOG_ERR("Failed to unsubscribe (err %d)", err);
|
||||
}
|
||||
|
||||
tester_rsp(BTP_SERVICE_ID_MESH, MESH_LPN_UNSUBSCRIBE, CONTROLLER_INDEX,
|
||||
err ? BTP_STATUS_FAILED : BTP_STATUS_SUCCESS);
|
||||
}
|
||||
|
||||
static void rpl_clear(u8_t *data, u16_t len)
|
||||
{
|
||||
int err;
|
||||
|
||||
SYS_LOG_DBG("");
|
||||
|
||||
err = bt_test_mesh_rpl_clear();
|
||||
if (err) {
|
||||
SYS_LOG_ERR("Failed to clear RPL (err %d)", err);
|
||||
}
|
||||
|
||||
tester_rsp(BTP_SERVICE_ID_MESH, MESH_RPL_CLEAR, CONTROLLER_INDEX,
|
||||
err ? BTP_STATUS_FAILED : BTP_STATUS_SUCCESS);
|
||||
}
|
||||
#endif /* CONFIG_BT_TESTING */
|
||||
|
||||
static void proxy_identity_enable(u8_t *data, u16_t len)
|
||||
{
|
||||
int err;
|
||||
|
||||
SYS_LOG_DBG("");
|
||||
|
||||
err = bt_mesh_proxy_identity_enable();
|
||||
if (err) {
|
||||
SYS_LOG_ERR("Failed to enable proxy identity (err %d)", err);
|
||||
}
|
||||
|
||||
tester_rsp(BTP_SERVICE_ID_MESH, MESH_PROXY_IDENTITY, CONTROLLER_INDEX,
|
||||
err ? BTP_STATUS_FAILED : BTP_STATUS_SUCCESS);
|
||||
}
|
||||
|
||||
void tester_handle_mesh(u8_t opcode, u8_t index, u8_t *data, u16_t len)
|
||||
{
|
||||
switch (opcode) {
|
||||
case MESH_READ_SUPPORTED_COMMANDS:
|
||||
supported_commands(data, len);
|
||||
break;
|
||||
case MESH_CONFIG_PROVISIONING:
|
||||
config_prov(data, len);
|
||||
break;
|
||||
case MESH_PROVISION_NODE:
|
||||
provision_node(data, len);
|
||||
break;
|
||||
case MESH_INIT:
|
||||
init(data, len);
|
||||
break;
|
||||
case MESH_RESET:
|
||||
reset(data, len);
|
||||
break;
|
||||
case MESH_INPUT_NUMBER:
|
||||
input_number(data, len);
|
||||
break;
|
||||
case MESH_INPUT_STRING:
|
||||
input_string(data, len);
|
||||
break;
|
||||
case MESH_IVU_TEST_MODE:
|
||||
ivu_test_mode(data, len);
|
||||
break;
|
||||
case MESH_IVU_TOGGLE_STATE:
|
||||
ivu_toggle_state(data, len);
|
||||
break;
|
||||
case MESH_LPN:
|
||||
lpn(data, len);
|
||||
break;
|
||||
case MESH_LPN_POLL:
|
||||
lpn_poll(data, len);
|
||||
break;
|
||||
case MESH_NET_SEND:
|
||||
net_send(data, len);
|
||||
break;
|
||||
case MESH_HEALTH_GENERATE_FAULTS:
|
||||
health_generate_faults(data, len);
|
||||
break;
|
||||
case MESH_HEALTH_CLEAR_FAULTS:
|
||||
health_clear_faults(data, len);
|
||||
break;
|
||||
case MESH_MODEL_SEND:
|
||||
model_send(data, len);
|
||||
break;
|
||||
#if defined(CONFIG_BT_TESTING)
|
||||
case MESH_LPN_SUBSCRIBE:
|
||||
lpn_subscribe(data, len);
|
||||
break;
|
||||
case MESH_LPN_UNSUBSCRIBE:
|
||||
lpn_unsubscribe(data, len);
|
||||
break;
|
||||
case MESH_RPL_CLEAR:
|
||||
rpl_clear(data, len);
|
||||
break;
|
||||
#endif /* CONFIG_BT_TESTING */
|
||||
case MESH_PROXY_IDENTITY:
|
||||
proxy_identity_enable(data, len);
|
||||
break;
|
||||
default:
|
||||
tester_rsp(BTP_SERVICE_ID_MESH, opcode, index,
|
||||
BTP_STATUS_UNKNOWN_CMD);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void net_recv_ev(u8_t ttl, u8_t ctl, u16_t src, u16_t dst, const void *payload,
|
||||
size_t payload_len)
|
||||
{
|
||||
NET_BUF_SIMPLE_DEFINE(buf, UINT8_MAX);
|
||||
struct mesh_net_recv_ev *ev;
|
||||
|
||||
SYS_LOG_DBG("ttl 0x%02x ctl 0x%02x src 0x%04x dst 0x%04x "
|
||||
"payload_len %d", ttl, ctl, src, dst, payload_len);
|
||||
|
||||
if (payload_len > net_buf_simple_tailroom(&buf)) {
|
||||
SYS_LOG_ERR("Payload size exceeds buffer size");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
ev = net_buf_simple_add(&buf, sizeof(*ev));
|
||||
ev->ttl = ttl;
|
||||
ev->ctl = ctl;
|
||||
ev->src = sys_cpu_to_le16(src);
|
||||
ev->dst = sys_cpu_to_le16(dst);
|
||||
ev->payload_len = payload_len;
|
||||
net_buf_simple_add_mem(&buf, payload, payload_len);
|
||||
|
||||
tester_send(BTP_SERVICE_ID_MESH, MESH_EV_NET_RECV, CONTROLLER_INDEX,
|
||||
buf.data, buf.len);
|
||||
}
|
||||
|
||||
static void model_bound_cb(u16_t addr, struct bt_mesh_model *model,
|
||||
u16_t key_idx)
|
||||
{
|
||||
int i;
|
||||
|
||||
SYS_LOG_DBG("remote addr 0x%04x key_idx 0x%04x model %p",
|
||||
addr, key_idx, model);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(model_bound); i++) {
|
||||
if (!model_bound[i].model) {
|
||||
model_bound[i].model = model;
|
||||
model_bound[i].addr = addr;
|
||||
model_bound[i].appkey_idx = key_idx;
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
SYS_LOG_ERR("model_bound is full");
|
||||
}
|
||||
|
||||
static void model_unbound_cb(u16_t addr, struct bt_mesh_model *model,
|
||||
u16_t key_idx)
|
||||
{
|
||||
int i;
|
||||
|
||||
SYS_LOG_DBG("remote addr 0x%04x key_idx 0x%04x model %p",
|
||||
addr, key_idx, model);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(model_bound); i++) {
|
||||
if (model_bound[i].model == model) {
|
||||
model_bound[i].model = NULL;
|
||||
model_bound[i].addr = 0x0000;
|
||||
model_bound[i].appkey_idx = BT_MESH_KEY_UNUSED;
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
SYS_LOG_INF("model not found");
|
||||
}
|
||||
|
||||
static void invalid_bearer_cb(u8_t opcode)
|
||||
{
|
||||
struct mesh_invalid_bearer_ev ev = {
|
||||
.opcode = opcode,
|
||||
};
|
||||
|
||||
SYS_LOG_DBG("opcode 0x%02x", opcode);
|
||||
|
||||
tester_send(BTP_SERVICE_ID_MESH, MESH_EV_INVALID_BEARER,
|
||||
CONTROLLER_INDEX, (u8_t *) &ev, sizeof(ev));
|
||||
}
|
||||
|
||||
static void incomp_timer_exp_cb(void)
|
||||
{
|
||||
tester_send(BTP_SERVICE_ID_MESH, MESH_EV_INCOMP_TIMER_EXP,
|
||||
CONTROLLER_INDEX, NULL, 0);
|
||||
}
|
||||
|
||||
static struct bt_test_cb bt_test_cb = {
|
||||
.mesh_net_recv = net_recv_ev,
|
||||
.mesh_model_bound = model_bound_cb,
|
||||
.mesh_model_unbound = model_unbound_cb,
|
||||
.mesh_prov_invalid_bearer = invalid_bearer_cb,
|
||||
.mesh_trans_incomp_timer_exp = incomp_timer_exp_cb,
|
||||
};
|
||||
|
||||
u8_t tester_init_mesh(void)
|
||||
{
|
||||
if (IS_ENABLED(CONFIG_BT_TESTING)) {
|
||||
bt_test_cb_register(&bt_test_cb);
|
||||
}
|
||||
|
||||
return BTP_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
u8_t tester_unregister_mesh(void)
|
||||
{
|
||||
return BTP_STATUS_SUCCESS;
|
||||
}
|
||||
Reference in New Issue
Block a user