1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2025-12-26 06:53:52 +01:00

pkg/nimble: add netif wrapper

This commit is contained in:
Hauke Petersen 2019-05-17 11:15:49 +02:00
parent 1cb54659c3
commit 6bbe16601a
9 changed files with 1329 additions and 0 deletions

View File

@ -71,6 +71,9 @@ nimble_drivers_nrf5x:
nimble_addr:
"$(MAKE)" -C $(TDIR)/addr/
nimble_netif:
"$(MAKE)" -C $(TDIR)/netif/
nimble_scanlist:
"$(MAKE)" -C $(TDIR)/scanlist

View File

@ -34,6 +34,7 @@ ifneq (,$(filter nimble_controller,$(USEMODULE)))
endif
endif
# RIOT specific submodule dependencies
ifneq (,$(filter nimble_addr,$(USEMODULE)))
USEMODULE += bluetil_addr
endif
@ -42,3 +43,21 @@ ifneq (,$(filter nimble_scanlist,$(USEMODULE)))
USEMODULE += nimble_addr
USEMODULE += bluetil_ad
endif
ifneq (,$(filter nimble_netif,$(USEMODULE)))
USEMODULE += l2util
USEMODULE += bluetil_addr
ifneq (,$(filter gnrc_ipv6_%,$(USEMODULE)))
USEMODULE += nimble_svc_ipss
endif
ifneq (,$(filter gnrc_ipv6_router_default,$(USEMODULE)))
USEMODULE += gnrc_ipv6_nib_6lr
USEMODULE += gnrc_sixlowpan
USEMODULE += gnrc_sixlowpan_iphc
endif
ifneq (,$(filter gnrc_ipv6_default,$(USEMODULE)))
USEMODULE += gnrc_ipv6_nib_6ln
USEMODULE += gnrc_sixlowpan
USEMODULE += gnrc_sixlowpan_iphc
endif
endif

View File

@ -76,6 +76,18 @@ endif
ifneq (,$(filter nimble_addr,$(USEMODULE)))
INCLUDES += -I$(RIOTPKG)/nimble/addr/include
endif
ifneq (,$(filter nimble_netif,$(USEMODULE)))
INCLUDES += -I$(RIOTPKG)/nimble/netif/include
# configure NimBLE's internals
NIMBLE_MAX_CONN ?= 3
CFLAGS += -DMYNEWT_VAL_MSYS_1_BLOCK_SIZE=264
CFLAGS += -DMYNEWT_VAL_BLE_L2CAP_COC_MAX_NUM=$(NIMBLE_MAX_CONN)
CFLAGS += -DMYNEWT_VAL_BLE_MAX_CONNECTIONS=$(NIMBLE_MAX_CONN)
# NimBLEs internal buffer need to hold one IPv6 MTU per connection
# for the internal MTU of 256 byte, we need 10 mbufs per connection...
CFLAGS += -DMYNEWT_VAL_MSYS_1_BLOCK_COUNT=35
endif
ifneq (,$(filter nimble_scanlist,$(USEMODULE)))
INCLUDES += -I$(RIOTPKG)/nimble/scanlist/include
endif

View File

@ -100,6 +100,11 @@ void nimble_riot_init(void)
assert(res == 0);
(void)res;
#ifdef MODULE_NIMBLE_NETIF
extern void nimble_netif_init(void);
nimble_netif_init();
#endif
/* initialize the configured, build-in services */
#ifdef MODULE_NIMBLE_SVC_GAP
ble_svc_gap_init();

View File

@ -0,0 +1,3 @@
MODULE = nimble_netif
include $(RIOTBASE)/Makefile.base

View File

