@@ -0,0 +1,258 @@
/*
* Copyright (C) 2018 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 examples
* @{
*
* @file
* @brief Helper tool for measuring BLE connection events
*
* @author Hauke Petersen <hauke.petersen@fu-berlin.de>
*
* @}
*/

#include <stdio.h>
#include <string.h>
#include <stdint.h>

#include "board.h"
#include "sched.h"
#include "thread_flags.h"

#include "net/gorm/ll.h"
#include "nrfble_rxtim.h"
#include "net/netdev/ble.h"


#define ADV_AA (0x8e89bed6)
#define ADV_CRC (0x00555555)
#define ADV_CHAN (39U)

#define SYNC_ADV (0x0001)
#define SYNC_DAT (0x0002)

#define HOP_MASK(x) (x & 0x1f)

typedef struct __attribute__((packed)) {
uint8_t inita[GORM_LL_ADDR_LEN];
uint8_t adva[GORM_LL_ADDR_LEN];
uint8_t aa[4];
uint8_t crc[GORM_LL_CRC_LEN];
uint8_t win_size;
uint8_t win_offset[2];
uint8_t interval[2];
uint8_t latency[2];
uint8_t timeout[2];
uint8_t chan_map[GORM_LL_CHANMAP_LEN];
uint8_t hop_sca;
} connect_ind_mtu_t;

/* state config */
static netdev_ble_pkt_t pkt;
static netdev_t *dev;
static thread_t *thread;
static thread_flags_t flag = SYNC_ADV;

static gorm_ll_connection_t con;

/* timings */
static uint16_t evt_cnt = 0xffff;
static uint32_t last = 0;
static uint32_t rxtim = 0;


static int _chan_used(uint8_t *map, uint8_t num)
{
return (map[num >> 3] & (1 << (num & 0x07)));
}

uint8_t gorm_ll_chan_count(uint8_t *map)
{
uint8_t cnt = 0;
for (uint8_t i = 0; i < GORM_LL_CHAN_NUMOF; i++) {
if (_chan_used(map, i)) {
++cnt;
}
}
return cnt;
}

void gorm_ll_chan_algo1(gorm_ll_connection_t *con)
{
con->chan_unmapped = (con->chan_unmapped + con->chan_hop) %
GORM_LL_CHAN_DATA_NUMOF;
if (_chan_used(con->chan_map, con->chan_unmapped)) {
con->ctx.chan = con->chan_unmapped;
}
else {
unsigned remap_index = (con->chan_unmapped % con->chan_cnt) + 1;
int pos = -1;
while (remap_index > 0) {
/* advance to the next used channel */
while (_chan_used(con->chan_map, ++pos) == 0) {}
--remap_index;
}
con->ctx.chan = pos;
}
}



static inline uint16_t _letohs(uint8_t *buf)
{
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
return (uint16_t)((buf[1] << 8) | buf[0]);
#else
return (uint16_t)((buf[0] << 8) | buf[1]);
#endif
}


static void _on_radio(netdev_t *netdev, netdev_event_t event)
{
(void)event;
(void)netdev;

LED0_OFF;

rxtim = xtimer_now_usec();
thread_flags_set(thread, flag);
}

static void _prep_next_pkt(void)
{
evt_cnt++;

if ((con.chan_update.cnt != 0) &&
(evt_cnt == con.chan_update.instant)) {
memcpy(con.chan_map, con.chan_update.map, 5);
con.chan_cnt = con.chan_update.cnt;
con.chan_update.cnt = 0;
printf("--- chan update applied ---\n");
}
gorm_ll_chan_algo1(&con);
}

static void _check_for_tim_update(void)
{
if (((pkt.flags & GORM_LL_LLID_MASK) == GORM_LL_LLID_CTRL)
&& (pkt.pdu[0] == GORM_LL_CONN_UPDATE_IND)) {
uint8_t *d = &pkt.pdu[1];
printf("--- connection update ---\n");
uint16_t win_size = d[0];
uint16_t win_offset = _letohs(&d[1]);
uint16_t interval = _letohs(&d[3]);
uint16_t timeout = _letohs(&d[7]);
uint16_t instant = _letohs(&d[9]);
printf("win_size: %u\n", (unsigned)win_size);
printf("win_offset: %u\n", (unsigned)win_offset);
printf("interval: %u\n", (unsigned)interval);
printf("timeout: %u\n", (unsigned)timeout);
printf("instant: %u\n", (unsigned)instant);
}
}

static void _check_for_chan_update(void)
{
if (((pkt.flags & GORM_LL_LLID_MASK) == GORM_LL_LLID_CTRL)
&& (pkt.pdu[0] == GORM_LL_CHANNEL_MAP_IND)) {
uint8_t *d = &pkt.pdu[1];

printf("--- channel map update ---\n");
printf("map: %02x %02x %02x %02x %02x\n",
(int)d[0], (int)d[1], (int)d[2], (int)d[3], (int)d[4]);
uint16_t instant = _letohs(&d[5]);
printf("instant: %i\n", (int)instant);

memcpy(con.chan_update.map, d, 5);
con.chan_update.instant = instant;
con.chan_update.cnt = gorm_ll_chan_count(d);
printf("count: %i\n", (int)con.chan_update.cnt);
}
}

static void _check_for_con(void)
{
if ((pkt.flags & GORM_LL_PDU_MASK) == GORM_LL_CONNECT_IND) {
connect_ind_mtu_t *pdu = (connect_ind_mtu_t *)pkt.pdu;

con.chan_hop = HOP_MASK(pdu->hop_sca);
memcpy(con.chan_map, pdu->chan_map, GORM_LL_CHANMAP_LEN);
con.chan_cnt = gorm_ll_chan_count(con.chan_map);
con.chan_unmapped = 0;

memcpy(con.ctx.aa.raw, pdu->aa, 4);
memcpy(&con.ctx.crc, pdu->crc, GORM_LL_CRC_LEN);
_prep_next_pkt();

flag = SYNC_DAT;

printf("New Connection\n");
// printf("hop_inc: %i\n", (int)con.chan_hop);
// printf("aa: 0x%04x\n", (int)con.ctx.aa.u32);
// printf("crc: 0x%03x\n", (int)con.ctx.crc);
// printf("--- timings ---\n");
// uint16_t win_size = pdu->win_size;
// uint16_t win_offset = _letohs(pdu->win_offset);
// uint16_t interval = _letohs(pdu->interval);
// uint16_t timeout = _letohs(pdu->timeout);
// printf("win_size: %u\n", (unsigned)win_size);
// printf("win_offset: %u\n", (unsigned)win_offset);
// printf("interval: %u\n", (unsigned)interval);
// printf("timeout: %u\n", (unsigned)timeout);
}
}

int main(void)
{
puts("BLE RX timer");

/* setup environment */
thread = (thread_t *)sched_active_thread;

last = xtimer_now_usec();

/* init radio */
dev = nrfble_setup();
dev->event_callback = _on_radio;
dev->driver->init(dev);

/* prepare packet */
con.ctx.crc = ADV_CRC;
con.ctx.aa.u32 = ADV_AA;
con.ctx.chan = ADV_CHAN;

while (1) {
LED0_ON;
netdev_ble_recv(dev, &pkt, &con.ctx);
thread_flags_t reason = thread_flags_wait_any(SYNC_ADV | SYNC_DAT);

if (reason & SYNC_ADV) {
// printf("adv: %u [%u us] | ", (unsigned)rxtim, (unsigned)(last - rxtim));
// _adv_info();
_check_for_con();
}
if (reason & SYNC_DAT) {
LED1_OFF;
printf("evt %i: %u [%u us] - ch %i\n",
(int)evt_cnt, (unsigned)rxtim,
(unsigned)(rxtim - last),
(int)con.ctx.chan);
_check_for_tim_update();
_check_for_chan_update();
_prep_next_pkt();
LED1_ON;
}

last = rxtim;
}

return 0;
}
@@ -0,0 +1,354 @@
/*
* Copyright (C) 2017 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 drivers_nrf5x_nrfble
*
* @note This driver is not thread safe and should only be used by a
* single thread at once!
* @{
*
* @file
* @brief Bluetooth low energy radio driver for nRF5x SoCs
*
* @author Hauke Petersen <hauke.petersen@fu-berlin.de>
*
* @}
*/

#include <errno.h>

#include "cpu.h"
#include "assert.h"

#include "nrfble.h"
#include "net/netdev/ble.h"

#define ENABLE_DEBUG (0)
#include "debug.h"

/**
* @brief Driver specific device configuration
* @{
*/
#define CONF_MODE RADIO_MODE_MODE_Ble_1Mbit
#define CONF_LEN (8U)
#define CONF_S0 (1U)
#define CONF_S1 (0U)
#define CONF_STATLEN (0U)
#define CONF_BASE_ADDR_LEN (3U)
#define CONF_ENDIAN RADIO_PCNF1_ENDIAN_Little
#define CONF_WHITENING RADIO_PCNF1_WHITEEN_Enabled
#define CONF_CRC_LEN (0X3 | RADIO_CRCCNF_SKIPADDR_Msk)
#define CONF_CRC_POLY (0x00065b)
#define CONF_CRC_INIT (0x555555)
/** @} */

/**
* @brief Allocate the netdev device descriptor
*/
netdev_t nrfble_dev;

/**
* @brief Remember the radio context when receiving things
*/
netdev_ble_ctx_t *rx_ctx;

/**
* @brief Map logical BLE channel values to actual radio frequencies
*/
static const uint8_t ble_chan_map[40] = {
[ 0] = 4,
[ 1] = 6,
[ 2] = 8,
[ 3] = 10,
[ 4] = 12,
[ 5] = 14,
[ 6] = 16,
[ 7] = 18,
[ 8] = 20,
[ 9] = 22,
[10] = 24,
[11] = 28,
[12] = 30,
[13] = 32,
[14] = 34,
[15] = 36,
[16] = 38,
[17] = 40,
[18] = 42,
[19] = 44,
[20] = 46,
[21] = 48,
[22] = 50,
[23] = 52,
[24] = 54,
[25] = 56,
[26] = 58,
[27] = 60,
[28] = 62,
[29] = 64,
[30] = 66,
[31] = 68,
[32] = 70,
[33] = 72,
[34] = 74,
[35] = 76,
[36] = 78,
[37] = 2,
[38] = 26,
[39] = 80,
};

