mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2024-12-29 04:50:03 +01:00
tests: add nimble_l2cap test application(s)
This commit is contained in:
parent
713fd19040
commit
07a12108da
24
tests/nimble_l2cap/Makefile
Normal file
24
tests/nimble_l2cap/Makefile
Normal file
@ -0,0 +1,24 @@
|
||||
# Configure default and allowed boards
|
||||
BOARD ?= nrf52dk
|
||||
BOARD_WHITELIST := nrf52dk nrf52840dk
|
||||
|
||||
# load the default test environment
|
||||
include ../Makefile.tests_common
|
||||
|
||||
# RIOT modules used in for this test
|
||||
USEMODULE += xtimer
|
||||
USEMODULE += shell
|
||||
USEMODULE += shell_commands
|
||||
USEMODULE += ps
|
||||
USEMODULE += bluetil_ad
|
||||
|
||||
# Get the shared NimBLE and test configuration from the backend server
|
||||
include $(RIOTBASE)/tests/nimble_l2cap_server/nimble.inc.mk
|
||||
|
||||
# Comment this out to disable code in RIOT that does safety checking
|
||||
# which is not needed in a production environment but helps in the
|
||||
# development process:
|
||||
CFLAGS += -DLOG_LEVEL=LOG_ALL
|
||||
CFLAGS += -DDEBUG_ASSERT_VERBOSE
|
||||
|
||||
include $(RIOTBASE)/Makefile.include
|
7
tests/nimble_l2cap/README.md
Normal file
7
tests/nimble_l2cap/README.md
Normal file
@ -0,0 +1,7 @@
|
||||
# About
|
||||
|
||||
This is a client application for testing and benchmarking of raw L2CAP
|
||||
connection oriented channels (COC). This application works together with the
|
||||
`tests/nimble_l2cap_server` application.
|
||||
|
||||
See `tests/nimble_l2cap_server/README.md` for more information.
|
334
tests/nimble_l2cap/main.c
Normal file
334
tests/nimble_l2cap/main.c
Normal file
@ -0,0 +1,334 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Freie Universität Berlin
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU Lesser
|
||||
* General Public License v2.1. See the file LICENSE in the top level
|
||||
* directory for more details.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ingroup tests
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief Client to test and benchmark raw L2CAP COC for NimBLE
|
||||
*
|
||||
* @author Hauke Petersen <hauke.petersen@fu-berlin.de>
|
||||
*
|
||||
* @}
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "nimble_riot.h"
|
||||
#include "host/ble_hs.h"
|
||||
#include "host/ble_gap.h"
|
||||
#include "host/util/util.h"
|
||||
|
||||
#include "assert.h"
|
||||
#include "shell.h"
|
||||
#include "net/bluetil/ad.h"
|
||||
|
||||
#include "nimble_l2cap_test_conf.h"
|
||||
|
||||
#define ENABLE_DEBUG (1)
|
||||
#include "debug.h"
|
||||
|
||||
#define FLAG_UP (1u << 0)
|
||||
#define FLAG_SYNC (1u << 1)
|
||||
|
||||
/* synchronization state */
|
||||
static thread_t *_main;
|
||||
static volatile uint32_t _last_rx_seq;
|
||||
|
||||
/* BLE connection state */
|
||||
static uint16_t _handle = 0;
|
||||
static struct ble_l2cap_chan *_coc = NULL;
|
||||
|
||||
/* buffer allocation */
|
||||
static os_membuf_t _coc_mem[OS_MEMPOOL_SIZE(MBUFCNT, MBUFSIZE)];
|
||||
static struct os_mempool _coc_mempool;
|
||||
static struct os_mbuf_pool _coc_mbuf_pool;
|
||||
static uint32_t _rxbuf[APP_MTU / 4];
|
||||
static uint32_t _txbuf[APP_MTU / 4];
|
||||
|
||||
static void _on_data(struct ble_l2cap_event *event)
|
||||
{
|
||||
int res;
|
||||
(void)res;
|
||||
struct os_mbuf *rxd = event->receive.sdu_rx;
|
||||
assert(rxd != NULL);
|
||||
int rx_len = (int)OS_MBUF_PKTLEN(rxd);
|
||||
assert(rx_len <= (int)APP_MTU);
|
||||
|
||||
res = os_mbuf_copydata(rxd, 0, rx_len, _rxbuf);
|
||||
assert(res == 0);
|
||||
_last_rx_seq = _rxbuf[POS_SEQ];
|
||||
if (_rxbuf[POS_TYPE] == TYPE_INCTEST) {
|
||||
thread_flags_set(_main, FLAG_SYNC);
|
||||
}
|
||||
|
||||
/* free buffer */
|
||||
res = os_mbuf_free_chain(rxd);
|
||||
assert(res == 0);
|
||||
rxd = os_mbuf_get_pkthdr(&_coc_mbuf_pool, 0);
|
||||
assert(rxd != NULL);
|
||||
res = ble_l2cap_recv_ready(_coc, rxd);
|
||||
assert(res == 0);
|
||||
}
|
||||
|
||||
static int _on_l2cap_evt(struct ble_l2cap_event *event, void *arg)
|
||||
{
|
||||
(void)arg;
|
||||
|
||||
switch (event->type) {
|
||||
case BLE_L2CAP_EVENT_COC_CONNECTED:
|
||||
_coc = event->connect.chan;
|
||||
puts("# L2CAP: CONNECTED");
|
||||
thread_flags_set(_main, FLAG_UP);
|
||||
break;
|
||||
case BLE_L2CAP_EVENT_COC_DISCONNECTED:
|
||||
_coc = NULL;
|
||||
puts("# L2CAP: DISCONNECTED");
|
||||
break;
|
||||
case BLE_L2CAP_EVENT_COC_DATA_RECEIVED:
|
||||
_on_data(event);
|
||||
break;
|
||||
case BLE_L2CAP_EVENT_COC_ACCEPT:
|
||||
/* this event should never be triggered for the L2CAP client */
|
||||
/* fallthrough */
|
||||
default:
|
||||
assert(0);
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _on_gap_evt(struct ble_gap_event *event, void *arg)
|
||||
{
|
||||
(void)arg;
|
||||
printf("# GAP event: %i\n", (int)event->type);
|
||||
|
||||
switch (event->type) {
|
||||
case BLE_GAP_EVENT_CONNECT: {
|
||||
_handle = event->connect.conn_handle;
|
||||
struct os_mbuf *sdu_rx = os_mbuf_get_pkthdr(&_coc_mbuf_pool, 0);
|
||||
assert(sdu_rx != NULL);
|
||||
int res = ble_l2cap_connect(_handle, APP_CID, APP_MTU, sdu_rx,
|
||||
_on_l2cap_evt, NULL);
|
||||
assert(res == 0);
|
||||
break;
|
||||
}
|
||||
case BLE_GAP_EVENT_DISCONNECT:
|
||||
_handle = 0;
|
||||
puts("# TERMINATED, bye bye");
|
||||
break;
|
||||
case BLE_GAP_EVENT_CONN_UPDATE:
|
||||
case BLE_GAP_EVENT_CONN_UPDATE_REQ:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void _filter_and_connect(struct ble_gap_disc_desc *disc)
|
||||
{
|
||||
int res;
|
||||
bluetil_ad_t ad;
|
||||
|
||||
bluetil_ad_init(&ad, disc->data,
|
||||
(size_t)disc->length_data, (size_t)disc->length_data);
|
||||
res = bluetil_ad_find_and_cmp(&ad, BLE_GAP_AD_NAME,
|
||||
APP_NODENAME, (sizeof(APP_NODENAME) - 1));
|
||||
if (res) {
|
||||
res = ble_gap_disc_cancel();
|
||||
assert(res == 0);
|
||||
|
||||
printf("# Found Server, connecting now");
|
||||
res = ble_gap_connect(nimble_riot_own_addr_type, &disc->addr,
|
||||
BLE_HS_FOREVER, NULL, _on_gap_evt, NULL);
|
||||
assert(res == 0);
|
||||
}
|
||||
}
|
||||
|
||||
static int _on_scan_evt(struct ble_gap_event *event, void *arg)
|
||||
{
|
||||
(void)arg;
|
||||
|
||||
switch (event->type) {
|
||||
case BLE_GAP_EVENT_DISC:
|
||||
_filter_and_connect(&event->disc);
|
||||
break;
|
||||
case BLE_GAP_EVENT_DISC_COMPLETE:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void _send(uint32_t type, uint32_t seq, size_t len)
|
||||
{
|
||||
int res = 0;
|
||||
(void)res;
|
||||
struct os_mbuf *txd;
|
||||
|
||||
assert(_coc);
|
||||
assert(len >= 8);
|
||||
assert(len <= APP_MTU);
|
||||
printf("# Sending: size %5u seq %5u\n", (unsigned)len, (unsigned)seq);
|
||||
|
||||
_txbuf[POS_TYPE] = type;
|
||||
_txbuf[POS_SEQ] = seq;
|
||||
txd = os_mbuf_get_pkthdr(&_coc_mbuf_pool, 0);
|
||||
assert(txd != NULL);
|
||||
res = os_mbuf_append(txd, _txbuf, len);
|
||||
assert(res == 0);
|
||||
|
||||
do {
|
||||
res = ble_l2cap_send(_coc, txd);
|
||||
} while (res == BLE_HS_EBUSY);
|
||||
|
||||
if (res != 0) {
|
||||
printf("# err: failed to send (%i)\n", res);
|
||||
assert(0);
|
||||
}
|
||||
}
|
||||
|
||||
static int _cmd_inctest(int argc, char **argv)
|
||||
{
|
||||
size_t step = 10;
|
||||
size_t limit = APP_MTU;
|
||||
|
||||
if ((argc == 2) && (strncmp(argv[1], "help", 4) == 0)) {
|
||||
printf("usage: %s [step [limit]]\n", argv[0]);
|
||||
return 0;
|
||||
}
|
||||
if (_coc == NULL) {
|
||||
puts("err: not connected");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* parse params */
|
||||
if (argc >= 2) {
|
||||
step = (size_t)atoi(argv[1]);
|
||||
}
|
||||
if (argc >= 3) {
|
||||
limit = (size_t)atoi(argv[2]);
|
||||
if ((limit < 8) || (limit > APP_MTU)) {
|
||||
puts("err: invalid limit payload length given");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* send out packets with increasing payload size */
|
||||
thread_flags_clear(FLAG_SYNC);
|
||||
uint32_t seq = 0;
|
||||
uint32_t time = xtimer_now_usec();
|
||||
for (size_t i = 8; i < limit; i += step) {
|
||||
_send(TYPE_INCTEST, ++seq, i);
|
||||
thread_flags_wait_all(FLAG_SYNC);
|
||||
if (_last_rx_seq != seq) {
|
||||
printf("# err: bad sequence number in response (%u)\n",
|
||||
(unsigned)_last_rx_seq);
|
||||
assert(0);
|
||||
}
|
||||
}
|
||||
time = (xtimer_now_usec() - time);
|
||||
puts("# TEST COMPLETE");
|
||||
printf("-> runtime: %ums\n", (unsigned)(time / 1000));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _cmd_floodtest(int argc, char **argv)
|
||||
{
|
||||
size_t pktsize = PKTSIZE_DEFAULT;
|
||||
unsigned limit = FLOOD_DEFAULT;
|
||||
|
||||
if ((argc == 2) && (strncmp(argv[1], "help", 4) == 0)) {
|
||||
printf("usage: %s [payload_size [num_of_packets]]\n", argv[0]);
|
||||
return 0;
|
||||
}
|
||||
if (_coc == NULL) {
|
||||
puts("err: not connected");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (argc >= 2) {
|
||||
pktsize = (size_t)atoi(argv[1]);
|
||||
if ((pktsize < 8) || (pktsize > APP_MTU)) {
|
||||
puts("err: invalid packet size given");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
if (argc >= 3) {
|
||||
limit = (unsigned)atoi(argv[2]);
|
||||
}
|
||||
|
||||
/* now lets flood */
|
||||
uint32_t seq = 0;
|
||||
uint32_t time = xtimer_now_usec();
|
||||
unsigned sum = 0;
|
||||
for (unsigned i = 0; i < limit; i++) {
|
||||
_send(TYPE_FLOODING, ++seq, pktsize);
|
||||
sum += pktsize;
|
||||
}
|
||||
/* we wait for the last packet to be returned to compensate any internal
|
||||
* packet buffering, but we also include this extra packet transmission in
|
||||
* the throughput calculation */
|
||||
sum += pktsize;
|
||||
while (_last_rx_seq != seq) {}
|
||||
time = (xtimer_now_usec() - time);
|
||||
puts("# TEST COMPLETE");
|
||||
printf("-> runtime: %ums\n", (unsigned)(time / 1000));
|
||||
printf("-> ~ %u bytes/s\n", (unsigned)((sum * 1000) / (time / 1000)));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const shell_command_t _cmds[] = {
|
||||
{ "inctest", "send stream of payloads of incremental size", _cmd_inctest },
|
||||
{ "flood", "flood connection with data", _cmd_floodtest },
|
||||
{ NULL, NULL, NULL }
|
||||
};
|
||||
|
||||
int main(void)
|
||||
{
|
||||
int res;
|
||||
(void)res;
|
||||
puts("NimBLE L2CAP test application");
|
||||
|
||||
/* save context of the main thread */
|
||||
_main = (thread_t *)thread_get(thread_getpid());
|
||||
|
||||
/* initialize buffers and setup the test environment */
|
||||
res = os_mempool_init(&_coc_mempool, MBUFCNT, MBUFSIZE, _coc_mem, "appbuf");
|
||||
assert(res == 0);
|
||||
res = os_mbuf_pool_init(&_coc_mbuf_pool, &_coc_mempool, MBUFSIZE, MBUFCNT);
|
||||
assert(res == 0);
|
||||
|
||||
/* start scanning for a suitable test server */
|
||||
puts("# Scanning now");
|
||||
struct ble_gap_disc_params params = { 0 };
|
||||
res = ble_gap_disc(nimble_riot_own_addr_type, BLE_HS_FOREVER,
|
||||
¶ms, _on_scan_evt, NULL);
|
||||
assert(res == 0);
|
||||
|
||||
/* wait until we are connected to the test server */
|
||||
thread_flags_wait_all(FLAG_UP);
|
||||
puts("# Connection established");
|
||||
printf("# MTUs: our %i, remote %i\n",
|
||||
ble_l2cap_get_our_mtu(_coc), ble_l2cap_get_peer_mtu(_coc));
|
||||
|
||||
/* start shell */
|
||||
puts("# Shell is now available");
|
||||
char line_buf[SHELL_DEFAULT_BUFSIZE];
|
||||
shell_run(_cmds, line_buf, SHELL_DEFAULT_BUFSIZE);
|
||||
|
||||
/* should be never reached */
|
||||
return 0;
|
||||
}
|
21
tests/nimble_l2cap_server/Makefile
Normal file
21
tests/nimble_l2cap_server/Makefile
Normal file
@ -0,0 +1,21 @@
|
||||
# Configure default and allowed boards
|
||||
BOARD ?= nrf52dk
|
||||
BOARD_WHITELIST := nrf52dk nrf52840dk
|
||||
|
||||
# load the default test environment
|
||||
include ../Makefile.tests_common
|
||||
|
||||
# RIOT modules used in for this test
|
||||
USEMODULE += xtimer
|
||||
USEMODULE += bluetil_ad
|
||||
|
||||
# Get the shared NimBLE and test configuration from the backend server
|
||||
include $(RIOTBASE)/tests/nimble_l2cap_server/nimble.inc.mk
|
||||
|
||||
# Comment this out to disable code in RIOT that does safety checking
|
||||
# which is not needed in a production environment but helps in the
|
||||
# development process:
|
||||
CFLAGS += -DLOG_LEVEL=LOG_ALL
|
||||
CFLAGS += -DDEBUG_ASSERT_VERBOSE
|
||||
|
||||
include $(RIOTBASE)/Makefile.include
|
69
tests/nimble_l2cap_server/README.md
Normal file
69
tests/nimble_l2cap_server/README.md
Normal file
@ -0,0 +1,69 @@
|
||||
# About
|
||||
|
||||
This application acts as a L2CAP echo server for testing and benchmarking raw
|
||||
L2CAP connection oriented channel (COC) connections using NimBLE on RIOT. This
|
||||
server application works in conjunction with the `nimble_l2cap` client test
|
||||
application in `tests/nimble_l2cap`.
|
||||
|
||||
Next to the actual L2CAP server implementation, this folder also holds some
|
||||
shared configuration for both the server and the client test applications.
|
||||
|
||||
|
||||
# Usage
|
||||
|
||||
To run the L2CAP test suite, you need to have two BLE (NimBLE) capable boards in
|
||||
radio range of each other available. Simple flash the server application on one
|
||||
board and the test application on the other one. The applications are build in a
|
||||
way, that they will automatically discover each other and open a dedicated
|
||||
L2CAP channel connection that is used for all subsequent test.
|
||||
|
||||
The server will print something like
|
||||
```
|
||||
2019-03-21 21:27:12,012 - INFO # main(): This is RIOT! (Version: XX)
|
||||
2019-03-21 21:27:12,012 - INFO # NimBLE L2CAP test server
|
||||
2019-03-21 21:27:12,013 - INFO # # now advertising
|
||||
[pausing here until the client is detected]
|
||||
2019-03-21 21:27:16,928 - INFO # # GAP event 0
|
||||
2019-03-21 21:27:16,981 - INFO # # L2CAP: CONNECTED
|
||||
2019-03-21 21:27:16,983 - INFO # # MTUs: our 250, remote 250
|
||||
```
|
||||
|
||||
The clients corresponding output will look similar to
|
||||
```
|
||||
2019-03-21 21:27:16,837 - INFO # main(): This is RIOT! (Version: XX)
|
||||
2019-03-21 21:27:16,839 - INFO # NimBLE L2CAP test application
|
||||
2019-03-21 21:27:16,840 - INFO # # Scanning now
|
||||
[pausing here until server is detected]
|
||||
2019-03-21 21:27:16,930 - INFO # # Found Server, connecting now# GAP event: 0
|
||||
2019-03-21 21:27:17,030 - INFO # # L2CAP: CONNECTED
|
||||
2019-03-21 21:27:17,035 - INFO # # Connection established, running test suite now
|
||||
2019-03-21 21:27:17,037 - INFO # # MTUs: our 250, remote 250
|
||||
2019-03-21 21:27:17,039 - INFO # # Shell is now available
|
||||
```
|
||||
|
||||
Once the connection between the client and server is established, you can use
|
||||
the shell commands provided by the client application to stress the opened
|
||||
L2CAP channel.
|
||||
|
||||
E.g. do something like:
|
||||
```
|
||||
flood 5000 10
|
||||
```
|
||||
to send 10 packets of 5000 bytes. The clients output should look like this:
|
||||
```
|
||||
flood 5000 10
|
||||
2019-03-21 21:30:57,151 - INFO # flood 5000 10
|
||||
2019-03-21 21:30:57,154 - INFO # # Sending: size 5000 seq 1
|
||||
2019-03-21 21:30:57,159 - INFO # # Sending: size 5000 seq 2
|
||||
2019-03-21 21:30:57,688 - INFO # # Sending: size 5000 seq 3
|
||||
2019-03-21 21:30:58,688 - INFO # # Sending: size 5000 seq 4
|
||||
2019-03-21 21:30:59,688 - INFO # # Sending: size 5000 seq 5
|
||||
2019-03-21 21:31:00,688 - INFO # # Sending: size 5000 seq 6
|
||||
2019-03-21 21:31:01,687 - INFO # # Sending: size 5000 seq 7
|
||||
2019-03-21 21:31:02,688 - INFO # # Sending: size 5000 seq 8
|
||||
2019-03-21 21:31:03,688 - INFO # # Sending: size 5000 seq 9
|
||||
2019-03-21 21:31:04,688 - INFO # # Sending: size 5000 seq 10
|
||||
2019-03-21 21:31:07,187 - INFO # # TEST COMPLETE
|
||||
2019-03-21 21:31:07,189 - INFO # -> runtime: 10082ms
|
||||
2019-03-21 21:31:07,190 - INFO # -> ~ 5455 bytes/s
|
||||
```
|
61
tests/nimble_l2cap_server/include/nimble_l2cap_test_conf.h
Normal file
61
tests/nimble_l2cap_server/include/nimble_l2cap_test_conf.h
Normal file
@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Freie Universität Berlin
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU Lesser
|
||||
* General Public License v2.1. See the file LICENSE in the top level
|
||||
* directory for more details.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ingroup tests
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief Shared configuration for NimBLE L2CAP tests
|
||||
*
|
||||
* @author Hauke Petersen <hauke.petersen@fu-berlin.de>
|
||||
*/
|
||||
|
||||
#ifndef NIMBLE_L2CAP_TEST_CONF_H
|
||||
#define NIMBLE_L2CAP_TEST_CONF_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @name Calculate values for the application specific mbuf pool
|
||||
* @{
|
||||
*/
|
||||
#define MBUFSIZE_OVHD (sizeof(struct os_mbuf) + \
|
||||
sizeof(struct os_mbuf_pkthdr))
|
||||
#define MBUFS_PER_MTU (APP_MTU / APP_BUF_CHUNKSIZE)
|
||||
#define MBUFSIZE (APP_BUF_CHUNKSIZE + MBUFSIZE_OVHD)
|
||||
#define MBUFCNT (APP_BUF_NUM * MBUFS_PER_MTU)
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @name Default parameters for selected test cases
|
||||
* @{
|
||||
*/
|
||||
#define PKTSIZE_DEFAULT (100U)
|
||||
#define FLOOD_DEFAULT (50U)
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @name Field offsets and type values for test packet payloads
|
||||
* @{
|
||||
*/
|
||||
#define TYPE_INCTEST (0x17192123)
|
||||
#define TYPE_FLOODING (0x73829384)
|
||||
|
||||
#define POS_TYPE (0U)
|
||||
#define POS_SEQ (1U)
|
||||
/** @} */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* NIMBLE_L2CAP_TEST_CONF_H */
|
||||
/** @} */
|
167
tests/nimble_l2cap_server/main.c
Normal file
167
tests/nimble_l2cap_server/main.c
Normal file
@ -0,0 +1,167 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Freie Universität Berlin
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU Lesser
|
||||
* General Public License v2.1. See the file LICENSE in the top level
|
||||
* directory for more details.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ingroup tests
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief Server to test and benchmark raw L2CAP COC for NimBLE
|
||||
*
|
||||
* @author Hauke Petersen <hauke.petersen@fu-berlin.de>
|
||||
*
|
||||
* @}
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "nimble_riot.h"
|
||||
#include "host/ble_hs.h"
|
||||
#include "host/ble_gap.h"
|
||||
#include "host/util/util.h"
|
||||
|
||||
#include "assert.h"
|
||||
#include "net/bluetil/ad.h"
|
||||
|
||||
#include "nimble_l2cap_test_conf.h"
|
||||
|
||||
/* BLE connection state */
|
||||
static uint16_t _handle = 0;
|
||||
static struct ble_l2cap_chan *_coc = NULL;
|
||||
|
||||
/* buffer allocation */
|
||||
static os_membuf_t _coc_mem[OS_MEMPOOL_SIZE(MBUFCNT, MBUFSIZE)];
|
||||
static struct os_mempool _coc_mempool;
|
||||
static struct os_mbuf_pool _coc_mbuf_pool;
|
||||
static uint32_t _rxbuf[APP_MTU / 4];
|
||||
|
||||
/* safe AD fields */
|
||||
static uint8_t _ad_buf[BLE_HS_ADV_MAX_SZ];
|
||||
static bluetil_ad_t _ad;
|
||||
static struct ble_gap_adv_params _adv_params = { 0 };
|
||||
|
||||
static void _advertise_now(void);
|
||||
|
||||
static void _on_data(struct ble_l2cap_event *event)
|
||||
{
|
||||
int res;
|
||||
struct os_mbuf *rxd;
|
||||
|
||||
rxd = event->receive.sdu_rx;
|
||||
assert(rxd != NULL);
|
||||
int rx_len = (int)OS_MBUF_PKTLEN(rxd);
|
||||
assert(rx_len <= (int)APP_MTU);
|
||||
|
||||
res = os_mbuf_copydata(rxd, 0, rx_len, _rxbuf);
|
||||
assert(res == 0);
|
||||
printf("# Received: len %5i, seq %5u\n", rx_len, (unsigned)_rxbuf[POS_SEQ]);
|
||||
|
||||
res = ble_l2cap_send(_coc, rxd);
|
||||
assert(res == 0);
|
||||
|
||||
/* allocate new mbuf for receiving new data */
|
||||
rxd = os_mbuf_get_pkthdr(&_coc_mbuf_pool, 0);
|
||||
assert(rxd != NULL);
|
||||
res = ble_l2cap_recv_ready(_coc, rxd);
|
||||
assert(res == 0);
|
||||
}
|
||||
|
||||
static int _on_gap_evt(struct ble_gap_event *event, void *arg)
|
||||
{
|
||||
(void)arg;
|
||||
printf("# GAP event %i\n", (int)event->type);
|
||||
|
||||
switch (event->type) {
|
||||
case BLE_GAP_EVENT_CONNECT:
|
||||
_handle = event->connect.conn_handle;
|
||||
break;
|
||||
case BLE_GAP_EVENT_DISCONNECT:
|
||||
_coc = NULL;
|
||||
_handle = 0;
|
||||
_advertise_now();
|
||||
break;
|
||||
case BLE_GAP_EVENT_CONN_UPDATE:
|
||||
case BLE_GAP_EVENT_CONN_UPDATE_REQ:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _on_l2cap_evt(struct ble_l2cap_event *event, void *arg)
|
||||
{
|
||||
(void)arg;
|
||||
|
||||
switch (event->type) {
|
||||
case BLE_L2CAP_EVENT_COC_CONNECTED:
|
||||
_coc = event->connect.chan;
|
||||
puts("# L2CAP: CONNECTED");
|
||||
printf("# MTUs: our %i, remote %i\n",
|
||||
ble_l2cap_get_our_mtu(_coc), ble_l2cap_get_peer_mtu(_coc));
|
||||
break;
|
||||
case BLE_L2CAP_EVENT_COC_DISCONNECTED:
|
||||
_coc = NULL;
|
||||
puts("# L2CAP: DISCONNECTED");
|
||||
break;
|
||||
case BLE_L2CAP_EVENT_COC_ACCEPT: {
|
||||
struct os_mbuf *sdu_rx = os_mbuf_get_pkthdr(&_coc_mbuf_pool, 0);
|
||||
assert(sdu_rx != NULL);
|
||||
ble_l2cap_recv_ready(event->accept.chan, sdu_rx);
|
||||
break;
|
||||
}
|
||||
case BLE_L2CAP_EVENT_COC_DATA_RECEIVED:
|
||||
_on_data(event);
|
||||
break;
|
||||
default:
|
||||
assert(0);
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void _advertise_now(void)
|
||||
{
|
||||
int res = ble_gap_adv_start(nimble_riot_own_addr_type, NULL, BLE_HS_FOREVER,
|
||||
&_adv_params, _on_gap_evt, NULL);
|
||||
assert(res == 0);
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
int res;
|
||||
(void)res;
|
||||
puts("NimBLE L2CAP test server");
|
||||
|
||||
/* initialize buffers and setup the test environment */
|
||||
res = os_mempool_init(&_coc_mempool, MBUFCNT, MBUFSIZE, _coc_mem, "appbuf");
|
||||
assert(res == 0);
|
||||
res = os_mbuf_pool_init(&_coc_mbuf_pool, &_coc_mempool, MBUFSIZE, MBUFCNT);
|
||||
assert(res == 0);
|
||||
|
||||
/* create l2cap server */
|
||||
res = ble_l2cap_create_server(APP_CID, APP_MTU, _on_l2cap_evt, NULL);
|
||||
assert(res == 0);
|
||||
|
||||
/* initialize advertising data and parameters */
|
||||
_adv_params.conn_mode = BLE_GAP_CONN_MODE_UND;
|
||||
_adv_params.disc_mode = BLE_GAP_DISC_MODE_GEN;
|
||||
bluetil_ad_init_with_flags(&_ad, _ad_buf, sizeof(_ad_buf),
|
||||
BLUETIL_AD_FLAGS_DEFAULT);
|
||||
bluetil_ad_add_name(&_ad, APP_NODENAME);
|
||||
res = ble_gap_adv_set_data(_ad.buf, (int)_ad.pos);
|
||||
assert(res == 0);
|
||||
|
||||
/* start advertising the test server */
|
||||
_advertise_now();
|
||||
puts("# now advertising");
|
||||
|
||||
/* nothing else to be done in the main thread */
|
||||
return 0;
|
||||
}
|
22
tests/nimble_l2cap_server/nimble.inc.mk
Normal file
22
tests/nimble_l2cap_server/nimble.inc.mk
Normal file
@ -0,0 +1,22 @@
|
||||
# Set the tests default configuration
|
||||
APP_MTU ?= 5000
|
||||
APP_BUF_CHUNKSIZE ?= 250 # must be full divider of APP_MTU
|
||||
APP_BUF_NUM ?= 3
|
||||
APP_NODENAME ?= \"nimble_l2cap_test_server\"
|
||||
APP_CID ?= 0x0235
|
||||
|
||||
# Apply configuration values
|
||||
CFLAGS += -DAPP_MTU=$(APP_MTU)
|
||||
CFLAGS += -DAPP_BUF_CHUNKSIZE=$(APP_BUF_CHUNKSIZE)
|
||||
CFLAGS += -DAPP_BUF_NUM=$(APP_BUF_NUM)
|
||||
CFLAGS += -DAPP_NODENAME=$(APP_NODENAME)
|
||||
CFLAGS += -DAPP_CID=$(APP_CID)
|
||||
|
||||
# configure NimBLE
|
||||
USEPKG += nimble
|
||||
CFLAGS += -DMYNEWT_VAL_BLE_L2CAP_COC_MAX_NUM=1
|
||||
CFLAGS += -DMYNEWT_VAL_BLE_L2CAP_COC_MTU=250
|
||||
CFLAGS += -DMYNEWT_VAL_BLE_MAX_CONNECTIONS=1
|
||||
CFLAGS += -DMYNEWT_VAL_MSYS_1_BLOCK_COUNT=55
|
||||
|
||||
INCLUDES += -I$(RIOTBASE)/tests/nimble_l2cap_server/include
|
Loading…
Reference in New Issue
Block a user