@ -0,0 +1,232 @@
/*
* Copyright (C) 2018-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.
*/
/**
* @defgroup pkg_nimble_netif GNRC netif Implementation
* @ingroup ble_nimble
* @brief GNRC netif implementation for NimBLE, enabling the integration
* of NimBLE into GNRC
*
* # About
* This NimBLE submodule provides a GNRC netif wrapper for integrating NimBLE
* with GNRC and other network stacks using netif (e.g. CCNlite).
*
* # Concept
* According to the IPv6-over-BLE standards (RFC7668 and IPSP), this module
* exposes a (configurable) number of point-to-point BLE connections as a single
* network device to BLE. Unicast traffic is only send using the corresponding
* BLE connection. Multicast and Broadcast packets are duplicated and send via
* each open BLE connection.
*
* # Structure
* The netif implementation is able to handle multiple connections
* simultaneously. The maximum number of concurrent connections is configured
* during compile time, using NimBLEs MYNEWT_VAL_BLE_MAX_CONNECTIONS option.
* Dependent on this value, the netif implementation takes care of allocation
* all the memory needed. The API of this submodule uses simply integer values
* to reference the used connection context (like file descriptors in linux).
*
* Like any other GNRC network device, the NimBLE netif wrapper runs in its own
* thread. This thread is started and configured by the common netif code. All
* send and get/set operations are handled by this thread. For efficiency
* reasons, receiving of data is however handled completely in the NimBLE host
* thread, from where the received data is directly passed on to the
* corresponding GNRC thread.
*
* Although the wrapper hooks into GNRC using the netif interface, it does need
* to implement parts of the netdev interface as well. This is done where
* needed.
*
* # Usage
* This submodule is designed to work fully asynchronous, in the same way as the
* NimBLE interfaces are designed. All functions in this submodule will only
* trigger the intended action. Once this action is complete, the module will
* report the result asynchronously using the configured callback.
*
* So before using this module, make sure to register a callback using the
* @ref nimble_netif_eventcb() function.
*
* After this, this module provides functions for managing BLE connections to
* other devices. Once these connections are established, this module takes care
* of mapping IP packets to the corresponding connections.
*
* @{
*
* @file
* @brief GNRC netif implementation for NimBLE
*
* @author Hauke Petersen <hauke.petersen@fu-berlin.de>
*/
#ifndef NIMBLE_NETIF_H
#define NIMBLE_NETIF_H
#include <stdint.h>
#include "net/ble.h"
#include "host/ble_hs.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Default L2CAP channel ID to use
*/
#ifndef NIMBLE_NETIF_CID
#define NIMBLE_NETIF_CID (BLE_L2CAP_CID_IPSP)
#endif
/**
* @brief Default MTU size supported by the NimBLE netif wrapper
*/
/* NOTE: We do not use the @ref IPV6_MIN_MTU define here, as the iov6.h header
pulls in some other RIOT headers that clash with NimBLE header (e.g.
* byteorder.h vs. endian.h) */
#ifndef NIMBLE_NETIF_MTU
#define NIMBLE_NETIF_MTU (1280U)
#endif
/**
* @brief Return codes used by the NimBLE netif module
*/
enum {
NIMBLE_NETIF_OK = 0, /**< everything went fine */
NIMBLE_NETIF_NOTCONN = -1, /**< not connected */
NIMBLE_NETIF_DEVERR = -2, /**< internal BLE stack error */
NIMBLE_NETIF_BUSY = -3, /**< network device is busy */
NIMBLE_NETIF_NOMEM = -4, /**< insufficient memory */
NIMBLE_NETIF_NOTADV = -5, /**< not advertising */
NIMBLE_NETIF_NOTFOUND = -6, /**< no fitting entry found */
};
/**
* @brief Event types triggered by the NimBLE netif module
*/
typedef enum {
NIMBLE_NETIF_CONNECTED_MASTER, /**< connection established as master */
NIMBLE_NETIF_CONNECTED_SLAVE, /**< connection established as slave */
NIMBLE_NETIF_CLOSED_MASTER, /**< connection closed (we were master) */
NIMBLE_NETIF_CLOSED_SLAVE, /**< connection closed (we were slave) */
NIMBLE_NETIF_CONNECT_ABORT, /**< connection establishment aborted */
NIMBLE_NETIF_CONN_UPDATED, /**< connection parameter update done */
} nimble_netif_event_t;
/**
* @brief Flags describing the state of a single connection context
*/
enum {
NIMBLE_NETIF_L2CAP_CLIENT = 0x0001, /**< L2CAP client */
NIMBLE_NETIF_L2CAP_SERVER = 0x0002, /**< L2CAP server */
NIMBLE_NETIF_L2CAP_CONNECTED = 0x0003, /**< L2CAP is connected */
NIMBLE_NETIF_GAP_MASTER = 0x0010, /**< GAP master */
NIMBLE_NETIF_GAP_SLAVE = 0x0020, /**< GAP slave */
NIMBLE_NETIF_GAP_CONNECTED = 0x0030, /**< GAP is connected */
NIMBLE_NETIF_ADV = 0x0100, /**< currently advertising */
NIMBLE_NETIF_CONNECTING = 0x4000, /**< connection in progress */
NIMBLE_NETIF_UNUSED = 0x8000, /**< context unused */
NIMBLE_NETIF_ANY = 0xffff, /**< match any state */
};
/**
* @brief Event callback signature used for asynchronous event signaling
*
* @note The event callback is always executed in NimBLE's host thread
*
* @param[in] handle handle to the connection that triggered the event
* @param[in] event type of the event
*/
typedef void(*nimble_netif_eventcb_t)(int handle, nimble_netif_event_t event);
/**
* @brief Initialize the netif implementation, spawns the netif thread
*
* This function is meant to be called once during system initialization, i.e.
* auto-init.
*/
void nimble_netif_init(void);
/**
* @brief Register a global event callback, servicing all NimBLE connections
*
* @note The event callback is always executed in NimBLE's host thread
*
* @param[in] cb event callback to register, may be NULL
*/
void nimble_netif_eventcb(nimble_netif_eventcb_t cb);
/**
* @brief Open a BLE connection as BLE master
*
* @param[in] addr address of the advertising BLE slave, in the NimBLE
* addr format (little endian)
* @param[in] conn_params connection (timing) parameters
* @param[in] timeout connect timeout
*
* @return the used connection handle on success
* @return NIMBLE_NETIF_BUSY if already connected to the given address or if
* a connection setup procedure is in progress
* @return NIMBLE_NETIF_NOMEM if no connection context memory is available
*/
int nimble_netif_connect(const ble_addr_t *addr,
const struct ble_gap_conn_params *conn_params,
uint32_t timeout);
/**
* @brief Close the connection with the given handle
*
* @param[in] handle handle for the connection to be closed
*
* @return NIMBLE_NETIF_OK on success
* @return NIMBLE_NETIF_NOTFOUND if the handle is invalid
* @return NIMBLE_NETIF_NOTCONN if context for given handle is not connected
*/
int nimble_netif_close(int handle);
/**
* @brief Accept incoming connections by starting to advertise this node
*
* @param[in] ad advertising data (in BLE AD format)
* @param[in] ad_len length of @p ad in bytes
* @param[in] adv_params advertising (timing) parameters to use
*
* @return NIMBLE_NETIF_OK on success
* @return NIMBLE_NETIF_BUSY if already advertising
* @return NIMBLE_NETIF_NOMEM on insufficient connection memory
*/
int nimble_netif_accept(const uint8_t *ad, size_t ad_len,
const struct ble_gap_adv_params *adv_params);
/**
* @brief Stop accepting incoming connections (stop advertising)
* *
* @return NIMBLE_NETIF_OK on success
* @return NIMBLE_NETIF_NOTADV if not currently advertising
*/
int nimble_netif_accept_stop(void);
/**
* @brief Update the connection parameters for the given connection
*
* @param[in] handle connection handle
* @param[in] conn_params new connection parameters to apply
*
* @return NIMBLE_NETIF_OK on success
* @return NIMBLE_NETIF_NOTCONN if handle does not point to a connection
* @return NIMBLE_NETIF_DEVERR if applying the given parameters failed
*/
int nimble_netif_update(int handle,
const struct ble_gap_upd_params *conn_params);
#ifdef __cplusplus
}
#endif
#endif /* NIMBLE_NETIF_H */
/** @} */