/**
* @brief Set radio into idle (DISABLED) state
*/
static void go_idle(void)
{
/* only do anything in case RX is active */
if (NRF_RADIO->INTENSET & RADIO_INTENSET_DISABLED_Msk) {
NRF_RADIO->INTENCLR = RADIO_INTENCLR_DISABLED_Msk;
/* set device into basic disabled state */
NRF_RADIO->EVENTS_DISABLED = 0;
NRF_RADIO->TASKS_DISABLE = 1;
while (NRF_RADIO->EVENTS_DISABLED == 0) {
/* TODO: enable sleep here */
// cortexm_sleep_until_event();
}
}
}

static void set_context(netdev_ble_ctx_t *ctx)
{
assert(ctx->chan <= NRFBLE_CHAN_MAX);

NRF_RADIO->FREQUENCY = ble_chan_map[ctx->chan];
NRF_RADIO->PREFIX0 = ctx->aa.raw[3];
NRF_RADIO->BASE0 = (uint32_t)((ctx->aa.raw[0] << 8) |
(ctx->aa.raw[1] << 16) |
(ctx->aa.raw[2] << 24));
NRF_RADIO->DATAWHITEIV = ctx->chan;
NRF_RADIO->CRCINIT = ctx->crc;
}

static int16_t nrfble_get_txpower(void)
{
int8_t p = (int8_t)NRF_RADIO->TXPOWER;
if (p < 0) {
return (int16_t)(0xff00 | p);
}
return (int16_t)p;
}

static void nrfble_set_txpower(int16_t power)
{
if (power > 2) {
NRF_RADIO->TXPOWER = RADIO_TXPOWER_TXPOWER_Pos4dBm;
}
else if (power > -2) {
NRF_RADIO->TXPOWER = RADIO_TXPOWER_TXPOWER_0dBm;
}
else if (power > -6) {
NRF_RADIO->TXPOWER = RADIO_TXPOWER_TXPOWER_Neg4dBm;
}
else if (power > -10) {
NRF_RADIO->TXPOWER = RADIO_TXPOWER_TXPOWER_Neg8dBm;
}
else if (power > -14) {
NRF_RADIO->TXPOWER = RADIO_TXPOWER_TXPOWER_Neg12dBm;
}
else if (power > -18) {
NRF_RADIO->TXPOWER = RADIO_TXPOWER_TXPOWER_Neg16dBm;
}
else if (power > -25) {
NRF_RADIO->TXPOWER = RADIO_TXPOWER_TXPOWER_Neg20dBm;
}
else {
NRF_RADIO->TXPOWER = RADIO_TXPOWER_TXPOWER_Neg30dBm;
}
}


/**
* @brief Radio interrupt routine
*/
void isr_radio(void)
{
if (NRF_RADIO->EVENTS_DISABLED == 1) {
NRF_RADIO->EVENTS_DISABLED = 0;

rx_ctx->crc = NRF_RADIO->CRCSTATUS;
nrfble_dev.event_callback(&nrfble_dev, NETDEV_EVENT_RX_COMPLETE);
}
cortexm_isr_end();
}


netdev_t *nrfble_setup(void)
{
nrfble_dev.driver = &nrfble_netdev;
nrfble_dev.event_callback = NULL;
nrfble_dev.context = NULL;
#ifdef MODULE_NETSTATS_L2
memset(&nrfble_dev.stats, 0, sizeof(netstats_t));;
#endif
return &nrfble_dev;
}

static int nrfble_init(netdev_t *dev)
{
(void)dev;
assert(nrfble_dev.driver && nrfble_dev.event_callback);

/* power on the NRFs radio */
NRF_RADIO->POWER = 1;
/* configure variable parameters to default values */
NRF_RADIO->TXPOWER = NRFBLE_TXPOWER_DEFAULT;
/* always send from and listen to logical address 0 */
NRF_RADIO->TXADDRESS = 0x00UL;
NRF_RADIO->RXADDRESSES = 0x01UL;
/* load driver specific configuration */
NRF_RADIO->MODE = CONF_MODE;
/* configure data fields and packet length whitening and endianess */
NRF_RADIO->PCNF0 = ((CONF_S1 << RADIO_PCNF0_S1LEN_Pos) |
(CONF_S0 << RADIO_PCNF0_S0LEN_Pos) |
(CONF_LEN << RADIO_PCNF0_LFLEN_Pos));
NRF_RADIO->PCNF1 = ((CONF_WHITENING << RADIO_PCNF1_WHITEEN_Pos) |
(CONF_ENDIAN << RADIO_PCNF1_ENDIAN_Pos) |
(CONF_BASE_ADDR_LEN << RADIO_PCNF1_BALEN_Pos) |
(CONF_STATLEN << RADIO_PCNF1_STATLEN_Pos) |
(NETDEV_BLE_PDU_MAXLEN << RADIO_PCNF1_MAXLEN_Pos));
/* configure the CRC unit, we skip the address field as this seems to lead
* to wrong checksum calculation on nRF52 devices in some cases */
NRF_RADIO->CRCCNF = CONF_CRC_LEN;
NRF_RADIO->CRCPOLY = CONF_CRC_POLY;
NRF_RADIO->CRCINIT = CONF_CRC_INIT;
/* set shortcuts for more efficient transfer */
NRF_RADIO->SHORTS = (RADIO_SHORTS_READY_START_Msk | RADIO_SHORTS_END_DISABLE_Msk);
/* enable global radio interrupts, but leave local interrupts masked */
NRF_RADIO->INTENCLR = 0xffffffff;
NVIC_EnableIRQ(RADIO_IRQn);

DEBUG("[nrfble] initialization successful\n");
return 0;
}

static int nrfble_send(netdev_t *dev, const struct iovec *data, unsigned count)
{
(void)dev;
(void)count;

assert(count == 2);
assert((data != NULL) &&
(data[0].iov_len == sizeof(netdev_ble_ctx_t)) &&
(data[1].iov_len == sizeof(netdev_ble_pkt_t)));

/* cancel any RX operation (if ongoing) */
go_idle();

/* get the BLE pkt data */
netdev_ble_ctx_t *ctx = data[0].iov_base;
netdev_ble_pkt_t *pkt = data[1].iov_base;

DEBUG("[nrfble] send: sending %i bytes on chan %i\n",
(int)pkt->len, (int)ctx->chan);

/* setup radio */
set_context(ctx);
NRF_RADIO->PACKETPTR = (uint32_t)(pkt);

NRF_RADIO->EVENTS_DISABLED = 0;
NRF_RADIO->TASKS_TXEN = 1;
while (NRF_RADIO->EVENTS_DISABLED == 0) {
/* TODO: enable (and verify) sleep below */
// cortexm_sleep_until_event();
}

return (int)pkt->len;
}

static int nrfble_recv(netdev_t *dev, void *buf, size_t len, void *info)
{
(void)dev;
(void)len;

/* cancel any ongoing operation */
go_idle();

if (buf) {
assert(info);
assert(len == sizeof(netdev_ble_pkt_t));

rx_ctx = (netdev_ble_ctx_t *)info;

/* setup radio */
set_context(rx_ctx);
NRF_RADIO->PACKETPTR = (uint32_t)(buf);
NRF_RADIO->INTENSET = RADIO_INTENSET_DISABLED_Msk;

/* put radio into RX mode */
NRF_RADIO->EVENTS_DISABLED = 0;
NRF_RADIO->TASKS_RXEN = 1;
DEBUG("[nrfble] recv: now in RX mode (chan %i)\n", (int)rx_ctx->chan);
}

return 0;
}

static void nrfble_isr(netdev_t *dev)
{
if (nrfble_dev.event_callback) {
nrfble_dev.event_callback(dev, NETDEV_EVENT_RX_COMPLETE);
}
}

static int nrfble_get(netdev_t *dev, netopt_t opt, void *val, size_t max_len)
{
(void)dev;
(void)max_len;

switch (opt) {
case NETOPT_TX_POWER:
assert(max_len >= sizeof(int16_t));
*((int16_t *)val) = nrfble_get_txpower();
return sizeof(int16_t);
case NETOPT_DEVICE_TYPE:
assert(max_len >= sizeof(uint16_t));
*((uint16_t *)val) = NETDEV_TYPE_BLE;
return sizeof(uint16_t);
default:
return -ENOTSUP;
}
}

static int nrfble_set(netdev_t *dev, netopt_t opt, const void *val, size_t len)
{
(void)dev;
(void)len;

switch (opt) {
case NETOPT_TX_POWER:
assert(len == sizeof(int16_t));
nrfble_set_txpower(*((const int16_t *)val));
return sizeof(int16_t);
default:
return -ENOTSUP;
}
}

/**
* @brief Export of the netdev interface
*/
const netdev_driver_t nrfble_netdev = {
.send = nrfble_send,
.recv = nrfble_recv,
.init = nrfble_init,
.isr = nrfble_isr,
.get = nrfble_get,
.set = nrfble_set
};
@@ -0,0 +1,73 @@
/*
* Copyright (C) 2017 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 drivers_nrf5x_nrfble nRF BLE radio driver
* @ingroup drivers_netdev
* @brief Radio driver for nRF5x SoCs for using the radio in BLE mode
*
* @note This driver is not thread safe (as of now)
*
* @todo add support for netdev_ble_pkt_ext_t
* @todo figure out how to synchronize send and receive and how to make
* them thread safe
*
* @{
*
* @file
* @brief Interface definition for the nrfble radio driver
*
* @author Hauke Petersen <hauke.petersen@fu-berlin.de>
*/

#ifndef NRFBLE_H
#define NRFBLE_H

#include "net/netdev.h"

#ifdef __cplusplus
extern "C" {
#endif

/**
* @brief nrfble channel configuration
* @{
*/
#define NRFBLE_CHAN_MIN (0U)
#define NRFBLE_CHAN_MAX (39U)
/** @} */

/**
* @brief Default transmission power used
*/
#define NRFBLE_TXPOWER_DEFAULT (0) /* 0dBm */

/**
* @brief Export the netdev device descriptor
*/
extern netdev_t nrfble_dev;

/**
* @brief Reference to the netdev driver interface
*/
extern const netdev_driver_t nrfble_netdev;

/**
* @brief Setup the device driver's data structures
*
* @return pointer to the device's netdev struct
*/
netdev_t *nrfble_setup(void);

#ifdef __cplusplus
}
#endif

