apps/bttester: Port app to Mynewt

This commit is contained in:
Michał Narajowski
2019-02-04 10:49:14 +01:00
parent 07df4bbae0
commit 2d3705b94f
16 changed files with 3639 additions and 1590 deletions
+5 -48
View File
@@ -2,56 +2,13 @@ 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.
Tester application uses binary protocol to control Mynewt Nimble stack
and is aimed at automated testing. It uses Bluetooth Testing Protocol (BTP)
to drive Bluetooth stack. BTP commands and events are received and buffered for
further processing.
--------------------------------------------------------------------------------
Supported Profiles:
GAP, GATT, SM
GAP, GATT, SM, L2CAP, MESH
--------------------------------------------------------------------------------
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
+42
View File
@@ -0,0 +1,42 @@
#
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
#
pkg.name: apps/bttester
pkg.type: app
pkg.description: Bluetooth tester application
pkg.author: "Apache Mynewt <[email protected]>"
pkg.homepage: "http://mynewt.apache.org/"
pkg.keywords:
pkg.deps:
- "@apache-mynewt-core/kernel/os"
- "@apache-mynewt-core/sys/console/full"
- "@apache-mynewt-core/sys/log/full"
- "@apache-mynewt-core/sys/log/modlog"
- "@apache-mynewt-core/sys/stats/full"
- "@apache-mynewt-core/sys/shell"
- "@apache-mynewt-nimble/nimble/controller"
- "@apache-mynewt-nimble/nimble/host"
- "@apache-mynewt-nimble/nimble/host/services/gap"
- "@apache-mynewt-nimble/nimble/host/services/gatt"
- "@apache-mynewt-nimble/nimble/host/store/ram"
- "@apache-mynewt-nimble/nimble/transport/ram"
- "@apache-mynewt-core/hw/drivers/uart"
- "@apache-mynewt-core/hw/drivers/rtt"
+386
View File
@@ -0,0 +1,386 @@
/* atomic operations */
/*
* Copyright (c) 1997-2015, Wind River Systems, Inc.
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef __ATOMIC_H__
#define __ATOMIC_H__
#ifdef __cplusplus
extern "C"
{
#endif
typedef int atomic_t;
typedef atomic_t atomic_val_t;
/**
* @defgroup atomic_apis Atomic Services APIs
* @ingroup kernel_apis
* @{
*/
/**
* @brief Atomic compare-and-set.
*
* This routine performs an atomic compare-and-set on @a target. If the current
* value of @a target equals @a old_value, @a target is set to @a new_value.
* If the current value of @a target does not equal @a old_value, @a target
* is left unchanged.
*
* @param target Address of atomic variable.
* @param old_value Original value to compare against.
* @param new_value New value to store.
* @return 1 if @a new_value is written, 0 otherwise.
*/
static inline int atomic_cas(atomic_t *target, atomic_val_t old_value,
atomic_val_t new_value)
{
return __atomic_compare_exchange_n(target, &old_value, new_value,
0, __ATOMIC_SEQ_CST,
__ATOMIC_SEQ_CST);
}
/**
*
* @brief Atomic addition.
*
* This routine performs an atomic addition on @a target.
*
* @param target Address of atomic variable.
* @param value Value to add.
*
* @return Previous value of @a target.
*/
static inline atomic_val_t atomic_add(atomic_t *target, atomic_val_t value)
{
return __atomic_fetch_add(target, value, __ATOMIC_SEQ_CST);
}
/**
*
* @brief Atomic subtraction.
*
* This routine performs an atomic subtraction on @a target.
*
* @param target Address of atomic variable.
* @param value Value to subtract.
*
* @return Previous value of @a target.
*/
static inline atomic_val_t atomic_sub(atomic_t *target, atomic_val_t value)
{
return __atomic_fetch_sub(target, value, __ATOMIC_SEQ_CST);
}
/**
*
* @brief Atomic increment.
*
* This routine performs an atomic increment by 1 on @a target.
*
* @param target Address of atomic variable.
*
* @return Previous value of @a target.
*/
static inline atomic_val_t atomic_inc(atomic_t *target)
{
return atomic_add(target, 1);
}
/**
*
* @brief Atomic decrement.
*
* This routine performs an atomic decrement by 1 on @a target.
*
* @param target Address of atomic variable.
*
* @return Previous value of @a target.
*/
static inline atomic_val_t atomic_dec(atomic_t *target)
{
return atomic_sub(target, 1);
}
/**
*
* @brief Atomic get.
*
* This routine performs an atomic read on @a target.
*
* @param target Address of atomic variable.
*
* @return Value of @a target.
*/
static inline atomic_val_t atomic_get(const atomic_t *target)
{
return __atomic_load_n(target, __ATOMIC_SEQ_CST);
}
/**
*
* @brief Atomic get-and-set.
*
* This routine atomically sets @a target to @a value and returns
* the previous value of @a target.
*
* @param target Address of atomic variable.
* @param value Value to write to @a target.
*
* @return Previous value of @a target.
*/
static inline atomic_val_t atomic_set(atomic_t *target, atomic_val_t value)
{
/* This builtin, as described by Intel, is not a traditional
* test-and-set operation, but rather an atomic exchange operation. It
* writes value into *ptr, and returns the previous contents of *ptr.
*/
return __atomic_exchange_n(target, value, __ATOMIC_SEQ_CST);
}
/**
*
* @brief Atomic clear.
*
* This routine atomically sets @a target to zero and returns its previous
* value. (Hence, it is equivalent to atomic_set(target, 0).)
*
* @param target Address of atomic variable.
*
* @return Previous value of @a target.
*/
static inline atomic_val_t atomic_clear(atomic_t *target)
{
return atomic_set(target, 0);
}
/**
*
* @brief Atomic bitwise inclusive OR.
*
* This routine atomically sets @a target to the bitwise inclusive OR of
* @a target and @a value.
*
* @param target Address of atomic variable.
* @param value Value to OR.
*
* @return Previous value of @a target.
*/
static inline atomic_val_t atomic_or(atomic_t *target, atomic_val_t value)
{
return __atomic_fetch_or(target, value, __ATOMIC_SEQ_CST);
}
/**
*
* @brief Atomic bitwise exclusive OR (XOR).
*
* This routine atomically sets @a target to the bitwise exclusive OR (XOR) of
* @a target and @a value.
*
* @param target Address of atomic variable.
* @param value Value to XOR
*
* @return Previous value of @a target.
*/
static inline atomic_val_t atomic_xor(atomic_t *target, atomic_val_t value)
{
return __atomic_fetch_xor(target, value, __ATOMIC_SEQ_CST);
}
/**
*
* @brief Atomic bitwise AND.
*
* This routine atomically sets @a target to the bitwise AND of @a target
* and @a value.
*
* @param target Address of atomic variable.
* @param value Value to AND.
*
* @return Previous value of @a target.
*/
static inline atomic_val_t atomic_and(atomic_t *target, atomic_val_t value)
{
return __atomic_fetch_and(target, value, __ATOMIC_SEQ_CST);
}
/**
*
* @brief Atomic bitwise NAND.
*
* This routine atomically sets @a target to the bitwise NAND of @a target
* and @a value. (This operation is equivalent to target = ~(target & value).)
*
* @param target Address of atomic variable.
* @param value Value to NAND.
*
* @return Previous value of @a target.
*/
static inline atomic_val_t atomic_nand(atomic_t *target, atomic_val_t value)
{
return __atomic_fetch_nand(target, value, __ATOMIC_SEQ_CST);
}
/**
* @brief Initialize an atomic variable.
*
* This macro can be used to initialize an atomic variable. For example,
* @code atomic_t my_var = ATOMIC_INIT(75); @endcode
*
* @param i Value to assign to atomic variable.
*/
#define ATOMIC_INIT(i) (i)
/**
* @cond INTERNAL_HIDDEN
*/
#define ATOMIC_BITS (sizeof(atomic_val_t) * 8)
#define ATOMIC_MASK(bit) (1 << ((bit) & (ATOMIC_BITS - 1)))
#define ATOMIC_ELEM(addr, bit) ((addr) + ((bit) / ATOMIC_BITS))
/**
* INTERNAL_HIDDEN @endcond
*/
/**
* @brief Define an array of atomic variables.
*
* This macro defines an array of atomic variables containing at least
* @a num_bits bits.
*
* @note
* If used from file scope, the bits of the array are initialized to zero;
* if used from within a function, the bits are left uninitialized.
*
* @param name Name of array of atomic variables.
* @param num_bits Number of bits needed.
*/
#define ATOMIC_DEFINE(name, num_bits) \
atomic_t name[1 + ((num_bits) - 1) / ATOMIC_BITS]
/**
* @brief Atomically test a bit.
*
* This routine tests whether bit number @a bit of @a target is set or not.
* The target may be a single atomic variable or an array of them.
*
* @param target Address of atomic variable or array.
* @param bit Bit number (starting from 0).
*
* @return 1 if the bit was set, 0 if it wasn't.
*/
static inline int
atomic_test_bit(const atomic_t *target, int bit)
{
atomic_val_t val = atomic_get(ATOMIC_ELEM(target, bit));
return (1 & (val >> (bit & (ATOMIC_BITS - 1))));
}
/**
* @brief Atomically test and clear a bit.
*
* Atomically clear bit number @a bit of @a target and return its old value.
* The target may be a single atomic variable or an array of them.
*
* @param target Address of atomic variable or array.
* @param bit Bit number (starting from 0).
*
* @return 1 if the bit was set, 0 if it wasn't.
*/
static inline int
atomic_test_and_clear_bit(atomic_t *target, int bit)
{
atomic_val_t mask = ATOMIC_MASK(bit);
atomic_val_t old;
old = atomic_and(ATOMIC_ELEM(target, bit), ~mask);
return (old & mask) != 0;
}
/**
* @brief Atomically set a bit.
*
* Atomically set bit number @a bit of @a target and return its old value.
* The target may be a single atomic variable or an array of them.
*
* @param target Address of atomic variable or array.
* @param bit Bit number (starting from 0).
*
* @return 1 if the bit was set, 0 if it wasn't.
*/
static inline int
atomic_test_and_set_bit(atomic_t *target, int bit)
{
atomic_val_t mask = ATOMIC_MASK(bit);
atomic_val_t old;
old = atomic_or(ATOMIC_ELEM(target, bit), mask);
return (old & mask) != 0;
}
/**
* @brief Atomically clear a bit.
*
* Atomically clear bit number @a bit of @a target.
* The target may be a single atomic variable or an array of them.
*
* @param target Address of atomic variable or array.
* @param bit Bit number (starting from 0).
*
* @return N/A
*/
static inline void
atomic_clear_bit(atomic_t *target, int bit)
{
atomic_val_t mask = ATOMIC_MASK(bit);
atomic_and(ATOMIC_ELEM(target, bit), ~mask);
}
/**
* @brief Atomically set a bit.
*
* Atomically set bit number @a bit of @a target.
* The target may be a single atomic variable or an array of them.
*
* @param target Address of atomic variable or array.
* @param bit Bit number (starting from 0).
*
* @return N/A
*/
static inline void
atomic_set_bit(atomic_t *target, int bit)
{
atomic_val_t mask = ATOMIC_MASK(bit);
atomic_or(ATOMIC_ELEM(target, bit), mask);
}
/**
* @}
*/
#ifdef __cplusplus
}
#endif
#endif /* __ATOMIC_H__ */
+98 -58
View File
@@ -6,25 +6,24 @@
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr.h>
#include <stdio.h>
#include <string.h>
#include <zephyr/types.h>
#include <stdlib.h>
#include <toolchain.h>
#include <bluetooth/bluetooth.h>
#include <misc/byteorder.h>
#include <console/uart_pipe.h>
#include "syscfg/syscfg.h"
#include "console/console.h"
#include "bttester_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
static struct os_eventq avail_queue;
static struct os_eventq *cmds_queue;
static struct os_event bttester_ev[CMD_QUEUED];
struct btp_buf {
u32_t _reserved;
struct os_event *ev;
union {
u8_t data[BTP_MTU];
struct btp_hdr hdr;
@@ -33,9 +32,6 @@ struct btp_buf {
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];
@@ -62,12 +58,12 @@ static void supported_services(u8_t *data, u16_t len)
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)
#if MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM)
tester_set_bit(buf, BTP_SERVICE_ID_L2CAP);
#endif /* CONFIG_BT_L2CAP_DYNAMIC_CHANNEL */
#if defined(CONFIG_BT_MESH)
#endif /* MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM) */
#if MYNEWT_VAL(BLE_MESH)
tester_set_bit(buf, BTP_SERVICE_ID_MESH);
#endif /* CONFIG_BT_MESH */
#endif /* MYNEWT_VAL(BLE_MESH) */
tester_send(BTP_SERVICE_ID_CORE, CORE_READ_SUPPORTED_SERVICES,
BTP_INDEX_NONE, (u8_t *) rp, sizeof(buf));
@@ -89,16 +85,16 @@ static void register_service(u8_t *data, u16_t len)
case BTP_SERVICE_ID_GATT:
status = tester_init_gatt();
break;
#if defined(CONFIG_BT_L2CAP_DYNAMIC_CHANNEL)
#if MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM)
case BTP_SERVICE_ID_L2CAP:
status = tester_init_l2cap();
#endif /* CONFIG_BT_L2CAP_DYNAMIC_CHANNEL */
break;
#if defined(CONFIG_BT_MESH)
#endif /* MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM) */
#if MYNEWT_VAL(BLE_MESH)
case BTP_SERVICE_ID_MESH:
status = tester_init_mesh();
break;
#endif /* CONFIG_BT_MESH */
#endif /* MYNEWT_VAL(BLE_MESH) */
default:
status = BTP_STATUS_FAILED;
break;
@@ -121,16 +117,16 @@ static void unregister_service(u8_t *data, u16_t len)
case BTP_SERVICE_ID_GATT:
status = tester_unregister_gatt();
break;
#if defined(CONFIG_BT_L2CAP_DYNAMIC_CHANNEL)
#if MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM)
case BTP_SERVICE_ID_L2CAP:
status = tester_unregister_l2cap();
break;
#endif /* CONFIG_BT_L2CAP_DYNAMIC_CHANNEL */
#if defined(CONFIG_BT_MESH)
#endif /* MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM) */
#if MYNEWT_VAL(BLE_MESH)
case BTP_SERVICE_ID_MESH:
status = tester_unregister_mesh();
break;
#endif /* CONFIG_BT_MESH */
#endif /* MYNEWT_VAL(BLE_MESH) */
default:
status = BTP_STATUS_FAILED;
break;
@@ -144,7 +140,8 @@ 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);
tester_rsp(BTP_SERVICE_ID_CORE, opcode, index,
BTP_STATUS_FAILED);
return;
}
@@ -168,21 +165,30 @@ static void handle_core(u8_t opcode, u8_t index, u8_t *data,
}
}
static void cmd_handler(void *p1, void *p2, void *p3)
static void cmd_handler(struct os_event *ev)
{
while (1) {
struct btp_buf *cmd;
u16_t len;
u16_t len;
struct btp_buf *cmd;
cmd = k_fifo_get(&cmds_queue, K_FOREVER);
if (!ev || !ev->ev_arg) {
return;
}
len = sys_le16_to_cpu(cmd->hdr.len);
cmd = ev->ev_arg;
/* TODO
* verify if service is registered before calling handler
*/
len = sys_le16_to_cpu(cmd->hdr.len);
if (MYNEWT_VAL(BTTESTER_DEBUG)) {
console_printf("[DBG] received %d bytes: %s\n",
sizeof(cmd->hdr) + len,
bt_hex(cmd->data,
sizeof(cmd->hdr) + len));
}
switch (cmd->hdr.service) {
/* 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);
@@ -195,32 +201,32 @@ static void cmd_handler(void *p1, void *p2, void *p3)
tester_handle_gatt(cmd->hdr.opcode, cmd->hdr.index,
cmd->hdr.data, len);
break;
#if defined(CONFIG_BT_L2CAP_DYNAMIC_CHANNEL)
#if MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM)
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)
#endif /* MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM) */
#if MYNEWT_VAL(BLE_MESH)
case BTP_SERVICE_ID_MESH:
tester_handle_mesh(cmd->hdr.opcode, cmd->hdr.index,
cmd->hdr.data, len);
break;
#endif /* CONFIG_BT_MESH */
#endif /* MYNEWT_VAL(BLE_MESH) */
default:
tester_rsp(cmd->hdr.service, cmd->hdr.opcode,
cmd->hdr.index, BTP_STATUS_FAILED);
break;
}
k_fifo_put(&avail_queue, cmd);
}
os_eventq_put(&avail_queue, ev);
}
static u8_t *recv_cb(u8_t *buf, size_t *off)
{
struct btp_hdr *cmd = (void *) buf;
struct btp_buf *new_buf;
struct os_event *new_ev;
struct btp_buf *new_buf, *old_buf;
u16_t len;
if (*off < sizeof(*cmd)) {
@@ -238,33 +244,58 @@ static u8_t *recv_cb(u8_t *buf, size_t *off)
return buf;
}
new_buf = k_fifo_get(&avail_queue, K_NO_WAIT);
if (!new_buf) {
new_ev = os_eventq_get_no_wait(&avail_queue);
if (!new_ev) {
SYS_LOG_ERR("BT tester: RX overflow");
*off = 0;
return buf;
}
k_fifo_put(&cmds_queue, CONTAINER_OF(buf, struct btp_buf, data));
old_buf = CONTAINER_OF(buf, struct btp_buf, data);
os_eventq_put(cmds_queue, old_buf->ev);
new_buf = new_ev->ev_arg;
*off = 0;
return new_buf->data;
}
void tester_init(void)
static void avail_queue_init(void)
{
int i;
struct btp_buf *buf;
os_eventq_init(&avail_queue);
for (i = 0; i < CMD_QUEUED; i++) {
k_fifo_put(&avail_queue, &cmd_buf[i]);
cmd_buf[i].ev = &bttester_ev[i];
bttester_ev[i].ev_cb = cmd_handler;
bttester_ev[i].ev_arg = &cmd_buf[i];
os_eventq_put(&avail_queue, &bttester_ev[i]);
}
}
void bttester_evq_set(struct os_eventq *evq)
{
cmds_queue = evq;
}
void tester_init(void)
{
struct os_event *ev;
struct btp_buf *buf;
avail_queue_init();
bttester_evq_set(os_eventq_dflt_get());
ev = os_eventq_get(&avail_queue);
buf = ev->ev_arg;
if (bttester_pipe_init()) {
SYS_LOG_ERR("Failed to initialize pipe");
return;
}
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);
bttester_pipe_register(buf->data, BTP_MTU, recv_cb);
tester_send(BTP_SERVICE_ID_CORE, CORE_EV_IUT_READY, BTP_INDEX_NONE,
NULL, 0);
@@ -280,9 +311,18 @@ void tester_send(u8_t service, u8_t opcode, u8_t index, u8_t *data,
msg.index = index;
msg.len = len;
uart_pipe_send((u8_t *)&msg, sizeof(msg));
bttester_pipe_send((u8_t *)&msg, sizeof(msg));
if (data && len) {
uart_pipe_send(data, len);
bttester_pipe_send(data, len);
}
if (MYNEWT_VAL(BTTESTER_DEBUG)) {
console_printf("[DBG] send %d bytes hdr: %s\n", sizeof(msg),
bt_hex((char *) &msg, sizeof(msg)));
if (data && len) {
console_printf("[DBG] send %d bytes data: %s\n", len,
bt_hex((char *) data, len));
}
}
}
+40 -7
View File
@@ -6,9 +6,19 @@
* SPDX-License-Identifier: Apache-2.0
*/
#include <misc/util.h>
#ifndef __BTTESTER_H__
#define __BTTESTER_H__
#define BTP_MTU 1024
#include "syscfg/syscfg.h"
#include "host/ble_gatt.h"
#if MYNEWT_VAL(BLE_MESH)
#include "mesh/glue.h"
#else
#include "glue.h"
#endif
#define BTP_MTU MYNEWT_VAL(BTTESTER_BTP_DATA_SIZE_MAX)
#define BTP_DATA_MAX_SIZE (BTP_MTU - sizeof(struct btp_hdr))
#define BTP_INDEX_NONE 0xff
@@ -24,9 +34,22 @@
#define BTP_STATUS_UNKNOWN_CMD 0x02
#define BTP_STATUS_NOT_READY 0x03
#define SYS_LOG_DBG(fmt, ...) \
if (MYNEWT_VAL(BTTESTER_DEBUG)) { \
console_printf("[INF] %s: " fmt "\n", \
__func__, ## __VA_ARGS__); \
}
#define SYS_LOG_INF(fmt, ...) console_printf("[INF] %s: " fmt "\n", \
__func__, ## __VA_ARGS__);
#define SYS_LOG_ERR(fmt, ...) console_printf("[WRN] %s: " fmt "\n", \
__func__, ## __VA_ARGS__);
#define SYS_LOG_LEVEL SYS_LOG_LEVEL_DEBUG
#define SYS_LOG_DOMAIN "bttester"
#include <logging/sys_log.h>
#define sys_cpu_to_le32 htole32
#define sys_le32_to_cpu le32toh
#define sys_cpu_to_le16 htole16
struct btp_hdr {
u8_t service;
@@ -831,16 +854,26 @@ 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);
int tester_gatt_notify_rx_ev(u16_t conn_handle, u16_t attr_handle,
u8_t indication, struct os_mbuf *om);
int tester_gatt_subscribe_ev(u16_t conn_handle, u16_t attr_handle, u8_t reason,
u8_t prev_notify, u8_t cur_notify,
u8_t prev_indicate, u8_t cur_indicate);
#if defined(CONFIG_BT_L2CAP_DYNAMIC_CHANNEL)
#if MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM)
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 */
#endif
#if defined(CONFIG_BT_MESH)
#if MYNEWT_VAL(BLE_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 */
#endif /* MYNEWT_VAL(BLE_MESH) */
void gatt_svr_register_cb(struct ble_gatt_register_ctxt *ctxt, void *arg);
int gatt_svr_init(void);
#endif /* __BTTESTER_H__ */
+39
View File
@@ -0,0 +1,39 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#ifndef __BTTESTER_PIPE_H__
#define __BTTESTER_PIPE_H__
#include <stdlib.h>
#include "bttester.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef u8_t *(*bttester_pipe_recv_cb)(u8_t *buf, size_t *off);
void bttester_pipe_register(u8_t *buf, size_t len, bttester_pipe_recv_cb cb);
int bttester_pipe_send(const u8_t *data, int len);
int bttester_pipe_init(void);
#ifdef __cplusplus
}
#endif
#endif /* __BTTESTER_PIPE_H__ */
+675 -224
View File
File diff suppressed because it is too large Load Diff
+1289 -1024
View File
File diff suppressed because it is too large Load Diff
+129
View File
@@ -0,0 +1,129 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#include "syscfg/syscfg.h"
#if !MYNEWT_VAL(BLE_MESH)
#include <assert.h>
#include <string.h>
#include "os/os.h"
#include "os/os_mbuf.h"
#include "glue.h"
#define ASSERT_NOT_CHAIN(om) assert(SLIST_NEXT(om, om_next) == NULL)
const char *bt_hex(const void *buf, size_t len)
{
static const char hex[] = "0123456789abcdef";
static char hexbufs[4][137];
static u8_t curbuf;
const u8_t *b = buf;
char *str;
int i;
str = hexbufs[curbuf++];
curbuf %= ARRAY_SIZE(hexbufs);
len = min(len, (sizeof(hexbufs[0]) - 1) / 2);
for (i = 0; i < len; i++) {
str[i * 2] = hex[b[i] >> 4];
str[i * 2 + 1] = hex[b[i] & 0xf];
}
str[i * 2] = '\0';
return str;
}
struct os_mbuf * NET_BUF_SIMPLE(uint16_t size)
{
struct os_mbuf *buf;
buf = os_msys_get(size, 0);
assert(buf);
return buf;
}
/* This is by purpose */
void net_buf_simple_init(struct os_mbuf *buf,
size_t reserve_head)
{
/* This is called in Zephyr after init.
* Note in Mynewt case we don't care abour reserved head*/
buf->om_data = &buf->om_databuf[buf->om_pkthdr_len] + reserve_head;
buf->om_len = 0;
}
void
net_buf_simple_add_le16(struct os_mbuf *om, uint16_t val)
{
val = htole16(val);
os_mbuf_append(om, &val, sizeof(val));
ASSERT_NOT_CHAIN(om);
}
void
net_buf_simple_add_be16(struct os_mbuf *om, uint16_t val)
{
val = htobe16(val);
os_mbuf_append(om, &val, sizeof(val));
ASSERT_NOT_CHAIN(om);
}
void
net_buf_simple_add_be32(struct os_mbuf *om, uint32_t val)
{
val = htobe32(val);
os_mbuf_append(om, &val, sizeof(val));
ASSERT_NOT_CHAIN(om);
}
void
net_buf_simple_add_u8(struct os_mbuf *om, uint8_t val)
{
os_mbuf_append(om, &val, 1);
ASSERT_NOT_CHAIN(om);
}
void*
net_buf_simple_add(struct os_mbuf *om, uint8_t len)
{
void * tmp;
tmp = os_mbuf_extend(om, len);
ASSERT_NOT_CHAIN(om);
return tmp;
}
uint8_t *
net_buf_simple_push(struct os_mbuf *om, uint8_t len)
{
uint8_t headroom = om->om_data - &om->om_databuf[om->om_pkthdr_len];
assert(headroom >= len);
om->om_data -= len;
om->om_len += len;
return om->om_data;
}
#endif
+65
View File
@@ -0,0 +1,65 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#ifndef __GLUE_H__
#define __GLUE_H__
#include "os/endian.h"
#define u8_t uint8_t
#define s8_t int8_t
#define u16_t uint16_t
#define u32_t uint32_t
#define s32_t int32_t
#ifndef BIT
#define BIT(n) (1UL << (n))
#endif
#define __packed __attribute__((__packed__))
#define sys_le16_to_cpu le16toh
#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
struct bt_data {
u8_t type;
u8_t data_len;
const u8_t *data;
};
#define BT_DATA(_type, _data, _data_len) \
{ \
.type = (_type), \
.data_len = (_data_len), \
.data = (const u8_t *)(_data), \
}
struct os_mbuf * NET_BUF_SIMPLE(uint16_t size);
void net_buf_simple_init(struct os_mbuf *buf, size_t reserve_head);
void net_buf_simple_add_le16(struct os_mbuf *om, uint16_t val);
void net_buf_simple_add_u8(struct os_mbuf *om, uint8_t val);
void *net_buf_simple_add(struct os_mbuf *om, uint8_t len);
uint8_t *net_buf_simple_push(struct os_mbuf *om, uint8_t len);
#define net_buf_simple_add_mem(a,b,c) os_mbuf_append(a,b,c)
const char *bt_hex(const void *buf, size_t len);
#endif /* __GLUE_H__ */
+247 -170
View File
@@ -6,117 +6,85 @@
* SPDX-License-Identifier: Apache-2.0
*/
#include <bluetooth/bluetooth.h>
#include "syscfg/syscfg.h"
#if MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM)
#include "console/console.h"
#include "host/ble_gap.h"
#include "host/ble_l2cap.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
#define CONTROLLER_INDEX 0
#define CHANNELS MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM)
#define TESTER_COC_MTU (230)
#define TESTER_COC_BUF_COUNT (3 * MYNEWT_VAL(BLE_L2CAP_COC_MAX_NUM))
NET_BUF_POOL_DEFINE(data_pool, 1, DATA_MTU, BT_BUF_USER_DATA_MIN, NULL);
static os_membuf_t tester_sdu_coc_mem[
OS_MEMPOOL_SIZE(TESTER_COC_BUF_COUNT, TESTER_COC_MTU)
];
struct os_mbuf_pool sdu_os_mbuf_pool;
static struct os_mempool sdu_coc_mbuf_mempool;
static struct channel {
u8_t chan_id; /* Internal number that identifies L2CAP channel. */
struct bt_l2cap_le_chan le;
u8_t state;
struct ble_l2cap_chan *chan;
} channels[CHANNELS];
/* TODO Extend to support multiple servers */
static struct bt_l2cap_server servers[SERVERS];
static u8_t recv_cb_buf[TESTER_COC_MTU + sizeof(struct l2cap_data_received_ev)];
static struct net_buf *alloc_buf_cb(struct bt_l2cap_chan *chan)
{
return net_buf_alloc(&data_pool, K_FOREVER);
struct channel *find_channel(struct ble_l2cap_chan *chan) {
int i;
for (i = 0; i < CHANNELS; ++i) {
if (channels[i].chan == chan) {
return &channels[i];
}
}
return NULL;
}
static u8_t recv_cb_buf[DATA_MTU + sizeof(struct l2cap_data_received_ev)];
static void
tester_l2cap_coc_recv(struct ble_l2cap_chan *chan, struct os_mbuf *sdu)
{
SYS_LOG_DBG("LE CoC SDU received, chan: 0x%08lx, data len %d",
(uint32_t) chan, OS_MBUF_PKTLEN(sdu));
static void recv_cb(struct bt_l2cap_chan *l2cap_chan, struct net_buf *buf)
os_mbuf_free_chain(sdu);
sdu = os_mbuf_get_pkthdr(&sdu_os_mbuf_pool, 0);
assert(sdu != NULL);
ble_l2cap_recv_ready(chan, sdu);
}
static void recv_cb(uint16_t conn_handle, struct ble_l2cap_chan *chan,
struct os_mbuf *buf, void *arg)
{
struct l2cap_data_received_ev *ev = (void *) recv_cb_buf;
struct channel *chan = CONTAINER_OF(l2cap_chan, struct channel, le);
struct channel *channel = arg;
ev->chan_id = chan->chan_id;
ev->data_length = sys_cpu_to_le16(buf->len);
memcpy(ev->data, buf->data, buf->len);
ev->chan_id = channel->chan_id;
ev->data_length = buf->om_len;
memcpy(ev->data, buf->om_data, buf->om_len);
tester_send(BTP_SERVICE_ID_L2CAP, L2CAP_EV_DATA_RECEIVED,
CONTROLLER_INDEX, recv_cb_buf, sizeof(*ev) + buf->len);
CONTROLLER_INDEX, recv_cb_buf, sizeof(*ev) + buf->om_len);
tester_l2cap_coc_recv(chan, buf);
}
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) {
if (channels[i].state) {
continue;
}
@@ -129,29 +97,173 @@ static struct channel *get_free_channel()
return NULL;
}
static void connected_cb(uint16_t conn_handle, struct ble_l2cap_chan *chan,
void *arg)
{
struct l2cap_connected_ev ev;
struct ble_gap_conn_desc desc;
struct channel *channel;
channel = get_free_channel();
if (!channel) {
assert(0);
}
channel->chan = chan;
channel->state = 0;
ev.chan_id = channel->chan_id;
channel->state = 1;
channel->chan = chan;
/* TODO: ev.psm */
if (!ble_gap_conn_find(conn_handle, &desc)) {
ev.address_type = desc.peer_ota_addr.type;
memcpy(ev.address, desc.peer_ota_addr.val,
sizeof(ev.address));
}
tester_send(BTP_SERVICE_ID_L2CAP, L2CAP_EV_CONNECTED, CONTROLLER_INDEX,
(u8_t *) &ev, sizeof(ev));
}
static void disconnected_cb(uint16_t conn_handle, struct ble_l2cap_chan *chan,
void *arg)
{
struct l2cap_disconnected_ev ev;
struct ble_gap_conn_desc desc;
struct channel *channel;
memset(&ev, 0, sizeof(struct l2cap_disconnected_ev));
channel = find_channel(chan);
if (channel != NULL) {
channel->state = 0;
channel->chan = chan;
ev.chan_id = channel->chan_id;
/* TODO: ev.result */
/* TODO: ev.psm */
}
if (!ble_gap_conn_find(conn_handle, &desc)) {
ev.address_type = desc.peer_ota_addr.type;
memcpy(ev.address, desc.peer_ota_addr.val,
sizeof(ev.address));
}
tester_send(BTP_SERVICE_ID_L2CAP, L2CAP_EV_DISCONNECTED,
CONTROLLER_INDEX, (u8_t *) &ev, sizeof(ev));
}
static int accept_cb(uint16_t conn_handle, uint16_t peer_mtu,
struct ble_l2cap_chan *chan)
{
struct os_mbuf *sdu_rx;
SYS_LOG_DBG("LE CoC accepting, chan: 0x%08lx, peer_mtu %d",
(uint32_t) chan, peer_mtu);
sdu_rx = os_mbuf_get_pkthdr(&sdu_os_mbuf_pool, 0);
if (!sdu_rx) {
return BLE_HS_ENOMEM;
}
ble_l2cap_recv_ready(chan, sdu_rx);
return 0;
}
static int
tester_l2cap_event(struct ble_l2cap_event *event, void *arg)
{
switch (event->type) {
case BLE_L2CAP_EVENT_COC_CONNECTED:
if (event->connect.status) {
console_printf("LE COC error: %d\n", event->connect.status);
disconnected_cb(event->connect.conn_handle,
event->connect.chan, arg);
return 0;
}
console_printf("LE COC connected, conn: %d, chan: 0x%08lx, scid: 0x%04x, "
"dcid: 0x%04x, our_mtu: 0x%04x, peer_mtu: 0x%04x\n",
event->connect.conn_handle,
(uint32_t) event->connect.chan,
ble_l2cap_get_scid(event->connect.chan),
ble_l2cap_get_dcid(event->connect.chan),
ble_l2cap_get_our_mtu(event->connect.chan),
ble_l2cap_get_peer_mtu(event->connect.chan));
connected_cb(event->connect.conn_handle,
event->connect.chan, arg);
return 0;
case BLE_L2CAP_EVENT_COC_DISCONNECTED:
console_printf("LE CoC disconnected, chan: 0x%08lx\n",
(uint32_t) event->disconnect.chan);
disconnected_cb(event->disconnect.conn_handle,
event->disconnect.chan, arg);
return 0;
case BLE_L2CAP_EVENT_COC_ACCEPT:
console_printf("LE CoC accept, chan: 0x%08lx, handle: %u, sdu_size: %u\n",
(uint32_t) event->accept.chan,
event->accept.conn_handle,
event->accept.peer_sdu_size);
return accept_cb(event->accept.conn_handle,
event->accept.peer_sdu_size,
event->accept.chan);
case BLE_L2CAP_EVENT_COC_DATA_RECEIVED:
console_printf("LE CoC data received, chan: 0x%08lx, handle: %u, sdu_len: %u\n",
(uint32_t) event->receive.chan,
event->receive.conn_handle,
event->receive.sdu_rx->om_len);
recv_cb(event->receive.conn_handle, event->receive.chan,
event->receive.sdu_rx, arg);
return 0;
default:
return 0;
}
}
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 ble_gap_conn_desc desc;
struct channel *chan;
int err;
struct os_mbuf *sdu_rx;
ble_addr_t *addr = (void *) data;
int rc;
conn = bt_conn_lookup_addr_le(BT_ID_DEFAULT, (bt_addr_le_t *)data);
if (!conn) {
SYS_LOG_DBG("connect: type: %d addr: %s", addr->type, bt_hex(addr->val, 6));
rc = ble_gap_conn_find_by_addr(addr, &desc);
if (rc) {
SYS_LOG_ERR("GAP conn find failed");
goto fail;
}
chan = get_free_channel();
if (!chan) {
SYS_LOG_ERR("No free channels");
goto fail;
}
chan->le.chan.ops = &l2cap_ops;
chan->le.rx.mtu = DATA_MTU;
sdu_rx = os_mbuf_get_pkthdr(&sdu_os_mbuf_pool, 0);
if (sdu_rx == NULL) {
SYS_LOG_ERR("Failed to alloc buf");
goto fail;
}
err = bt_l2cap_chan_connect(conn, &chan->le.chan, cmd->psm);
if (err < 0) {
rc = ble_l2cap_connect(desc.conn_handle, htole16(cmd->psm),
TESTER_COC_MTU, sdu_rx,
tester_l2cap_event, chan);
if (rc) {
SYS_LOG_ERR("L2CAP connect failed\n");
goto fail;
}
@@ -170,11 +282,15 @@ fail:
static void disconnect(u8_t *data, u16_t len)
{
const struct l2cap_disconnect_cmd *cmd = (void *) data;
struct channel *chan = &channels[cmd->chan_id];
struct channel *chan;
u8_t status;
int err;
err = bt_l2cap_chan_disconnect(&chan->le.chan);
SYS_LOG_DBG("");
chan = &channels[cmd->chan_id];
err = ble_l2cap_disconnect(chan->chan);
if (err) {
status = BTP_STATUS_FAILED;
goto rsp;
@@ -191,106 +307,53 @@ 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;
struct os_mbuf *sdu_tx;
int rc;
u16_t data_len = sys_le16_to_cpu(cmd->data_len);
SYS_LOG_DBG("cmd->chan_id=%d", cmd->chan_id);
/* FIXME: For now, fail if data length exceeds buffer length */
if (data_len > DATA_MTU - BT_L2CAP_CHAN_SEND_RESERVE) {
if (data_len > TESTER_COC_MTU) {
SYS_LOG_ERR("Data length exceeds buffer length");
goto fail;
}
/* FIXME: For now, fail if data length exceeds remote's L2CAP SDU */
if (data_len > chan->le.tx.mtu) {
sdu_tx = os_mbuf_get_pkthdr(&sdu_os_mbuf_pool, 0);
if (sdu_tx == NULL) {
SYS_LOG_ERR("No memory in the test sdu pool\n");
goto fail;
}
buf = net_buf_alloc(&data_pool, K_FOREVER);
net_buf_reserve(buf, BT_L2CAP_CHAN_SEND_RESERVE);
os_mbuf_append(sdu_tx, cmd->data, data_len);
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);
rc = ble_l2cap_send(chan->chan, sdu_tx);
if (rc) {
SYS_LOG_ERR("Unable to send data: %d", rc);
os_mbuf_free_chain(sdu_tx);
goto fail;
}
tester_rsp(BTP_SERVICE_ID_L2CAP, L2CAP_SEND_DATA, CONTROLLER_INDEX,
BTP_STATUS_SUCCESS);
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;
BTP_STATUS_FAILED);
}
static void listen(u8_t *data, u16_t len)
{
const struct l2cap_listen_cmd *cmd = (void *) data;
struct bt_l2cap_server *server;
int rc;
SYS_LOG_DBG("");
/* 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;
rc = ble_l2cap_create_server(cmd->psm, TESTER_COC_MTU,
tester_l2cap_event, NULL);
if (rc) {
goto fail;
}
@@ -348,6 +411,18 @@ void tester_handle_l2cap(u8_t opcode, u8_t index, u8_t *data,
u8_t tester_init_l2cap(void)
{
int rc;
/* For testing we want to support all the available channels */
rc = os_mempool_init(&sdu_coc_mbuf_mempool, TESTER_COC_BUF_COUNT,
TESTER_COC_MTU, tester_sdu_coc_mem,
"tester_coc_sdu_pool");
assert(rc == 0);
rc = os_mbuf_pool_init(&sdu_os_mbuf_pool, &sdu_coc_mbuf_mempool,
TESTER_COC_MTU, TESTER_COC_BUF_COUNT);
assert(rc == 0);
return BTP_STATUS_SUCCESS;
}
@@ -355,3 +430,5 @@ u8_t tester_unregister_l2cap(void)
{
return BTP_STATUS_SUCCESS;
}
#endif
+39 -4
View File
@@ -6,13 +6,48 @@
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr.h>
#include <zephyr/types.h>
#include <toolchain.h>
#include "sysinit/sysinit.h"
#include "modlog/modlog.h"
#include "host/ble_uuid.h"
#include "host/ble_hs.h"
#include "bttester.h"
void main(void)
static void on_reset(int reason)
{
MODLOG_DFLT(ERROR, "Resetting state; reason=%d\n", reason);
}
static void on_sync(void)
{
MODLOG_DFLT(INFO, "Bluetooth initialized\n");
tester_init();
}
int main(int argc, char **argv)
{
int rc;
#ifdef ARCH_sim
mcu_sim_parse_args(argc, argv);
#endif
/* Initialize OS */
sysinit();
/* Initialize the NimBLE host configuration. */
ble_hs_cfg.reset_cb = on_reset;
ble_hs_cfg.sync_cb = on_sync;
ble_hs_cfg.gatts_register_cb = gatt_svr_register_cb,
ble_hs_cfg.store_status_cb = ble_store_util_status_rr;
rc = gatt_svr_init();
assert(rc == 0);
while (1) {
os_eventq_run(os_eventq_dflt_get());
}
return 0;
}
+76 -55
View File
@@ -6,12 +6,17 @@
* SPDX-License-Identifier: Apache-2.0
*/
#include <bluetooth/bluetooth.h>
#include "syscfg/syscfg.h"
#if MYNEWT_VAL(BLE_MESH)
#include <errno.h>
#include <bluetooth/mesh.h>
#include <bluetooth/testing.h>
#include <misc/byteorder.h>
#include "mesh/mesh.h"
#include "mesh/glue.h"
#include "mesh/testing.h"
#include "console/console.h"
#include "bttester.h"
#define CONTROLLER_INDEX 0
@@ -60,39 +65,39 @@ static struct {
static void supported_commands(u8_t *data, u16_t len)
{
struct net_buf_simple *buf = NET_BUF_SIMPLE(BTP_DATA_MAX_SIZE);
struct os_mbuf *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);
tester_set_bit(buf->om_data, MESH_READ_SUPPORTED_COMMANDS);
tester_set_bit(buf->om_data, MESH_CONFIG_PROVISIONING);
tester_set_bit(buf->om_data, MESH_PROVISION_NODE);
tester_set_bit(buf->om_data, MESH_INIT);
tester_set_bit(buf->om_data, MESH_RESET);
tester_set_bit(buf->om_data, MESH_INPUT_NUMBER);
tester_set_bit(buf->om_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);
tester_set_bit(buf->om_data, MESH_IVU_TEST_MODE);
tester_set_bit(buf->om_data, MESH_IVU_TOGGLE_STATE);
tester_set_bit(buf->om_data, MESH_NET_SEND);
tester_set_bit(buf->om_data, MESH_HEALTH_GENERATE_FAULTS);
tester_set_bit(buf->om_data, MESH_HEALTH_CLEAR_FAULTS);
tester_set_bit(buf->om_data, MESH_LPN);
tester_set_bit(buf->om_data, MESH_LPN_POLL);
tester_set_bit(buf->om_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);
#if MYNEWT_VAL(BLE_MESH_TESTING)
tester_set_bit(buf->om_data, MESH_LPN_SUBSCRIBE);
tester_set_bit(buf->om_data, MESH_LPN_UNSUBSCRIBE);
tester_set_bit(buf->om_data, MESH_RPL_CLEAR);
#endif /* CONFIG_BT_TESTING */
tester_set_bit(buf->data, MESH_PROXY_IDENTITY);
tester_set_bit(buf->om_data, MESH_PROXY_IDENTITY);
tester_send(BTP_SERVICE_ID_MESH, MESH_READ_SUPPORTED_COMMANDS,
CONTROLLER_INDEX, buf->data, buf->len);
CONTROLLER_INDEX, buf->om_data, buf->om_len);
}
static struct bt_mesh_cfg_srv cfg_srv = {
@@ -192,7 +197,13 @@ 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_model_pub health_pub;
static void
health_pub_init(void)
{
health_pub.msg = BT_MESH_HEALTH_FAULT_MSG(CUR_FAULTS_MAX);
}
static struct bt_mesh_cfg_cli cfg_cli = {
};
@@ -293,7 +304,7 @@ 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);
SYS_LOG_DBG("action 0x%04x number 0x%08lx", action, number);
ev.action = sys_cpu_to_le16(action);
ev.number = sys_cpu_to_le32(number);
@@ -307,7 +318,7 @@ static int output_number(bt_mesh_output_action_t action, u32_t number)
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);
struct os_mbuf *buf = NET_BUF_SIMPLE(BTP_DATA_MAX_SIZE);
SYS_LOG_DBG("str %s", str);
@@ -319,7 +330,7 @@ static int output_string(const char *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);
CONTROLLER_INDEX, buf->om_data, buf->om_len);
return 0;
}
@@ -422,7 +433,7 @@ static void init(u8_t *data, u16_t len)
SYS_LOG_DBG("");
err = bt_mesh_init(&prov, &comp);
err = bt_mesh_init(0, &prov, &comp);
if (err) {
status = BTP_STATUS_FAILED;
@@ -469,7 +480,7 @@ static void input_number(u8_t *data, u16_t len)
number = sys_le32_to_cpu(cmd->number);
SYS_LOG_DBG("number 0x%04x", number);
SYS_LOG_DBG("number 0x%04lx", number);
err = bt_mesh_input_number(number);
if (err) {
@@ -499,9 +510,9 @@ static void input_string(u8_t *data, u16_t len)
goto rsp;
}
strncpy(str_auth, cmd->string, cmd->string_len);
strncpy((char *)str_auth, (char *)cmd->string, cmd->string_len);
err = bt_mesh_input_string(str_auth);
err = bt_mesh_input_string((char *)str_auth);
if (err) {
status = BTP_STATUS_FAILED;
}
@@ -574,7 +585,7 @@ static void lpn_poll(u8_t *data, u16_t len)
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 os_mbuf *msg = NET_BUF_SIMPLE(UINT8_MAX);
struct bt_mesh_msg_ctx ctx = {
.net_idx = net.net_idx,
.app_idx = BT_MESH_KEY_DEV,
@@ -586,41 +597,43 @@ static void net_send(u8_t *data, u16_t len)
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);
net_buf_simple_add_mem(msg, cmd->payload, cmd->payload_len);
err = bt_mesh_model_send(&vnd_models[0], &ctx, &msg, NULL, NULL);
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);
os_mbuf_free_chain(msg);
}
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) +
struct os_mbuf *buf = NET_BUF_SIMPLE(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));
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);
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);
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);
CONTROLLER_INDEX, buf->om_data, buf->om_len);
}
static void health_clear_faults(u8_t *data, u16_t len)
@@ -639,7 +652,7 @@ static void health_clear_faults(u8_t *data, u16_t len)
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 os_mbuf *msg = NET_BUF_SIMPLE(UINT8_MAX);
struct bt_mesh_msg_ctx ctx = {
.net_idx = net.net_idx,
.app_idx = BT_MESH_KEY_DEV,
@@ -670,9 +683,9 @@ static void model_send(u8_t *data, u16_t len)
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);
net_buf_simple_add_mem(msg, cmd->payload, cmd->payload_len);
err = bt_mesh_model_send(model, &ctx, &msg, NULL, NULL);
err = bt_mesh_model_send(model, &ctx, msg, NULL, NULL);
if (err) {
SYS_LOG_ERR("Failed to send (err %d)", err);
}
@@ -680,9 +693,11 @@ static void model_send(u8_t *data, u16_t len)
fail:
tester_rsp(BTP_SERVICE_ID_MESH, MESH_MODEL_SEND, CONTROLLER_INDEX,
err ? BTP_STATUS_FAILED : BTP_STATUS_SUCCESS);
os_mbuf_free_chain(msg);
}
#if defined(CONFIG_BT_TESTING)
#if MYNEWT_VAL(BLE_MESH_TESTING)
static void lpn_subscribe(u8_t *data, u16_t len)
{
struct mesh_lpn_subscribe_cmd *cmd = (void *) data;
@@ -731,7 +746,7 @@ static void rpl_clear(u8_t *data, u16_t len)
tester_rsp(BTP_SERVICE_ID_MESH, MESH_RPL_CLEAR, CONTROLLER_INDEX,
err ? BTP_STATUS_FAILED : BTP_STATUS_SUCCESS);
}
#endif /* CONFIG_BT_TESTING */
#endif /* MYNEWT_VAL(BLE_MESH_TESTING) */
static void proxy_identity_enable(u8_t *data, u16_t len)
{
@@ -796,7 +811,7 @@ void tester_handle_mesh(u8_t opcode, u8_t index, u8_t *data, u16_t len)
case MESH_MODEL_SEND:
model_send(data, len);
break;
#if defined(CONFIG_BT_TESTING)
#if MYNEWT_VAL(BLE_MESH_TESTING)
case MESH_LPN_SUBSCRIBE:
lpn_subscribe(data, len);
break;
@@ -806,7 +821,7 @@ void tester_handle_mesh(u8_t opcode, u8_t index, u8_t *data, u16_t len)
case MESH_RPL_CLEAR:
rpl_clear(data, len);
break;
#endif /* CONFIG_BT_TESTING */
#endif /* MYNEWT_VAL(BLE_MESH_TESTING) */
case MESH_PROXY_IDENTITY:
proxy_identity_enable(data, len);
break;
@@ -820,28 +835,30 @@ void tester_handle_mesh(u8_t opcode, u8_t index, u8_t *data, u16_t len)
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 os_mbuf *buf = NET_BUF_SIMPLE(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)) {
if (payload_len > net_buf_simple_tailroom(buf)) {
SYS_LOG_ERR("Payload size exceeds buffer size");
return;
goto done;
}
ev = net_buf_simple_add(&buf, sizeof(*ev));
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);
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);
buf->om_data, buf->om_len);
done:
os_mbuf_free_chain(buf);
}
static void model_bound_cb(u16_t addr, struct bt_mesh_model *model,
@@ -914,6 +931,8 @@ static struct bt_test_cb bt_test_cb = {
u8_t tester_init_mesh(void)
{
health_pub_init();
if (IS_ENABLED(CONFIG_BT_TESTING)) {
bt_test_cb_register(&bt_test_cb);
}
@@ -925,3 +944,5 @@ u8_t tester_unregister_mesh(void)
{
return BTP_STATUS_SUCCESS;
}
#endif /* MYNEWT_VAL(BLE_MESH) */
+136
View File
@@ -0,0 +1,136 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#include "syscfg/syscfg.h"
#if MYNEWT_VAL(BTTESTER_PIPE_RTT)
#include "os/mynewt.h"
#include "console/console.h"
#include "rtt/SEGGER_RTT.h"
#include "bttester_pipe.h"
static struct hal_timer rtt_timer;
static bttester_pipe_recv_cb app_cb;
static u8_t *recv_buf;
static size_t recv_buf_len;
static size_t recv_off;
static uint8_t rtt_buf_up[MYNEWT_VAL(BTTESTER_RTT_BUFFER_SIZE_UP)];
static uint8_t rtt_buf_down[MYNEWT_VAL(BTTESTER_RTT_BUFFER_SIZE_DOWN)];
static int rtt_index_up, rtt_index_down;
#define RTT_INPUT_POLL_INTERVAL_MIN 10 /* ms */
#define RTT_INPUT_POLL_INTERVAL_STEP 10 /* ms */
#define RTT_INPUT_POLL_INTERVAL_MAX 250 /* ms */
static int rtt_pipe_get_char(unsigned int index)
{
char c;
int r;
r = (int)SEGGER_RTT_Read(index, &c, 1u);
if (r == 1) {
r = (int)(unsigned char)c;
} else {
r = -1;
}
return r;
}
static void
rtt_pipe_poll_func(void *arg)
{
static uint32_t itvl_ms = RTT_INPUT_POLL_INTERVAL_MIN;
static int key = -1;
int avail = recv_buf_len - recv_off;
if (key < 0) {
key = rtt_pipe_get_char((unsigned int) rtt_index_down);
}
if (key < 0) {
itvl_ms += RTT_INPUT_POLL_INTERVAL_STEP;
itvl_ms = min(itvl_ms, RTT_INPUT_POLL_INTERVAL_MAX);
} else {
while (key >= 0 && avail > 0) {
recv_buf[recv_off] = (u8_t) key;
recv_off++;
avail = recv_buf_len - recv_off;
key = rtt_pipe_get_char((unsigned int) rtt_index_down);
}
/*
* Call application callback with received data. Application
* may provide new buffer or alter data offset.
*/
recv_buf = app_cb(recv_buf, &recv_off);
itvl_ms = RTT_INPUT_POLL_INTERVAL_MIN;
}
os_cputime_timer_relative(&rtt_timer, itvl_ms * 1000);
}
int
bttester_pipe_send(const u8_t *data, int len)
{
SEGGER_RTT_Write((unsigned int) rtt_index_up, data, (unsigned int) len);
return 0;
}
void
bttester_pipe_register(u8_t *buf, size_t len, bttester_pipe_recv_cb cb)
{
recv_buf = buf;
recv_buf_len = len;
app_cb = cb;
}
int
bttester_pipe_init(void)
{
rtt_index_up = SEGGER_RTT_AllocUpBuffer(MYNEWT_VAL(BTTESTER_RTT_BUFFER_NAME),
rtt_buf_up, sizeof(rtt_buf_up),
SEGGER_RTT_MODE_BLOCK_IF_FIFO_FULL);
if (rtt_index_up < 0) {
return -1;
}
rtt_index_down = SEGGER_RTT_AllocDownBuffer(MYNEWT_VAL(BTTESTER_RTT_BUFFER_NAME),
rtt_buf_down, sizeof(rtt_buf_down),
SEGGER_RTT_MODE_BLOCK_IF_FIFO_FULL);
if (rtt_index_down < 0) {
return -1;
}
console_printf("Using up-buffer #%d\n", rtt_index_up);
console_printf("Using down-buffer #%d\n", rtt_index_down);
os_cputime_timer_init(&rtt_timer, rtt_pipe_poll_func, NULL);
os_cputime_timer_relative(&rtt_timer, 200000);
return 0;
}
#endif /* MYNEWT_VAL(BTTESTER_PIPE_RTT) */
+256
View File
@@ -0,0 +1,256 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#include "syscfg/syscfg.h"
#if MYNEWT_VAL(BTTESTER_PIPE_UART)
#include "os/mynewt.h"
#include "uart/uart.h"
#include "bttester_pipe.h"
static u8_t *recv_buf;
static size_t recv_buf_len;
static bttester_pipe_recv_cb app_cb;
static size_t recv_off;
struct uart_pipe_ring {
uint8_t head;
uint8_t tail;
uint16_t size;
uint8_t *buf;
};
static struct uart_dev *uart_dev;
static struct uart_pipe_ring cr_tx;
static uint8_t cr_tx_buf[2048];
typedef void (*console_write_char)(struct uart_dev*, uint8_t);
static console_write_char write_char_cb;
static struct uart_pipe_ring cr_rx;
static uint8_t cr_rx_buf[2048];
static volatile bool uart_console_rx_stalled;
struct os_event rx_ev;
static inline int
inc_and_wrap(int i, int max)
{
return (i + 1) & (max - 1);
}
static void
uart_pipe_ring_add_char(struct uart_pipe_ring *cr, char ch)
{
cr->buf[cr->head] = ch;
cr->head = inc_and_wrap(cr->head, cr->size);
}
static uint8_t
uart_pipe_ring_pull_char(struct uart_pipe_ring *cr)
{
uint8_t ch;
ch = cr->buf[cr->tail];
cr->tail = inc_and_wrap(cr->tail, cr->size);
return ch;
}
static bool
uart_pipe_ring_is_full(const struct uart_pipe_ring *cr)
{
return inc_and_wrap(cr->head, cr->size) == cr->tail;
}
static bool
uart_pipe_ring_is_empty(const struct uart_pipe_ring *cr)
{
return cr->head == cr->tail;
}
static void
uart_pipe_queue_char(struct uart_dev *uart_dev, uint8_t ch)
{
int sr;
if ((uart_dev->ud_dev.od_flags & OS_DEV_F_STATUS_OPEN) == 0) {
return;
}
OS_ENTER_CRITICAL(sr);
while (uart_pipe_ring_is_full(&cr_tx)) {
/* TX needs to drain */
uart_start_tx(uart_dev);
OS_EXIT_CRITICAL(sr);
if (os_started()) {
os_time_delay(1);
}
OS_ENTER_CRITICAL(sr);
}
uart_pipe_ring_add_char(&cr_tx, ch);
OS_EXIT_CRITICAL(sr);
}
/*
* Interrupts disabled when console_tx_char/console_rx_char are called.
* Characters sent only in blocking mode.
*/
static int
uart_console_tx_char(void *arg)
{
if (uart_pipe_ring_is_empty(&cr_tx)) {
return -1;
}
return uart_pipe_ring_pull_char(&cr_tx);
}
/*
* Interrupts disabled when console_tx_char/console_rx_char are called.
*/
static int
uart_console_rx_char(void *arg, uint8_t byte)
{
if (uart_pipe_ring_is_full(&cr_rx)) {
uart_console_rx_stalled = true;
return -1;
}
uart_pipe_ring_add_char(&cr_rx, byte);
if (!rx_ev.ev_queued) {
os_eventq_put(os_eventq_dflt_get(), &rx_ev);
}
return 0;
}
static int
uart_pipe_handle_char(int key)
{
recv_buf[recv_off] = (u8_t) key;
recv_off++;
return 0;
}
static void
uart_console_rx_char_event(struct os_event *ev)
{
static int b = -1;
int sr;
int ret;
/* We may have unhandled character - try it first */
if (b >= 0) {
ret = uart_pipe_handle_char(b);
if (ret < 0) {
return;
}
}
while (!uart_pipe_ring_is_empty(&cr_rx)) {
OS_ENTER_CRITICAL(sr);
b = uart_pipe_ring_pull_char(&cr_rx);
OS_EXIT_CRITICAL(sr);
/* If UART RX was stalled due to a full receive buffer, restart RX now
* that we have removed a byte from the buffer.
*/
if (uart_console_rx_stalled) {
uart_console_rx_stalled = false;
uart_start_rx(uart_dev);
}
ret = uart_pipe_handle_char(b);
if (ret < 0) {
return;
}
}
/*
* Call application callback with received data. Application
* may provide new buffer or alter data offset.
*/
recv_buf = app_cb(recv_buf, &recv_off);
b = -1;
}
int
bttester_pipe_send(const u8_t *data, int len)
{
int i;
/* Assure that there is a write cb installed; this enables to debug
* code that is faulting before the console was initialized.
*/
if (!write_char_cb) {
return -1;
}
for (i = 0; i < len; ++i) {
write_char_cb(uart_dev, data[i]);
}
uart_start_tx(uart_dev);
return 0;
}
int
bttester_pipe_init(void)
{
struct uart_conf uc = {
.uc_speed = MYNEWT_VAL(CONSOLE_UART_BAUD),
.uc_databits = 8,
.uc_stopbits = 1,
.uc_parity = UART_PARITY_NONE,
.uc_flow_ctl = MYNEWT_VAL(CONSOLE_UART_FLOW_CONTROL),
.uc_tx_char = uart_console_tx_char,
.uc_rx_char = uart_console_rx_char,
};
cr_tx.size = 2048;
cr_tx.buf = cr_tx_buf;
write_char_cb = uart_pipe_queue_char;
cr_rx.size = 2048;
cr_rx.buf = cr_rx_buf;
rx_ev.ev_cb = uart_console_rx_char_event;
if (!uart_dev) {
uart_dev = (struct uart_dev *)os_dev_open(MYNEWT_VAL(CONSOLE_UART_DEV),
OS_TIMEOUT_NEVER, &uc);
if (!uart_dev) {
return -1;
}
}
return 0;
}
void
bttester_pipe_register(u8_t *buf, size_t len, bttester_pipe_recv_cb cb)
{
recv_buf = buf;
recv_buf_len = len;
app_cb = cb;
}
#endif /* MYNEWT_VAL(BTTESTER_PIPE_UART) */
+117
View File
@@ -0,0 +1,117 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
#
# Package: apps/blemesh
syscfg.defs:
BTTESTER_PIPE_UART:
description: 'Set communication pipe to UART'
value: 0
BTTESTER_PIPE_RTT:
description: 'Set communication pipe to RTT'
value: 1
BTTESTER_RTT_BUFFER_NAME:
description: Bttester rtt pipe buffer name
value: '"bttester"'
BTTESTER_RTT_BUFFER_SIZE_UP:
description: Bttester upstream buffer size
value: 512
BTTESTER_RTT_BUFFER_SIZE_DOWN:
description: Bttester downstream buffer size
value: 512
BTTESTER_PRIVACY_MODE:
description: Use Resolvable Private Address
value: 0
BTTESTER_BTP_DATA_SIZE_MAX:
description: Maximum BTP payload
value: MYNEWT_VAL_BTTESTER_RTT_BUFFER_SIZE_UP
BTTESTER_CONN_PARAM_UPDATE:
description: Trigger conn param update after connection establish
value: 0
BTTESTER_DEBUG:
description: Enable debug logging
value: 0
syscfg.vals:
OS_MAIN_STACK_SIZE: 12444
SHELL_TASK: 1
SHELL_NEWTMGR: 0
LOG_LEVEL: 1
MSYS_1_BLOCK_COUNT: 48
BLE_PUBLIC_DEV_ADDR: "((uint8_t[6]){0x66, 0x55, 0x44, 0x33, 0x22, 0x11})"
BLE_MONITOR_RTT: 1
CONSOLE_RTT: 0
CONSOLE_UART: 1
RTT_NUM_BUFFERS_UP: 1
RTT_NUM_BUFFERS_DOWN: 1
BLE_L2CAP_COC_MAX_NUM: 2
BLE_RPA_TIMEOUT: 10
BLE_SM_BONDING: 1
BLE_SM_MITM: 1
BLE_SM_SC: 1
BLE_SM_OUR_KEY_DIST: 7
BLE_SM_THEIR_KEY_DIST: 7
BLE_MESH: 1
BLE_MESH_SHELL: 0
BLE_MESH_PROV: 1
BLE_MESH_RELAY: 1
BLE_MESH_PB_ADV: 1
BLE_MESH_PB_GATT: 1
BLE_MESH_LOW_POWER: 1
BLE_MESH_LPN_AUTO: 0
BLE_MESH_GATT_PROXY: 1
BLE_MESH_LABEL_COUNT: 2
BLE_MESH_SUBNET_COUNT: 2
BLE_MESH_MODEL_GROUP_COUNT: 2
BLE_MESH_APP_KEY_COUNT: 4
BLE_MESH_IV_UPDATE_TEST: 1
BLE_MESH_TESTING: 1
BLE_MESH_FRIEND: 1
BLE_MESH_CFG_CLI: 1
BLE_MESH_ADV_BUF_COUNT: 20
BLE_MESH_TX_SEG_MAX: 6
BLE_MESH_DEBUG: 0
BLE_MESH_DEBUG_NET: 0
BLE_MESH_DEBUG_TRANS: 0
BLE_MESH_DEBUG_BEACON: 0
BLE_MESH_DEBUG_CRYPTO: 0
BLE_MESH_DEBUG_PROV: 0
BLE_MESH_DEBUG_ACCESS: 0
BLE_MESH_DEBUG_MODEL: 0
BLE_MESH_DEBUG_ADV: 0
BLE_MESH_DEBUG_LOW_POWER: 0
BLE_MESH_DEBUG_FRIEND: 0
BLE_MESH_DEBUG_PROXY: 0
syscfg.vals.BTTESTER_PIPE_UART:
CONSOLE_UART: 0
CONSOLE_RTT: 1