View File

@ -0,0 +1,223 @@
/*
* Copyright (C) 2018-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.
*/
/**
* @defgroup pkg_nimble_netif_conn Connection State Management for netif
* @ingroup pkg_nimble_netif
* @brief Helper module for managing the memory needed to store the
* BLE connection state for the netif wrapper
* @{
*
* @file
* @brief Connection allocation and maintenance for NimBLE netif
*
* @author Hauke Petersen <hauke.petersen@fu-berlin.de>
*/
#ifndef NIMBLE_NETIF_CONN_H
#define NIMBLE_NETIF_CONN_H
#include <stdint.h>
#include "nimble_netif.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Value for marking a handle invalid
*/
#define NIMBLE_NETIF_CONN_INVALID (-1)
/**
* @brief Memory layout for holding the relevant connection information
*/
typedef struct {
struct ble_l2cap_chan *coc; /**< l2cap context as exposed by NimBLE */
uint16_t gaphandle; /**< GAP handle exposed by NimBLE */
uint16_t state; /**< the current state of the context */
uint8_t addr[BLE_ADDR_LEN]; /**< BLE address of connected peer
(in network byte order) */
} nimble_netif_conn_t;
/**
* @brief Iterator function signature used by @ref nimble_netif_conn_foreach()
*
* @param[in] conn connection context of the current entry
* @param[in] handle handle of the current entry
* @param[in] arg user supplied argument
*
* @return 0 to continue
* @return != 0 to stop iterating
*/
typedef int (*nimble_netif_conn_iter_t)(nimble_netif_conn_t *conn,
int handle, void *arg);
/**
* @brief Initialize the connection state manager
*
* This functions is typically called by @ref nimble_netif_init().
*/
void nimble_netif_conn_init(void);
/**
* @brief Get the connection context corresponding to the given handle
*
* @param[in] handle handle to a connection context
*
* @return pointer to the corresponding connection context
* @return NULL if handle in invalid
*/
nimble_netif_conn_t *nimble_netif_conn_get(int handle);
/**
* @brief Get the handle to the context that is currently advertising
*
* @return handle to the currently advertising context
* @return NIMBLE_NETIF_CONN_INVALID if not advertising
*/
int nimble_netif_conn_get_adv(void);
/**
* @brief Get the handle to the context that is busy connecting
*
* @return handle to the busy context
* @return NIMBLE_NETIF_CONN_INVALID if not busy connecting
*/
int nimble_netif_conn_get_connecting(void);
/**
* @brief Find the connection to the peer with the given BLE address
*
* @param[in] addr BLE address, in network byte order
*
* @return handle to the matching connection context
* @return NIMBLE_NETIF_CONN_INVALID if no matching connection was found
*/
int nimble_netif_conn_get_by_addr(const uint8_t *addr);
/**
* @brief Find the connection using the given NimBLE GAP handle
*
* @param[in] gaphandle GAP handle as exposed by NimBLE
*
* @return handle to the matching connection context
* @return NIMBLE_NETIF_CONN_INVALID if no matching connection was found
*/
int nimble_netif_conn_get_by_gaphandle(uint16_t gaphandle);
/**
* @brief Iterate over all connection contexts that match the filter condition
*
* @warning Do not call any other nimble_netif_conn function from within the
* callback, this will lead to a deadlock!
*
* @param[in] filter filter mask
* @param[in] cb callback called on each filtered entry
* @param[in] arg user argument
*/
void nimble_netif_conn_foreach(uint16_t filter,
nimble_netif_conn_iter_t cb, void *arg);
/**
* @brief Count the number of connections contexts for which the given filter
* applies
*
* @param[in] filter filter mask
*
* @return number of contexts for which the filter applied
*/
unsigned nimble_netif_conn_count(uint16_t filter);
/**
* @brief Allocate an unused context for starting a connection
*
* @param[in] addr the BLE address of the peer node, in network byte
* order
*
* @return handle used for the new connection
*/
int nimble_netif_conn_start_connection(const uint8_t *addr);
/**
* @brief Reserve a unused context for the purpose of accepting a new
* connection
*
* @return handle of the reserved context
* @return NIMBLE_NETIF_CONN_INVALID if no unused context was available
*/
int nimble_netif_conn_start_adv(void);
/**
* @brief Free the connection context with the given handle
*/
void nimble_netif_conn_free(int handle);
/**
* @brief Find the connection context with a given GAP handle and return a
* pointer to it
*
* @param[in] gh GAP handle used by NimBLE
*
* @return Pointer to the selected context
* @return NULL if no fitting context was found
*/
static inline
nimble_netif_conn_t *nimble_netif_conn_from_gaphandle(uint16_t gh)
{
return nimble_netif_conn_get(nimble_netif_conn_get_by_gaphandle(gh));
}
/**
* @brief Convenience function to check if any context is currently in the
* connecting state (@ref NIMBLE_NETIF_CONNECTING)
*
* @return != 0 if true
* @return 0 if false
*/
static inline int nimble_netif_conn_connecting(void)
{
return (nimble_netif_conn_get_connecting() != NIMBLE_NETIF_CONN_INVALID);
}
/**
* @brief Convenience function to check if we are currently connected to a
* peer with the given address
*
* @param[in] addr BLE address, in network byte order
*
* @return != 0 if true
* @return 0 if false
*/
static inline int nimble_netif_conn_connected(const uint8_t *addr)
{
return (nimble_netif_conn_get_by_addr(addr) != NIMBLE_NETIF_CONN_INVALID);
}
/**
* @brief Convenience function to check if any context is currently in the
* advertising state (@ref NIMBLE_NETIF_ADV)
*
* @return != 0 if true
* @return 0 if false
*/
static inline int nimble_netif_conn_is_adv(void)
{
return (nimble_netif_conn_get_adv() != NIMBLE_NETIF_CONN_INVALID);
}
#ifdef __cplusplus
}
#endif
#endif /* NIMBLE_NETIF_CONN_H */
/** @} */