#endif /* NRFBLE_H */
/** @} */
@@ -122,3 +122,13 @@ NO_PSEUDOMODULES += periph_common
PSEUDOMODULES += auto_init_skald
PSEUDOMODULES += skald_ibeacon
PSEUDOMODULES += skald_eddystone

# Gorm provides a number of pseudomodules for fine grained stack configuration
PSEUDOMODULES += gorm_standalone
PSEUDOMODULES += gorm_broadcaster
PSEUDOMODULES += gorm_peripheral
PSEUDOMODULES += gorm_scanner
PSEUDOMODULES += gorm_central

# Provide a framework for Gorm GATT services (GGS)
PSEUDOMODULES += ggs_%
@@ -127,9 +127,15 @@ endif
ifneq (,$(filter skald,$(USEMODULE)))
DIRS += net/skald
endif
ifneq (,$(filter gorm,$(USEMODULE)))
DIRS += net/gorm
endif
ifneq (,$(filter harald,$(USEMODULE)))
DIRS += net/harald
endif
ifneq (,$(filter gorm_ibeacon,$(USEMODULE)))
DIRS += net/gorm/ibeacon
endif

DIRS += $(dir $(wildcard $(addsuffix /Makefile, $(USEMODULE))))

@@ -14,6 +14,10 @@ ifneq (,$(filter auto_init_can,$(USEMODULE)))
DIRS += can
endif

ifneq (,$(filter auto_init_gorm,$(USEMODULE)))
DIRS += gorm
endif

SRC = auto_init.c

ifneq (,$(filter auto_init_skald,$(USEMODULE)))
@@ -80,6 +80,9 @@
#include "net/gnrc/ipv6/nib.h"
#endif

#ifdef MODULE_GGS
#include "net/ggs.h"
#endif

#define ENABLE_DEBUG (0)
#include "debug.h"
@@ -156,6 +159,15 @@ void auto_init(void)
extern void auto_init_skald(void);
auto_init_skald();
#endif
#ifdef MODULE_AUTO_INIT_GORM
DEBUG("Auto init Gorm\n");
extern void auto_init_gorm(void);
auto_init_gorm();
#endif /* MODULE_GORM */
#ifdef MODULE_GGS
DEBUG("Auto init Gorm GATT Services\n");
ggs_init();
#endif

/* initialize network devices */
#ifdef MODULE_AUTO_INIT_GNRC_NETIF
@@ -0,0 +1,3 @@
MODULE = auto_init_gorm

include $(RIOTBASE)/Makefile.base
@@ -0,0 +1,124 @@
/*
* Copyright (C) 2017 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 net_gorm
* @{
*
* @file
* @brief Gorm's link layer implementation
*
* @author Hauke Petersen <hauke.petersen@fu-berlin.de>
* @}
*/

#include "log.h"
#include "assert.h"
#include "thread.h"

#include "net/gorm.h"
#include "net/gorm/pdupool.h"

/* we do some ugly device specific multiplexing here for now */
#if defined(MODULE_NRFBLE)
#include "nrfble.h"
#elif defined(MODULE_SOME_BLE_RADIO_DRIVER)
#include "somedrv.h"
#include "somedrv_params.h"
static somedrv_t somedrv_dev;
#else
#error "[auto_init_gorm] error: now compatible radio driver found"
#endif

/* set CREATE_STACKTEST flag depending on log level */
#if (LOG_LEVEL >= LOG_DEBUG)
#define TFLAGS THREAD_CREATE_STACKTEST
#else
#define TFLAGS (0)
#endif

/* allocate stack(s) */
static char stack_ctrl[GORM_CFG_THREAD_STACKSIZE_CONTROLLER];
#ifdef MODULE_GORM_STANDALONE
static char stack_host[GORM_CFG_THREAD_STACKSIZE_HOST]
#endif

/* TODO: move together with the pool mem allocation below */
static char pool[GORM_CFG_POOLSIZE * sizeof(gorm_buf_t)];

static netdev_t *radio_setup(void)
{
netdev_t *dev = NULL;

#if defined(MODULE_NRFBLE)
LOG_DEBUG("[auto_init_gorm] initializing nrfble #0\n");
dev = nrfble_setup();
#elif defined(MODULE_SOME_BLE_RADIO_DRIVER)
LOG_DEBUG("[auto_init_gorm] initializing somedrv #0\n");
somedrv_setup(&somedrv_dev, &somedrv_params[0]);
dev = (netdev_t *)(&somedrv_dev);
#endif

return dev;
}

static void *ctrl_thread(void *arg)
{
netdev_t *dev = (netdev_t *)arg;
gorm_run_controller(dev);
return NULL;
}

#ifdef MODULE_GORM_STANDALONE
static void *host_thread(void *arg)
{
(void)arg;
gorm_host_run();
return NULL;
}
#endif

void auto_init_gorm(void)
{
netdev_t *dev = radio_setup();

/* make sure we got a device handle */
if (!dev) {
LOG_ERROR("[auto_init_gorm] unable to setup radio for Gorm\n");
return;
}

/* allocate the PDU pool */
/* TODO: move somewhere better suited */
gorm_pdupool_addmem(pool, sizeof(pool));

/* initialize the host */
gorm_host_init();

/* run Gorm's link layer */
LOG_DEBUG("[auto_init_gorm] setting up Gorm's link layer thread now\n");
thread_create(stack_ctrl,
sizeof(stack_ctrl),
GORM_CFG_THREAD_PRIO_CONTROLLER,
TFLAGS,
ctrl_thread,
dev,
GORM_CFG_THREAD_NAME_CONTROLLER);

/* if standalone mode is activated, we also spawn Gorm's host thread now */
#ifdef MODULE_GORM_STANDALONE
LOG_DEBUG("[auto_init_gorm] setting up Gorm's host thread now\n");
thread_create(stack_host,
sizeof(stack_host),
GORM_CFG_THREAD_PRIO_HOST,
TFLAGS,
host_thread,
NULL,
GORM_CFG_THREAD_NAME_HOST);
#endif
}
@@ -0,0 +1,4 @@
SRC = ggs.c
SRC += $(patsubst ggs_%,%.c,$(filter ggs_%,$(USEMODULE)))

include $(RIOTBASE)/Makefile.base
@@ -0,0 +1,48 @@
/*
* Copyright (C) 2018 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 net_ggs
* @{
*
* @file
* @brief Bootstrapping of included GATT services for GORM
*
* @author Hauke Petersen <hauke.petersen@fu-berlin.de>
* @}
*/

#include "net/gorm/gatt.h"
#include "net/gorm/gatt/tab.h"

#include "net/ggs.h"
#include "net/ggs/riot.h"
#include "net/ggs/sig.h"

/* TDODO: move SIG services into the Gorm stack at some point? */
#ifdef MODULE_GGS_IPS
static gorm_gatt_entry_t ips_entry;
extern gorm_gatt_service_t ggs_ips_service;
#endif

#ifdef MODULE_GGS_RIOT_LED
static gorm_gatt_entry_t led_entry;
extern gorm_gatt_service_t ggs_riot_led_service;
#endif

void ggs_init(void)
{
#ifdef MODULE_GGS_IPS
gorm_gatt_tab_reg_service(&ips_entry, &ggs_ips_service);
#endif

#ifdef MODULE_GGS_RIOT_LED
gorm_gatt_tab_reg_service(&led_entry, &ggs_riot_led_service);
#endif
/* TODO: add SAUL etc ... */
}
@@ -0,0 +1,28 @@
/*
* Copyright (C) 2018 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 net_ggs_sig
* @{
*
* @file
* @brief Implementation of the BT SIG Internet Protocol Support Service
*
* @author Hauke Petersen <hauke.petersen@fu-berlin.de>
* @}
*/

#include "net/gorm/uuid.h"

#include "net/ggs/sig.h"

const gorm_gatt_service_t ggs_ips_service = {
.uuid = GORM_UUID(GGS_UUID_IPS, NULL),
.char_cnt = 0, /* this service has no characteristics */
.chars = NULL,
};
@@ -0,0 +1,194 @@
/*
* Copyright (C) 2018 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 net_ggs_riot
* @{
*
* @file
* @brief RIOT specific LED GATT service for Gorm
*
* @author Hauke Petersen <hauke.petersen@fu-berlin.de>
* @}
*/

#include "net/ggs/riot.h"
#include "net/gorm/gatt/desc.h"

#define ENABLE_DEBUG (0)
#include "debug.h"

/* we assume them all initially turned off */
static uint8_t _led_state = 0;

#if defined(LED7_ON)
#define NUMOF_LED (8U)
#elif defined(LED6_ON)
#define NUMOF_LED (7U)
#elif defined(LED5_ON)
#define NUMOF_LED (6U)
#elif defined(LED4_ON)
#define NUMOF_LED (5U)
#elif defined(LED3_ON)
#define NUMOF_LED (4U)
#elif defined(LED2_ON)
#define NUMOF_LED (3U)
#elif defined(LED1_ON)
#define NUMOF_LED (2U)
#elif defined(LED0_ON)
#define NUMOF_LED (1U)
#else
#error "Gorm GATT service 'LED': unable to build - no LEDs available"
#endif

static inline void _write(unsigned led, uint8_t state)
{
if (state) {
_led_state |= (1 << led);
switch (led) {
case 0: LED0_ON; break;
case 1: LED1_ON; break;
case 2: LED2_ON; break;
case 3: LED3_ON; break;
case 4: LED4_ON; break;
case 5: LED5_ON; break;
case 6: LED6_ON; break;
case 7: LED7_ON; break;
default: break; /* do nothing */
}
}
else {
_led_state &= ~(1 << led);
switch (led) {
case 0: LED0_OFF; break;
case 1: LED1_OFF; break;
case 2: LED2_OFF; break;
case 3: LED3_OFF; break;
case 4: LED4_OFF; break;
case 5: LED5_OFF; break;
case 6: LED6_OFF; break;
case 7: LED7_OFF; break;
default: break; /* do nothing */
}
}
}

