From 49bd85d00ac6abb0cfb8c6e5fbd493155d55e752 Mon Sep 17 00:00:00 2001
From: Hauke Petersen <hauke.petersen@fu-berlin.de>
Date: Thu, 5 Apr 2018 11:10:40 +0200
Subject: [PATCH] sys/net: added Skald

Skald is a very small and simple, TX-only BLE stack that supports
sending advertisements only. It is useful for building all kinds
of BLE beacons with very minimal memory footprints.
---
 Makefile.dep                      |   9 ++
 makefiles/pseudomodules.inc.mk    |   5 +
 sys/Makefile                      |   3 +
 sys/include/net/skald.h           | 124 +++++++++++++++++++++++
 sys/include/net/skald/eddystone.h |  83 ++++++++++++++++
 sys/include/net/skald/ibeacon.h   |  54 ++++++++++
 sys/net/skald/Makefile            |  11 +++
 sys/net/skald/skald.c             | 158 ++++++++++++++++++++++++++++++
 sys/net/skald/skald_eddystone.c   | 109 +++++++++++++++++++++
 sys/net/skald/skald_ibeacon.c     |  63 ++++++++++++
 10 files changed, 619 insertions(+)
 create mode 100644 sys/include/net/skald.h
 create mode 100644 sys/include/net/skald/eddystone.h
 create mode 100644 sys/include/net/skald/ibeacon.h
 create mode 100644 sys/net/skald/Makefile
 create mode 100644 sys/net/skald/skald.c
 create mode 100644 sys/net/skald/skald_eddystone.c
 create mode 100644 sys/net/skald/skald_ibeacon.c

diff --git a/Makefile.dep b/Makefile.dep
index c0b24c5f55..0e999f9411 100644
--- a/Makefile.dep
+++ b/Makefile.dep
@@ -698,6 +698,15 @@ ifneq (,$(filter benchmark,$(USEMODULE)))
   USEMODULE += xtimer
 endif
 
+ifneq (,$(filter skald_%,$(USEMODULE)))
+  USEMODULE += skald
+endif
+
+ifneq (,$(filter skald,$(USEMODULE)))
+  FEATURES_REQUIRED += radio_ble
+  USEMODULE += xtimer
+  USEMODULE += random
+endif
 # always select gpio (until explicit dependencies are sorted out)
 FEATURES_OPTIONAL += periph_gpio
 
diff --git a/makefiles/pseudomodules.inc.mk b/makefiles/pseudomodules.inc.mk
index 4fec53670a..15b562684e 100644
--- a/makefiles/pseudomodules.inc.mk
+++ b/makefiles/pseudomodules.inc.mk
@@ -117,3 +117,8 @@ PSEUDOMODULES += stm32_periph_%
 # declare periph submodules as pseudomodules, but exclude periph_common
 PSEUDOMODULES += periph_%
 NO_PSEUDOMODULES += periph_common
+
+# Submodules and auto-init code provided by Skald
+PSEUDOMODULES += auto_init_skald
+PSEUDOMODULES += skald_ibeacon
+PSEUDOMODULES += skald_eddystone
diff --git a/sys/Makefile b/sys/Makefile
index 76701bd74d..cc6d2ab074 100644
--- a/sys/Makefile
+++ b/sys/Makefile
@@ -124,6 +124,9 @@ endif
 ifneq (,$(filter nanocoap,$(USEMODULE)))
   DIRS += net/application_layer/nanocoap
 endif
+ifneq (,$(filter skald,$(USEMODULE)))
+  DIRS += net/skald
+endif
 
 DIRS += $(dir $(wildcard $(addsuffix /Makefile, $(USEMODULE))))
 
