mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2024-12-29 04:50:03 +01:00
879 lines
26 KiB
C
879 lines
26 KiB
C
/*
|
|
* Copyright (C) 2018-2021 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 pkg_nimble_netif
|
|
* @{
|
|
*
|
|
* @file
|
|
* @brief GNRC netif wrapper for NimBLE
|
|
*
|
|
* @author Hauke Petersen <hauke.petersen@fu-berlin.de>
|
|
*
|
|
* @}
|
|
*/
|
|
|
|
#include <limits.h>
|
|
#include <errno.h>
|
|
|
|
#include "assert.h"
|
|
#include "thread.h"
|
|
#include "thread_flags.h"
|
|
|
|
#include "net/ble.h"
|
|
#include "net/bluetil/addr.h"
|
|
#include "net/gnrc/netif.h"
|
|
#include "net/gnrc/netif/hdr.h"
|
|
#include "net/gnrc/netreg.h"
|
|
#include "net/gnrc/pktbuf.h"
|
|
#include "net/gnrc/nettype.h"
|
|
|
|
#include "nimble_netif.h"
|
|
#include "nimble_netif_conn.h"
|
|
#include "nimble_riot.h"
|
|
#include "host/ble_gap.h"
|
|
#include "host/util/util.h"
|
|
#include "mem/mem.h"
|
|
|
|
#define ENABLE_DEBUG 0
|
|
#include "debug.h"
|
|
|
|
#ifdef MODULE_GNRC_SIXLOWPAN
|
|
#define NETTYPE GNRC_NETTYPE_SIXLOWPAN
|
|
#elif defined(MODULE_GNRC_IPV6)
|
|
#define NETTYPE GNRC_NETTYPE_IPV6
|
|
#else
|
|
#define NETTYPE GNRC_NETTYPE_UNDEF
|
|
#endif
|
|
|
|
#ifndef NIMBLE_NETIF_PRIO
|
|
#define NIMBLE_NETIF_PRIO GNRC_NETIF_PRIO
|
|
#endif
|
|
|
|
#ifndef CONFIG_NIMBLE_NETIF_ADV_INSTANCE
|
|
#define CONFIG_NIMBLE_NETIF_ADV_INSTANCE 0
|
|
#endif
|
|
|
|
/* thread flag used for signaling transmit readiness */
|
|
#define FLAG_TX_UNSTALLED (1u << 13)
|
|
#define FLAG_TX_NOTCONN (1u << 12)
|
|
#define FLAG_ALL (FLAG_TX_UNSTALLED | FLAG_TX_NOTCONN)
|
|
|
|
/* allocate a stack for the netif device */
|
|
static char _stack[THREAD_STACKSIZE_DEFAULT];
|
|
static thread_t *_netif_thread;
|
|
|
|
/* keep the actual device state */
|
|
static gnrc_netif_t _netif;
|
|
static gnrc_nettype_t _nettype = NETTYPE;
|
|
|
|
/* keep a reference to the event callback */
|
|
static nimble_netif_eventcb_t _eventcb;
|
|
|
|
/* notify the user about state changes for a connection context */
|
|
static void _notify(int handle, nimble_netif_event_t event, uint8_t *addr)
|
|
{
|
|
if (_eventcb) {
|
|
_eventcb(handle, event, addr);
|
|
}
|
|
}
|
|
|
|
static int _netif_init(gnrc_netif_t *netif)
|
|
{
|
|
(void)netif;
|
|
|
|
int res = gnrc_netif_default_init(netif);
|
|
if (res < 0) {
|
|
return res;
|
|
}
|
|
|
|
/* save the threads context pointer, so we can set its flags */
|
|
_netif_thread = thread_get_active();
|
|
|
|
#if IS_USED(MODULE_GNRC_NETIF_6LO)
|
|
/* we disable fragmentation for this device, as the L2CAP layer takes care
|
|
* of this */
|
|
_netif.sixlo.max_frag_size = 0;
|
|
#endif /* IS_USED(MODULE_GNRC_NETIF_6LO) */
|
|
res = 0;
|
|
|
|
return res;
|
|
}
|
|
|
|
static int _send_pkt(nimble_netif_conn_t *conn, gnrc_pktsnip_t *pkt)
|
|
{
|
|
int res;
|
|
int num_bytes = 0;
|
|
|
|
if (conn == NULL || conn->coc == NULL) {
|
|
return -ENOTCONN;
|
|
}
|
|
|
|
/* copy the data into a newly allocated mbuf */
|
|
struct os_mbuf *sdu = os_msys_get_pkthdr(gnrc_pkt_len(pkt), 0);
|
|
if (sdu == NULL) {
|
|
return -ENOBUFS;
|
|
}
|
|
while (pkt) {
|
|
res = os_mbuf_append(sdu, pkt->data, pkt->size);
|
|
if (res != 0) {
|
|
os_mbuf_free_chain(sdu);
|
|
return -ENOBUFS;
|
|
}
|
|
num_bytes += (int)pkt->size;
|
|
pkt = pkt->next;
|
|
}
|
|
|
|
/* send packet via the given L2CAP COC */
|
|
do {
|
|
res = ble_l2cap_send(conn->coc, sdu);
|
|
if (res == BLE_HS_EBUSY) {
|
|
thread_flags_t state = thread_flags_wait_any(FLAG_ALL);
|
|
/* abort if the active connection was lost in the mean time */
|
|
if ((state & FLAG_TX_NOTCONN) && !nimble_netif_conn_is_open(conn)) {
|
|
return -ECONNRESET;
|
|
}
|
|
}
|
|
} while (res == BLE_HS_EBUSY);
|
|
|
|
if ((res != 0) && (res != BLE_HS_ESTALLED)) {
|
|
os_mbuf_free_chain(sdu);
|
|
return -ECANCELED;
|
|
}
|
|
|
|
return num_bytes;
|
|
}
|
|
|
|
static int _netif_send(gnrc_netif_t *netif, gnrc_pktsnip_t *pkt)
|
|
{
|
|
assert(pkt->type == GNRC_NETTYPE_NETIF);
|
|
|
|
(void)netif;
|
|
int res;
|
|
|
|
gnrc_netif_hdr_t *hdr = (gnrc_netif_hdr_t *)pkt->data;
|
|
/* if packet is bcast or mcast, we send it to every connected node */
|
|
if (hdr->flags &
|
|
(GNRC_NETIF_HDR_FLAGS_BROADCAST | GNRC_NETIF_HDR_FLAGS_MULTICAST)) {
|
|
int handle = nimble_netif_conn_get_next(NIMBLE_NETIF_CONN_INVALID,
|
|
NIMBLE_NETIF_L2CAP_CONNECTED);
|
|
while (handle != NIMBLE_NETIF_CONN_INVALID) {
|
|
res = _send_pkt(nimble_netif_conn_get(handle), pkt->next);
|
|
handle = nimble_netif_conn_get_next(handle, NIMBLE_NETIF_L2CAP_CONNECTED);
|
|
}
|
|
}
|
|
/* send unicast */
|
|
else {
|
|
int handle = nimble_netif_conn_get_by_addr(
|
|
gnrc_netif_hdr_get_dst_addr(hdr));
|
|
nimble_netif_conn_t *conn = nimble_netif_conn_get(handle);
|
|
res = _send_pkt(conn, pkt->next);
|
|
}
|
|
|
|
/* release the packet in GNRC's packet buffer */
|
|
gnrc_pktbuf_release(pkt);
|
|
return res;
|
|
}
|
|
|
|
/* not used, we pass incoming data to GNRC directly from the NimBLE thread */
|
|
static gnrc_pktsnip_t *_netif_recv(gnrc_netif_t *netif)
|
|
{
|
|
(void)netif;
|
|
return NULL;
|
|
}
|
|
|
|
static const gnrc_netif_ops_t _nimble_netif_ops = {
|
|
.init = _netif_init,
|
|
.send = _netif_send,
|
|
.recv = _netif_recv,
|
|
.get = gnrc_netif_get_from_netdev,
|
|
.set = gnrc_netif_set_from_netdev,
|
|
.msg_handler = NULL,
|
|
};
|
|
|
|
static inline int _netdev_init(netdev_t *dev)
|
|
{
|
|
(void)dev;
|
|
|
|
/* get our own address from the controller */
|
|
uint8_t tmp[6];
|
|
int res = ble_hs_id_copy_addr(nimble_riot_own_addr_type, tmp, NULL);
|
|
assert(res == 0);
|
|
(void)res;
|
|
|
|
bluetil_addr_swapped_cp(tmp, _netif.l2addr);
|
|
|
|
/* signal link UP */
|
|
dev->event_callback(dev, NETDEV_EVENT_LINK_UP);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static inline int _netdev_get(netdev_t *dev, netopt_t opt,
|
|
void *value, size_t max_len)
|
|
{
|
|
(void)dev;
|
|
int res = -ENOTSUP;
|
|
|
|
switch (opt) {
|
|
case NETOPT_ADDRESS:
|
|
assert(max_len >= BLE_ADDR_LEN);
|
|
memcpy(value, _netif.l2addr, BLE_ADDR_LEN);
|
|
res = BLE_ADDR_LEN;
|
|
break;
|
|
case NETOPT_ADDR_LEN:
|
|
case NETOPT_SRC_LEN:
|
|
assert(max_len == sizeof(uint16_t));
|
|
*((uint16_t *)value) = BLE_ADDR_LEN;
|
|
res = sizeof(uint16_t);
|
|
break;
|
|
case NETOPT_MAX_PDU_SIZE:
|
|
assert(max_len >= sizeof(uint16_t));
|
|
*((uint16_t *)value) = NIMBLE_NETIF_MTU;
|
|
res = sizeof(uint16_t);
|
|
break;
|
|
case NETOPT_PROTO:
|
|
assert(max_len == sizeof(gnrc_nettype_t));
|
|
*((gnrc_nettype_t *)value) = _nettype;
|
|
res = sizeof(gnrc_nettype_t);
|
|
break;
|
|
case NETOPT_DEVICE_TYPE:
|
|
assert(max_len == sizeof(uint16_t));
|
|
*((uint16_t *)value) = NETDEV_TYPE_BLE;
|
|
res = sizeof(uint16_t);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
static inline int _netdev_set(netdev_t *dev, netopt_t opt,
|
|
const void *value, size_t val_len)
|
|
{
|
|
(void)dev;
|
|
int res = -ENOTSUP;
|
|
|
|
switch (opt) {
|
|
case NETOPT_PROTO:
|
|
assert(val_len == sizeof(_nettype));
|
|
memcpy(&_nettype, value, sizeof(_nettype));
|
|
res = sizeof(_nettype);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
static const netdev_driver_t _nimble_netdev_driver = {
|
|
.send = NULL,
|
|
.recv = NULL,
|
|
.init = _netdev_init,
|
|
.isr = NULL,
|
|
.get = _netdev_get,
|
|
.set = _netdev_set,
|
|
};
|
|
|
|
static netdev_t _nimble_netdev_dummy = {
|
|
.driver = &_nimble_netdev_driver,
|
|
};
|
|
|
|
static void _on_data(nimble_netif_conn_t *conn, struct ble_l2cap_event *event)
|
|
{
|
|
struct os_mbuf *rxb = event->receive.sdu_rx;
|
|
size_t rx_len = (size_t)OS_MBUF_PKTLEN(rxb);
|
|
|
|
/* allocate netif header */
|
|
gnrc_pktsnip_t *if_snip = gnrc_netif_hdr_build(conn->addr, BLE_ADDR_LEN,
|
|
_netif.l2addr,
|
|
BLE_ADDR_LEN);
|
|
if (if_snip == NULL) {
|
|
goto end;
|
|
}
|
|
|
|
/* we need to add the device PID to the netif header */
|
|
gnrc_netif_hdr_set_netif(if_snip->data, &_netif);
|
|
|
|
/* allocate space in the pktbuf to store the packet */
|
|
gnrc_pktsnip_t *payload = gnrc_pktbuf_add(if_snip, NULL, rx_len, _nettype);
|
|
if (payload == NULL) {
|
|
gnrc_pktbuf_release(if_snip);
|
|
goto end;
|
|
}
|
|
|
|
/* copy payload from mbuf into pktbuffer */
|
|
int res = os_mbuf_copydata(rxb, 0, rx_len, payload->data);
|
|
if (res != 0) {
|
|
gnrc_pktbuf_release(payload);
|
|
goto end;
|
|
}
|
|
|
|
/* finally dispatch the receive packet to GNRC */
|
|
if (!gnrc_netapi_dispatch_receive(payload->type, GNRC_NETREG_DEMUX_CTX_ALL,
|
|
payload)) {
|
|
gnrc_pktbuf_release(payload);
|
|
}
|
|
|
|
end:
|
|
/* free the mbuf and allocate a new one for receiving new data */
|
|
os_mbuf_free_chain(rxb);
|
|
rxb = os_msys_get_pkthdr(MYNEWT_VAL_BLE_L2CAP_COC_MPS, 0);
|
|
/* due to buffer provisioning, there should always be enough space */
|
|
assert(rxb != NULL);
|
|
ble_l2cap_recv_ready(event->receive.chan, rxb);
|
|
}
|
|
|
|
static int _on_l2cap_client_evt(struct ble_l2cap_event *event, void *arg)
|
|
{
|
|
int handle = (int)arg;
|
|
nimble_netif_conn_t *conn = nimble_netif_conn_get(handle);
|
|
assert(conn && (conn->state & NIMBLE_NETIF_GAP_MASTER));
|
|
|
|
switch (event->type) {
|
|
case BLE_L2CAP_EVENT_COC_CONNECTED:
|
|
if (event->connect.status != 0) {
|
|
/* in the unlikely event the L2CAP connection establishment
|
|
* fails, we close the GAP connection */
|
|
ble_gap_terminate(conn->gaphandle, BLE_ERR_REM_USER_CONN_TERM);
|
|
break;
|
|
}
|
|
conn->coc = event->connect.chan;
|
|
conn->state |= NIMBLE_NETIF_L2CAP_CLIENT;
|
|
conn->state &= ~NIMBLE_NETIF_CONNECTING;
|
|
_notify(handle, NIMBLE_NETIF_CONNECTED_MASTER, conn->addr);
|
|
break;
|
|
case BLE_L2CAP_EVENT_COC_DISCONNECTED:
|
|
assert(conn->state & NIMBLE_NETIF_L2CAP_CLIENT);
|
|
conn->state &= ~NIMBLE_NETIF_L2CAP_CONNECTED;
|
|
break;
|
|
case BLE_L2CAP_EVENT_COC_ACCEPT:
|
|
/* this event should never be triggered for the L2CAP client */
|
|
assert(0);
|
|
break;
|
|
case BLE_L2CAP_EVENT_COC_DATA_RECEIVED:
|
|
_on_data(conn, event);
|
|
break;
|
|
case BLE_L2CAP_EVENT_COC_TX_UNSTALLED:
|
|
thread_flags_set(_netif_thread, FLAG_TX_UNSTALLED);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int _on_l2cap_server_evt(struct ble_l2cap_event *event, void *arg)
|
|
{
|
|
(void)arg;
|
|
int handle;
|
|
nimble_netif_conn_t *conn;
|
|
|
|
switch (event->type) {
|
|
case BLE_L2CAP_EVENT_COC_CONNECTED:
|
|
handle = nimble_netif_conn_get_by_gaphandle(event->connect.conn_handle);
|
|
conn = nimble_netif_conn_get(handle);
|
|
assert(conn);
|
|
|
|
/* in the unlikely event the L2CAP connection establishment fails,
|
|
* we close the GAP connection */
|
|
if (event->connect.status != 0) {
|
|
ble_gap_terminate(conn->gaphandle, BLE_ERR_REM_USER_CONN_TERM);
|
|
break;
|
|
}
|
|
|
|
/* we need to update the state to keep everything in sync */
|
|
conn->coc = event->connect.chan;
|
|
conn->state |= NIMBLE_NETIF_L2CAP_SERVER;
|
|
conn->state &= ~(NIMBLE_NETIF_ADV | NIMBLE_NETIF_CONNECTING);
|
|
|
|
/* in case conn itvl spacing is enabled, make sure that the conn
|
|
* itvl of the new connection is sufficiently spaced */
|
|
if ((NIMBLE_NETIF_CONN_ITVL_SPACING > 0) &&
|
|
nimble_netif_conn_itvl_used(conn->itvl, handle)) {
|
|
ble_gap_terminate(conn->gaphandle, BLE_ERR_REM_USER_CONN_TERM);
|
|
break;
|
|
}
|
|
|
|
_notify(handle, NIMBLE_NETIF_CONNECTED_SLAVE, conn->addr);
|
|
break;
|
|
case BLE_L2CAP_EVENT_COC_DISCONNECTED:
|
|
conn = nimble_netif_conn_from_gaphandle(event->disconnect.conn_handle);
|
|
assert(conn && (conn->state & NIMBLE_NETIF_L2CAP_SERVER));
|
|
conn->coc = NULL;
|
|
conn->state &= ~NIMBLE_NETIF_L2CAP_CONNECTED;
|
|
break;
|
|
case BLE_L2CAP_EVENT_COC_ACCEPT: {
|
|
struct os_mbuf *sdu_rx = os_msys_get_pkthdr(
|
|
MYNEWT_VAL_BLE_L2CAP_COC_MPS, 0);
|
|
/* there should always be enough buffer space */
|
|
assert(sdu_rx != NULL);
|
|
ble_l2cap_recv_ready(event->accept.chan, sdu_rx);
|
|
break;
|
|
}
|
|
case BLE_L2CAP_EVENT_COC_DATA_RECEIVED:
|
|
conn = nimble_netif_conn_from_gaphandle(event->receive.conn_handle);
|
|
assert(conn);
|
|
_on_data(conn, event);
|
|
break;
|
|
case BLE_L2CAP_EVENT_COC_TX_UNSTALLED:
|
|
thread_flags_set(_netif_thread, FLAG_TX_UNSTALLED);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void _on_gap_connected(nimble_netif_conn_t *conn, uint16_t conn_handle)
|
|
{
|
|
struct ble_gap_conn_desc desc;
|
|
int res = ble_gap_conn_find(conn_handle, &desc);
|
|
assert(res == 0);
|
|
(void)res;
|
|
|
|
conn->gaphandle = conn_handle;
|
|
conn->itvl = desc.conn_itvl;
|
|
bluetil_addr_swapped_cp(desc.peer_id_addr.val, conn->addr);
|
|
}
|
|
|
|
static void _on_gap_param_update(int handle, nimble_netif_conn_t *conn)
|
|
{
|
|
struct ble_gap_conn_desc desc;
|
|
int res = ble_gap_conn_find(conn->gaphandle, &desc);
|
|
assert(res == 0) ;
|
|
(void)res;
|
|
|
|
conn->itvl = desc.conn_itvl;
|
|
_notify(handle, NIMBLE_NETIF_CONN_UPDATED, conn->addr);
|
|
}
|
|
|
|
static int _on_gap_master_evt(struct ble_gap_event *event, void *arg)
|
|
{
|
|
int res = 0;
|
|
int handle = (int)arg;
|
|
nimble_netif_conn_t *conn = nimble_netif_conn_get(handle);
|
|
assert(conn);
|
|
|
|
switch (event->type) {
|
|
case BLE_GAP_EVENT_CONNECT: {
|
|
if (event->connect.status != 0) {
|
|
uint8_t addr[BLE_ADDR_LEN];
|
|
nimble_netif_conn_free(handle, addr);
|
|
_notify(handle, NIMBLE_NETIF_ABORT_MASTER, addr);
|
|
return 0;
|
|
}
|
|
_on_gap_connected(conn, event->connect.conn_handle);
|
|
conn->state |= NIMBLE_NETIF_GAP_MASTER;
|
|
|
|
struct os_mbuf *sdu_rx = os_msys_get_pkthdr(
|
|
MYNEWT_VAL_BLE_L2CAP_COC_MPS, 0);
|
|
/* in the rare case that we run out of buffers, we close the new
|
|
* connection right away */
|
|
if (sdu_rx == NULL) {
|
|
ble_gap_terminate(handle, BLE_ERR_REM_USER_CONN_TERM);
|
|
break;
|
|
}
|
|
res = ble_l2cap_connect(event->connect.conn_handle,
|
|
NIMBLE_NETIF_CID, NIMBLE_NETIF_MTU, sdu_rx,
|
|
_on_l2cap_client_evt, (void *)handle);
|
|
/* should always be successful */
|
|
assert(res == 0);
|
|
break;
|
|
}
|
|
case BLE_GAP_EVENT_DISCONNECT: {
|
|
nimble_netif_event_t type;
|
|
type = (conn->coc != NULL) ? NIMBLE_NETIF_CLOSED_MASTER
|
|
: NIMBLE_NETIF_ABORT_MASTER;
|
|
uint8_t addr[BLE_ADDR_LEN];
|
|
nimble_netif_conn_free(handle, addr);
|
|
thread_flags_set(_netif_thread, FLAG_TX_NOTCONN);
|
|
_notify(handle, type, addr);
|
|
break;
|
|
}
|
|
case BLE_GAP_EVENT_CONN_UPDATE:
|
|
_on_gap_param_update(handle, conn);
|
|
break;
|
|
case BLE_GAP_EVENT_CONN_UPDATE_REQ:
|
|
case BLE_GAP_EVENT_MTU:
|
|
/* nothing to do here */
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
static int _on_gap_slave_evt(struct ble_gap_event *event, void *arg)
|
|
{
|
|
int handle = (int)arg;
|
|
nimble_netif_conn_t *conn = nimble_netif_conn_get(handle);
|
|
assert(conn);
|
|
|
|
switch (event->type) {
|
|
case BLE_GAP_EVENT_CONNECT: {
|
|
if (event->connect.status != 0) {
|
|
uint8_t addr[BLE_ADDR_LEN];
|
|
nimble_netif_conn_free(handle, addr);
|
|
_notify(handle, NIMBLE_NETIF_ABORT_SLAVE, addr);
|
|
break;
|
|
}
|
|
_on_gap_connected(conn, event->connect.conn_handle);
|
|
assert(conn->state == NIMBLE_NETIF_ADV);
|
|
conn->state = NIMBLE_NETIF_GAP_SLAVE;
|
|
_notify(handle, NIMBLE_NETIF_INIT_SLAVE, conn->addr);
|
|
break;
|
|
}
|
|
case BLE_GAP_EVENT_DISCONNECT: {
|
|
nimble_netif_event_t type;
|
|
type = (conn->coc != NULL) ? NIMBLE_NETIF_CLOSED_SLAVE
|
|
: NIMBLE_NETIF_ABORT_SLAVE;
|
|
uint8_t addr[BLE_ADDR_LEN];
|
|
nimble_netif_conn_free(handle, addr);
|
|
thread_flags_set(_netif_thread, FLAG_TX_NOTCONN);
|
|
_notify(handle, type, addr);
|
|
break;
|
|
}
|
|
case BLE_GAP_EVENT_CONN_UPDATE:
|
|
_on_gap_param_update(handle, conn);
|
|
break;
|
|
case BLE_GAP_EVENT_CONN_UPDATE_REQ:
|
|
/* nothing to do here */
|
|
break;
|
|
case BLE_GAP_EVENT_ADV_COMPLETE: {
|
|
if (conn->state == NIMBLE_NETIF_ADV) {
|
|
uint8_t addr[BLE_ADDR_LEN];
|
|
nimble_netif_conn_free(handle, addr);
|
|
_notify(handle, NIMBLE_NETIF_ACCEPT_STOP, addr);
|
|
}
|
|
}
|
|
case BLE_GAP_EVENT_PHY_UPDATE_COMPLETE:
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void nimble_netif_init(void)
|
|
{
|
|
int res;
|
|
(void)res;
|
|
|
|
/* setup the connection context table */
|
|
nimble_netif_conn_init();
|
|
|
|
res = ble_l2cap_create_server(NIMBLE_NETIF_CID, NIMBLE_NETIF_MTU,
|
|
_on_l2cap_server_evt, NULL);
|
|
assert(res == 0);
|
|
(void)res;
|
|
|
|
gnrc_netif_create(&_netif, _stack, sizeof(_stack), NIMBLE_NETIF_PRIO,
|
|
"nimble_netif", &_nimble_netdev_dummy, &_nimble_netif_ops);
|
|
}
|
|
|
|
void nimble_netif_eventcb(nimble_netif_eventcb_t cb)
|
|
{
|
|
_eventcb = cb;
|
|
}
|
|
|
|
int nimble_netif_connect(const ble_addr_t *addr,
|
|
const nimble_netif_connect_cfg_t *params)
|
|
{
|
|
assert(addr);
|
|
assert(params);
|
|
|
|
/* the netif_conn module expects addresses in network byte order */
|
|
uint8_t addrn[BLE_ADDR_LEN];
|
|
bluetil_addr_swapped_cp(addr->val, addrn);
|
|
|
|
/* check that there is no open connection with the given address */
|
|
if (nimble_netif_conn_connected(addrn) ||
|
|
nimble_netif_conn_connecting()) {
|
|
return -EBUSY;
|
|
}
|
|
|
|
/* get empty connection context */
|
|
int handle = nimble_netif_conn_start_connection(addrn);
|
|
if (handle == NIMBLE_NETIF_CONN_INVALID) {
|
|
return -ENOMEM;
|
|
}
|
|
|
|
/* generate connection interval */
|
|
uint16_t itvl = params->conn_itvl_min_ms;
|
|
if (params->conn_itvl_min_ms < params->conn_itvl_max_ms) {
|
|
itvl = nimble_netif_conn_gen_itvl(params->conn_itvl_min_ms,
|
|
params->conn_itvl_max_ms);
|
|
}
|
|
if (itvl == 0) {
|
|
nimble_netif_conn_free(handle, NULL);
|
|
return -ECANCELED;
|
|
}
|
|
|
|
struct ble_gap_conn_params p = {
|
|
.scan_itvl = BLE_GAP_SCAN_ITVL_MS(params->scan_itvl_ms),
|
|
.scan_window = BLE_GAP_SCAN_WIN_MS(params->scan_window_ms),
|
|
.itvl_min = BLE_GAP_CONN_ITVL_MS(itvl),
|
|
.itvl_max = BLE_GAP_CONN_ITVL_MS(itvl),
|
|
.latency = params->conn_slave_latency,
|
|
.supervision_timeout = BLE_GAP_SUPERVISION_TIMEOUT_MS(
|
|
params->conn_supervision_timeout_ms),
|
|
.min_ce_len = 0,
|
|
.max_ce_len = 0,
|
|
};
|
|
|
|
#if MYNEWT_VAL_BLE_EXT_ADV
|
|
uint8_t phy_mask;
|
|
if (params->phy_mode == NIMBLE_PHY_1M) {
|
|
phy_mask = BLE_GAP_LE_PHY_1M_MASK;
|
|
}
|
|
#if IS_USED(MODULE_NIMBLE_PHY_2MBIT)
|
|
else if (params->phy_mode == NIMBLE_PHY_2M) {
|
|
phy_mask = (BLE_GAP_LE_PHY_1M_MASK | BLE_GAP_LE_PHY_2M_MASK);
|
|
}
|
|
#endif
|
|
#if IS_USED(MODULE_NIMBLE_PHY_CODED)
|
|
else if (params->phy_mode == NIMBLE_PHY_CODED) {
|
|
phy_mask = BLE_GAP_LE_PHY_CODED_MASK;
|
|
}
|
|
#endif
|
|
else {
|
|
return -EINVAL;
|
|
}
|
|
|
|
int res = ble_gap_ext_connect(params->own_addr_type, addr,
|
|
params->timeout_ms, phy_mask, &p, &p, &p,
|
|
_on_gap_master_evt, (void *)handle);
|
|
#else
|
|
uint32_t timeout = (params->timeout_ms == 0) ? BLE_HS_FOREVER
|
|
: params->timeout_ms;
|
|
int res = ble_gap_connect(params->own_addr_type, addr,
|
|
timeout, &p,
|
|
_on_gap_master_evt, (void *)handle);
|
|
#endif
|
|
if (res != 0) {
|
|
return -EIO;
|
|
}
|
|
|
|
_notify(handle, NIMBLE_NETIF_INIT_MASTER, addrn);
|
|
return handle;
|
|
}
|
|
|
|
int nimble_netif_close(int handle)
|
|
{
|
|
nimble_netif_conn_t *conn = nimble_netif_conn_get(handle);
|
|
if (conn == NULL) {
|
|
return -EINVAL;
|
|
}
|
|
else if (!(conn->state & NIMBLE_NETIF_L2CAP_CONNECTED)) {
|
|
return -ENOTCONN;
|
|
}
|
|
|
|
int res = ble_gap_terminate(conn->gaphandle, BLE_ERR_REM_USER_CONN_TERM);
|
|
if (res != 0) {
|
|
return -EIO;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int _accept(const uint8_t *ad, size_t ad_len, const ble_addr_t *addr,
|
|
const nimble_netif_accept_cfg_t *params)
|
|
{
|
|
int handle;
|
|
int res;
|
|
(void)res;
|
|
|
|
assert(params);
|
|
|
|
/* allocate a connection context for incoming connections */
|
|
handle = nimble_netif_conn_start_adv();
|
|
if (handle < 0) {
|
|
return handle;
|
|
}
|
|
|
|
/* remember address if applicable */
|
|
if (addr) {
|
|
nimble_netif_conn_t *conn = nimble_netif_conn_get(handle);
|
|
bluetil_addr_swapped_cp(addr->val, conn->addr);
|
|
}
|
|
|
|
#if MYNEWT_VAL_BLE_EXT_ADV
|
|
struct ble_gap_ext_adv_params p;
|
|
memset(&p, 0, sizeof(p));
|
|
|
|
/* figure out PHY modes */
|
|
int phy_pri = nimble_riot_get_phy_hci(params->primary_phy);
|
|
int phy_sec = nimble_riot_get_phy_hci(params->secondary_phy);
|
|
if ((phy_pri < 0) || (phy_sec < 0)) {
|
|
nimble_netif_conn_free(handle, NULL);
|
|
return -EINVAL;
|
|
}
|
|
/* the 2M PHY is not allowed as primary phy, we need to used the 1M PHY
|
|
* instead. This is for convenience so uses may define 2M as primary PHY */
|
|
if (phy_pri == BLE_HCI_LE_PHY_2M) {
|
|
phy_pri = BLE_HCI_LE_PHY_1M;
|
|
}
|
|
|
|
if (addr != NULL) {
|
|
p.directed = 1;
|
|
memcpy(&p.peer, addr, sizeof(p.peer));
|
|
if (params->flags & NIMBLE_NETIF_FLAG_HD_MODE) {
|
|
p.high_duty_directed = 1;
|
|
}
|
|
}
|
|
else {
|
|
p.connectable = 1;
|
|
}
|
|
if (params->flags & NIMBLE_NETIF_FLAG_LEGACY) {
|
|
p.legacy_pdu = 1;
|
|
/* legacy connectable PDUs are always scannable */
|
|
p.scannable = 1;
|
|
}
|
|
p.itvl_min = BLE_GAP_ADV_ITVL_MS(params->adv_itvl_ms);
|
|
p.itvl_max = BLE_GAP_ADV_ITVL_MS(params->adv_itvl_ms);
|
|
p.channel_map = params->channel_map;
|
|
p.own_addr_type = params->own_addr_type;
|
|
p.primary_phy = (uint8_t)phy_pri;
|
|
p.secondary_phy = (uint8_t)phy_sec;
|
|
p.tx_power = params->tx_power;
|
|
|
|
res = ble_gap_ext_adv_configure(CONFIG_NIMBLE_NETIF_ADV_INSTANCE, &p, NULL,
|
|
_on_gap_slave_evt, (void *)handle);
|
|
if (res != 0) {
|
|
nimble_netif_conn_free(handle, NULL);
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (ad != NULL) {
|
|
struct os_mbuf *data = os_msys_get_pkthdr(ad_len, 0);
|
|
if (data == NULL) {
|
|
nimble_netif_conn_free(handle, NULL);
|
|
return -ENOMEM;
|
|
}
|
|
res = os_mbuf_append(data, ad, ad_len);
|
|
if (res != 0) {
|
|
os_mbuf_free_chain(data);
|
|
nimble_netif_conn_free(handle, NULL);
|
|
return -ENOMEM;
|
|
}
|
|
res = ble_gap_ext_adv_set_data(CONFIG_NIMBLE_NETIF_ADV_INSTANCE, data);
|
|
assert(res == 0);
|
|
}
|
|
res = ble_gap_ext_adv_start(CONFIG_NIMBLE_NETIF_ADV_INSTANCE, params->timeout_ms / 10, 0);
|
|
#else
|
|
uint8_t mode = (addr != NULL) ? BLE_GAP_CONN_MODE_DIR
|
|
: BLE_GAP_CONN_MODE_UND;
|
|
struct ble_gap_adv_params p = {
|
|
.conn_mode = mode,
|
|
.disc_mode = BLE_GAP_DISC_MODE_GEN,
|
|
.itvl_min = BLE_GAP_ADV_ITVL_MS(params->adv_itvl_ms),
|
|
.itvl_max = BLE_GAP_ADV_ITVL_MS(params->adv_itvl_ms),
|
|
.channel_map = params->channel_map,
|
|
.filter_policy = 0,
|
|
.high_duty_cycle = (params->flags & NIMBLE_NETIF_FLAG_HD_MODE) ? 1 : 0,
|
|
};
|
|
|
|
/* set advertisement data, if applicable */
|
|
if (ad != NULL) {
|
|
res = ble_gap_adv_set_data(ad, (int)ad_len);
|
|
if (res != 0) {
|
|
nimble_netif_conn_free(handle, NULL);
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
|
|
/* start advertising */
|
|
uint32_t timeout = (params->timeout_ms == 0) ? BLE_HS_FOREVER
|
|
: params->timeout_ms;
|
|
res = ble_gap_adv_start(params->own_addr_type, addr, timeout,
|
|
&p, _on_gap_slave_evt, (void *)handle);
|
|
#endif
|
|
if (res != 0) {
|
|
nimble_netif_conn_free(handle, NULL);
|
|
return -ECANCELED;
|
|
}
|
|
|
|
_notify(handle, NIMBLE_NETIF_ACCEPTING, _netif.l2addr);
|
|
return 0;
|
|
}
|
|
|
|
int nimble_netif_accept(const uint8_t *ad, size_t ad_len,
|
|
const nimble_netif_accept_cfg_t *params)
|
|
{
|
|
assert(ad != NULL);
|
|
assert(ad_len > 0);
|
|
return _accept(ad, ad_len, NULL, params);
|
|
}
|
|
|
|
int nimble_netif_accept_direct(const ble_addr_t *addr,
|
|
const nimble_netif_accept_cfg_t *params)
|
|
{
|
|
assert(addr);
|
|
return _accept(NULL, 0, addr, params);
|
|
}
|
|
|
|
int nimble_netif_accept_stop(void)
|
|
{
|
|
int handle = nimble_netif_conn_get_adv();
|
|
if (handle == NIMBLE_NETIF_CONN_INVALID) {
|
|
return -EALREADY;
|
|
}
|
|
|
|
int res;
|
|
#if MYNEWT_VAL_BLE_EXT_ADV
|
|
res = ble_gap_ext_adv_stop(CONFIG_NIMBLE_NETIF_ADV_INSTANCE);
|
|
#else
|
|
res = ble_gap_adv_stop();
|
|
#endif
|
|
if (res != 0) {
|
|
return -EIO;
|
|
}
|
|
nimble_netif_conn_free(handle, NULL);
|
|
_notify(handle, NIMBLE_NETIF_ACCEPT_STOP, _netif.l2addr);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int nimble_netif_update(int handle,
|
|
const struct ble_gap_upd_params *conn_params)
|
|
{
|
|
nimble_netif_conn_t *conn = nimble_netif_conn_get(handle);
|
|
if (conn == NULL) {
|
|
return -ENOTCONN;
|
|
}
|
|
|
|
int res = ble_gap_update_params(conn->gaphandle, conn_params);
|
|
if (res != 0) {
|
|
return -ECANCELED;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int nimble_netif_used_chanmap(int handle, uint8_t map[5])
|
|
{
|
|
nimble_netif_conn_t *conn = nimble_netif_conn_get(handle);
|
|
if (conn == NULL) {
|
|
return -ENOTCONN;
|
|
}
|
|
|
|
int res = ble_hs_hci_read_chan_map(conn->gaphandle, map);
|
|
if (res != 0) {
|
|
return -ECANCELED;
|
|
}
|
|
|
|
return 0;
|
|
}
|