static size_t _led_cb(const gorm_gatt_char_t *c, uint8_t method,
uint8_t *buf, size_t buf_len)
{
if (buf_len < 1) {
return 0;
}

unsigned led = (unsigned)c->arg;


switch (method) {
case GORM_GATT_READ:
buf[0] = ((_led_state >> led) & 0x1);
return 1;
case GORM_GATT_WRITE:
_write(led, buf[0]);
return 1;
default:
return 0;
}
}

static const gorm_gatt_desc_t _led_desc[] = {
GORM_GATT_DESC_FMT(BLE_UNIT_BLE_FMT_BOOL, 0, BLE_UNIT_NONE),
};

static const gorm_gatt_char_t _led_chars[] = {
#ifdef LED0_ON
{
.cb = _led_cb,
.arg = (void *)0,
.type = GORM_UUID(GORM_UUID_RIOT_LED_CHAR, &ggs_uuid_riot_base),
.perm = (BLE_ATT_READ | BLE_ATT_WRITE),
.desc_cnt = 1,
.desc = _led_desc,
},
#endif
#ifdef LED1_ON
{
.cb = _led_cb,
.arg = (void *)1,
.type = GORM_UUID(GORM_UUID_RIOT_LED_CHAR, &ggs_uuid_riot_base),
.perm = (BLE_ATT_READ | BLE_ATT_WRITE),
.desc_cnt = 1,
.desc = _led_desc,
},
#endif
#ifdef LED2_ON
{
.cb = _led_cb,
.arg = (void *)2,
.type = GORM_UUID(GORM_UUID_RIOT_LED_CHAR, &ggs_uuid_riot_base),
.perm = (BLE_ATT_READ | BLE_ATT_WRITE),
.desc_cnt = 1,
.desc = _led_desc,
},
#endif
#ifdef LED3_ON
{
.cb = _led_cb,
.arg = (void *)3,
.type = GORM_UUID(GORM_UUID_RIOT_LED_CHAR, &ggs_uuid_riot_base),
.perm = (BLE_ATT_READ | BLE_ATT_WRITE),
.desc_cnt = 1,
.desc = _led_desc,
},
#endif
#ifdef LED4_ON
{
.cb = _led_cb,
.arg = (void *)4,
.type = GORM_UUID(GORM_UUID_RIOT_LED_CHAR, &ggs_uuid_riot_base),
.perm = (BLE_ATT_READ | BLE_ATT_WRITE),
.desc_cnt = 1,
.desc = _led_desc,
},
#endif
#ifdef LED5_ON
{
.cb = _led_cb,
.arg = (void *)5,
.type = GORM_UUID(GORM_UUID_RIOT_LED_CHAR, &ggs_uuid_riot_base),
.perm = (BLE_ATT_READ | BLE_ATT_WRITE),
.desc_cnt = 1,
.desc = _led_desc,
},
#endif
#ifdef LED6_ON
{
.cb = _led_cb,
.arg = (void *)6,
.type = GORM_UUID(GORM_UUID_RIOT_LED_CHAR, &ggs_uuid_riot_base),
.perm = (BLE_ATT_READ | BLE_ATT_WRITE),
.desc_cnt = 1,
.desc = _led_desc,
},
#endif
#ifdef LED7_ON
{
.cb = _led_cb,
.arg = (void *)7,
.type = GORM_UUID(GORM_UUID_RIOT_LED_CHAR, &ggs_uuid_riot_base),
.perm = (BLE_ATT_READ | BLE_ATT_WRITE),
.desc_cnt = 1,
.desc = _led_desc,
},
#endif
};

const gorm_gatt_service_t ggs_riot_led_service = {
.uuid = GORM_UUID(GORM_UUID_RIOT_LED_SERVICE, &ggs_uuid_riot_base),
.char_cnt = NUMOF_LED,
.chars = _led_chars,
};
@@ -0,0 +1,39 @@
/*
* Copyright (C) 2018 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 net_ggs GATT Services for Gorm
* @ingroup net
* @brief Collection of pre-defined GATT services to be used with Gorm
* @{
*
* @file
* @brief Bootstrapping interface for the built-in Gorm GATT services
*
* @author Hauke Petersen <hauke.petersen@fu-berlin.de>
*/

#ifndef GORM_SERVICE_RIOT_H
#define GORM_SERVICE_RIOT_H

#ifdef __cplusplus
extern "C" {
#endif

/**
* @brief Initialize all configured built-in services and register them with
* the GATT table
*/
void ggs_init(void);

#ifdef __cplusplus
}
#endif

#endif /* GORM_SERVICE_RIOT_H */
/** @} */
@@ -0,0 +1,50 @@
/*
* Copyright (C) 2018 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 net_ggs_riot RIOT GATT services for Gorm
* @ingroup net_ggs
* @brief RIOT specific GATT services to be used with Gorm
* @{
*
* @file
* @brief Definition of RIOT specific GATT defines for Gorm
*
* @author Hauke Petersen <hauke.petersen@fu-berlin.de>
*/

#ifndef GORM_SERVICE_RIOT_H
#define GORM_SERVICE_RIOT_H

#include "net/gorm/gatt.h"
#include "net/gorm/uuid.h"

#ifdef __cplusplus
extern "C" {
#endif

#define GORM_UUID_RIOT_LED_SERVICE (0x0001)
#define GORM_UUID_RIOT_LED_CHAR (0x0002)

static const gorm_uuid_base_t ggs_uuid_riot_base = {{
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x0a, 0x0f, 0x0f, 0x0e,
0x00, 0x00, 0x52, 0x49, 0x4f, 0x54
}};

/**
* @brief Export the RIOT LED service
*/
extern const gorm_gatt_service_t ggs_riot_led_service;

#ifdef __cplusplus
}
#endif

#endif /* GORM_SERVICE_RIOT_H */
/** @} */
@@ -0,0 +1,49 @@
/*
* Copyright (C) 2018 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 net_ggs_sig BT SIG defined GATT services
* @ingroup net_ggs
* @brief BT SIG specific GATT services
* @{
*
* @file
* @brief Definition of BT SIG specific GATT
*
* @author Hauke Petersen <hauke.petersen@fu-berlin.de>
*/

#ifndef GORM_SERVICE_RIOT_H
#define GORM_SERVICE_RIOT_H

#include "net/gorm/gatt.h"

#define GGS_UUID_IPS (0x1820)

#ifdef __cplusplus
extern "C" {
#endif

/* TODO: not sure we ever need this */
// static const gorm_uuid_base_t gorm_uuid_sig_base = {{
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// 0x10, 0x00, 0x80, 0x00,
// 0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb
// }};

/**
* @brief Export the `Internet Protocol Support Service`
*/
extern const gorm_gatt_service_t ggs_ips_service;

#ifdef __cplusplus
}
#endif

#endif /* GORM_SERVICE_RIOT_H */
/** @} */
@@ -0,0 +1,85 @@
/*
* Copyright (C) 2018 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 net_gorm Gorm, the daddy of Bluetooth
* @ingroup net
* @brief Gorm, the BLE stack for RIOT
*
* TODO more high-level doc
*
* @todo Unify concept of static vs dynamic configuaration values, e.g.
* - advertisement intervals (conn vs nonconn)
* @todo Address handling: generation of public addresses etc
* @todo Use netdev correctly (do proper switching to thread context)
* @todo Implement and use `event_post_first()` and use in link layer for
* received packets
* @todo check for consistent naming of static functions (_x vs x)
*
* @{
* @file
* @brief High level interface for telling Gorm how to behave
*
* @author Hauke Petersen <hauke.petersen@fu-berlin.de>
*/

#ifndef GORM_H
#define GORM_H

#include <stdint.h>

#include "clist.h"

#include "net/netdev/ble.h"
#include "net/gorm/config.h"

#ifdef __cplusplus
extern "C" {
#endif

/**
* @brief Maximum link layer PDU length (excluding 2 byte header) [0:255] bytes
*/
#define GORM_PDU_MAXLEN (255U)

typedef struct gorm_pdu {
struct gorm_pdu *next;
netdev_ble_pkt_t pkt;
} gorm_buf_t;

/**
* @brief Gorm's error codes
*/
/* TODO: Move to dedicated header?! */
enum {
GORM_OK = 0,
GORM_ERR_TIMINGS = -1,
};


/**
* @brief Run Gorm's link layer
*/
void gorm_run_controller(netdev_t *radio);

/**
* @brief Initialize Gorm's host implementation
*/
void gorm_host_init(void);

/**
* @brief Run Gorm's host layers
*/
void gorm_host_run(void);

#ifdef __cplusplus
}
#endif

#endif /* GORM_H */
/** @} */
@@ -0,0 +1,5 @@


typedef event_t gorm_arch_event_t;

gorm_arch_event_post_first(gorm_arch_event_t )
@@ -0,0 +1,46 @@
/*
* Copyright (C) 2018 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 net_gorm_arch
* @{
*
* @file
* @brief Mapping of the platform dependent (pseudo-)random number
* generator
*
* @todo Think about inlining...
*
* @author Hauke Petersen <hauke.petersen@fu-berlin.de>
*/

#ifndef GORM_ARCH_RAND_H
#define GORM_ARCH_RAND_H

#include <stdio.h>

#ifdef __cplusplus
extern "C" {
#endif

/**
* @brief Generate a random number that lies within the [min,max)-interval
*
* @param[in] min lower bound of output values (including this value)
* @param[in] max upper bound of output values (excluding this value)
*
* @return (pseudo-)random value from the [min,max)-interval
*/
uint32_t gorm_arch_rand(uint32_t min, uint32_t max);

#ifdef __cplusplus
}
#endif

#endif /* RANDOM_H */
/** @} */
@@ -0,0 +1,57 @@
/*
* Copyright (C) 2018 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 net_gorm_arch
* @{
*
* @file
* @brief Gorm's timer abstraction
*
* @note All timer callbacks are supposed to be run in interrupt context
*
* @author Hauke Petersen <hauke.petersen@fu-berlin.de>
*/

#ifndef GORM_ARCH_TIMER_H
#define GORM_ARCH_TIMER_H

#include "xtimer.h"

