apps/bttester: Copy files from Zephyr

This commit is contained in:
Michał Narajowski
2019-01-11 16:54:16 +01:00
parent a14826c3c6
commit 07df4bbae0
9 changed files with 6845 additions and 0 deletions
+57
View File
@@ -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
+300
View File
@@ -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));
}
+846
View File
@@ -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 */
+748
View File
@@ -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(&current_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(&current_settings, GAP_SETTINGS_CONNECTABLE);
} else {
atomic_clear_bit(&current_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(&current_settings, GAP_SETTINGS_DISCOVERABLE);
break;
case GAP_GENERAL_DISCOVERABLE:
ad_flags &= ~BT_LE_AD_LIMITED;
ad_flags |= BT_LE_AD_GENERAL;
atomic_set_bit(&current_settings, GAP_SETTINGS_DISCOVERABLE);
break;
case GAP_LIMITED_DISCOVERABLE:
ad_flags &= ~BT_LE_AD_GENERAL;
ad_flags |= BT_LE_AD_LIMITED;
atomic_set_bit(&current_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(&current_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(&current_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(&current_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(&current_settings);
atomic_set_bit(&current_settings, GAP_SETTINGS_POWERED);
atomic_set_bit(&current_settings, GAP_SETTINGS_CONNECTABLE);
atomic_set_bit(&current_settings, GAP_SETTINGS_BONDABLE);
atomic_set_bit(&current_settings, GAP_SETTINGS_LE);
#if defined(CONFIG_BT_PRIVACY)
atomic_set_bit(&current_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
+357
View File
@@ -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;
}
+18
View File
@@ -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();
}
+927
View File
@@ -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;
}