View File

@ -0,0 +1,630 @@
/*
* Copyright (C) 2018-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 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"
#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
/* buffer configuration
* - we need one RX and one TX buffer per connection */
#define MTU_SIZE (NIMBLE_NETIF_MTU)
#define MBUF_OVHD (sizeof(struct os_mbuf) + \
sizeof(struct os_mbuf_pkthdr))
#define MBUF_SIZE (MBUF_OVHD + MYNEWT_VAL_BLE_L2CAP_COC_MPS)
#define MBUF_CNT (MYNEWT_VAL_BLE_MAX_CONNECTIONS * 2 * \
((MTU_SIZE + (MBUF_SIZE - 1)) / MBUF_SIZE))
/* thread flag used for signaling transmit readiness */
#define FLAG_TX_UNSTALLED (1u << 13)
/* 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 *_nimble_netif = NULL;
static gnrc_nettype_t _nettype = NETTYPE;
/* keep a reference to the event callback */
static nimble_netif_eventcb_t _eventcb;
/* allocation of memory for buffering IP packets when handing them to NimBLE */
static os_membuf_t _mem[OS_MEMPOOL_SIZE(MBUF_CNT, MBUF_SIZE)];
static struct os_mempool _mem_pool;
static struct os_mbuf_pool _mbuf_pool;
/* notify the user about state changes for a connection context */
static void _notify(int handle, nimble_netif_event_t event)
{
if (_eventcb) {
_eventcb(handle, event);
}
}
static void _netif_init(gnrc_netif_t *netif)
{
(void)netif;
/* save the threads context pointer, so we can set its flags */
_netif_thread = (thread_t *)thread_get(thread_getpid());
#ifdef MODULE_GNRC_SIXLOWPAN
/* we disable fragmentation for this device, as the L2CAP layer takes care
* of this */
_nimble_netif->sixlo.max_frag_size = 0;
#endif
}
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_mbuf_get_pkthdr(&_mbuf_pool, 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_wait_all(FLAG_TX_UNSTALLED);
}
} while (res == BLE_HS_EBUSY);
if ((res != 0) && (res != BLE_HS_ESTALLED)) {
os_mbuf_free_chain(sdu);
return -ENOBUFS;
}
return num_bytes;
}
static int _netif_send_iter(nimble_netif_conn_t *conn,
int handle, void *arg)
{
(void)handle;
_send_pkt(conn, (gnrc_pktsnip_t *)arg);
return 0;
}
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)) {
nimble_netif_conn_foreach(NIMBLE_NETIF_L2CAP_CONNECTED,
_netif_send_iter, pkt->next);
res = (int)gnrc_pkt_len(pkt->next);
}
/* 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)
{
_nimble_netif = dev->context;
/* 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, _nimble_netif->l2addr);
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, _nimble_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_PACKET_SIZE:
assert(max_len >= sizeof(uint16_t));
*((uint16_t *)value) = MTU_SIZE;
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,
_nimble_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_t *netif_hdr = (gnrc_netif_hdr_t *)if_snip->data;
netif_hdr->if_pid = _nimble_netif->pid;
/* 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_mbuf_get_pkthdr(&_mbuf_pool, 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:
conn->coc = event->connect.chan;
conn->state |= NIMBLE_NETIF_L2CAP_CLIENT;
conn->state &= ~NIMBLE_NETIF_CONNECTING;
_notify(handle, NIMBLE_NETIF_CONNECTED_MASTER);
break;
case BLE_L2CAP_EVENT_COC_DISCONNECTED:
assert(conn->coc);
conn->coc = NULL;
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);
conn->coc = event->connect.chan;
conn->state |= NIMBLE_NETIF_L2CAP_SERVER;
conn->state &= ~(NIMBLE_NETIF_ADV | NIMBLE_NETIF_CONNECTING);
_notify(handle, NIMBLE_NETIF_CONNECTED_SLAVE);
break;
case BLE_L2CAP_EVENT_COC_DISCONNECTED:
conn = nimble_netif_conn_from_gaphandle(event->disconnect.conn_handle);
assert(conn && conn->coc);
conn->coc = NULL;
conn->state &= ~NIMBLE_NETIF_L2CAP_CONNECTED;
break;
case BLE_L2CAP_EVENT_COC_ACCEPT: {
struct os_mbuf *sdu_rx = os_mbuf_get_pkthdr(&_mbuf_pool, 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;
bluetil_addr_swapped_cp(desc.peer_id_addr.val, 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) {
nimble_netif_conn_free(handle);
_notify(handle, NIMBLE_NETIF_CONNECT_ABORT);
return 0;
}
_on_gap_connected(conn, event->connect.conn_handle);
conn->state |= NIMBLE_NETIF_GAP_MASTER;
struct os_mbuf *sdu_rx = os_mbuf_get_pkthdr(&_mbuf_pool, 0);
/* we should never run out of buffer space... */
assert(sdu_rx != NULL);
res = ble_l2cap_connect(event->connect.conn_handle,
NIMBLE_NETIF_CID, MTU_SIZE, sdu_rx,
_on_l2cap_client_evt, (void *)handle);
/* should always success as well */
assert(res == 0);
break;
}
case BLE_GAP_EVENT_DISCONNECT:
nimble_netif_conn_free(handle);
_notify(handle, NIMBLE_NETIF_CLOSED_MASTER);
break;
case BLE_GAP_EVENT_CONN_UPDATE:
_notify(handle, NIMBLE_NETIF_CONN_UPDATED);
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) {
nimble_netif_conn_free(handle);
_notify(handle, NIMBLE_NETIF_CONNECT_ABORT);
break;
}
_on_gap_connected(conn, event->connect.conn_handle);
assert(conn->state == NIMBLE_NETIF_ADV);
conn->state = NIMBLE_NETIF_GAP_SLAVE;
break;
}
case BLE_GAP_EVENT_DISCONNECT:
nimble_netif_conn_free(handle);
_notify(handle, NIMBLE_NETIF_CLOSED_SLAVE);
break;
case BLE_GAP_EVENT_CONN_UPDATE:
_notify(handle, NIMBLE_NETIF_CONN_UPDATED);
break;
case BLE_GAP_EVENT_CONN_UPDATE_REQ:
/* nothing to do here */
break;
default:
break;
}
return 0;
}
void nimble_netif_init(void)
{
int res;
(void)res;
/* setup the connection context table */
nimble_netif_conn_init();
/* initialize of BLE related buffers */
res = os_mempool_init(&_mem_pool, MBUF_CNT, MBUF_SIZE, _mem, "nim_gnrc");
assert(res == 0);
res = os_mbuf_pool_init(&_mbuf_pool, &_mem_pool, MBUF_SIZE, MBUF_CNT);
assert(res == 0);
res = ble_l2cap_create_server(NIMBLE_NETIF_CID, MTU_SIZE,
_on_l2cap_server_evt, NULL);
assert(res == 0);
(void)res;
gnrc_netif_create(_stack, sizeof(_stack), GNRC_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 struct ble_gap_conn_params *conn_params,
uint32_t timeout)
{
assert(addr);
assert(_eventcb);
/* 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 NIMBLE_NETIF_BUSY;
}
/* get empty connection context */
int handle = nimble_netif_conn_start_connection(addrn);
if (handle == NIMBLE_NETIF_CONN_INVALID) {
return NIMBLE_NETIF_NOMEM;
}
int res = ble_gap_connect(nimble_riot_own_addr_type, addr, timeout,
conn_params, _on_gap_master_evt, (void *)handle);
assert(res == 0);
(void)res;
return handle;
}
int nimble_netif_close(int handle)
{
nimble_netif_conn_t *conn = nimble_netif_conn_get(handle);
if (conn == NULL) {
return NIMBLE_NETIF_NOTFOUND;
}
else if (!(conn->state & NIMBLE_NETIF_L2CAP_CONNECTED)) {
return NIMBLE_NETIF_NOTCONN;
}
int res = ble_gap_terminate(ble_l2cap_get_conn_handle(conn->coc),
BLE_ERR_REM_USER_CONN_TERM);
assert(res == 0);
(void)res;
return NIMBLE_NETIF_OK;
}
int nimble_netif_accept(const uint8_t *ad, size_t ad_len,
const struct ble_gap_adv_params *adv_params)
{
assert(ad);
assert(adv_params);
int handle;
int res;
(void)res;
/* allocate a connection context for incoming connections */
handle = nimble_netif_conn_start_adv();
if (handle < 0) {
return handle;
}
/* set advertisement data */
res = ble_gap_adv_set_data(ad, (int)ad_len);
assert(res == 0);
/* remember context and start advertising */
res = ble_gap_adv_start(nimble_riot_own_addr_type, NULL, BLE_HS_FOREVER,
adv_params, _on_gap_slave_evt, (void *)handle);
assert(res == 0);
return NIMBLE_NETIF_OK;
}
int nimble_netif_accept_stop(void)
{
int handle = nimble_netif_conn_get_adv();
if (handle == NIMBLE_NETIF_CONN_INVALID) {
return NIMBLE_NETIF_NOTADV;
}
int res = ble_gap_adv_stop();
assert(res == 0);
(void)res;
nimble_netif_conn_free(handle);
return NIMBLE_NETIF_OK;
}
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 NIMBLE_NETIF_NOTCONN;
}
int res = ble_gap_update_params(conn->gaphandle, conn_params);
if (res != 0) {
return NIMBLE_NETIF_DEVERR;
}
return NIMBLE_NETIF_OK;
}