#ifdef __cplusplus
extern "C" {
#endif

/* TODO: let architecture specific implementation define this somehow */
typedef struct {
xtimer_t base;
uint32_t last;
} gorm_arch_timer_t;

typedef void (*gorm_arch_timer_cb_t)(void *);

/* does set last value to now() */
void gorm_arch_timer_anchor(gorm_arch_timer_t *timer, uint32_t compensation);

/* will change last value to last + timeout */
void gorm_arch_timer_set_from_last(gorm_arch_timer_t *timer, uint32_t timeout,
gorm_arch_timer_cb_t cb, void *arg);

/* does not touch last value */
void gorm_arch_timer_set_from_now(gorm_arch_timer_t *timer, uint32_t timeout,
gorm_arch_timer_cb_t cb, void *arg);

/* cancel the timer */
void gorm_arch_timer_cancel(gorm_arch_timer_t *timer);

#ifdef __cplusplus
}
#endif

#endif /* GORM_ARCH_TIMER_H */
/** @} */
@@ -0,0 +1,65 @@
/*
* Copyright (C) 2018 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 net_gorm
* @{
*
* @file
* @brief Gorm's ATT (Attribute Protocol) implementation
*
* @author Hauke Petersen <hauke.petersen@fu-berlin.de>
*/

#ifndef GORM_ATT_H
#define GORM_ATT_H

#include "net/gorm.h"
#include "net/gorm/uuid.h"

#ifdef __cplusplus
extern "C" {
#endif

/**
* @name ATT error codes
* @{
*/
/** @} */

/**
* @name Attribute opcodes
* @{
*/
/** @} */

/**
* @name Attribute properties
* @{
*/
/** @} */

/**
* @name Permission flags
*
* TODO: probably need re-definition...
* @{
*/
#define GORM_ATT_READABLE (0x01)
#define GORM_ATT_WRITABLE (0x02)
#define GORM_ATT_ENC_REQ (0x04)
#define GORM_ATT_AUTHEN_REQ (0x08)
#define GORM_ATT_AUTHOR_REQ (0x10)
/** @} */

#ifdef __cplusplus
}
#endif

#endif /* GORM_PDUQ_H */
/** @} */
@@ -0,0 +1,119 @@
/*
* Copyright (C) 2018 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 sys_gorm
* @{
*
* @file
* @brief Gorm's global configuration
*
* @author Hauke Petersen <hauke.petersen@fu-berlin.de>
*/

#ifndef GORM_CONFIG_H
#define GORM_CONFIG_H

#ifdef __cplusplus
extern "C" {
#endif

#ifndef GORM_CFG_LL_PERIPH_CONNECTIONS_LIMIT
#define GORM_CFG_LL_PERIPH_CONNECTIONS_LIMIT (1U)
#endif

#ifndef GORM_CFG_LL_PERIPH_ADV_INTERVAL
#define GORM_CFG_LL_PERIPH_ADV_INTERVAL (1U * US_PER_SEC)
#endif

#ifndef GORM_CFG_LL_PERIPH_ADV_EVENT_DURATION
#define GORM_CFG_LL_PERIPH_ADV_EVENT_DURATION (10U * US_PER_MS)
#endif

#ifndef GORM_CFG_LL_PERIPH_ADV_CHANNELS
#define GORM_CFG_LL_PERIPH_ADV_CHANNELS { 37, 38, 39 }
// #define GORM_CFG_LL_PERIPH_ADV_CHANNELS { 39 }
#endif

/* ADVANCED */
/**
* @brief Compensate for the time passed between the start of the first packet
* received in a connection and the time when the actual callback is
* triggered, in [us]
*/
#ifndef GORM_CFG_LL_ANCHOR_OFFSET
#define GORM_CFG_LL_ANCHOR_OFFSET (350U)
#endif

/* ADVANCED */
/**
* @brief Static window widening value, roughly estimated...
*/
#ifndef GORM_CFG_LL_WIN_WIDENING
#define GORM_CFG_LL_WIN_WIDENING (50U)
#endif

/* ADVANCED */
/**
* @brief Time to listen for incoming data while in a connection
*
* Default time is 300us (2 times T_IFS)
*/
#ifndef GORM_CFG_LL_RX_TIMEOUT
#define GORM_CFG_LL_RX_TIMEOUT (300U + GORM_CFG_LL_RX_RAMPUP_DELAY)
#endif


/* TODO: nRF52 specific timing, move to CPU/radio specific config section */
#ifndef GORM_CFG_LL_RX_RAMPUP_DELAY
#define GORM_CFG_LL_RX_RAMPUP_DELAY (165U)
#endif


#ifndef GORM_CFG_VENDOR_ID
#define GORM_CFG_VENDOR_ID (0xffff)
#endif

#ifndef GORM_CFG_SUB_VERS_NR
#define GORM_CFG_SUB_VERS_NR (0xabcd)
#endif

#ifndef GORM_CFG_POOLSIZE
#define GORM_CFG_POOLSIZE (10U)
#endif

/**
* @brief Thread priority assigned to the Gorm thread in standalone mode
*
* @todo Move to RIOT specific configuration!
*/
#ifndef GORM_CFG_THREAD_STACKSIZE_HOST
#define GORM_CFG_THREAD_STACKSIZE_HOST (THREAD_STACKSIZE_DEFAULT)
#endif
#ifndef GORM_CFG_THREAD_PRIO_HOST
#define GORM_CFG_THREAD_PRIO_HOST (THREAD_PRIORITY_MAIN - 1)
#endif
#ifndef GORM_CFG_THREAD_NAME_HOST
#define GORM_CFG_THREAD_NAME_HOST "gorm_host"
#endif

#ifndef GORM_CFG_THREAD_STACKSIZE_CONTROLLER
#define GORM_CFG_THREAD_STACKSIZE_CONTROLLER (THREAD_STACKSIZE_DEFAULT)
#endif
#ifndef GORM_CFG_THREAD_PRIO_CONTROLLER
#define GORM_CFG_THREAD_PRIO_CONTROLLER (0)
#endif
#ifndef GORM_CFG_THREAD_NAME_CONTROLLER
#define GORM_CFG_THREAD_NAME_CONTROLLER "gorm_ctrl"
#endif

#ifdef __cplusplus
}
#endif

#endif /* GORM_CONFIG_H */
@@ -0,0 +1,156 @@
/*
* Copyright (C) 2017 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 net_gorm_gap GAP for Gorm
* @ingroup net_gorm
* @brief Gorm's GAP implementation
*
* # Design Decisions and Limitations
* - no support for sending additional information in a SCAN_RESP packet
*
* # Implementation State
*
* @{
*
* @file
* @brief Gorm's GAP mapping
*
* @author Hauke Petersen <hauke.petersen@fu-berlin.de>
*/

#ifndef GORM_GAP_H
#define GORM_GAP_H

#include <stddef.h>
#include <stdint.h>

#include "net/ble.h"
#include "net/gorm/ll.h"

#ifdef __cplusplus
extern "C" {
#endif

/**
* @brief Maximum AD data length allowed [in bytes]
*/
#define GORM_GAP_AD_MAXLEN (31U)

/* TODO: move to ble.h */
/**
* @name GAP advertisement data type values
*
* @see https://www.bluetooth.com/specifications/assigned-numbers/generic-access-profile
*
* @{
*/
#define GORM_GAP_FLAGS (0x01)
#define GORM_GAP_UUID16_INCOMP (0x02)
#define GORM_GAP_UUID16_COMP (0x03)
#define GORM_GAP_UUID32_INCOMP (0x04)
#define GORM_GAP_UUID32_COMP (0x05)
#define GORM_GAP_UUID128_INCOMP (0x06)
#define GORM_GAP_UUID128_COMP (0x07)
#define GORM_GAP_NAME_SHORT (0x08)
#define GORM_GAP_NAME (0x09)
// #define GORM_GAP Tx Power Level (0x0A)
// #define GORM_GAP Class of Device (0x0D)
// #define GORM_GAP Simple Pairing Hash C (0x0E)
// #define GORM_GAP Simple Pairing Hash C-192 (0x0E)
// #define GORM_GAP Simple Pairing Randomizer R (0x0F)
// #define GORM_GAP Simple Pairing Randomizer R-192 (0x0F)
// #define GORM_GAP Device ID (0x10)
// #define GORM_GAP Security Manager TK Value (0x10)
// #define GORM_GAP Security Manager Out of Band Flags (0x11)
// #define GORM_GAP Slave Connection Interval Range (0x12)
// #define GORM_GAP List of 16-bit Service Solicitation UUIDs (0x14)
// #define GORM_GAP List of 128-bit Service Solicitation UUIDs (0x15)
#define GORM_GAP_SERVICE_DATA (0x16)
#define GORM_GAP_SERVICE_DATA_UUID16 (0x16)
#define GORM_GAP_ADDR_PUBLIC (0x17)
#define GORM_GAP_ADDR_RANDOM (0x18)
#define GORM_GAP_APPEARANCE (0x19)
// #define GORM_GAP Advertising Interval (0x1A)
// #define GORM_GAP LE Bluetooth Device Address (0x1B)
// #define GORM_GAP LE Role (0x1C)
// #define GORM_GAP Simple Pairing Hash C-256 (0x1D)
// #define GORM_GAP Simple Pairing Randomizer R-256 (0x1E)
// #define GORM_GAP List of 32-bit Service Solicitation UUIDs (0x1F)
// #define GORM_GAP Service Data - 32-bit UUID (0x20)
// #define GORM_GAP Service Data - 128-bit UUID (0x21)
// #define GORM_GAP LE Secure Connections Confirmation Value (0x22)
// #define GORM_GAP LE Secure Connections Random Value (0x23)
// #define GORM_GAP URI (0x24)
// #define GORM_GAP Indoor Positioning (0x25)
// #define GORM_GAP Transport Discovery Data (0x26)
// #define GORM_GAP LE Supported Features (0x27)
// #define GORM_GAP Channel Map Update Indication (0x28)
// #define GORM_GAP 3D Information Data (0x3D)
#define GORM_GAP_VENDOR (0xFF)
/** @} */

/* TODO: move to ble.h */
/**
* @name Available flags in the GORM_GAP_FLAGS field
* @{
*/
#define GORM_GAP_DISCOVER_LIM (0x01)
#define GORM_GAP_DISCOVERABLE (0x02)
#define GORM_GAP_FLAG_BREDR_NOTSUP (0x04)
/** @} */


#define GORM_GAP_FLAGS_DEFAULT (GORM_GAP_DISCOVERABLE | \
GORM_GAP_FLAG_BREDR_NOTSUP)


void gorm_gap_init(const char *name, uint16_t appearance);

