mirror of
https://github.com/espressif/esp-nimble.git
synced 2026-08-17 15:30:11 +00:00
apps/bttester: Port app to Mynewt
This commit is contained in:
+5
-48
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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__ */
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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__ */
|
||||
|
||||
@@ -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
File diff suppressed because it is too large
Load Diff
+1289
-1024
File diff suppressed because it is too large
Load Diff
@@ -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
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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
@@ -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) */
|
||||
|
||||
@@ -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) */
|
||||
@@ -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) */
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user