diff --git a/sys/include/net/skald.h b/sys/include/net/skald.h
new file mode 100644
index 0000000000..4583341c53
--- /dev/null
+++ b/sys/include/net/skald.h
@@ -0,0 +1,124 @@
+/*
+ * 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_skald Skald, who advertises to the world
+ * @ingroup     net
+ * @brief       Skald, a minimalistic BLE advertising stack
+ *
+ * # About
+ *
+ * Skald is a very minimalistic BLE implementation, implementing the
+ * `broadcaster` role only. With this focus, the stack allows for setting up
+ * different kind of beacons using an extremely low memory footprint.
+ *
+ * # Design Decisions and Limitations
+ * - support for local addresses only (using `luid` to generate them)
+ * - advertising interval is configured during compile time, override by setting
+ *   `CFLAGS+=-DSKALD_INTERVAL=xxx`
+ * - advertising channels are configured during compile time, override by
+ *   setting `CFLAGS+=-DSKALD_ADV_CHAN={37,39}`
+ *
+ * # Implementation state
+ * Supported:
+ * - advertising of custom GAP payloads
+ * - iBeacon (full support)
+ * - Eddystone (partly supported)
+ *
+ * Limitations:
+ * - currently Skald supports random static addresses only (generated using
+ *   the `luid` module)
+ *
+ * @{
+ * @file
+ * @brief       Skald's basic interface
+ *
+ * @author      Hauke Petersen <hauke.petersen@fu-berlin.de>
+ */
+
+#ifndef NET_SKALD_H
+#define NET_SKALD_H
+
+#include <stdint.h>
+
+#include "xtimer.h"
+#include "net/ble.h"
+#include "net/netdev/ble.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief   Static advertising interval
+ */
+#ifndef SKALD_INTERVAL
+#define SKALD_INTERVAL          (1 * US_PER_SEC)
+#endif
+
+/**
+ * @brief   Static list of used advertising channels
+ */
+#ifndef SKALD_ADV_CHAN
+#define SKALD_ADV_CHAN          { 37, 38, 39 }
+#endif
+
+/**
+ * @brief   UUID representation format used by Skald
+ */
+typedef struct {
+    uint8_t u8[16];         /**< UUID with byte-wise access */
+} skald_uuid_t;
+
+/**
+ * @brief   Advertising context holding the advertising data and state
+ */
+typedef struct {
+    netdev_ble_pkt_t pkt;   /**< packet holding the advertisement (GAP) data */
+    xtimer_t timer;         /**< timer for scheduling advertising events */
+    uint32_t last;          /**< last timer trigger (for offset compensation) */
+    uint8_t cur_chan;       /**< keep track of advertising channels */
+} skald_ctx_t;
+
+/**
+ * @brief   Initialize Skald and the underlying radio
+ */
+void skald_init(void);
+
+/**
+ * @brief   Start advertising the given packet
+ *
+ * The packet will be send out each advertising interval (see SKALD_INTERVAL) on
+ * each of the defined advertising channels (see SKALD_ADV_CHAN).
+ *
+ * @param[in,out] ctx   start advertising this context
+ */
+void skald_adv_start(skald_ctx_t *ctx);
+
+/**
+ * @brief   Stop the ongoing advertisement
+ *
+ * @param[in,out] ctx   stop advertising this context
+ */
+void skald_adv_stop(skald_ctx_t *ctx);
+
+/**
+ * @brief   Generate a random public address
+ *
+ * @note    @p buf must be able to hold BLE_ADDR_LEN (6) bytes
+ *
+ * @param[out] buf      the generated address is written to this buffer
+ */
+void skald_generate_random_addr(uint8_t *buf);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* NET_SKALD_H */
+/** @} */
diff --git a/sys/include/net/skald/eddystone.h b/sys/include/net/skald/eddystone.h
new file mode 100644
index 0000000000..9c8ddd6365
--- /dev/null
+++ b/sys/include/net/skald/eddystone.h
@@ -0,0 +1,83 @@
+/*
+ * 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_skald_eddystone Skald meets Eddy
+ * @ingroup     net_skald
+ * @brief       Skald's Eddystone implementation
+ *
+ * # About
+ * This module allows for creation and advertisement of Eddystone beacons (see
+ * https://github.com/google/eddystone).
+ *
+ *
+ * # Implementation state
+ * supported:
+ * - Eddystone-UID
+ * - Eddystone-URL
+ *
+ * not (yet) supported:
+ * - Eddystone-TLM
+ * - Eddystone-EID
+ *
+ * @{
+ * @file
+ * @brief       Skald's basic interface
+ *
+ * @author      Hauke Petersen <hauke.petersen@fu-berlin.de>
+ */
+
+#ifndef NET_SKALD_EDDYSTONE_H
+#define NET_SKALD_EDDYSTONE_H
+
+#include "net/eddystone.h"
+#include "net/skald.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief   Unique and opaque 16-byte beacon id format used by Eddystone
+ */
+typedef struct __attribute__((packed)) {
+    uint8_t namespace[EDDYSTONE_NAMESPACE_LEN]; /**< 10-byte namespace */
+    uint8_t instance[EDDYSTONE_INSTANCE_LEN];   /**< 6-byte instance */
+} skald_eddystone_uid_t;
+
+/**
+ * @brief   Advertise Eddystone-UID data
+ *
+ * @see https://github.com/google/eddystone/tree/master/eddystone-uid
+ *
+ * @param[out] ctx      advertising context
+ * @param[in] uid       UID to advertise
+ * @param[in] tx_pwr    calibrated TX power to be advertised by the beacon
+ */
+void skald_eddystone_uid_adv(skald_ctx_t *ctx,
+                             const skald_eddystone_uid_t *uid, uint8_t tx_pwr);
+
+/**
+ * @brief   Advertise Eddystone-URL data
+ *
+ * @see https://github.com/google/eddystone/tree/master/eddystone-url
+ *
+ * @param[out] ctx      advertising context
+ * @param[in] scheme    encoded URL scheme prefix
+ * @param[in] url       (short) url as \0 terminated string
+ * @param[in] tx_pwr    calibrated TX power to be advertised by the beacon
+ */
+void skald_eddystone_url_adv(skald_ctx_t *ctx,
+                             uint8_t scheme, const char *url, uint8_t tx_pwr);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* NET_SKALD_EDDYSTONE_H */
+/** @} */
diff --git a/sys/include/net/skald/ibeacon.h b/sys/include/net/skald/ibeacon.h
new file mode 100644
index 0000000000..0f87430068
--- /dev/null
+++ b/sys/include/net/skald/ibeacon.h
@@ -0,0 +1,54 @@
+/*
+ * 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_skald_ibeacon Skald about iBeacon
+ * @ingroup     net_skald
+ * @brief       Skald's simple iBeacon abstraction
+ *
+ * # About
+ * This Skald module supports the creation and advertisement of BLE iBeacons as
+ * defined by Apple (see https://developer.apple.com/ibeacon/).
+ *
+ * # Implementation state
+ * - all known iBeacon properties are supported
+ *
+ * @{
+ * @file
+ * @brief       Skald's basic interface
+ *
+ * @author      Hauke Petersen <hauke.petersen@fu-berlin.de>
+ */
+
+#ifndef NET_SKALD_IBEACON_H
+#define NET_SKALD_IBEACON_H
+
+#include "net/skald.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief   Configure the IBeacon payload and start advertising
+ *
+ * @param[out] ctx      advertising context
+ * @param[in] uuid      UUID advertised by the iBeacon
+ * @param[in] major     the iBeacon's major number
+ * @param[in] minor     the iBeacon's minor number
+ * @param[in] txpower   calibrated TX power to be advertised by the beacon
+ */
+void skald_ibeacon_advertise(skald_ctx_t *ctx, const skald_uuid_t *uuid,
+                             uint16_t major, uint16_t minor, uint8_t txpower);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* NET_SKALD_IBEACON_H */
+/** @} */
diff --git a/sys/net/skald/Makefile b/sys/net/skald/Makefile
new file mode 100644
index 0000000000..c835b4d8dd
--- /dev/null
+++ b/sys/net/skald/Makefile
@@ -0,0 +1,11 @@
+SRC = skald.c
+
+ifneq (,$(filter skald_ibeacon,$(USEMODULE)))
+  SRC += skald_ibeacon.c
+endif
+
+ifneq (,$(filter skald_eddystone,$(USEMODULE)))
+  SRC += skald_eddystone.c
+endif
+
+include $(RIOTBASE)/Makefile.base
diff --git a/sys/net/skald/skald.c b/sys/net/skald/skald.c
new file mode 100644
index 0000000000..858f8b03a5
--- /dev/null
+++ b/sys/net/skald/skald.c
@@ -0,0 +1,158 @@
+/*
+ * 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_skald
+ * @{
+ *
+ * @file
+ * @brief       Skald's link layer implementation
+ *
+ * @author      Hauke Petersen <hauke.petersen@fu-berlin.de>
+ *
+ * @}
+ */
+
+#include <stdint.h>
+
+#include "assert.h"
+#include "random.h"
+#include "luid.h"
+
+#include "net/netdev/ble.h"
+#include "net/skald.h"
+
+/* include fitting radio driver */
+#if defined(MODULE_NRFBLE)
+#include "nrfble.h"
+/* add other BLE radio drivers once implemented - and potentially move to
+ * auto-init at some point */
+#else
+#error "[skald] error: unable to find any netdev-ble capable radio"
+#endif
+
+#define ENABLE_DEBUG            (0)
+#include "debug.h"
+
+#define JITTER_MIN              (0U)            /* 0ms */
+#define JITTER_MAX              (10000U)        /* 10ms */
+
+#define ADV_CHAN_NUMOF          sizeof(_adv_chan)
+#define ADV_AA                  (0x8e89bed6)    /* access address */
+#define ADV_CRC                 (0x00555555)    /* CRC initializer */
+
+static const uint8_t _adv_chan[] = SKALD_ADV_CHAN;
+
+static netdev_ble_ctx_t _ble_ctx = {
+    .aa.u32 = ADV_AA,
+    .crc = ADV_CRC,
+};
+
+static netdev_t *_radio;
+
+static void _stop_radio(void)
+{
+    netdev_ble_stop(_radio);
+    _radio->context = NULL;
+}
+
+static void _sched_next(skald_ctx_t *ctx)
+{
+    ctx->last += SKALD_INTERVAL;
+    /* schedule next advertising event, adding a random jitter between
+     * 0ms and 10ms (see spec v5.0-vol6-b-4.4.2.2.1) */
+    ctx->last += random_uint32_range(JITTER_MIN, JITTER_MAX);
+    /* compensate the time passed since the timer triggered last by using the
+     * current value of the timer */
+    xtimer_set(&ctx->timer, (ctx->last - xtimer_now_usec()));
+}
+
+static void _on_adv_evt(void *arg)
+{
+    skald_ctx_t *ctx = (skald_ctx_t *)arg;
+
+    /* advertise on the next adv channel - or skip this event if the radio is
+     * busy */
+    if ((ctx->cur_chan < ADV_CHAN_NUMOF) && (_radio->context == NULL)) {
+        _radio->context = ctx;
+        _ble_ctx.chan = _adv_chan[ctx->cur_chan];
+        netdev_ble_set_ctx(_radio, &_ble_ctx);
+        netdev_ble_send(_radio, &ctx->pkt);
+        ++ctx->cur_chan;
+    }
+    else {
+        ctx->cur_chan = 0;
+        _sched_next(ctx);
+    }
+}
+
+static void _on_radio_evt(netdev_t *netdev, netdev_event_t event)
+{
+    (void)netdev;
+
+    if (event == NETDEV_EVENT_TX_COMPLETE) {
+        skald_ctx_t *ctx = _radio->context;
+        _stop_radio();
+        xtimer_set(&ctx->timer, 150);
+    }
+}
+
+void skald_init(void)
+{
+    assert(dev);
+
+    /* setup and a fitting radio driver - potentially move to auto-init at some
+     * point */
+#if defined(MODULE_NRFBLE)
+    _radio = nrfble_setup();
+#endif
+
+    _radio->event_callback = _on_radio_evt;
+    _radio->driver->init(_radio);
+}
+
+void skald_adv_start(skald_ctx_t *ctx)
+{
+    assert(ctx);
+
+    /* make sure the given context is not advertising at the moment */
+    skald_adv_stop(ctx);
+
+    /* initialize advertising context */
+    ctx->timer.callback = _on_adv_evt;
+    ctx->timer.arg = ctx;
+    ctx->last = xtimer_now_usec();
+    ctx->cur_chan = 0;
+    ctx->pkt.flags = (BLE_ADV_NONCON_IND | BLE_LL_FLAG_TXADD);
+
+    /* start advertising */
+    _sched_next(ctx);
+}
+
+void skald_adv_stop(skald_ctx_t *ctx)
+{
+    assert(ctx);
+
+    xtimer_remove(&ctx->timer);
+    if (_radio->context == (void *)ctx) {
+        _stop_radio();
+    }
+}
+
+void skald_generate_random_addr(uint8_t *buf)
+{
+    assert(buf);
+
+    luid_get(buf, BLE_ADDR_LEN);
+    /* swap byte 0 and 5, so that the unique byte given by luid does not clash
+     * with universal/local and individual/group bits of address */
+    uint8_t tmp = buf[5];
+    buf[5] = buf[0];
+    /* make address individual and local */
+    buf[0] = ((tmp & 0xfc) | 0x02);
+}
diff --git a/sys/net/skald/skald_eddystone.c b/sys/net/skald/skald_eddystone.c
new file mode 100644
index 0000000000..847d6f09d8
--- /dev/null
+++ b/sys/net/skald/skald_eddystone.c
@@ -0,0 +1,109 @@
+/*
+ * 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_skald_eddystone
+ * @{
+ *
+ * @file
+ * @brief       Skald's Eddystone implementation
+ *
+ * @author      Hauke Petersen <hauke.petersen@fu-berlin.de>
+ *
+ * @}
+ */
+
+#include <string.h>
+
+#include "assert.h"
+#include "net/skald/eddystone.h"
+
+#define PREAMBLE_LEN            (11U)
+#define PA_LEN                  (7U)
+#define PB_LEN                  (3U)
+
+#define URL_HDR_LEN             (6U)
+
+#define UID_LEN                 (23U)
+
+typedef struct __attribute__((packed)) {
+    uint8_t txadd[BLE_ADDR_LEN];
+    uint8_t pa[PA_LEN];
+    uint8_t service_data_len;
+    uint8_t pb[PB_LEN];
+    uint8_t type;
+} pre_t;
+
+typedef struct __attribute__((packed)) {
+    pre_t pre;
+    uint8_t tx_pwr;
+    uint8_t namespace[EDDYSTONE_NAMESPACE_LEN];
+    uint8_t instance[EDDYSTONE_INSTANCE_LEN];
+    uint8_t reserved[2];
+} eddy_uid_t;
+
+typedef struct __attribute__((packed)) {
+    pre_t pre;
+    uint8_t tx_pwr;
+    uint8_t scheme;
+    uint8_t url[];
+} eddy_url_t;
+
+/* ćonstant GAP data preamble parts, containing the following GAP fields:
+ * - flags:  BR/EDR not support set
+ * - complete list of 16-bit UUIDs: holding the Eddystone UUID only (0xfeaa)
+ * - service data of type 0xfeaa (Eddystone) */
+static const uint8_t _pa[PA_LEN] = { 0x02, 0x01, 0x04, 0x03, 0x03, 0xaa, 0xfe };
+static const uint8_t _pb[PB_LEN] = { 0x16, 0xaa, 0xfe };
+
+static void _init_pre(pre_t *data, uint8_t type, uint8_t len)
+{
+    skald_generate_random_addr(data->txadd);
+    memcpy(data->pa, _pa, PA_LEN);
+    memcpy(data->pb, _pb, PB_LEN);
+    data->service_data_len = len;
+    data->type = type;
+}
+
+void skald_eddystone_uid_adv(skald_ctx_t *ctx,
+                             const skald_eddystone_uid_t *uid, uint8_t tx_pwr)
+{
+    assert(ctx && uid);
+
+    eddy_uid_t *pdu = (eddy_uid_t *)ctx->pkt.pdu;
+    _init_pre(&pdu->pre, EDDYSTONE_UID, UID_LEN);
+
+    pdu->tx_pwr = tx_pwr;
+    memcpy(pdu->namespace, uid->namespace, EDDYSTONE_NAMESPACE_LEN);
+    memcpy(pdu->instance, uid->instance, EDDYSTONE_INSTANCE_LEN);
+    memset(pdu->reserved, 0, 2);
+
+    /* start advertising */
+    ctx->pkt.len = sizeof(eddy_uid_t);
+    skald_adv_start(ctx);
+}
+
+void skald_eddystone_url_adv(skald_ctx_t *ctx,
+                             uint8_t scheme, const char *url, uint8_t tx_pwr)
+{
+    assert(url && ctx);
+    size_t len = strlen(url);
+    assert(len <= (NETDEV_BLE_PDU_MAXLEN - (URL_HDR_LEN + PREAMBLE_LEN)));
+
+    eddy_url_t *pdu = (eddy_url_t *)ctx->pkt.pdu;
+    _init_pre(&pdu->pre, EDDYSTONE_URL, (URL_HDR_LEN + len));
+
+    /* set remaining service data fields */
+    pdu->tx_pwr = tx_pwr;
+    pdu->scheme = scheme;
+    memcpy(pdu->url, url, len);
+
+    /* start advertising */
+    ctx->pkt.len = (sizeof(pre_t) + 2 + len);
+    skald_adv_start(ctx);
+}
diff --git a/sys/net/skald/skald_ibeacon.c b/sys/net/skald/skald_ibeacon.c
new file mode 100644
index 0000000000..7d67eb7da2
--- /dev/null
+++ b/sys/net/skald/skald_ibeacon.c
@@ -0,0 +1,63 @@
+/*
+ * 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_skald_ibeacon
+ * @{
+ *
+ * @file
+ * @brief       Skald's iBeacon implementation
+ *
+ * @author      Hauke Petersen <hauke.petersen@fu-berlin.de>
+ *
+ * @}
+ */
+
+#include <string.h>
+
+#include "byteorder.h"
+
+#include "net/skald/ibeacon.h"
+
+#define PREFIX_LEN          (9U)
+
+/**
+ * @brief   PDU format for iBeacon packets
+ */
+typedef struct __attribute__((packed)) {
+    uint8_t txadd[BLE_ADDR_LEN];
+    uint8_t prefix[PREFIX_LEN];
+    skald_uuid_t uuid;
+    be_uint16_t major;
+    be_uint16_t minor;
+    uint8_t txpower;
+} ibeacon_t;
+
+/* constant GAP type value fields, fixed for the iBeacon format */
+static const uint8_t prefix[PREFIX_LEN] = { 0x02, 0x01, 0x06, 0x1a, 0xff,
+                                            0x4c, 0x00, 0x02, 0x15 };
+
+void skald_ibeacon_advertise(skald_ctx_t *ctx, const skald_uuid_t *uuid,
+                             uint16_t major, uint16_t minor, uint8_t txpower)
+{
+    /* configure the iBeacon PDU */
+    ibeacon_t *pdu = (ibeacon_t *)ctx->pkt.pdu;
+
+    ctx->pkt.len = (uint8_t)sizeof(ibeacon_t);
+
+    skald_generate_random_addr(pdu->txadd);
+    memcpy(pdu->prefix, prefix, PREFIX_LEN);
+    memcpy(&pdu->uuid, uuid, sizeof(skald_uuid_t));
+    be_uint16_t tmp = byteorder_htons(major);
+    memcpy(&pdu->major, &tmp, sizeof(uint16_t));
+    tmp = byteorder_htons(minor);
+    memcpy(&pdu->minor, &tmp, sizeof(uint16_t));
+    pdu->txpower = txpower;
+
+    skald_adv_start(ctx);
+}
-- 
GitLab