size_t gorm_gap_copy_name(uint8_t *buf, size_t max_len);

void gorm_gap_copy_appearance(uint8_t *buf);


/**
* @brief Add advertising data field to the given buffer
*
* @param[out] buf [description]
* @param[in] pos
* @param[in] type [description]
* @param[in] data [description]
* @param[in] len [description]
*
* @return overall length of AD data in the buffer
*/
size_t gorm_gap_ad_add(uint8_t *buf, size_t pos,
uint8_t type, const void *data, size_t len);


/**
* @brief Convenience function for writing flags to given AD data
*
* @param[out] buf AD data buffer
* @param[in] flags flags to use, typically GORM_GAP_FLAGS_DEFAULT
*
* @return always returns 3 (as the flag field is 3 bytes long)
*/
static inline size_t gorm_gap_ad_add_flags(uint8_t *buf, size_t pos,
uint8_t flags)
{
return gorm_gap_ad_add(buf, pos, GORM_GAP_FLAGS, &flags, 1);
}

/* @todo add functions for reading AD data when running a scanner */

#ifdef __cplusplus
}
#endif

#endif /* GORM_LL_H */
/** @} */
@@ -0,0 +1,160 @@
/*
* Copyright (C) 2017-2018 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 net_gorm_gatt
* @ingroup net_gorm
* @brief Gorm's GATT implementation
*
*
* # Implementation status
*
* ## Descriptors
* So far the implementation supports only descriptors of type
* - extended properties (0x2900)
* - presentation format (0x2904)
* - user description (0x2901)
*
* @{
*
* @file
* @brief Gorm's GATT interface
*
* @author Hauke Petersen <hauke.petersen@fu-berlin.de>
*/

#ifndef GORM_GATT_H
#define GORM_GATT_H

#include <stdint.h>

#include "net/ble.h"
#include "net/gorm/ll.h"
#include "net/gorm/att.h"
#include "net/gorm/uuid.h"

#ifdef __cplusplus
extern "C" {
#endif

/**
* @name GATT MTU size
*
* TODO: make this non-static an negotiate this from the con object
*/
#define GORM_GATT_MAX_MTU (NETDEV_BLE_PDU_MAXLEN - 4)
#define GORM_GATT_DEFAULT_MTU (23U)

enum {
GORM_GATT_READ,
GORM_GATT_WRITE,
};

enum {
GORM_GATT_OP_NOT_SUPPORTED = 0,
GORM_GATT_OP_OK = 1,
};

typedef struct gorm_gatt_char gorm_gatt_char_t;
typedef struct gorm_gatt_desc gorm_gatt_desc_t;

typedef size_t (*gorm_gatt_char_cb_t)(const gorm_gatt_char_t *characteristic,
uint8_t method,
uint8_t *buf, size_t buf_len);

/* as of now, Gorm's descriptors are read-only */
typedef size_t (*gorm_gatt_desc_cb_t)(const gorm_gatt_desc_t *descriptor,
uint8_t *buf, size_t buf_len);

struct gorm_gatt_desc {
gorm_gatt_desc_cb_t cb;
uint32_t arg;
uint16_t type; /**< 16-bit UUIDs only */
};

struct gorm_gatt_char {
gorm_gatt_char_cb_t cb;
void *arg;
gorm_uuid_t type;
uint8_t perm;
uint16_t desc_cnt;
const gorm_gatt_desc_t *desc;
};

typedef struct {
gorm_uuid_t uuid;
/* TODO: how to handle includes?
* -> maybe simply: GORM does not support includes?! */
uint16_t char_cnt;
const gorm_gatt_char_t *chars;
} gorm_gatt_service_t;

/**
* @brief Entry in the GATT table containing a service
*/
typedef struct gorm_gatt_entry {
struct gorm_gatt_entry *next;
const gorm_gatt_service_t *service;
uint16_t handle;
} gorm_gatt_entry_t;



void gorm_gatt_server_init(void);

void gorm_gatt_on_data(gorm_ll_connection_t *con, gorm_buf_t *buf,
uint8_t *data, size_t len);






gorm_gatt_entry_t *gorm_gatt_tab_find_service(uint16_t start_from);

/**
* @brief Get the service identified by the service part of the given handle
*
* @param[in] handle handle with valid service part
*
* @return the service entry for the given handle
* @return NULL if service could not be found
*/
gorm_gatt_entry_t *gorm_gatt_tab_get_service(uint16_t handle);





int gorm_gatt_tab_find_char(gorm_gatt_entry_t *entry, uint16_t start_handle);

gorm_gatt_desc_t *gorm_gatt_tab_get_desc(uint16_t handle);

uint16_t gorm_gatt_tab_get_end_handle(gorm_gatt_entry_t *entry);
uint16_t gorm_gatt_tab_get_char_handle(gorm_gatt_entry_t *entry, uint16_t num);
uint16_t gorm_gatt_tab_get_val_handle(gorm_gatt_entry_t *entry, uint16_t num);

/* TDOO: just for debugging -> remove or move ... */
void gorm_gatt_tab_print(void);




/**
* GATT Table interface:
*
* - get base x: ignore lower bits and match only higher ones
* - get exact x: use all bits and check lower ones to be 0
*/

#ifdef __cplusplus
}
#endif

#endif /* GORM_GATT_H */
/** @} */
@@ -0,0 +1,76 @@
/*
* Copyright (C) 2018 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 net_gorm_gatt
* @{
*
* @file
* @brief GATT characteristic description formats
*
* @author Hauke Petersen <hauke.petersen@fu-berlin.de>
*/

#ifndef GORM_GATT_DESC_H
#define GORM_GATT_DESC_H

#include <stdint.h>

#include "net/gorm/gatt.h"

#ifdef __cplusplus
extern "C" {
#endif

#define GORM_GATT_DESC_FMT(f, e, u) \
{ \
.type = BLE_DESC_PRES_FMT, \
.cb = gorm_gatt_desc_pres_fmt_cb, \
.arg = (uint32_t)((f << 24) | (e << 16) | u), \
}

#define GORM_GATT_DESC_EXT_PROP(rwe, wae) \
{ \
.type = BLE_DESC_EXT_PROP, \
.cb = gorm_gatt_desc_ext_prop_cb, \
.arg = (uint32_t)((rwe & 0x1) | ((wae & 0x01) << 1)), \
}

#define GORM_GATT_DESC_USER_DESC(str_ptr) \
{ \
.type = BLE_DESC_USER_DESC, \
.cb = gorm_gatt_desc_user_desc_cb, \
.arg = (uint32_t)str_ptr, \
}

#define GORM_GATT_DESC_CLIENT_CONF(noe, ine) \
{ \
.type = BLE_DESC_CLIENT_CONFIG, \
.cb = gorm_gatt_desc_client_conf_cb, \
.arg = (uint32_t)((noe & 0x01) | ((ine & 0x01) << 1)), \
}

size_t gorm_gatt_desc_pres_fmt_cb(const gorm_gatt_desc_t *desc,
uint8_t *buf, size_t buf_len);

size_t gorm_gatt_desc_ext_prop_cb(const gorm_gatt_desc_t *desc,
uint8_t *buf, size_t buf_len);

size_t gorm_gatt_desc_user_desc_cb(const gorm_gatt_desc_t *desc,
uint8_t *buf, size_t buf_len);

size_t gorm_gatt_desc_client_conf_cb(const gorm_gatt_desc_t *desc,
uint8_t *buf, size_t buf_len);

#ifdef __cplusplus
}
#endif

#endif /* GORM_GATT_DESC_H */
/** @} */

@@ -0,0 +1,90 @@
/*
* Copyright (C) 2018 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 net_gorm_gatt_tab Gorm's GATT Table
* @ingroup net_gorm_gatt
* @brief Gorm's GATT table implementation
* @{
*
* @file
* @brief Gorm's GATT table interface
*
* @author Hauke Petersen <hauke.petersen@fu-berlin.de>
*/

#ifndef GORM_GATT_TAB_H
#define GORM_GATT_TAB_H

#include "net/gorm/gatt.h"

#ifdef __cplusplus
extern "C" {
#endif

#define GORM_GATT_CHAR_VAL_MASK (0x0008)

/**
* @brief GATT table iterator
*/
typedef struct {
const gorm_gatt_entry_t *e; /**< base entry (points to a single service) */
const gorm_gatt_char_t *c; /**< pointer to a characteristic */
const gorm_gatt_desc_t *d; /**< pointer to a descriptor */
uint16_t handle; /**< corresponding/current handle */
} gorm_gatt_tab_iter_t;


/**
* @brief Initialize Gorm's GATT table
*/
void gorm_gatt_tab_init(void);


void gorm_gatt_tab_reg_service(gorm_gatt_entry_t *entry,
const gorm_gatt_service_t *service);

/**
* @brief Read the given handle
*/
/* good */
void gorm_gatt_tab_get_by_handle(gorm_gatt_tab_iter_t *iter);

/* good */
void gorm_gatt_tab_get_next(gorm_gatt_tab_iter_t *iter);


static inline int gorm_gatt_tab_is_service(const gorm_gatt_tab_iter_t *iter)
{
return ((iter->e != NULL) && (iter->c == NULL));
}

static inline int gorm_gatt_tab_is_char_decl(const gorm_gatt_tab_iter_t *iter)
{
return ((iter->c != NULL) && (iter->d == NULL) &&
!(iter->handle & GORM_GATT_CHAR_VAL_MASK));
}

static inline int gorm_gatt_tab_is_char_val(const gorm_gatt_tab_iter_t *iter)
{
return ((iter->c != NULL) && (iter->d == NULL) &&
(iter->handle & GORM_GATT_CHAR_VAL_MASK));
}

static inline int gorm_gatt_tab_is_decl(const gorm_gatt_tab_iter_t *iter)
{
return (iter->d != NULL);
}


#ifdef __cplusplus
}
#endif

#endif /* GORM_GATT_TAB_H */
/** @} */
@@ -0,0 +1,38 @@
/*
* Copyright (C) 2017 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 net_gorm_ibeacon Gorm's iBeacon interface
* @ingroup net_gorm
* @brief Gorm's dedicated iBeacon handling submodule
* @{
*
* @file
* @brief Gorm's iBeacon specific interface
*
* @author Hauke Petersen <hauke.petersen@fu-berlin.de>
*/