View File

@ -0,0 +1,202 @@
/*
* Copyright (C) 2018-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 pkg_nimble_netif_conn
* @{
*
* @file
* @brief Connection context handling for NimBLE netif
*
* @author Hauke Petersen <hauke.petersen@fu-berlin.de>
*
* @}
*/
#include "nimble_netif_conn.h"
#define ENABLE_DEBUG (0)
#include "debug.h"
#define CONN_CNT (MYNEWT_VAL_BLE_MAX_CONNECTIONS)
static mutex_t _lock = MUTEX_INIT;
static nimble_netif_conn_t _conn[CONN_CNT];
static int _find_by_state(uint16_t filter)
{
for (unsigned i = 0; i < CONN_CNT; i++) {
if (_conn[i].state & filter) {
return (int)i;
}
}
return NIMBLE_NETIF_CONN_INVALID;
}
void nimble_netif_conn_init(void)
{
DEBUG("conn_init\n");
memset(_conn, 0, sizeof(_conn));
for (unsigned i = 0; i < CONN_CNT; i++) {
_conn[i].state = NIMBLE_NETIF_UNUSED;
}
}
nimble_netif_conn_t *nimble_netif_conn_get(int handle)
{
if ((handle < 0) || (handle >= CONN_CNT)) {
return NULL;
}
return &_conn[handle];
}
int nimble_netif_conn_get_adv(void)
{
int handle;
DEBUG("nimble_netif_conn_get_adv\n");
mutex_lock(&_lock);
handle = _find_by_state(NIMBLE_NETIF_ADV);
mutex_unlock(&_lock);
return handle;
}
int nimble_netif_conn_get_connecting(void)
{
int handle;
DEBUG("nimble_netif_conn_get_connecting\n");
mutex_lock(&_lock);
handle = _find_by_state(NIMBLE_NETIF_CONNECTING);
mutex_unlock(&_lock);
DEBUG("nimble_netif_conn_get_connecting - handle %i\n", handle);
return handle;
}
int nimble_netif_conn_get_by_addr(const uint8_t *addr)
{
assert(addr);
int handle = NIMBLE_NETIF_CONN_INVALID;
DEBUG("nimble_netif_conn_get_by_addr %02x\n", (int)addr[5]);
mutex_lock(&_lock);
for (unsigned i = 0; i < CONN_CNT; i++) {
if ((_conn[i].state & NIMBLE_NETIF_L2CAP_CONNECTED) &&
memcmp(_conn[i].addr, addr, BLE_ADDR_LEN) == 0) {
handle = (int)i;
break;
}
}
mutex_unlock(&_lock);
DEBUG("nimble_netif_conn_get_by_addr - found: %i\n", handle);
return handle;
}
int nimble_netif_conn_get_by_gaphandle(uint16_t gaphandle)
{
int handle = NIMBLE_NETIF_CONN_INVALID;
DEBUG("nimble_netif_conn_get_by_gaphandle %i\n", (int)gaphandle);
mutex_lock(&_lock);
for (unsigned i = 0; i < CONN_CNT; i++) {
if (_conn[i].gaphandle == gaphandle) {
handle = (int)i;
break;
}
}
mutex_unlock(&_lock);
DEBUG("nimble_netif_conn_get_by_gaphandle - found %i\n", handle);
return handle;
}
int nimble_netif_conn_start_connection(const uint8_t *addr)
{
int handle;
DEBUG("nimble_netif_conn_start_connection, addr %02x\n", (int)addr[5]);
mutex_lock(&_lock);
handle = _find_by_state(NIMBLE_NETIF_UNUSED);
if (handle != NIMBLE_NETIF_CONN_INVALID) {
_conn[handle].state = NIMBLE_NETIF_CONNECTING;
memcpy(_conn[handle].addr, addr, BLE_ADDR_LEN);
}
mutex_unlock(&_lock);
return handle;
}
int nimble_netif_conn_start_adv(void)
{
int handle;
DEBUG("nimble_netif_conn_start_adv\n");
mutex_lock(&_lock);
handle = _find_by_state(NIMBLE_NETIF_ADV);
if (handle != NIMBLE_NETIF_CONN_INVALID) {
handle = NIMBLE_NETIF_BUSY;
}
else {
handle = _find_by_state(NIMBLE_NETIF_UNUSED);
if (handle != NIMBLE_NETIF_CONN_INVALID) {
_conn[handle].state = NIMBLE_NETIF_ADV;
}
else {
handle = NIMBLE_NETIF_NOMEM;
}
}
mutex_unlock(&_lock);
return handle;
}
void nimble_netif_conn_free(int handle)
{
assert((handle >= 0) && (handle < CONN_CNT));
DEBUG("nimble_netif_conn_free, handle %i\n", handle);
mutex_lock(&_lock);
memset(&_conn[handle], 0, sizeof(nimble_netif_conn_t));
_conn[handle].state = NIMBLE_NETIF_UNUSED;
mutex_unlock(&_lock);
}
void nimble_netif_conn_foreach(uint16_t filter,
nimble_netif_conn_iter_t cb, void *arg)
{
assert(cb);
DEBUG("nimble_netif_conn_foreach 0x%04x\n", (int)filter);
mutex_lock(&_lock);
for (unsigned i = 0; i < CONN_CNT; i++) {
if (_conn[i].state & filter) {
int res = cb(&_conn[i], (int)i, arg);
if (res != 0) {
break;
}
}
}
mutex_unlock(&_lock);
}
unsigned nimble_netif_conn_count(uint16_t filter)
{
unsigned cnt = 0;
DEBUG("nimble_netif_conn_count, filter 0x%04x\n", (int)filter);
mutex_lock(&_lock);
for (unsigned i = 0; i < CONN_CNT; i++) {
if (_conn[i].state & filter) {
++cnt;
}
}
mutex_unlock(&_lock);
return cnt;
}