#ifndef GORM_IBEACON_H
#define GORM_IBEACON_H

#include "net/gorm.h"

#ifdef __cplusplus
extern "C" {
#endif

void gorm_ibeacon_setup(gorm_uuid_t *uuid, uint16_t major, uint16_t minor,
uint8_t txpower, uint32_t adv_interval);

#ifdef __cplusplus
}
#endif

#endif /* GORM_IBEACON_H */
/** @} */
@@ -0,0 +1,59 @@
/*
* Copyright (C) 2018 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 net_gorm
* @{
*
* @file
* @brief Gorm's L2CAP layer interface
*
* @author Hauke Petersen <hauke.petersen@fu-berlin.de>
*/

#ifndef GORM_L2CAP_H
#define GORM_L2CAP_H

#include "net/gorm/ll.h"
#include "net/gorm/pduq.h"
#include "net/gorm/util.h"

#ifdef __cplusplus
extern "C" {
#endif

/**
* @name Channel identifiers used by L2CAP for LE-U link layers
* @{
*/
#define GORM_L2CAP_CID_ATT (0x0004)
#define GORM_L2CAP_CID_LE_SIGNAL (0x0005)
#define GORM_L2CAP_CID_SM (0x0006)
#define GORM_L2CAP_CID_CB_MIN (0x0040)
#define GORM_L2CAP_CID_CB_MAX (0x007f)
/** @} */

#define GORM_L2CAP_HDR_LEN (4U)

/* higher layer interface functions */
int gorm_l2cap_send(gorm_ll_connection_t *con, void *data, size_t len);



void gorm_l2cap_reply(gorm_ll_connection_t *con,
gorm_buf_t *buf, uint16_t data_len);

void gorm_l2cap_on_data(gorm_ll_connection_t *con, uint8_t llid,
gorm_buf_t *data);

#ifdef __cplusplus
}
#endif

#endif /* GORM_L2CAP_H */
/** @} */
@@ -0,0 +1,231 @@
/*
* Copyright (C) 2017,2018 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 net_gorm
* @{
*
* @file
* @brief Gorm's BLE link layer
*
* @author Hauke Petersen <hauke.petersen@fu-berlin.de>
*
* @todo Split this header for periph/bcaster/host/...
*/

#ifndef GORM_LL_H
#define GORM_LL_H

#include "mutex.h"
#include "assert.h"
#include "event/timeout.h"
#include "event/callback.h"

#include "net/netdev/ble.h"
#include "net/gorm.h"
#include "net/gorm/pduq.h"
#include "net/gorm/arch/timer.h"

#ifdef __cplusplus
extern "C" {
#endif

/**
* @name Constant value of advertising CRC and access address
* @{
*/
#define GORM_LL_ADV_AA (0x8e89bed6)
#define GORM_LL_ADV_CRC (0x00555555)
/** @} */

/* TODO: moved to ble.h */
/**
* @name BLE advertising data packet types
* @{
*/
#define GORM_LL_PDU_MASK (0x0f)
#define GORM_LL_ADV_IND (0x00)
#define GORM_LL_DIRECT_IND (0x01)
#define GORM_LL_ADV_NONCON_IND (0x02)
#define GORM_LL_SCAN_REQ (0x03)
#define GORM_LL_AUX_SCAN_REQ (0x03)
#define GORM_LL_SCAN_RESP (0x04)
#define GORM_LL_CONNECT_IND (0x05)
#define GORM_LL_AUX_CONNECT_REQ (0x05)
#define GORM_LL_ADV_SCAN_IND (0x06)
#define GORM_LL_ADV_EXT_IND (0x07)
#define GORM_LL_AUX_ADV_IND (0x07)
#define GORM_LL_AUX_SCAN_RSP (0x07)
#define GORM_LL_AUX_SYNC_IND (0x07)
#define GORM_LL_AUX_CHAIN_IND (0x07)
#define GORM_LL_CONNECT_RESP (0x08)
/** @} */

/**
* @name Advertising data PDU flags
*/
#define GORM_LL_FLAG_CHSEL (0x20)
#define GORM_LL_FLAG_TXADD (0x40)
#define GORM_LL_FLAG_RXADD (0x80)
/** @} */

/**
* @name Link layer data PDU flags
* @{
*/
#define GORM_LL_LLID_MASK (0x03)
#define GORM_LL_LLID_DATA_CONT (0x01) /**< continued or empty packet */
#define GORM_LL_LLID_DATA_START (0x02)
#define GORM_LL_LLID_CTRL (0x03)
#define GORM_LL_NESN (0x04) /**< next expected sequence number */
#define GORM_LL_SN (0x08) /**< sequence number */
#define GORM_LL_MD (0x10) /**< more data */
#define GORM_LL_SEQ_MASK (0x0c) /**< mask SN and NESN */
#define GORM_LL_FLOW_MASK (0x1c) /**< mask SN, NESN, and MD */
/** @} */


/* TODO: re-order fields to optimize memory usage... */
typedef struct gorm_ll_connection_struct {
/* connection state: holds ll state, flow control, and anchor state */
uint8_t state;

/* context data when in connection state */
uint16_t event_counter;
netdev_ble_ctx_t ctx;

/* channel configuration */
uint8_t chan_hop; /**< hop increment value */
uint8_t chan_map[5]; /**< active channel map */
uint8_t chan_cnt; /**< number of active channels */
uint8_t chan_unmapped; /**< next unmapped channel */
/* TODO: add channel selection algorithm as function pointer? */ /**< remapped next channel */

/* used timers */
gorm_arch_timer_t timer_spv;
gorm_arch_timer_t timer_con;

/* timing parameters */
uint32_t timeout_spv; /**< supervision timeout */
uint32_t timeout_rx; /**< receive timeout */
uint32_t interval; /**< connection event interval */
uint16_t slave_latency; /**< count number of connection events, reset on new connection */
// uint32_t adv_interval; /**< -> we use a static define for this (for now) */

/* buffered timing parameter update values */
struct {
uint16_t instant; /**< apply the update when event_counter hits this value */
uint8_t raw[9];
} timings_update;

/* buffered channel map update values */
struct {
uint16_t instant;
uint8_t map[5];
uint8_t cnt;
} chan_update;

/* shadow fields for updating channel config and timings */
/* TODO */

/* pkt queues for passing data to and receiving data from l2cap layer */
gorm_pduq_t rxq;
gorm_pduq_t txq;
gorm_buf_t *in_tx;
gorm_buf_t *in_rx;

/* TODO: additional information needed for triggering events on higher layer? */

/* GATT specific values */
/* TODO: split these into separate struct(s) and join structs on higher layer */
// uint16_t gatt_max_mtu;
} gorm_ll_connection_t;

typedef struct {
gorm_arch_timer_t timer;
uint32_t interval;
netdev_ble_pkt_t *pkt;
} gorm_ll_adv_nonconn_t;

extern event_queue_t gorm_ll_runqueue;
extern event_queue_t gorm_ll_waitqueue;

/* TODO: only include in peripheral, scanner, and central roles */
extern event_callback_t gorm_ll_rx_action;


void gorm_ll_run(netdev_t *dev);

uint8_t *gorm_ll_addr_rand(void);


/* TODO: only include in peripheral, scanner, and central roles */
static inline int gorm_ll_busy(void)
{
return (gorm_ll_rx_action.callback != NULL);
}


static inline int gorm_ll_get_type(netdev_ble_pkt_t *pkt)
{
return (int)(pkt->flags & GORM_LL_PDU_MASK);
}


/* TODO: merge broadcaster code with `ll_perihp`, use gorm_ll_connection_t for
* this also. */
#if 0
#ifdef MODULE_GORM_BROADCASTER
void gorm_ll_adv_nonconn_setup(gorm_ll_adv_nonconn_t *adv,
netdev_ble_pkt_t *pkt, uint32_t interval);

static inline void gorm_ll_adv_nonconn_start(gorm_ll_adv_nonconn_t *adv)
{
assert(adv);
gorm_arch_evtim_set_from_now(&adv->timer, adv->interval);
}

static inline void gorm_ll_adv_nonconn_stop(gorm_ll_adv_nonconn_t *adv)
{
assert(adv);
gorm_arch_evtim_cancel(&adv->timer);
}
#endif
#endif


#ifdef MODULE_GORM_PERIPHERAL
void gorm_ll_periph_init(void);
void gorm_ll_periph_adv_start(void);
void gorm_ll_periph_adv_setup(void *ad_adv, size_t adv_len,
void *ad_scan, size_t scan_len);

/**
* @brief Close the given connection, independent of its state
*
* If the connection is currently in advertising or connected state, it will
* be put into STANDBY state. If the connection is already STANDBY, nothing
* will happen.
*
* @param[in,out] con connection to close
*/
void gorm_ll_periph_terminate(gorm_ll_connection_t *con);

/* TODO: find a better way to pass connections form LL to l2cap, probably turn
* the buffer handling around, so that the upper layer passes connection
* structs to the LL?! */
gorm_ll_connection_t *gorm_ll_periph_getcon(void);
#endif


#ifdef __cplusplus
}
#endif

#endif /* GORM_LL_H */
/** @} */
@@ -0,0 +1,45 @@
/*
* Copyright (C) 2018 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 net_gorm
* @{
*
* @file
* @brief Gorm's channel selection algorithms
*
* @author Hauke Petersen <hauke.petersen@fu-berlin.de>
*/

#include <stdint.h>

#include "net/gorm/ll.h"

/**
* @brief Count the allowed channels in the given channel map
*
* @param[in] map map masking the allowed channels
*
* @return number of allowed channels
*/
uint8_t gorm_ll_chan_count(const uint8_t *map);

/**
* @brief Compute the next used channel using channel selection algorithm #1
*
* @param[in,out] con connection context
*/
void gorm_ll_chan_algo1(gorm_ll_connection_t *con);

/**
* @brief Compute the next used channel using channel selection algorithm #2
*
* @param[in,out] con connection context
*/
/* TODO: implement */
//void gorm_ll_chan_algo2(gorm_ll_connection_t *con);
@@ -0,0 +1,48 @@
/*
* Copyright (C) 2018 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 net_gorm
* @brief
* @{
*
* @file
* @brief Gorm's interface for handling link layer control messages
*
* @author Hauke Petersen <hauke.petersen@fu-berlin.de>
*/

#ifndef GORM_LL_CTRL_H
#define GORM_LL_CTRL_H

#include "net/gorm/ll.h"

#ifdef __cplusplus
extern "C" {
#endif

/**
* @brief Build a response to a FEATURE_REQ packet
*
* @param[out] pkt response is written to this packet
*/
void gorm_ll_ctrl_feature_resp(gorm_buf_t *buf);

void gorm_ll_ctrl_version(gorm_buf_t *buf);

void gorm_ll_ctrl_1b(gorm_buf_t *buf, uint8_t opcode, uint8_t data);


void gorm_ll_ctrl_on_data(gorm_ll_connection_t *con, gorm_buf_t *buf);

#ifdef __cplusplus
}
#endif

#endif /* GORM_LL_CTRL_H */
/** @} */
@@ -0,0 +1,38 @@
/*
* Copyright (C) 2018 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 net_gorm
* @brief
* @{
*
* @file
* @brief Gorm's link layer part that runs in the host thread
*
* @author Hauke Petersen <hauke.petersen@fu-berlin.de>
*/

#ifndef GORM_LL_HOST_H
#define GORM_LL_HOST_H

#include "net/gorm/ll.h"

#ifdef __cplusplus
extern "C" {
#endif

void gorm_ll_host_run(void);

void gorm_ll_host_notify(gorm_ll_connection_t *con);

#ifdef __cplusplus
}
#endif

#endif /* GORM_LL_HOST_H */
/** @} */
@@ -0,0 +1,48 @@
/*
* Copyright (C) 2018 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.
*/

#include "net/gorm.h"
#include "net/netdev/ble.h"

/**
* @ingroup net_gorm
* @{
*
* @file
* @brief Gorm's radio wrapper
*
* @author Hauke Petersen <hauke.petersen@fu-berlin.de>
*/

typedef int(*gorm_ll_trx_cb_t)(gorm_buf_t *buf, void *arg);

/**
* @brief Initialize Gorm's radio wrapper
*
* @param[in] dev network device to use
*
* @return GORM_OK on success
* @return GORM_ERR_RADIO on failed radio initialization
*/
int gorm_ll_trx_init(netdev_t *dev);

/**
* @brief Cancel any ongoing operation and put the radio into idle mode
*/
void gorm_ll_trx_stop(void);


void gorm_ll_trx_send(gorm_buf_t *buf, netdev_ble_ctx_t *ctx,
gorm_ll_trx_cb_t cb, void *arg);

void gorm_ll_trx_recv(gorm_buf_t *buf, netdev_ble_ctx_t *ctx,
gorm_ll_trx_cb_t cb, void *arg);

void gorm_ll_trx_send_next(gorm_buf_t *buf, gorm_ll_trx_cb_t cb);

void gorm_ll_trx_recv_next(gorm_buf_t *buf, gorm_ll_trx_cb_t cb);
@@ -0,0 +1,57 @@
/*
* Copyright (C) 2018 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 net_gorm
* @{
*
* @file
* @brief Gorm's PDU (packet) pool
*
* @author Hauke Petersen <hauke.petersen@fu-berlin.de>
*/

#ifndef GORM_PDUPOOL_H
#define GORM_PDUPOOL_H

#include "net/gorm.h"

#ifdef __cplusplus
extern "C" {
#endif


/**
* @brief Initialize the PDU pool
*
* @param mem [description]
* @param len [description]
*/
void gorm_pdupool_addmem(void *mem, size_t len);

/**
* @brief Get a number of packets from the pool
*
* @param[in] num
* @return [description]
*/
gorm_buf_t *gorm_pdupool_get(unsigned num);

/**
* @brief Free the given packet by putting it back into the pool
*
* @param[in,out] pdu packet to return to pool
*/
void gorm_pdupool_return(gorm_buf_t *buf);

#ifdef __cplusplus
}
#endif

#endif /* GORM_PDUPOOL_H */
/** @} */
@@ -0,0 +1,49 @@
/*
* Copyright (C) 2018 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 net_gorm
* @{
*
* @file
* @brief Gorm's PDU (packet) pool
*
* @author Hauke Petersen <hauke.petersen@fu-berlin.de>
*/

#ifndef GORM_PDUQ_H
#define GORM_PDUQ_H

#include "net/gorm.h"

#ifdef __cplusplus
extern "C" {
#endif

#define GORM_PDUQ_INIT { NULL, NULL }

typedef struct gorm_pduq {
gorm_buf_t *head;
gorm_buf_t *tail;
} gorm_pduq_t;

void gorm_pduq_enq(gorm_pduq_t *queue, gorm_buf_t *pkt);

gorm_buf_t *gorm_pduq_deq(gorm_pduq_t *queue);

static inline gorm_buf_t *gorm_pduq_peek(gorm_pduq_t *queue)
{
return queue->head;
}

#ifdef __cplusplus
}
#endif

#endif /* GORM_PDUQ_H */
/** @} */
@@ -0,0 +1,73 @@
/*
* Copyright (C) 2018 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 net_gorm
* @{
*
* @file
* @brief Gorm's little helpers
*
* @author Hauke Petersen <hauke.petersen@fu-berlin.de>
*/

#ifndef GORM_UTIL_H
#define GORM_UTIL_H

#include <stdint.h>

#ifdef __cplusplus
extern "C" {
#endif

static inline uint16_t gorm_util_letohs(uint8_t *buf)
{
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
return (uint16_t)((buf[1] << 8) | buf[0]);
#else
return (uint16_t)((buf[0] << 8) | buf[1]);
#endif
}

static inline uint16_t gorm_util_betohs(uint8_t *buf)
{
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
return (uint16_t)((buf[0] << 8) | buf[1]);
#else
return (uint16_t)((buf[1] << 8) | buf[0]);
#endif
}

static inline void gorm_util_htoles(uint8_t *buf, uint16_t val)
{
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
buf[0] = (uint8_t)(val & 0xff);
buf[1] = (uint8_t)(val >> 8);
#else
buf[0] = (uint8_t)(val >> 8);
buf[1] = (uint8_t)(val & 0xff);
#endif
}

static inline void gorm_util_htobes(uint8_t *buf, uint16_t val)
{
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
buf[0] = (uint8_t)(val >> 8);
buf[1] = (uint8_t)(val & 0xff);
#else
buf[0] = (uint8_t)(val & 0xff);
buf[1] = (uint8_t)(val >> 8);
#endif
}

#ifdef __cplusplus
}
#endif

#endif /* GORM_LL_H */
/** @} */
@@ -0,0 +1,95 @@
/*
* Copyright (C) 2018 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 net_gorm
* @{
*
* @file
* @brief Gorm's UUID handling
*
* Implementation restrictions:
* - only support for 128-bit and 16-bit UUIDs (is that true?)
* - handle allocation:
* - 0b0xxxxxxxNNNNNNNN -> 16-bit UUIDed services
* - 0b1xxxxxxxNNNNNNNN -> 128-bit UUIDed services
* - 0bxxxxxxxxCCCC0000 -> 4 MSB of lower byte: characteristics
* - 0bxxxxxxxxCCCCDDDD -> 4 LSB of lower byte: char descriptions
*
*
* @author Hauke Petersen <hauke.petersen@fu-berlin.de>
*/

#ifndef GORM_UUID_H
#define GORM_UUID_H

#include <stdint.h>
#include <stddef.h>

#define GORM_UUID(u16, b) { .uuid16 = u16, .base = b }

/* TODO: move to ble.h */
#define GORM_UUID_GAP (0x1800)
#define GORM_UUID_ATT (0x1801)

/* TODO: move to ble.h */
#define GORM_UUID_DEVICE_NAME (0x2a00)
#define GORM_UUID_APPEARANCE (0x2a01)
#define GORM_UUID_PREF_CON_PARAM (0x2a04)

typedef union {
uint8_t raw[16];
uint16_t u16[8];
uint32_t u32[4];
} gorm_uuid_base_t;

typedef struct {
const gorm_uuid_base_t *base; /**< base UUID, NULL refers to Bluetooth UUID */
uint16_t uuid16;
} gorm_uuid_t;


void gorm_uuid_from_buf(gorm_uuid_t *uuid, uint8_t *buf, size_t len);
size_t gorm_uuid_to_buf(uint8_t *buf, const gorm_uuid_t *uuid);

/**
* @brief Compare two given UUIDs
*
* @param[in] a first UUID to compare
* @param[in] b second UUID to compare
*
* @return 0 if UUIDs are different
* @return != 0 if UUIDs are equal
*/
int gorm_uuid_cmp(const gorm_uuid_t *a, const gorm_uuid_t *b);


static inline int gorm_uuid_sig(const gorm_uuid_t *uuid)
{
return (uuid->base == NULL);
}

static inline int gorm_uuid_eq16(const gorm_uuid_t *uuid, uint16_t uuid16)
{
return (gorm_uuid_sig(uuid) && (uuid->uuid16 == uuid16));
}

static inline size_t gorm_uuid_len(const gorm_uuid_t *uuid)
{
return (gorm_uuid_sig(uuid)) ? 2 : 16;
}

static inline void gorm_uuid_init(gorm_uuid_t *uuid, gorm_uuid_base_t *base,
uint16_t uuid16) {
uuid->base = base;
uuid->uuid16 = uuid16;
}



#endif /* GORM_UUID_H */
@@ -0,0 +1,27 @@
MODULE = gorm

SRC = ll.c ll_trx.c util.c gorm.c

ifneq (,$(filter gorm_peripheral,$(USEMODULE)))
SRC += ll_periph.c ll_chan.c ll_ctrl.c ll_host.c l2cap.c pdupool.c pduq.c
endif

ifneq (,$(filter gorm_gatt,$(USEMODULE)))
SRC += uuid.c
DIRS += $(RIOTBASE)/sys/net/gorm/gatt
endif

ifneq (,$(filter gorm_gap,$(USEMODULE)))
DIRS += $(RIOTBASE)/sys/net/gorm/gap
endif

ifneq (,$(filter gorm_arch_riot,$(USEMODULE)))
DIRS += $(RIOTBASE)/sys/net/gorm/arch/riot
endif

# include select build-in services
ifneq (,$(filter gorm_service_%,$(USEMODULE)))
DIRS += $(RIOTBASE)/sys/net/gorm/service
endif

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