Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • card10/firmware
  • annejan/firmware
  • astro/firmware
  • fpletz/firmware
  • gerd/firmware
  • fleur/firmware
  • swym/firmware
  • l/firmware
  • uberardy/firmware
  • wink/firmware
  • madonius/firmware
  • mot/firmware
  • filid/firmware
  • q3k/firmware
  • hauke/firmware
  • Woazboat/firmware
  • pink/firmware
  • mossmann/firmware
  • omniskop/firmware
  • zenox/firmware
  • trilader/firmware
  • Danukeru/firmware
  • shoragan/firmware
  • zlatko/firmware
  • sistason/firmware
  • datenwolf/firmware
  • bene/firmware
  • amedee/firmware
  • martinling/firmware
  • griffon/firmware
  • chris007/firmware
  • adisbladis/firmware
  • dbrgn/firmware
  • jelly/firmware
  • rnestler/firmware
  • mh/firmware
  • ln/firmware
  • penguineer/firmware
  • monkeydom/firmware
  • jens/firmware
  • jnaulty/firmware
  • jeffmakes/firmware
  • marekventur/firmware
  • pete/firmware
  • h2obrain/firmware
  • DooMMasteR/firmware
  • jackie/firmware
  • prof_r/firmware
  • Draradech/firmware
  • Kartoffel/firmware
  • hinerk/firmware
  • abbradar/firmware
  • JustTB/firmware
  • LuKaRo/firmware
  • iggy/firmware
  • ente/firmware
  • flgr/firmware
  • Lorphos/firmware
  • matejo/firmware
  • ceddral7/firmware
  • danb/firmware
  • joshi/firmware
  • melle/firmware
  • fitch/firmware
  • deurknop/firmware
  • sargon/firmware
  • markus/firmware
  • kloenk/firmware
  • lucaswerkmeister/firmware
  • derf/firmware
  • meh/firmware
  • dx/card10-firmware
  • torben/firmware
  • yuvadm/firmware
  • AndyBS/firmware
  • klausdieter1/firmware
  • katzenparadoxon/firmware
  • xiretza/firmware
  • ole/firmware
  • techy/firmware
  • thor77/firmware
  • TilCreator/firmware
  • fuchsi/firmware
  • dos/firmware
  • yrlf/firmware
  • PetePriority/firmware
  • SuperVirus/firmware
  • sur5r/firmware
  • tazz/firmware
  • Alienmaster/firmware
  • flo_h/firmware
  • baldo/firmware
  • mmu_man/firmware
  • Foaly/firmware
  • sodoku/firmware
  • Guinness/firmware
  • ssp/firmware
  • led02/firmware
  • Stormwind/firmware
  • arist/firmware
  • coon/firmware
  • mdik/firmware
  • pippin/firmware
  • royrobotiks/firmware
  • zigot83/firmware
  • mo_k/firmware
106 results
Show changes
Showing
with 2968 additions and 1029 deletions
#include "wsf_types.h"
#include "wsf_os.h"
#include "wsf_buf.h"
#include "wsf_timer.h"
#include "wsf_trace.h"
#include "app_ui.h"
#include "fit/fit_api.h"
#include "hci_vs.h"
#include "ble_api.h"
#include "ff.h"
#include "epicardium.h"
#include "wsf_types.h"
#include "util/bstream.h"
#include "wsf_assert.h"
#include "att_api.h"
#include "FreeRTOS.h"
#include "crc32.h"
#include "epicardium.h"
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
#include <machine/endian.h>
#define CARD10_UUID_SUFFIX \
0x42, 0x23, 0x42, 0x23, 0x42, 0x23, 0x42, 0x23, 0x42, 0x23, 0x42, 0x23
#define CARD10_UUID_PREFIX 0x02, 0x23, 0x42
/*!< \brief Service start handle. */
#define CARD10_START_HDL 0x920
/*!< \brief Service end handle. */
......@@ -39,8 +25,8 @@ enum {
/*!< \brief card10 service declaration */
CARD10_SVC_HDL = CARD10_START_HDL,
/*!< \brief time update characteristic */
CARD10_TIME_UPDATE_CH_HDL,
CARD10_TIME_UPDATE_VAL_HDL,
CARD10_TIME_CH_HDL,
CARD10_TIME_VAL_HDL,
/*!< \brief vibra characteristic */
CARD10_VIRBA_CH_HDL,
CARD10_VIBRA_VAL_HDL,
......@@ -71,6 +57,9 @@ enum {
/*!< \brief flashlight characteristic */
CARD10_FLASHLIGHT_CH_HDL,
CARD10_FLASHLIGHT_VAL_HDL,
/*!< \brief flashlight characteristic */
CARD10_PERSONAL_STATE_CH_HDL,
CARD10_PERSONAL_STATE_VAL_HDL,
/*!< \brief leds above characteristic */
CARD10_LEDS_ABOVE_CH_HDL,
CARD10_LEDS_ABOVE_VAL_HDL,
......@@ -85,24 +74,31 @@ enum {
/* BLE UUID for card10 service*/
static const uint8_t UUID_svc[] = { CARD10_UUID_SUFFIX, 0x0, CARD10_UUID_PREFIX };
// works vor everyone?
static const uint16_t UUID_len = sizeof(UUID_svc);
// starting at 0x01 with write (non visual) charateristics
/* BLE UUID for card10 time update */
/* BLE UUID for card10 time */
static const uint8_t UUID_char_time[] = {
ATT_PROP_WRITE,
UINT16_TO_BYTES(CARD10_TIME_UPDATE_VAL_HDL),
(ATT_PROP_READ | ATT_PROP_WRITE_NO_RSP),
UINT16_TO_BYTES(CARD10_TIME_VAL_HDL),
CARD10_UUID_SUFFIX, 0x01, CARD10_UUID_PREFIX
};
static uint8_t timeValue[] = { UINT32_TO_BYTES(0), UINT32_TO_BYTES(0) };
static uint16_t timeLen = sizeof(timeValue);
// works vor everyone?
static const uint16_t UUID_char_len = sizeof(UUID_char_time);
static const uint8_t UUID_attChar_time[] = {
CARD10_UUID_SUFFIX, 0x01, CARD10_UUID_PREFIX
};
/* BLE UUID for card10 char vibra */
static const uint8_t UUID_char_vibra[] = {
ATT_PROP_WRITE,
ATT_PROP_WRITE_NO_RSP,
UINT16_TO_BYTES(CARD10_VIBRA_VAL_HDL),
CARD10_UUID_SUFFIX, 0x0f, CARD10_UUID_PREFIX
};
......@@ -116,7 +112,7 @@ static const uint8_t UUID_attChar_vibra[] = {
/* BLE UUID for card10 char rockets */
static const uint8_t UUID_char_rockets[] = {
ATT_PROP_WRITE,
ATT_PROP_READ | ATT_PROP_WRITE_NO_RSP,
UINT16_TO_BYTES(CARD10_ROCKETS_VAL_HDL),
CARD10_UUID_SUFFIX, 0x10, CARD10_UUID_PREFIX
};
......@@ -125,9 +121,12 @@ static const uint8_t UUID_attChar_rockets[] = {
CARD10_UUID_SUFFIX, 0x10, CARD10_UUID_PREFIX
};
static uint8_t rocketsValue[] = { 0, 0, 0 };
static uint16_t rocketsLen = sizeof(rocketsValue);
/* BLE UUID for card10 led background bottom left */
static const uint8_t UUID_char_led_bg_bottom_left[] = {
ATT_PROP_WRITE,
ATT_PROP_READ | ATT_PROP_WRITE_NO_RSP,
UINT16_TO_BYTES(CARD10_LED_BG_BOTTOM_LEFT_VAL_HDL),
CARD10_UUID_SUFFIX, 0x11, CARD10_UUID_PREFIX
};
......@@ -136,9 +135,13 @@ static const uint8_t UUID_attChar_led_bg_bottom_left[] = {
CARD10_UUID_SUFFIX, 0x11, CARD10_UUID_PREFIX
};
static uint8_t ledBGBottomLeftValue[] = { 0,0,0 };
// works vor everyone?
static uint16_t rgbLen = sizeof(ledBGBottomLeftValue);
/* BLE UUID for card10 led background bottom right */
static const uint8_t UUID_char_led_bg_bottom_right[] = {
ATT_PROP_WRITE,
ATT_PROP_READ | ATT_PROP_WRITE_NO_RSP,
UINT16_TO_BYTES(CARD10_LED_BG_BOTTOM_RIGHT_VAL_HDL),
CARD10_UUID_SUFFIX, 0x12, CARD10_UUID_PREFIX
};
......@@ -147,9 +150,11 @@ static const uint8_t UUID_attChar_led_bg_bottom_right[] = {
CARD10_UUID_SUFFIX, 0x12, CARD10_UUID_PREFIX
};
static uint8_t ledBGBottomRightValue[] = { 0,0,0 };
/* BLE UUID for card10 led background top right */
static const uint8_t UUID_char_led_bg_top_right[] = {
ATT_PROP_WRITE,
ATT_PROP_READ | ATT_PROP_WRITE_NO_RSP,
UINT16_TO_BYTES(CARD10_LED_BG_TOP_RIGHT_VAL_HDL),
CARD10_UUID_SUFFIX, 0x13, CARD10_UUID_PREFIX
};
......@@ -158,9 +163,11 @@ static const uint8_t UUID_attChar_led_bg_top_right[] = {
CARD10_UUID_SUFFIX, 0x13, CARD10_UUID_PREFIX
};
static uint8_t ledBGTopRightValue[] = { 0,0,0 };
/* BLE UUID for card10 led background top left */
static const uint8_t UUID_char_led_bg_top_left[] = {
ATT_PROP_WRITE,
ATT_PROP_READ | ATT_PROP_WRITE_NO_RSP,
UINT16_TO_BYTES(CARD10_LED_BG_TOP_LEFT_VAL_HDL),
CARD10_UUID_SUFFIX, 0x14, CARD10_UUID_PREFIX
};
......@@ -169,6 +176,8 @@ static const uint8_t UUID_attChar_led_bg_top_left[] = {
CARD10_UUID_SUFFIX, 0x14, CARD10_UUID_PREFIX
};
static uint8_t ledBGTopLeftValue[] = { 0,0,0 };
/* BLE UUID for card10 dim leds on bottom */
static const uint8_t UUID_char_leds_bottom_dim[] = {
ATT_PROP_WRITE,
......@@ -193,7 +202,7 @@ static const uint8_t UUID_attChar_leds_top_dim[] = {
/* BLE UUID for card10 powersafe */
static const uint8_t UUID_char_led_powersafe[] = {
ATT_PROP_WRITE,
ATT_PROP_WRITE_NO_RSP,
UINT16_TO_BYTES(CARD10_LED_POWERSAFE_VAL_HDL),
CARD10_UUID_SUFFIX, 0x17, CARD10_UUID_PREFIX
};
......@@ -204,7 +213,7 @@ static const uint8_t UUID_attChar_led_powersafe[] = {
/* BLE UUID for card10 flashlight */
static const uint8_t UUID_char_flashlight[] = {
ATT_PROP_WRITE,
ATT_PROP_WRITE_NO_RSP,
UINT16_TO_BYTES(CARD10_FLASHLIGHT_VAL_HDL),
CARD10_UUID_SUFFIX, 0x18, CARD10_UUID_PREFIX
};
......@@ -213,9 +222,23 @@ static const uint8_t UUID_attChar_flashlight[] = {
CARD10_UUID_SUFFIX, 0x18, CARD10_UUID_PREFIX
};
/* BLE UUID for card10 personal state */
static const uint8_t UUID_char_personal_state[] = {
ATT_PROP_READ | ATT_PROP_WRITE,
UINT16_TO_BYTES(CARD10_PERSONAL_STATE_VAL_HDL),
CARD10_UUID_SUFFIX, 0x19, CARD10_UUID_PREFIX
};
static const uint8_t UUID_attChar_personal_state[] = {
CARD10_UUID_SUFFIX, 0x19, CARD10_UUID_PREFIX
};
static uint8_t personalStateValue = 0;
static uint16_t personalStateLen = sizeof(personalStateValue);
/* BLE UUID for card10 above leds */
static const uint8_t UUID_char_leds_above[] = {
ATT_PROP_WRITE,
ATT_PROP_READ | ATT_PROP_WRITE_NO_RSP,
UINT16_TO_BYTES(CARD10_LEDS_ABOVE_VAL_HDL),
CARD10_UUID_SUFFIX, 0x20, CARD10_UUID_PREFIX
};
......@@ -223,6 +246,21 @@ static const uint8_t UUID_char_leds_above[] = {
static const uint8_t UUID_attChar_leds_above[] = {
CARD10_UUID_SUFFIX, 0x20, CARD10_UUID_PREFIX
};
static uint8_t aboveLEDsValue[] = {
0,0,0, // 0
0,0,0, // 1
0,0,0, // 2
0,0,0, // 3
0,0,0, // 4
0,0,0, // 5
0,0,0, // 6
0,0,0, // 7
0,0,0, // 8
0,0,0, // 9
0,0,0, // 10
};
static uint16_t aboveLEDsLen = sizeof(aboveLEDsValue);
// starting at 0xf0 with read only characteristics
/* BLE UUID for card10 char light sensor */
......@@ -234,308 +272,299 @@ static const uint8_t UUID_char_light_sensor[] = {
static const uint8_t UUID_attChar_light_sensor[] = {
CARD10_UUID_SUFFIX, 0xf0, CARD10_UUID_PREFIX
};
static uint8_t initLightSensorValue[] = { UINT16_TO_BYTES(0) };
static uint16_t initLightSensorLen = sizeof(initLightSensorValue);
/* clang-format on */
/*
* Create the BLE service description.
* Create the BLE service description.
*/
static void *addCard10GroupDyn(void)
{
void *pSHdl;
uint8_t initLightSensorValue[] = { UINT16_TO_BYTES(0) };
/* Create the service */
pSHdl = AttsDynCreateGroup(CARD10_START_HDL, CARD10_END_HDL);
if (pSHdl != NULL) {
/* Primary service */
AttsDynAddAttrConst(
pSHdl,
attPrimSvcUuid,
UUID_svc,
sizeof(UUID_svc),
0,
ATTS_PERMIT_READ
);
// TIME UPDTAE
AttsDynAddAttrConst(
pSHdl,
attChUuid,
UUID_char_time,
sizeof(UUID_char_time),
0,
ATTS_PERMIT_READ
);
AttsDynAddAttr(
pSHdl,
UUID_attChar_time,
NULL,
0,
sizeof(uint64_t),
ATTS_SET_WRITE_CBACK,
ATTS_PERMIT_WRITE | ATTS_PERMIT_WRITE_ENC |
ATTS_PERMIT_WRITE_AUTH);
// VIBRA
AttsDynAddAttrConst(
pSHdl,
attChUuid,
UUID_char_vibra,
sizeof(UUID_char_vibra),
0,
ATTS_PERMIT_READ
);
AttsDynAddAttr(
pSHdl,
UUID_attChar_vibra,
NULL,
0,
sizeof(uint16_t),
ATTS_SET_WRITE_CBACK,
ATTS_PERMIT_WRITE | ATTS_PERMIT_WRITE_ENC |
ATTS_PERMIT_WRITE_AUTH);
// ROCKETS
AttsDynAddAttrConst(
pSHdl,
attChUuid,
UUID_char_rockets,
sizeof(UUID_char_rockets),
0,
ATTS_PERMIT_READ
);
AttsDynAddAttr(
pSHdl,
UUID_attChar_rockets,
NULL,
0,
3 * sizeof(uint8_t),
ATTS_SET_WRITE_CBACK,
ATTS_PERMIT_WRITE | ATTS_PERMIT_WRITE_ENC |
ATTS_PERMIT_WRITE_AUTH);
// BG LED Bottom left
AttsDynAddAttrConst(
pSHdl,
attChUuid,
UUID_char_led_bg_bottom_left,
sizeof(UUID_char_led_bg_bottom_left),
0,
ATTS_PERMIT_READ
);
AttsDynAddAttr(
pSHdl,
UUID_attChar_led_bg_bottom_left,
NULL,
0,
3 * sizeof(uint8_t),
ATTS_SET_WRITE_CBACK,
ATTS_PERMIT_WRITE | ATTS_PERMIT_WRITE_ENC |
ATTS_PERMIT_WRITE_AUTH);
// BG LED Bottom right
AttsDynAddAttrConst(
pSHdl,
attChUuid,
UUID_char_led_bg_bottom_right,
sizeof(UUID_char_led_bg_bottom_right),
0,
ATTS_PERMIT_READ
);
AttsDynAddAttr(
pSHdl,
UUID_attChar_led_bg_bottom_right,
NULL,
0,
3 * sizeof(uint8_t),
ATTS_SET_WRITE_CBACK,
ATTS_PERMIT_WRITE | ATTS_PERMIT_WRITE_ENC |
ATTS_PERMIT_WRITE_AUTH);
// BG LED top right
AttsDynAddAttrConst(
pSHdl,
attChUuid,
UUID_char_led_bg_top_right,
sizeof(UUID_char_led_bg_top_right),
0,
ATTS_PERMIT_READ
);
AttsDynAddAttr(
pSHdl,
UUID_attChar_led_bg_top_right,
NULL,
0,
3 * sizeof(uint8_t),
ATTS_SET_WRITE_CBACK,
ATTS_PERMIT_WRITE | ATTS_PERMIT_WRITE_ENC |
ATTS_PERMIT_WRITE_AUTH);
// BG LED top left
AttsDynAddAttrConst(
pSHdl,
attChUuid,
UUID_char_led_bg_top_left,
sizeof(UUID_char_led_bg_top_left),
0,
ATTS_PERMIT_READ
);
AttsDynAddAttr(
pSHdl,
UUID_attChar_led_bg_top_left,
NULL,
0,
3 * sizeof(uint8_t),
ATTS_SET_WRITE_CBACK,
ATTS_PERMIT_WRITE | ATTS_PERMIT_WRITE_ENC |
ATTS_PERMIT_WRITE_AUTH);
// Dim bottom module
AttsDynAddAttrConst(
pSHdl,
attChUuid,
UUID_char_leds_bottom_dim,
sizeof(UUID_char_leds_bottom_dim),
0,
ATTS_PERMIT_READ
);
AttsDynAddAttr(
pSHdl,
UUID_attChar_leds_bottom_dim,
NULL,
0,
sizeof(uint8_t),
ATTS_SET_WRITE_CBACK,
ATTS_PERMIT_WRITE | ATTS_PERMIT_WRITE_ENC |
ATTS_PERMIT_WRITE_AUTH);
// Dim top module
AttsDynAddAttrConst(
pSHdl,
attChUuid,
UUID_char_leds_top_dim,
sizeof(UUID_char_leds_top_dim),
0,
ATTS_PERMIT_READ
);
static const attsAttr_t card10SvcAttrList[] = {
{
.pUuid = attPrimSvcUuid,
.pValue = (uint8_t *)UUID_svc,
.pLen = (uint16_t *)&UUID_len,
.maxLen = sizeof(UUID_svc),
.permissions = ATTS_PERMIT_READ,
},
// TIME
{
.pUuid = attChUuid,
.pValue = (uint8_t *)UUID_char_time,
.pLen = (uint16_t *)&UUID_char_len,
.maxLen = sizeof(UUID_char_time),
.permissions = ATTS_PERMIT_READ,
},
{
.pUuid = UUID_attChar_time,
.pValue = timeValue,
.pLen = &timeLen,
.maxLen = sizeof(uint64_t),
.settings = ATTS_SET_WRITE_CBACK | ATTS_SET_READ_CBACK,
.permissions = ATTS_PERMIT_WRITE | ATTS_PERMIT_WRITE_ENC |
ATTS_PERMIT_WRITE_AUTH | ATTS_PERMIT_READ |
ATTS_PERMIT_READ_ENC | ATTS_PERMIT_READ_AUTH,
},
// VIBRA
{
.pUuid = attChUuid,
.pValue = (uint8_t *)UUID_char_vibra,
.pLen = (uint16_t *)&UUID_char_len,
.maxLen = sizeof(UUID_char_vibra),
.permissions = ATTS_PERMIT_READ,
},
{
.pUuid = UUID_attChar_vibra,
.pValue = NULL,
.maxLen = sizeof(uint16_t),
.settings = ATTS_SET_WRITE_CBACK,
.permissions = ATTS_PERMIT_WRITE | ATTS_PERMIT_WRITE_ENC |
ATTS_PERMIT_WRITE_AUTH,
},
// ROCKETS
{
.pUuid = attChUuid,
.pValue = (uint8_t *)UUID_char_rockets,
.pLen = (uint16_t *)&UUID_char_len,
.maxLen = sizeof(UUID_char_rockets),
.permissions = ATTS_PERMIT_READ,
},
{
.pUuid = UUID_attChar_rockets,
.pValue = rocketsValue,
.pLen = &rocketsLen,
.maxLen = 3 * sizeof(uint8_t),
.settings = ATTS_SET_WRITE_CBACK | ATTS_SET_READ_CBACK,
.permissions = ATTS_PERMIT_WRITE | ATTS_PERMIT_WRITE_ENC |
ATTS_PERMIT_WRITE_AUTH | ATTS_PERMIT_READ |
ATTS_PERMIT_READ_ENC | ATTS_PERMIT_READ_AUTH,
},
// BG LED Bottom left
{
.pUuid = attChUuid,
.pValue = (uint8_t *)UUID_char_led_bg_bottom_left,
.pLen = (uint16_t *)&UUID_char_len,
.maxLen = sizeof(UUID_char_led_bg_bottom_left),
.permissions = ATTS_PERMIT_READ,
},
{
.pUuid = UUID_attChar_led_bg_bottom_left,
.pValue = ledBGBottomLeftValue,
.pLen = &rgbLen,
.maxLen = 3 * sizeof(uint8_t),
.settings = ATTS_SET_WRITE_CBACK | ATTS_SET_READ_CBACK,
.permissions = ATTS_PERMIT_WRITE | ATTS_PERMIT_WRITE_ENC |
ATTS_PERMIT_WRITE_AUTH | ATTS_PERMIT_READ |
ATTS_PERMIT_READ_ENC | ATTS_PERMIT_READ_AUTH,
},
// BG LED Bottom right
{
.pUuid = attChUuid,
.pValue = (uint8_t *)UUID_char_led_bg_bottom_right,
.pLen = (uint16_t *)&UUID_char_len,
.maxLen = sizeof(UUID_char_led_bg_bottom_right),
.permissions = ATTS_PERMIT_READ,
},
{
.pUuid = UUID_attChar_led_bg_bottom_right,
.pValue = ledBGBottomRightValue,
.pLen = &rgbLen,
.maxLen = 3 * sizeof(uint8_t),
.settings = ATTS_SET_WRITE_CBACK | ATTS_SET_READ_CBACK,
.permissions = ATTS_PERMIT_WRITE | ATTS_PERMIT_WRITE_ENC |
ATTS_PERMIT_WRITE_AUTH | ATTS_PERMIT_READ |
ATTS_PERMIT_READ_ENC | ATTS_PERMIT_READ_AUTH,
},
// BG LED top right
{
.pUuid = attChUuid,
.pValue = (uint8_t *)UUID_char_led_bg_top_right,
.pLen = (uint16_t *)&UUID_char_len,
.maxLen = sizeof(UUID_char_led_bg_top_right),
.settings = 0,
.permissions = ATTS_PERMIT_READ,
},
{
.pUuid = UUID_attChar_led_bg_top_right,
.pValue = ledBGTopRightValue,
.pLen = &rgbLen,
.maxLen = 3 * sizeof(uint8_t),
.settings = ATTS_SET_WRITE_CBACK | ATTS_SET_READ_CBACK,
.permissions = ATTS_PERMIT_WRITE | ATTS_PERMIT_WRITE_ENC |
ATTS_PERMIT_WRITE_AUTH | ATTS_PERMIT_READ |
ATTS_PERMIT_READ_ENC | ATTS_PERMIT_READ_AUTH,
},
// BG LED top left
{
.pUuid = attChUuid,
.pValue = (uint8_t *)UUID_char_led_bg_top_left,
.pLen = (uint16_t *)&UUID_char_len,
.maxLen = sizeof(UUID_char_led_bg_top_left),
.permissions = ATTS_PERMIT_READ,
},
{
.pUuid = UUID_attChar_led_bg_top_left,
.pValue = ledBGTopLeftValue,
.pLen = &rgbLen,
.maxLen = 3 * sizeof(uint8_t),
.settings = ATTS_SET_WRITE_CBACK | ATTS_SET_READ_CBACK,
.permissions = ATTS_PERMIT_WRITE | ATTS_PERMIT_WRITE_ENC |
ATTS_PERMIT_WRITE_AUTH | ATTS_PERMIT_READ |
ATTS_PERMIT_READ_ENC | ATTS_PERMIT_READ_AUTH,
},
// Dim bottom module
{
.pUuid = attChUuid,
.pValue = (uint8_t *)UUID_char_leds_bottom_dim,
.pLen = (uint16_t *)&UUID_char_len,
.maxLen = sizeof(UUID_char_leds_bottom_dim),
.permissions = ATTS_PERMIT_READ,
},
{
.pUuid = UUID_attChar_leds_bottom_dim,
.pValue = NULL,
.pLen = 0,
.maxLen = sizeof(uint8_t),
.settings = ATTS_SET_WRITE_CBACK,
.permissions = ATTS_PERMIT_WRITE | ATTS_PERMIT_WRITE_ENC |
ATTS_PERMIT_WRITE_AUTH,
},
// Dim top module
{
.pUuid = attChUuid,
.pValue = (uint8_t *)UUID_char_leds_top_dim,
.pLen = (uint16_t *)&UUID_char_len,
.maxLen = sizeof(UUID_char_leds_top_dim),
.permissions = ATTS_PERMIT_READ,
},
{
.pUuid = UUID_attChar_leds_top_dim,
.pValue = NULL,
.maxLen = sizeof(uint8_t),
.settings = ATTS_SET_WRITE_CBACK,
.permissions = ATTS_PERMIT_WRITE | ATTS_PERMIT_WRITE_ENC |
ATTS_PERMIT_WRITE_AUTH,
},
AttsDynAddAttr(
pSHdl,
UUID_attChar_leds_top_dim,
NULL,
0,
sizeof(uint8_t),
ATTS_SET_WRITE_CBACK,
ATTS_PERMIT_WRITE | ATTS_PERMIT_WRITE_ENC |
ATTS_PERMIT_WRITE_AUTH);
// led powersafe
AttsDynAddAttrConst(
pSHdl,
attChUuid,
UUID_char_led_powersafe,
sizeof(UUID_char_led_powersafe),
0,
ATTS_PERMIT_READ
);
AttsDynAddAttr(
pSHdl,
UUID_attChar_led_powersafe,
NULL,
0,
sizeof(uint8_t),
ATTS_SET_WRITE_CBACK,
ATTS_PERMIT_WRITE | ATTS_PERMIT_WRITE_ENC |
ATTS_PERMIT_WRITE_AUTH);
// flashlight
AttsDynAddAttrConst(
pSHdl,
attChUuid,
UUID_char_flashlight,
sizeof(UUID_char_flashlight),
0,
ATTS_PERMIT_READ
);
// led powersafe
AttsDynAddAttr(
pSHdl,
UUID_attChar_flashlight,
NULL,
0,
sizeof(uint8_t),
ATTS_SET_WRITE_CBACK,
ATTS_PERMIT_WRITE | ATTS_PERMIT_WRITE_ENC |
ATTS_PERMIT_WRITE_AUTH);
// ABOVE LEDS
AttsDynAddAttrConst(
pSHdl,
attChUuid,
UUID_char_leds_above,
sizeof(UUID_char_leds_above),
0,
ATTS_PERMIT_READ
);
{
.pUuid = attChUuid,
.pValue = (uint8_t *)UUID_char_led_powersafe,
.pLen = (uint16_t *)&UUID_char_len,
.maxLen = sizeof(UUID_char_led_powersafe),
.permissions = ATTS_PERMIT_READ,
},
{
.pUuid = UUID_attChar_led_powersafe,
.pValue = NULL,
.maxLen = sizeof(uint8_t),
.settings = ATTS_SET_WRITE_CBACK,
.permissions = ATTS_PERMIT_WRITE | ATTS_PERMIT_WRITE_ENC |
ATTS_PERMIT_WRITE_AUTH,
},
AttsDynAddAttr(
pSHdl,
UUID_attChar_leds_above,
NULL,
0,
11 * 3 * sizeof(uint8_t),
ATTS_SET_WRITE_CBACK,
ATTS_PERMIT_WRITE | ATTS_PERMIT_WRITE_ENC |
ATTS_PERMIT_WRITE_AUTH);
// LIGHT_SENSOR
AttsDynAddAttrConst(
pSHdl,
attChUuid,
UUID_char_light_sensor,
sizeof(UUID_char_light_sensor),
0,
ATTS_PERMIT_READ
);
// flashlight
AttsDynAddAttr(
pSHdl,
UUID_attChar_light_sensor,
initLightSensorValue,
sizeof(uint8_t),
sizeof(uint8_t),
ATTS_SET_READ_CBACK,
ATTS_PERMIT_READ | ATTS_PERMIT_READ_ENC |
ATTS_PERMIT_READ_AUTH);
APP_TRACE_INFO0("ble-card10: services bound\n");
}
return pSHdl;
}
{
.pUuid = attChUuid,
.pValue = (uint8_t *)UUID_char_flashlight,
.pLen = (uint16_t *)&UUID_char_len,
.maxLen = sizeof(UUID_char_flashlight),
.permissions = ATTS_PERMIT_READ,
},
{
.pUuid = UUID_attChar_flashlight,
.pValue = NULL,
.maxLen = sizeof(uint8_t),
.settings = ATTS_SET_WRITE_CBACK,
.permissions = ATTS_PERMIT_WRITE | ATTS_PERMIT_WRITE_ENC |
ATTS_PERMIT_WRITE_AUTH,
},
// personal state
{
.pUuid = attChUuid,
.pValue = (uint8_t *)UUID_char_personal_state,
.pLen = (uint16_t *)&UUID_char_len,
.maxLen = sizeof(UUID_char_personal_state),
.permissions = ATTS_PERMIT_READ,
},
{
.pUuid = UUID_attChar_personal_state,
.pValue = &personalStateValue,
.pLen = &personalStateLen,
.maxLen = sizeof(uint16_t),
.settings = ATTS_SET_WRITE_CBACK | ATTS_SET_READ_CBACK,
.permissions = ATTS_PERMIT_WRITE | ATTS_PERMIT_WRITE_ENC |
ATTS_PERMIT_WRITE_AUTH | ATTS_PERMIT_READ |
ATTS_PERMIT_READ_ENC | ATTS_PERMIT_READ_AUTH,
},
// ABOVE LEDS
{
.pUuid = attChUuid,
.pValue = (uint8_t *)UUID_char_leds_above,
.pLen = (uint16_t *)&UUID_char_len,
.maxLen = sizeof(UUID_char_leds_above),
.permissions = ATTS_PERMIT_READ,
},
{
.pUuid = UUID_attChar_leds_above,
.pValue = aboveLEDsValue,
.pLen = &aboveLEDsLen,
.maxLen = 11 * 3 * sizeof(uint8_t),
.settings = ATTS_SET_WRITE_CBACK | ATTS_SET_READ_CBACK,
.permissions = ATTS_PERMIT_WRITE | ATTS_PERMIT_WRITE_ENC |
ATTS_PERMIT_WRITE_AUTH | ATTS_PERMIT_READ |
ATTS_PERMIT_READ_ENC | ATTS_PERMIT_READ_AUTH,
},
// Light sensor
{
.pUuid = attChUuid,
.pValue = (uint8_t *)UUID_char_light_sensor,
.pLen = (uint16_t *)&UUID_char_len,
.maxLen = sizeof(UUID_char_light_sensor),
.permissions = ATTS_PERMIT_READ,
},
{
.pUuid = UUID_attChar_light_sensor,
.pValue = initLightSensorValue,
.pLen = &initLightSensorLen,
.maxLen = sizeof(uint8_t),
.settings = ATTS_SET_READ_CBACK,
.permissions = ATTS_PERMIT_READ | ATTS_PERMIT_READ_ENC |
ATTS_PERMIT_READ_AUTH,
},
};
// validating, that the service really get all charateristics
WSF_CT_ASSERT(
((sizeof(card10SvcAttrList) / sizeof(card10SvcAttrList[0])) ==
CARD10_END_HDL - CARD10_START_HDL + 1));
/*
* Set the time given in milliseconds since 1.1.1970 as 64 bit integer.
*/
......@@ -548,7 +577,39 @@ static uint8_t setTime(uint8_t *pValue)
time = __bswap64(timeNet);
epic_rtc_set_milliseconds(time);
APP_TRACE_INFO1("set time to: %d\n", time);
APP_TRACE_INFO0("ble-card10: set time");
return ATT_SUCCESS;
}
/*
* Set a rgb led
*/
static uint8_t setRGBLed(uint8_t led, uint8_t *pValue)
{
epic_leds_set(led, pValue[0], pValue[1], pValue[2]);
APP_TRACE_INFO4(
"ble-card10: set rgb led %d: #%02x%02x%02x\n",
led,
pValue[0],
pValue[1],
pValue[2]
);
return ATT_SUCCESS;
}
/*
* Get value of a rgb led
*/
static uint8_t getRGBLed(uint8_t led, attsAttr_t *pAttr)
{
epic_leds_get_rgb(led, pAttr->pValue);
APP_TRACE_INFO4(
"ble-card10: set rgb led %d: #%02x%02x%02x\n",
led,
pAttr->pValue[0],
pAttr->pValue[1],
pAttr->pValue[2]
);
return ATT_SUCCESS;
}
......@@ -570,7 +631,7 @@ static uint8_t writeCard10CB(
switch (handle) {
// time
case CARD10_TIME_UPDATE_VAL_HDL:
case CARD10_TIME_VAL_HDL:
return setTime(pValue);
// vibra
case CARD10_VIBRA_VAL_HDL:
......@@ -592,60 +653,80 @@ static uint8_t writeCard10CB(
return ATT_SUCCESS;
// bg leds
case CARD10_LED_BG_BOTTOM_LEFT_VAL_HDL:
epic_leds_set(11, pValue[0], pValue[1], pValue[2]);
APP_TRACE_INFO3(
"ble-card10: set bg bottom left: #%02x%02x%02x\n",
pValue[0],
pValue[1],
pValue[2]
);
return ATT_SUCCESS;
return setRGBLed(11, pValue);
case CARD10_LED_BG_BOTTOM_RIGHT_VAL_HDL:
epic_leds_set(12, pValue[0], pValue[1], pValue[2]);
APP_TRACE_INFO3(
"ble-card10: set bg bottom right: #%02x%02x%02x\n",
pValue[0],
pValue[1],
pValue[2]
);
return ATT_SUCCESS;
return setRGBLed(12, pValue);
case CARD10_LED_BG_TOP_RIGHT_VAL_HDL:
epic_leds_set(13, pValue[0], pValue[1], pValue[2]);
APP_TRACE_INFO3(
"ble-card10: set bg top right: #%02x%02x%02x\n",
pValue[0],
pValue[1],
pValue[2]
);
return ATT_SUCCESS;
return setRGBLed(13, pValue);
case CARD10_LED_BG_TOP_LEFT_VAL_HDL:
epic_leds_set(14, pValue[0], pValue[1], pValue[2]);
APP_TRACE_INFO3(
"ble-card10: set bg top left: #%02x%02x%02x\n",
pValue[0],
pValue[1],
pValue[2]
);
return ATT_SUCCESS;
return setRGBLed(14, pValue);
// dim
case CARD10_LEDS_BOTTOM_DIM_VAL_HDL:
ui8 = pValue[0];
if (ui8 >= 1 && ui8 <= 8) {
epic_leds_dim_bottom(pValue[0]);
APP_TRACE_INFO1("dim bottom to: %d\n", pValue[0]);
return ATT_SUCCESS;
if (operation == ATT_PDU_WRITE_CMD ||
operation == ATT_PDU_SIGNED_WRITE_CMD ||
operation == ATT_PDU_WRITE_REQ ||
operation == ATT_PDU_EXEC_WRITE_REQ) {
epic_leds_dim_bottom(pValue[0]);
APP_TRACE_INFO1(
"ble-card10: dim bottom to: %d\n",
pValue[0]
);
return ATT_SUCCESS;
} else if (operation == ATT_PDU_PREP_WRITE_REQ) {
APP_TRACE_INFO1(
"ble_card10: value for dim bottom would be okay: %d\n",
pValue[0]
);
return ATT_SUCCESS;
} else {
APP_TRACE_INFO1(
"ble-card10: dim bottom with unknown operation: %d\n",
operation
);
return ATT_ERR_INVALID_PDU;
}
} else {
APP_TRACE_INFO1(
"ble-card: prep dim bottom invalid value (1-8): %d\n",
ui8
);
return ATT_ERR_RANGE;
}
APP_TRACE_INFO1("dim bottom invalid value (1-8): %d\n", ui8);
return ATT_ERR_RANGE;
case CARD10_LEDS_TOP_DIM_VAL_HDL:
ui8 = pValue[0];
if (ui8 >= 1 && ui8 <= 8) {
epic_leds_dim_top(ui8);
APP_TRACE_INFO1("dim top to: %d\n", ui8);
return ATT_SUCCESS;
if (operation == ATT_PDU_WRITE_CMD ||
operation == ATT_PDU_SIGNED_WRITE_CMD ||
operation == ATT_PDU_WRITE_REQ ||
operation == ATT_PDU_EXEC_WRITE_REQ) {
epic_leds_dim_top(pValue[0]);
APP_TRACE_INFO1(
"ble-card10: dim top to: %d\n",
pValue[0]
);
return ATT_SUCCESS;
} else if (operation == ATT_PDU_PREP_WRITE_REQ) {
APP_TRACE_INFO1(
"ble_card10: value for dim top would be okay: %d\n",
pValue[0]
);
return ATT_SUCCESS;
} else {
APP_TRACE_INFO1(
"ble-card10: dim top with unknown operation: %d\n",
operation
);
return ATT_ERR_INVALID_PDU;
}
} else {
APP_TRACE_INFO1(
"ble-card: prep dim top invalid value (1-8): %d\n",
ui8
);
return ATT_ERR_RANGE;
}
APP_TRACE_INFO1("dim top invalid value (1-8): %d\n", ui8);
return ATT_ERR_RANGE;
// led powersafe
case CARD10_LED_POWERSAFE_VAL_HDL:
epic_leds_set_powersave(pValue[0]);
......@@ -656,27 +737,57 @@ static uint8_t writeCard10CB(
epic_set_flashlight(pValue[0]);
APP_TRACE_INFO1("set flashlight to: %d\n", pValue[0]);
return ATT_SUCCESS;
// personal state
case CARD10_PERSONAL_STATE_VAL_HDL:
BYTES_TO_UINT16(ui16, pValue);
if (ui16 <= STATE_MAX) {
if (operation == ATT_PDU_WRITE_CMD ||
operation == ATT_PDU_SIGNED_WRITE_CMD ||
operation == ATT_PDU_WRITE_REQ ||
operation == ATT_PDU_EXEC_WRITE_REQ) {
epic_personal_state_set(ui16, true);
APP_TRACE_INFO1(
"ble-card10: set personal state to: %d\n",
ui16
);
return ATT_SUCCESS;
} else if (operation == ATT_PDU_PREP_WRITE_REQ) {
APP_TRACE_INFO1(
"ble_card10: personal state would be okay: %d\n",
ui16
);
return ATT_SUCCESS;
} else {
APP_TRACE_INFO1(
"ble-card10: personal state with unknown operation: %d\n",
operation
);
return ATT_ERR_INVALID_PDU;
}
} else {
APP_TRACE_INFO2(
"ble-card: personal state invalid value (0-%d): %d\n",
STATE_MAX - 1,
ui16
);
return ATT_ERR_RANGE;
}
// leds above
case CARD10_LEDS_ABOVE_VAL_HDL:
APP_TRACE_INFO0("ble-card10: update LEDs above");
for (ui16 = 0; ui16 < 11; ui16++) {
epic_leds_set(
ui16,
pValue[ui16 * 3],
pValue[ui16 * 3 + 1],
pValue[ui16 * 3 + 2]
);
APP_TRACE_INFO4(
"ble-card10: set led %ld above to #%02x%02x%02x\n",
epic_leds_prep(
ui16,
pValue[ui16 * 3],
pValue[ui16 * 3 + 1],
pValue[ui16 * 3 + 2]
);
}
epic_leds_update();
return ATT_SUCCESS;
default:
APP_TRACE_INFO1(
"ble-card10: unsupported characteristic: %c\n", handle
);
APP_TRACE_INFO1("ble-card10: unsupported handle: %x\n", handle);
return ATT_ERR_HANDLE;
}
}
......@@ -693,8 +804,63 @@ static uint8_t readCard10CB(
attsAttr_t *pAttr
) {
uint16_t ui16 = 0;
uint64_t ui64 = 0;
uint8_t rgb[] = { 0, 0, 0 };
switch (handle) {
// time
case CARD10_TIME_VAL_HDL:
ui64 = epic_rtc_get_milliseconds();
uint64_t time;
time = __bswap64(ui64);
memcpy(pAttr->pValue, &time, sizeof(time));
APP_TRACE_INFO0("ble-card10: read time\n");
return ATT_SUCCESS;
case CARD10_ROCKETS_VAL_HDL:
pAttr->pValue[0] = epic_leds_get_rocket(0);
pAttr->pValue[1] = epic_leds_get_rocket(1);
pAttr->pValue[2] = epic_leds_get_rocket(2);
APP_TRACE_INFO3(
"ble-card10: get rockets 0:%d, 1:%d, 2:%d\n",
pAttr->pValue[0],
pAttr->pValue[1],
pAttr->pValue[2]
);
return ATT_SUCCESS;
// background leds
case CARD10_LED_BG_BOTTOM_LEFT_VAL_HDL:
return getRGBLed(11, pAttr);
case CARD10_LED_BG_BOTTOM_RIGHT_VAL_HDL:
return getRGBLed(12, pAttr);
case CARD10_LED_BG_TOP_RIGHT_VAL_HDL:
return getRGBLed(13, pAttr);
case CARD10_LED_BG_TOP_LEFT_VAL_HDL:
return getRGBLed(14, pAttr);
// personal state
case CARD10_PERSONAL_STATE_VAL_HDL:
ui16 = epic_personal_state_get();
*pAttr->pValue = ui16;
APP_TRACE_INFO1("ble-card10: read personal state: %d\n", ui16);
return ATT_SUCCESS;
// leds above
case CARD10_LEDS_ABOVE_VAL_HDL:
for (ui16 = 0; ui16 < 11; ui16++) {
epic_leds_get_rgb(ui16, rgb);
pAttr->pValue[ui16 * 3] = rgb[0];
pAttr->pValue[ui16 * 3 + 1] = rgb[1];
pAttr->pValue[ui16 * 3 + 2] = rgb[2];
APP_TRACE_INFO4(
"ble-card10: get led %ld above to #%02x%02x%02x\n",
ui16,
pAttr->pValue[ui16 * 3],
pAttr->pValue[ui16 * 3 + 1],
pAttr->pValue[ui16 * 3 + 2]
);
}
return ATT_SUCCESS;
// light sensor
case CARD10_LIGHT_SENSOR_VAL_HDL:
epic_light_sensor_get(&ui16);
*pAttr->pValue = ui16;
......@@ -706,12 +872,20 @@ static uint8_t readCard10CB(
}
}
static attsGroup_t svcCard10Group = {
.pNext = NULL,
.pAttr = (attsAttr_t *)card10SvcAttrList,
.readCback = readCard10CB,
.writeCback = writeCard10CB,
.startHandle = CARD10_START_HDL,
.endHandle = CARD10_END_HDL,
};
/*
* This registers and starts the BLE card10 service.
*/
void bleCard10_init(void)
{
void *pSHdl = addCard10GroupDyn();
AttsDynRegister(pSHdl, readCard10CB, writeCard10CB);
AttsAddGroup(&svcCard10Group);
}
#pragma once
/*! enumeration of client characteristic configuration descriptors */
enum
{
BLE_GATT_SC_CCC_IDX, /*! GATT service, service changed characteristic */
BLE_BATT_LVL_CCC_IDX, /*! Battery service, battery level characteristic */
BLE_ESS_TEMP_CCC_IDX, /*! Environmental sensing service, temperature characteristic */
BLE_ESS_HUMI_CCC_IDX, /*! Environmental sensing service, humidity characteristic */
BLE_ESS_PRES_CCC_IDX, /*! Environmental sensing service, pressure characteristic */
HIDAPP_MBI_CCC_HDL, /*! HID Boot Mouse Input characteristic */
HIDAPP_KBI_CCC_HDL, /*! HID Boot Keyboard Input characteristic */
HIDAPP_IN_KEYBOARD_CCC_HDL, /*! HID Input Report characteristic for keyboard inputs */
HIDAPP_IN_MOUSE_CCC_HDL, /*! HID Input Report characteristic for mouse inputs */
HIDAPP_IN_CONSUMER_CCC_HDL, /*! HID Input Report characteristic for consumer control inputs */
BLE_ESS_IAQ_CCC_IDX, /*! Environmental sensing service, IAQ characteristic */
UART_TX_CH_CCC_IDX,
BLE_NUM_CCC_IDX
};
#include "ble_api.h"
#include "epicardium.h"
#include "os/core.h"
#include "wsf_types.h"
#include "util/bstream.h"
#include "wsf_msg.h"
#include "att_api.h"
#include "wsf_buf.h"
#include "gatt/gatt_api.h"
#include "FreeRTOS.h"
#include "queue.h"
#include <stdio.h>
#include <string.h>
/* We allow up to 10 dynamically defined services */
#define ATTS_DYN_GROUP_COUNT 10
#define ATTS_DYN_START_HANDLE 0x200
static void *dyn_groups[ATTS_DYN_GROUP_COUNT] = {};
static int next_dyn_group = 0;
static int next_handle = ATTS_DYN_START_HANDLE;
void ble_epic_att_api_event(attEvt_t *att_event)
{
if (att_event->hdr.event != ATTS_HANDLE_VALUE_CNF ||
(att_event->handle >= ATTS_DYN_START_HANDLE &&
att_event->handle < next_handle)) {
size_t value_len = 0;
if (att_event->hdr.event == ATTC_READ_BY_GROUP_TYPE_RSP ||
att_event->hdr.event == ATTC_READ_BY_TYPE_RSP ||
att_event->hdr.event == ATTC_FIND_INFO_RSP ||
att_event->hdr.event == ATTC_HANDLE_VALUE_NTF ||
att_event->hdr.event == ATTC_HANDLE_VALUE_IND) {
value_len = att_event->valueLen;
}
attEvt_t *e = WsfBufAlloc(sizeof(*e) + value_len);
if (e) {
memcpy(e, att_event, sizeof(*e));
memcpy(e + 1, att_event->pValue, value_len);
e->pValue = (uint8_t *)(e + 1);
ble_epic_ble_api_trigger_event(BLE_EVENT_ATT_EVENT, e);
} else {
LOG_WARN(
"ble",
"could not allocate att event of size %d",
sizeof(*e) + att_event->valueLen
);
}
}
}
void ble_epic_att_api_free_att_write_data(struct epic_att_write *w)
{
WsfBufFree(w->buffer);
WsfBufFree(w);
}
static uint8_t DynAttsWriteCback(
dmConnId_t connId,
uint16_t handle,
uint8_t operation,
uint16_t offset,
uint16_t len,
uint8_t *pValue,
attsAttr_t *pAttr
) {
struct epic_att_write *att_write = WsfBufAlloc(sizeof(*att_write));
if (att_write) {
att_write->hdr.param = connId;
att_write->handle = handle;
att_write->valueLen = len;
att_write->operation = operation;
att_write->offset = offset;
att_write->buffer = WsfBufAlloc(len);
if (att_write->buffer) {
memcpy(att_write->buffer, pValue, len);
ble_epic_ble_api_trigger_event(
BLE_EVENT_ATT_WRITE, att_write
);
} else {
LOG_WARN("ble", "could allocate att write data");
WsfBufFree(att_write);
}
} else {
LOG_WARN("ble", "could allocate att write");
}
// TODO: handle offset != 0
return AttsSetAttr(handle, len, pValue);
}
int epic_atts_dyn_create_service(
const uint8_t *uuid,
uint8_t uuid_len,
uint16_t group_size,
void **pSvcHandle
) {
uint16_t start_handle = next_handle;
uint16_t end_handle = start_handle + group_size - 1;
*pSvcHandle = AttsDynCreateGroup(start_handle, end_handle);
dyn_groups[next_dyn_group++] = *pSvcHandle;
AttsDynRegister(*pSvcHandle, NULL, DynAttsWriteCback);
AttsDynAddAttr(
*pSvcHandle,
attPrimSvcUuid,
uuid,
uuid_len,
uuid_len,
0,
ATTS_PERMIT_READ
);
next_handle++;
// TODO: validate parameters and pointer and current service count
return 0;
}
int epic_atts_dyn_add_characteristic(
void *pSvcHandle,
const uint8_t *uuid,
uint8_t uuid_len,
uint8_t flags,
uint16_t maxLen,
uint16_t *value_handle
) {
uint8_t settings = ATTS_SET_VARIABLE_LEN;
if (flags & ATT_PROP_WRITE) {
settings |= ATTS_SET_WRITE_CBACK;
}
uint8_t permissions = 0;
if (flags & ATT_PROP_READ) {
permissions |= ATTS_PERMIT_READ;
}
if (flags & ATT_PROP_WRITE) {
permissions |= ATTS_PERMIT_WRITE;
}
uint8_t characteristic[1 + 2 + 16] = {
flags, UINT16_TO_BYTES(next_handle + 1)
};
memcpy(characteristic + 3, uuid, uuid_len);
uint8_t characteristic_len = 1 + 2 + uuid_len;
/* Characteristic */
AttsDynAddAttr(
pSvcHandle,
attChUuid,
characteristic,
characteristic_len,
characteristic_len,
0,
ATTS_PERMIT_READ
);
next_handle++;
/* Value */
*value_handle = next_handle;
if ((flags & ATT_PROP_READ) == 0) {
AttsDynAddAttrDynConst(
pSvcHandle,
uuid,
uuid_len,
NULL,
0,
maxLen,
settings,
permissions
);
} else {
AttsDynAddAttrDyn(
pSvcHandle,
uuid,
uuid_len,
NULL,
0,
maxLen,
settings,
permissions
);
}
next_handle++;
return 0;
}
int epic_ble_atts_dyn_add_descriptor(
void *pSvcHandle,
const uint8_t *uuid,
uint8_t uuid_len,
uint8_t flags,
const uint8_t *value,
uint16_t value_len,
uint16_t maxLen,
uint16_t *descriptor_handle
) {
uint8_t settings = 0;
if (flags & ATT_PROP_WRITE) {
settings |= ATTS_SET_WRITE_CBACK;
}
uint8_t permissions = 0;
if (flags & ATT_PROP_READ) {
permissions |= ATTS_PERMIT_READ;
}
if (flags & ATT_PROP_WRITE) {
permissions |= ATTS_PERMIT_WRITE;
}
*descriptor_handle = next_handle;
AttsDynAddAttrDyn(
pSvcHandle,
uuid,
uuid_len,
value,
value_len,
maxLen,
settings,
permissions
);
next_handle++;
return 0;
}
int epic_atts_dyn_send_service_changed_ind(void)
{
/* Indicate to the server that our GATT DB changed.
* TODO: Handling of CCCDs in pairings might still be broken:
* See https://git.card10.badge.events.ccc.de/card10/firmware/-/issues/197 */
GattSendServiceChangedInd(
DM_CONN_ID_NONE, ATT_HANDLE_START, ATT_HANDLE_MAX
);
return 0;
}
int epic_ble_atts_dyn_delete_groups(void)
{
for (int i = 0; i < ATTS_DYN_GROUP_COUNT; i++) {
if (dyn_groups[i]) {
AttsDynDeleteGroup(dyn_groups[i]);
dyn_groups[i] = NULL;
}
}
next_dyn_group = 0;
next_handle = ATTS_DYN_START_HANDLE;
return 0;
}
void ble_epic_att_api_init(void)
{
}
int epic_ble_atts_handle_value_ntf(
uint8_t connId, uint16_t handle, uint16_t valueLen, uint8_t *pValue
) {
if (!DmConnInUse(connId)) {
return -EIO;
}
AttsHandleValueNtf(connId, handle, valueLen, pValue);
return 0;
}
int epic_ble_atts_handle_value_ind(
uint8_t connId, uint16_t handle, uint16_t valueLen, uint8_t *pValue
) {
if (!DmConnInUse(connId)) {
return -EIO;
}
AttsHandleValueInd(connId, handle, valueLen, pValue);
return 0;
}
int epic_ble_atts_set_buffer(uint16_t value_handle, size_t len, bool append)
{
return AttsDynResize(value_handle, len);
}
int epic_ble_atts_set_attr(
uint16_t handle, const uint8_t *value, uint16_t value_len
) {
uint8_t ret = AttsSetAttr(handle, value_len, (uint8_t *)value);
return ret;
}
int epic_ble_attc_discover_primary_services(
uint8_t connId, const uint8_t *uuid, uint8_t uuid_len
) {
if (uuid_len == 0 || uuid == NULL) {
AttcReadByGroupTypeReq(
connId, 1, 0xFFFF, 2, (uint8_t *)attPrimSvcUuid, TRUE
);
} else {
AttcFindByTypeValueReq(
connId,
ATT_HANDLE_START,
ATT_HANDLE_MAX,
ATT_UUID_PRIMARY_SERVICE,
uuid_len,
(uint8_t *)uuid,
FALSE
);
}
return 0;
}
int epic_ble_attc_discover_characteristics(
uint8_t connId, uint16_t start_handle, uint16_t end_handle
) {
AttcReadByTypeReq(
connId,
start_handle,
end_handle,
ATT_16_UUID_LEN,
(uint8_t *)attChUuid,
TRUE
);
return 0;
}
int epic_ble_attc_discover_descriptors(
uint8_t connId, uint16_t start_handle, uint16_t end_handle
) {
AttcFindInfoReq(connId, start_handle, end_handle, TRUE);
return 0;
}
int epic_ble_attc_read(uint8_t connId, uint16_t value_handle)
{
AttcReadReq(connId, value_handle);
return 0;
}
int epic_ble_attc_write_no_rsp(
uint8_t connId,
uint16_t value_handle,
const uint8_t *value,
uint16_t value_len
) {
AttcWriteCmd(connId, value_handle, value_len, (uint8_t *)value);
return 0;
}
int epic_ble_attc_write(
uint8_t connId,
uint16_t value_handle,
const uint8_t *value,
uint16_t value_len
) {
AttcWriteReq(connId, value_handle, value_len, (uint8_t *)value);
return 0;
}
#include "ble_api.h"
#include "epicardium.h"
#include "os/core.h"
#include "modules/modules.h"
#include "user_core/interrupts.h"
#include "wsf_buf.h"
#include "app_api.h"
#include "svc_core.h"
#include "FreeRTOS.h"
#include "queue.h"
#include "timers.h"
#include <stdint.h>
#include <string.h>
#include <stdio.h>
#define BLE_EVENT_QUEUE_SIZE 10
static QueueHandle_t ble_event_queue;
static uint8_t ble_event_queue_buffer
[sizeof(struct epic_ble_event) * BLE_EVENT_QUEUE_SIZE];
static StaticQueue_t ble_event_queue_data;
static uint8_t adv_data_buf[HCI_ADV_DATA_LEN];
static uint8_t sr_data_buf[HCI_ADV_DATA_LEN];
static TimerHandle_t dm_timer;
static StaticTimer_t dm_timer_data;
static dmEvt_t connection_open_event;
static bool connection_open;
int epic_ble_free_event(struct epic_ble_event *e)
{
if (e->data) {
if (e->type == BLE_EVENT_ATT_WRITE) {
ble_epic_att_api_free_att_write_data(e->att_write);
} else {
// Generic free
WsfBufFree(e->data);
}
}
return 0;
}
void ble_epic_ble_api_trigger_event(enum epic_ble_event_type type, void *data)
{
bool enabled;
epic_interrupt_is_enabled(EPIC_INT_BLE, &enabled);
struct epic_ble_event e = { .type = type, .data = data };
if (type == BLE_EVENT_DM_EVENT) {
dmEvt_t *dm_event = data;
if (dm_event->hdr.event == DM_CONN_OPEN_IND) {
connection_open = true;
}
}
if (!connection_open &&
(type == BLE_EVENT_ATT_EVENT || type == BLE_EVENT_DM_EVENT)) {
// Don't forward DM and ATT events until epicardium is done setting up
// the connection
epic_ble_free_event(&e);
return;
}
if (enabled) {
if (xQueueSend(ble_event_queue, &e, 0) != pdTRUE) {
/* Print a warning if the app is missing events. Missing scan results
* is considered OK though, as they are queued and periodic. */
if (type != BLE_EVENT_SCAN_REPORT) {
LOG_WARN(
"ble",
"Application missed event %u",
type
);
}
epic_ble_free_event(&e);
}
interrupt_trigger(EPIC_INT_BLE);
} else {
epic_ble_free_event(&e);
}
if (type == BLE_EVENT_DM_EVENT) {
dmEvt_t *dm_event = data;
if (dm_event->hdr.event == DM_CONN_CLOSE_IND) {
connection_open = false;
}
}
}
int epic_ble_get_event(struct epic_ble_event *e)
{
if (!ble_is_enabled()) {
return -EIO;
}
if (xQueueReceive(ble_event_queue, e, 0) != pdTRUE) {
return -ENOENT;
}
return uxQueueMessagesWaiting(ble_event_queue);
}
void ble_epic_ble_api_init(void)
{
ble_event_queue = xQueueCreateStatic(
BLE_EVENT_QUEUE_SIZE,
sizeof(struct epic_ble_event),
ble_event_queue_buffer,
&ble_event_queue_data
);
ble_epic_att_api_init();
}
static void send_dm_event(dmEvt_t *dm_event)
{
dmEvt_t *e = WsfBufAlloc(sizeof(*e));
if (e) {
memcpy(e, dm_event, sizeof(*e));
ble_epic_ble_api_trigger_event(BLE_EVENT_DM_EVENT, e);
} else {
LOG_WARN("ble", "could not allocate dm event");
}
}
void ble_epic_dm_api_event(dmEvt_t *dm_event)
{
if (dm_event->hdr.event == DM_CONN_OPEN_IND) {
/* Cache the connection open indication until
* epicardium is done dicovering services. */
memcpy(&connection_open_event,
dm_event,
sizeof(connection_open_event));
} else {
send_dm_event(dm_event);
}
}
void ble_epic_disc_cfg_complete(void)
{
send_dm_event(&connection_open_event);
}
void epic_ble_close_connection(uint8_t connId)
{
AppConnClose(connId);
}
int epic_ble_is_connection_open(void)
{
return AppConnIsOpen();
}
void vDmTimerCallback()
{
send_dm_event(&connection_open_event);
}
int epic_ble_init(void)
{
if (!ble_is_enabled()) {
return -EIO;
}
if (dm_timer == NULL) {
dm_timer = xTimerCreateStatic(
"dmtimer",
1,
pdFALSE, /* one-shot */
0,
vDmTimerCallback,
&dm_timer_data);
}
epic_interrupt_enable(EPIC_INT_BLE);
if (connection_open) {
// Give pycardium a bit of time and then let it
// know that there already is an open connection
int millis = 100;
int ticks = millis * (configTICK_RATE_HZ / 1000);
xTimerChangePeriod(dm_timer, ticks, 0);
}
return 0;
}
int epic_ble_deinit(void)
{
xTimerStop(dm_timer, 0);
epic_interrupt_disable(EPIC_INT_BLE);
return 0;
}
int epic_ble_set_device_name(const uint8_t *buf, uint16_t len)
{
return epic_ble_atts_set_attr(GAP_DN_HDL, buf, len);
}
int epic_ble_get_device_name(uint8_t **buf, uint16_t *len)
{
uint8_t ret = AttsGetAttr(GAP_DN_HDL, len, buf);
return ret;
}
int epic_ble_advertise(
int interval_us,
const uint8_t *adv_data,
size_t adv_data_len,
const uint8_t *sr_data,
size_t sr_data_len,
bool connectable
) {
if (adv_data_len > sizeof(adv_data_buf)) {
adv_data_len = sizeof(adv_data_buf);
}
if (sr_data_len > sizeof(sr_data_buf)) {
sr_data_len = sizeof(sr_data_buf);
}
memcpy(adv_data_buf, adv_data, adv_data_len);
memcpy(sr_data_buf, sr_data, sr_data_len);
ble_adv_set_interval(interval_us);
if (connectable) {
AppAdvSetData(
APP_ADV_DATA_CONNECTABLE, adv_data_len, adv_data_buf
);
AppAdvSetData(
APP_SCAN_DATA_CONNECTABLE, sr_data_len, sr_data_buf
);
ble_adv_start(APP_MODE_CONNECTABLE);
} else {
AppAdvSetData(
APP_ADV_DATA_DISCOVERABLE, adv_data_len, adv_data_buf
);
AppAdvSetData(
APP_SCAN_DATA_DISCOVERABLE, sr_data_len, sr_data_buf
);
ble_adv_start(APP_MODE_DISCOVERABLE);
}
return 0;
}
int epic_ble_advertise_stop(void)
{
ble_adv_stop();
return 0;
}
#include "ess.h"
#include "cccd.h"
#include "wsf_types.h"
#include "util/bstream.h"
#include "wsf_assert.h"
#include "att_api.h"
#include "app_api.h"
#include "epicardium.h"
#include "os/core.h"
#include "os/work_queue.h"
#include "modules/modules.h"
#include "drivers/drivers.h"
#include "ble/ble_api.h"
#include "FreeRTOS.h"
#include "timers.h"
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
#include <machine/endian.h>
/* clang-format off */
/* BLE UUID for ESS service*/
static const uint8_t UUID_svc[] = { UINT16_TO_BYTES(ATT_UUID_ENVIRONMENTAL_SENSING_SERVICE) };
static const uint16_t UUID_svc_len = sizeof(UUID_svc);
/* BLE UUID for temperature */
static const uint8_t UUID_char_temperature[] = {
ATT_PROP_READ | ATT_PROP_NOTIFY,
UINT16_TO_BYTES(ESS_TEMPERATURE_VAL_HDL),
UINT16_TO_BYTES(ATT_UUID_TEMPERATURE)
};
/* BLE UUID for humidity */
static const uint8_t UUID_char_humidity[] = {
ATT_PROP_READ | ATT_PROP_NOTIFY,
UINT16_TO_BYTES(ESS_HUMIDITY_VAL_HDL),
UINT16_TO_BYTES(ATT_UUID_HUMIDITY)
};
/* BLE UUID for pressure */
static const uint8_t UUID_char_pressure[] = {
ATT_PROP_READ | ATT_PROP_NOTIFY,
UINT16_TO_BYTES(ESS_PRESSURE_VAL_HDL),
UINT16_TO_BYTES(ATT_UUID_PRESSURE)
};
/* BLE UUID for IAQ */
static const uint8_t UUID_char_IAQ[] = {
ATT_PROP_READ | ATT_PROP_NOTIFY,
UINT16_TO_BYTES(ESS_IAQ_VAL_HDL),
CARD10_UUID_SUFFIX, 0xf1, CARD10_UUID_PREFIX
};
static const uint8_t UUID_attChar_IAQ[] = {
CARD10_UUID_SUFFIX, 0xf1, CARD10_UUID_PREFIX
};
static const uint16_t UUID_char_len = sizeof(UUID_char_temperature);
static const uint16_t UUID_char_IAQ_len = sizeof(UUID_char_IAQ);
static uint8_t initTemperatureValue[] = { UINT16_TO_BYTES(0) };
static uint16_t initTemperatureLen = sizeof(initTemperatureValue);
static uint8_t initHumidityValue[] = { UINT16_TO_BYTES(0) };
static uint16_t initHumidityLen = sizeof(initHumidityValue);
static uint8_t initPressureValue[] = { UINT32_TO_BYTES(0) };
static uint16_t initPressureLen = sizeof(initPressureValue);
static uint8_t initIAQValue[] = {0x00, UINT16_TO_BYTES(0), UINT16_TO_BYTES(0)};
static uint16_t initIAQLen = sizeof(initIAQValue);
/* Temperature client characteristic configuration */
static uint8_t essValTempChCcc[] = {UINT16_TO_BYTES(0x0000)};
static const uint16_t essLenTempChCcc = sizeof(essValTempChCcc);
/* Humidity client characteristic configuration */
static uint8_t essValHumidityChCcc[] = {UINT16_TO_BYTES(0x0000)};
static const uint16_t essLenHumidityChCcc = sizeof(essValHumidityChCcc);
/* Pressure client characteristic configuration */
static uint8_t essValPressureChCcc[] = {UINT16_TO_BYTES(0x0000)};
static const uint16_t essLenPressureChCcc = sizeof(essValPressureChCcc);
/* IAQ client characteristic configuration */
static uint8_t essValIAQChCcc[] = {UINT16_TO_BYTES(0x0000)};
static const uint16_t essLenIAQChCcc = sizeof(essValIAQChCcc);
/* clang-format on */
/*
* BLE service description
*/
static const attsAttr_t ESSSvcAttrList[] = {
{
.pUuid = attPrimSvcUuid,
.pValue = (uint8_t *)UUID_svc,
.pLen = (uint16_t *)&UUID_svc_len,
.maxLen = sizeof(UUID_svc),
.permissions = ATTS_PERMIT_READ,
},
// Temperature
{
.pUuid = attChUuid,
.pValue = (uint8_t *)UUID_char_temperature,
.pLen = (uint16_t *)&UUID_char_len,
.maxLen = sizeof(UUID_char_temperature),
.permissions = ATTS_PERMIT_READ,
},
{
.pUuid = attTemperatureChUuid,
.pValue = initTemperatureValue,
.pLen = &initTemperatureLen,
.maxLen = sizeof(initTemperatureValue),
.settings = ATTS_SET_READ_CBACK,
.permissions = ATTS_PERMIT_READ | ATTS_PERMIT_READ_ENC |
ATTS_PERMIT_READ_AUTH,
},
/* Characteristic CCC descriptor */
{
.pUuid = attCliChCfgUuid,
.pValue = essValTempChCcc,
.pLen = (uint16_t *)&essLenTempChCcc,
.maxLen = sizeof(essValTempChCcc),
.settings = ATTS_SET_CCC,
.permissions =
(ATTS_PERMIT_READ |
ATTS_PERMIT_WRITE) // How about security?
},
// Humidity
{
.pUuid = attChUuid,
.pValue = (uint8_t *)UUID_char_humidity,
.pLen = (uint16_t *)&UUID_char_len,
.maxLen = sizeof(UUID_char_humidity),
.permissions = ATTS_PERMIT_READ,
},
{
.pUuid = attHumidityChUuid,
.pValue = initHumidityValue,
.pLen = &initHumidityLen,
.maxLen = sizeof(initHumidityValue),
.settings = ATTS_SET_READ_CBACK,
.permissions = ATTS_PERMIT_READ | ATTS_PERMIT_READ_ENC |
ATTS_PERMIT_READ_AUTH,
},
/* Characteristic CCC descriptor */
{
.pUuid = attCliChCfgUuid,
.pValue = essValHumidityChCcc,
.pLen = (uint16_t *)&essLenHumidityChCcc,
.maxLen = sizeof(essValHumidityChCcc),
.settings = ATTS_SET_CCC,
.permissions =
(ATTS_PERMIT_READ |
ATTS_PERMIT_WRITE) // How about security?
},
// Pressure
{
.pUuid = attChUuid,
.pValue = (uint8_t *)UUID_char_pressure,
.pLen = (uint16_t *)&UUID_char_len,
.maxLen = sizeof(UUID_char_pressure),
.permissions = ATTS_PERMIT_READ,
},
{
.pUuid = attPressureChUuid,
.pValue = initPressureValue,
.pLen = &initPressureLen,
.maxLen = sizeof(initPressureValue),
.settings = ATTS_SET_READ_CBACK,
.permissions = ATTS_PERMIT_READ | ATTS_PERMIT_READ_ENC |
ATTS_PERMIT_READ_AUTH,
},
/* Characteristic CCC descriptor */
{
.pUuid = attCliChCfgUuid,
.pValue = essValPressureChCcc,
.pLen = (uint16_t *)&essLenPressureChCcc,
.maxLen = sizeof(essValPressureChCcc),
.settings = ATTS_SET_CCC,
.permissions =
(ATTS_PERMIT_READ |
ATTS_PERMIT_WRITE) // How about security?
},
// IAQ
{
.pUuid = attChUuid,
.pValue = (uint8_t *)UUID_char_IAQ,
.pLen = (uint16_t *)&UUID_char_IAQ_len,
.maxLen = sizeof(UUID_char_IAQ),
.permissions = ATTS_PERMIT_READ,
},
{
.pUuid = UUID_attChar_IAQ,
.pValue = initIAQValue,
.pLen = &initIAQLen,
.maxLen = sizeof(initIAQValue),
.settings = ATTS_SET_READ_CBACK,
.permissions = ATTS_PERMIT_READ | ATTS_PERMIT_READ_ENC |
ATTS_PERMIT_READ_AUTH,
},
/* Characteristic CCC descriptor */
{
.pUuid = attCliChCfgUuid,
.pValue = essValIAQChCcc,
.pLen = (uint16_t *)&essLenIAQChCcc,
.maxLen = sizeof(essValIAQChCcc),
.settings = ATTS_SET_CCC,
.permissions =
(ATTS_PERMIT_READ |
ATTS_PERMIT_WRITE) // How about security?
},
};
// validating that the service really has all charateristics
WSF_CT_ASSERT(
((sizeof(ESSSvcAttrList) / sizeof(ESSSvcAttrList[0])) ==
ESS_END_HDL - ESS_START_HDL + 1));
static TimerHandle_t poll_timer;
static StaticTimer_t poll_timer_buffer;
static void update_from_bme680(struct bme680_sensor_data *data);
static void workpoll(void *data)
{
struct bme680_sensor_data sensor_data;
if (epic_bme680_read_sensors(&sensor_data) == 0) {
update_from_bme680(&sensor_data);
}
}
static void poll(TimerHandle_t xTimer)
{
workqueue_schedule(workpoll, NULL);
}
static void periodic(int period)
{
if (poll_timer == NULL) {
poll_timer = xTimerCreateStatic(
"bme680_poll",
1,
pdTRUE,
NULL,
poll,
&poll_timer_buffer
);
}
if (period < 1) {
xTimerStop(poll_timer, 0);
} else {
xTimerChangePeriod(poll_timer, pdMS_TO_TICKS(period), 0);
}
}
static void
sendNotification(dmConnId_t connId, uint16_t handle, uint16_t cccidx)
{
if (connId != DM_CONN_ID_NONE) {
uint16_t len;
uint8_t *value;
uint8_t ret = AttsGetAttr(handle, &len, &value);
if (ret == ATT_SUCCESS) {
if (AttsCccEnabled(connId, cccidx)) {
AttsHandleValueNtf(connId, handle, len, value);
}
}
}
}
static void setAttrFromBME680(struct bme680_sensor_data *data)
{
int16_t temperature;
uint16_t humidity;
uint32_t pressure;
temperature = data->temperature * 100;
AttsSetAttr(
ESS_TEMPERATURE_VAL_HDL,
sizeof(temperature),
(uint8_t *)&temperature
);
humidity = data->humidity * 100;
AttsSetAttr(
ESS_HUMIDITY_VAL_HDL, sizeof(humidity), (uint8_t *)&humidity
);
pressure = data->pressure * 1000;
AttsSetAttr(
ESS_PRESSURE_VAL_HDL, sizeof(pressure), (uint8_t *)&pressure
);
}
static void setAttrFromBSEC(struct bsec_sensor_data *data)
{
setAttrFromBME680((struct bme680_sensor_data *)data);
uint16_t iaq = data->indoor_air_quality;
uint8_t accuracy = data->accuracy;
uint16_t co2_equivalent = data->co2_equivalent;
uint8_t IAQValue[] = { accuracy,
UINT16_TO_BYTES(iaq),
UINT16_TO_BYTES(co2_equivalent) };
AttsSetAttr(ESS_IAQ_VAL_HDL, sizeof(IAQValue), IAQValue);
}
/*
* BLE ESS read callback.
*
*/
static uint8_t readESSCB(
dmConnId_t connId,
uint16_t handle,
uint8_t operation,
uint16_t offset,
attsAttr_t *pAttr
) {
struct bme680_sensor_data data;
int ret = epic_bme680_read_sensors(&data);
if (ret != 0) {
return ATT_ERR_UNDEFINED;
}
setAttrFromBME680(&data);
/* Send notifications (if enabled) for the _other_ characteristics. */
switch (handle) {
case ESS_TEMPERATURE_VAL_HDL:
sendNotification(
connId, ESS_HUMIDITY_VAL_HDL, BLE_ESS_HUMI_CCC_IDX
);
sendNotification(
connId, ESS_PRESSURE_VAL_HDL, BLE_ESS_PRES_CCC_IDX
);
APP_TRACE_INFO1("ble-ess: read temperature: %d\n", temperature);
return ATT_SUCCESS;
case ESS_HUMIDITY_VAL_HDL:
sendNotification(
connId, ESS_TEMPERATURE_VAL_HDL, BLE_ESS_TEMP_CCC_IDX
);
sendNotification(
connId, ESS_PRESSURE_VAL_HDL, BLE_ESS_PRES_CCC_IDX
);
APP_TRACE_INFO1("ble-ess: read humidity: %u\n", humidity);
return ATT_SUCCESS;
case ESS_PRESSURE_VAL_HDL:
sendNotification(
connId, ESS_TEMPERATURE_VAL_HDL, BLE_ESS_TEMP_CCC_IDX
);
sendNotification(
connId, ESS_HUMIDITY_VAL_HDL, BLE_ESS_HUMI_CCC_IDX
);
APP_TRACE_INFO1("ble-ess: read pressure: %u\n", pressure);
return ATT_SUCCESS;
case ESS_IAQ_VAL_HDL:
return ATT_SUCCESS;
default:
APP_TRACE_INFO0("ble-card10: read no handler found\n");
return ATT_ERR_HANDLE;
}
}
static attsGroup_t svcESSGroup = {
.pNext = NULL,
.pAttr = (attsAttr_t *)ESSSvcAttrList,
.readCback = readESSCB,
.writeCback = NULL,
.startHandle = ESS_START_HDL,
.endHandle = ESS_END_HDL,
};
static void update_from_bme680(struct bme680_sensor_data *data)
{
setAttrFromBME680(data);
/* Send notifications (if enabled) for all characteristics. */
dmConnId_t connId = AppConnIsOpen();
sendNotification(connId, ESS_TEMPERATURE_VAL_HDL, BLE_ESS_TEMP_CCC_IDX);
sendNotification(connId, ESS_HUMIDITY_VAL_HDL, BLE_ESS_HUMI_CCC_IDX);
sendNotification(connId, ESS_PRESSURE_VAL_HDL, BLE_ESS_PRES_CCC_IDX);
}
void bleESS_update_from_bsec_data(struct bsec_sensor_data *data)
{
setAttrFromBSEC(data);
/* Send notifications (if enabled) for all characteristics. */
dmConnId_t connId = AppConnIsOpen();
sendNotification(connId, ESS_TEMPERATURE_VAL_HDL, BLE_ESS_TEMP_CCC_IDX);
sendNotification(connId, ESS_HUMIDITY_VAL_HDL, BLE_ESS_HUMI_CCC_IDX);
sendNotification(connId, ESS_PRESSURE_VAL_HDL, BLE_ESS_PRES_CCC_IDX);
sendNotification(connId, ESS_IAQ_VAL_HDL, BLE_ESS_IAQ_CCC_IDX);
}
/*
* This registers and starts the ESS service.
*/
void bleESS_init(void)
{
AttsAddGroup(&svcESSGroup);
}
/*
* Instruct the ESS service to check if any characterstics have
* notifications enabled and enable/disable periodic measurements.
*/
void bleESS_ccc_update(void)
{
if (bsec_active()) {
return;
}
dmConnId_t connId = AppConnIsOpen();
if (connId != DM_CONN_ID_NONE &&
(AttsCccEnabled(connId, BLE_ESS_TEMP_CCC_IDX) ||
AttsCccEnabled(connId, BLE_ESS_HUMI_CCC_IDX) ||
AttsCccEnabled(connId, BLE_ESS_PRES_CCC_IDX) ||
AttsCccEnabled(connId, BLE_ESS_IAQ_CCC_IDX))) {
LOG_INFO("ess", "enable periodic measurement");
periodic(3000);
} else {
LOG_INFO("ess", "disable periodic measurement");
periodic(0);
}
}
#pragma once
#include "epicardium.h"
/*!< \brief Service start handle. */
#define ESS_START_HDL 0x1000
/*!< \brief Service end handle. */
#define ESS_END_HDL (ESS_MAX_HDL - 1)
enum {
/*!< \brief environmental sensing services service declaration */
ESS_SVC_HDL = ESS_START_HDL,
/*!< \brief temperature characteristic */
ESS_TEMPERATURE_CH_HDL,
ESS_TEMPERATURE_VAL_HDL,
ESS_TEMP_CH_CCC_HDL, /*!< Temperature CCCD */
/*!< \brief humidity characteristic */
ESS_HUMIDITY_CH_HDL,
ESS_HUMIDITY_VAL_HDL,
ESS_HUMI_CH_CCC_HDL, /*!< Humidity CCCD */
/*!< \brief pressure characteristic */
ESS_PRESSURE_CH_HDL,
ESS_PRESSURE_VAL_HDL,
ESS_PRES_CH_CCC_HDL, /*!< Pressure CCCD */
/*!< \brief IAQ/CO2 characteristic */
ESS_IAQ_CH_HDL,
ESS_IAQ_VAL_HDL,
ESS_IAQ_CH_CCC_HDL, /*!< IAQ CCCD */
/*!< \brief Maximum handle. */
ESS_MAX_HDL
};
void bleESS_ccc_update(void);
void bleESS_update_from_bsec_data(struct bsec_sensor_data *data);
/*
* BLE (Bluetooth Low energy) file transfer service.
*
* This file provides a BLE service and a characteristic which allows to
* write the content of the mytest.py file over BLE into the file system.
* We haven't found any existing BLE service which provides such a
* This file provides a BLE service and a characteristic which allows to
* write the content of the mytest.py file over BLE into the file system.
* We haven't found any existing BLE service which provides such a
* functionality so we implemented our own service.
* The service uses the UUID in fileTransSvc
......@@ -11,11 +11,11 @@
* BLE point to point links use a pretty small MTU (min 23 bytes, max 244
* bytes) which is negotiated between the central and the peripheral (card10).
*
*
* The first byte of each message is the type of message being send,
* different types are supported and based on this type the next bytes
* have different meaning.
*
*
* TODOs:
* * File deletion
* * File read
......@@ -33,6 +33,7 @@
#include "hci_vs.h"
#include <epicardium.h>
#include "os/core.h"
#include "util/bstream.h"
#include "att_api.h"
......@@ -40,6 +41,7 @@
#include "FreeRTOS.h"
#include "crc32.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
......@@ -76,119 +78,109 @@ enum {
};
/* BLE File transfer Service UUID */
static const uint8_t fileTransSvc[] = { FILE_TRANS_UUID_SUFFIX,
0x00,
FILE_TRANS_UUID_PREFIX };
static const uint8_t fileTransSvc[] = { FILE_TRANS_UUID_SUFFIX,
0x00,
FILE_TRANS_UUID_PREFIX };
static const uint16_t fileTransSvc_len = sizeof(fileTransSvc);
/* BLE File transfer Central TX configuration */
static const uint8_t txChConfig[] = { ATT_PROP_WRITE_NO_RSP,
UINT16_TO_BYTES(
FILE_TRANS_CENTRAL_TX_VAL_HDL),
FILE_TRANS_UUID_SUFFIX,
0x01,
FILE_TRANS_UUID_PREFIX };
static const uint8_t txChConfig[] = { ATT_PROP_WRITE_NO_RSP,
UINT16_TO_BYTES(
FILE_TRANS_CENTRAL_TX_VAL_HDL),
FILE_TRANS_UUID_SUFFIX,
0x01,
FILE_TRANS_UUID_PREFIX };
static const uint16_t txChConfig_len = sizeof(txChConfig);
/* BLE File transfer Central TX UUID */
static const uint8_t attTxChConfigUuid[] = { FILE_TRANS_UUID_SUFFIX,
0x01,
FILE_TRANS_UUID_PREFIX };
/* BLE File transfer Central RX configuration */
static const uint8_t rxChConfig[] = { ATT_PROP_READ | ATT_PROP_NOTIFY,
UINT16_TO_BYTES(
FILE_TRANS_CENTRAL_RX_VAL_HDL),
FILE_TRANS_UUID_SUFFIX,
0x02,
FILE_TRANS_UUID_PREFIX };
static const uint8_t rxChConfig[] = { ATT_PROP_READ | ATT_PROP_NOTIFY,
UINT16_TO_BYTES(
FILE_TRANS_CENTRAL_RX_VAL_HDL),
FILE_TRANS_UUID_SUFFIX,
0x02,
FILE_TRANS_UUID_PREFIX };
static const uint16_t rxChConfig_len = sizeof(rxChConfig);
/* BLE File transfer Central RX UUID */
static const uint8_t attRxChConfigUuid[] = { FILE_TRANS_UUID_SUFFIX,
0x02,
FILE_TRANS_UUID_PREFIX };
static uint8_t attRxChConfigValue[64];
static uint16_t attRxChConfigValue_len = 0;
/* File descriptor of the currently transferred file */
static int file_fd = -1;
/*
* Create the BLE service description.
*/
static void *fileTransAddGroupDyn(void)
{
void *pSHdl;
uint8_t initCcc[] = { UINT16_TO_BYTES(0x0000) };
/* Create the service */
pSHdl = AttsDynCreateGroup(FILE_TRANS_START_HDL, FILE_TRANS_END_HDL);
if (pSHdl != NULL) {
/* Primary service */
AttsDynAddAttrConst(
pSHdl,
attPrimSvcUuid,
fileTransSvc,
sizeof(fileTransSvc),
0,
ATTS_PERMIT_READ
);
/* File transfer Central TX characteristic */
AttsDynAddAttrConst(
pSHdl,
attChUuid,
txChConfig,
sizeof(txChConfig),
0,
ATTS_PERMIT_READ
);
/* File transfer Central TX, this contains information about the real data */
AttsDynAddAttr(
pSHdl,
attTxChConfigUuid,
NULL,
0,
128,
ATTS_SET_WRITE_CBACK | ATTS_SET_VARIABLE_LEN,
ATTS_PERMIT_WRITE
);
/* File transfer Central RX characteristic */
AttsDynAddAttrConst(
pSHdl,
attChUuid,
rxChConfig,
sizeof(rxChConfig),
0,
ATTS_PERMIT_READ
);
/* File transfer Central RX, this contains information about the real data */
AttsDynAddAttr(
pSHdl,
attRxChConfigUuid,
NULL,
0,
64,
ATTS_SET_READ_CBACK,
ATTS_PERMIT_READ
);
/* File transfer Central RX notification channel */
AttsDynAddAttr(
pSHdl,
attCliChCfgUuid,
initCcc,
sizeof(uint16_t),
sizeof(uint16_t),
ATTS_SET_CCC,
ATTS_PERMIT_READ | ATTS_PERMIT_WRITE
);
}
return pSHdl;
}
/* Attribute list for uriCfg group */
static const attsAttr_t fileTransCfgList[] = {
/* Service declaration */
{
.pUuid = attPrimSvcUuid,
.pValue = (uint8_t *)fileTransSvc,
.pLen = (uint16_t *)&fileTransSvc_len,
.maxLen = sizeof(fileTransSvc),
.settings = 0,
.permissions = ATTS_PERMIT_READ,
},
/* File transfer Central TX characteristic */
{
.pUuid = attChUuid,
.pValue = (uint8_t *)txChConfig,
.pLen = (uint16_t *)&txChConfig_len,
.maxLen = sizeof(txChConfig),
.settings = 0,
.permissions = ATTS_PERMIT_READ,
},
/* File transfer Central TX, this contains information about the real data */
{
.pUuid = attTxChConfigUuid,
.pValue = NULL,
.pLen = NULL,
.maxLen = 128,
.settings = ATTS_SET_WRITE_CBACK | ATTS_SET_VARIABLE_LEN,
.permissions = ATTS_PERMIT_WRITE | ATTS_PERMIT_WRITE_ENC |
ATTS_PERMIT_WRITE_AUTH,
},
/* File transfer Central RX characteristic */
{
.pUuid = attChUuid,
.pValue = (uint8_t *)rxChConfig,
.pLen = (uint16_t *)&rxChConfig_len,
.maxLen = sizeof(rxChConfig),
.settings = 0,
.permissions = ATTS_PERMIT_READ,
},
/* File transfer Central RX, this contains information about the real data */
{
.pUuid = attRxChConfigUuid,
.pValue = attRxChConfigValue,
.pLen = &attRxChConfigValue_len,
.maxLen = sizeof(attRxChConfigValue),
.settings = ATTS_SET_VARIABLE_LEN,
.permissions = ATTS_PERMIT_READ | ATTS_PERMIT_READ_ENC |
ATTS_PERMIT_READ_AUTH,
},
/* File transfer Central RX notification channel */
{
.pUuid = attCliChCfgUuid,
.pValue = attRxChConfigValue,
.pLen = &attRxChConfigValue_len,
.maxLen = sizeof(attRxChConfigValue),
.settings = ATTS_SET_CCC,
.permissions = ATTS_PERMIT_READ | ATTS_PERMIT_READ_ENC |
ATTS_PERMIT_READ_AUTH | ATTS_PERMIT_WRITE |
ATTS_PERMIT_WRITE_ENC | ATTS_PERMIT_WRITE_AUTH,
},
};
/**
* Send a repose with an optional CRC.
*
*
* The Central RX characteristic is filled and a notification is send in addition.
* If msg is given message is added in addition.
*/
......@@ -217,9 +209,11 @@ static void sendCrcResponse(
msg,
sizeof(answer) - len);
len += strlen(msg);
printf("BLE file transfer: %s\n", msg);
LOG_ERR("filetrans", "%s\n", msg);
} else {
printf("error message \"%s\" too long\n", msg);
LOG_ERR("filetrans",
"error message \"%s\" too long\n",
msg);
}
}
......@@ -227,6 +221,41 @@ static void sendCrcResponse(
AttsHandleValueNtf(connId, FILE_TRANS_CENTRAL_RX_VAL_HDL, len, answer);
}
/*
* This function splits the path into the folders and the file name and
* creates all the missing folders.
*/
static int bleFileCreateOrOpen(char *filepath)
{
char *pathEnd;
int pos = 0;
int ret;
while (true) {
pathEnd = strchr(filepath + pos, '/');
if (!pathEnd)
return epic_file_open(filepath, "w");
pathEnd[0] = '\00';
pos = pathEnd - filepath + 1;
if (strlen(filepath)) {
ret = epic_file_stat(filepath, NULL);
if (ret == -ENOENT) {
ret = epic_file_mkdir(filepath);
if (ret) {
LOG_ERR("filetrans",
"mkdir failed: %s, ret: %i\n",
filepath,
ret);
return ret;
}
}
}
pathEnd[0] = '/';
}
}
static uint8_t bleFileOpen(dmConnId_t connId, uint8_t *pValue, uint16_t len)
{
char filepath[100];
......@@ -238,11 +267,12 @@ static uint8_t bleFileOpen(dmConnId_t connId, uint8_t *pValue, uint16_t len)
/* Copy only file path and not type, make sure this is NULL terminated */
strncpy(filepath, (char *)pValue + 1, len - 1);
filepath[len - 1] = 0;
if (file_fd != -1)
epic_file_close(file_fd);
file_fd = epic_file_open(filepath, "w");
file_fd = bleFileCreateOrOpen(filepath);
if (file_fd < 0) {
sendCrcResponse(connId, 'e', 0, NULL, "open failed");
return ATT_ERR_RESOURCES;
......@@ -305,8 +335,9 @@ static uint8_t handleCentralTX(
} else if (
operation != ATT_PDU_EXEC_WRITE_REQ &&
operation != ATT_PDU_WRITE_CMD) {
printf("operation 0x%x not supported, try normal write\n",
operation);
LOG_ERR("filetrans",
"operation 0x%x not supported, try normal write\n",
operation);
return ATT_ERR_INVALID_PDU;
}
......@@ -334,7 +365,7 @@ static uint8_t handleCentralTX(
return ATT_SUCCESS;
case 'E':
printf("Error was acked");
LOG_ERR("filetrans", "Error was acked");
return ATT_SUCCESS;
default:
......@@ -346,7 +377,7 @@ static uint8_t handleCentralTX(
/*
* BLE file transfer write callback.
*
*
* This gets called when data is written by a BLE central to our BLE
* peripheral. Here we take care of handling the received data.
*/
......@@ -365,27 +396,22 @@ static uint8_t writeCallback(
connId, handle, operation, offset, len, pValue, pAttr
);
default:
printf("unsupported characteristic: %c\n", handle);
LOG_ERR("filetrans", "unsupported handle: %x\n", handle);
return ATT_ERR_HANDLE;
}
}
static uint8_t readCallback(
dmConnId_t connId,
uint16_t handle,
uint8_t operation,
uint16_t offset,
attsAttr_t *pAttr
) {
printf("read callback\n");
return ATT_SUCCESS;
}
static attsGroup_t fileTransCfgGroup = {
.pAttr = (attsAttr_t *)fileTransCfgList,
.writeCback = writeCallback,
.startHandle = FILE_TRANS_START_HDL,
.endHandle = FILE_TRANS_END_HDL,
};
/*
* This registers and starts the BLE file transfer service.
*/
void bleFileTransfer_init(void)
{
void *pSHdl = fileTransAddGroupDyn();
AttsDynRegister(pSHdl, readCallback, writeCallback);
AttsAddGroup(&fileTransCfgGroup);
}
/*
* Based on ble-profiles/sources/apps/hidapp/hidapp_main.c
*/
#include "cccd.h"
#include "hid.h"
#include "wsf_types.h"
#include "dm_api.h"
#include "att_api.h"
#include "svc_hid.h"
#include "hid/hid_api.h"
#include "os/core.h"
#include <stdio.h>
#include <string.h>
#include <stdint.h>
/*! HidApp Report Map (Descriptor) */
/* clang-format off */
/* Based on https://github.com/adafruit/Adafruit_CircuitPython_BLE/blob/master/adafruit_ble/services/standard/hid.py */
const uint8_t hidReportMap[] =
{
0x05, 0x01, /* Usage Page (Generic Desktop Ctrls) */
0x09, 0x06, /* Usage (Keyboard) */
0xA1, 0x01, /* Collection (Application) */
0x85, HIDAPP_KEYBOARD_REPORT_ID, /* Report ID (1) */
0x05, 0x07, /* Usage Page (Kbrd/Keypad) */
0x19, 0xE0, /* Usage Minimum (\xE0) */
0x29, 0xE7, /* Usage Maximum (\xE7) */
0x15, 0x00, /* Logical Minimum (0) */
0x25, 0x01, /* Logical Maximum (1) */
0x75, 0x01, /* Report Size (1) */
0x95, 0x08, /* Report Count (8) */
0x81, 0x02, /* Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) */
0x81, 0x01, /* Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position) */
0x19, 0x00, /* Usage Minimum (\x00) */
0x29, 0x89, /* Usage Maximum (\x89) */
0x15, 0x00, /* Logical Minimum (0) */
0x25, 0x89, /* Logical Maximum (137) */
0x75, 0x08, /* Report Size (8) */
0x95, 0x06, /* Report Count (6) */
0x81, 0x00, /* Input (Data,Array,Abs,No Wrap,Linear,Preferred State,No Null Position) */
0x05, 0x08, /* Usage Page (LEDs) */
0x19, 0x01, /* Usage Minimum (Num Lock) */
0x29, 0x05, /* Usage Maximum (Kana) */
0x15, 0x00, /* Logical Minimum (0) */
0x25, 0x01, /* Logical Maximum (1) */
0x75, 0x01, /* Report Size (1) */
0x95, 0x05, /* Report Count (5) */
0x91, 0x02, /* Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) */
0x95, 0x03, /* Report Count (3) */
0x91, 0x01, /* Output (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) */
0xC0, /* End Collection */
0x05, 0x01, /* Usage Page (Generic Desktop Ctrls) */
0x09, 0x02, /* Usage (Mouse) */
0xA1, 0x01, /* Collection (Application) */
0x09, 0x01, /* Usage (Pointer) */
0xA1, 0x00, /* Collection (Physical) */
0x85, HIDAPP_MOUSE_REPORT_ID, /* Report ID (2) */
0x05, 0x09, /* Usage Page (Button) */
0x19, 0x01, /* Usage Minimum (\x01) */
0x29, 0x05, /* Usage Maximum (\x05) */
0x15, 0x00, /* Logical Minimum (0) */
0x25, 0x01, /* Logical Maximum (1) */
0x95, 0x05, /* Report Count (5) */
0x75, 0x01, /* Report Size (1) */
0x81, 0x02, /* Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) */
0x95, 0x01, /* Report Count (1) */
0x75, 0x03, /* Report Size (3) */
0x81, 0x01, /* Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position) */
0x05, 0x01, /* Usage Page (Generic Desktop Ctrls) */
0x09, 0x30, /* Usage (X) */
0x09, 0x31, /* Usage (Y) */
0x15, 0x81, /* Logical Minimum (-127) */
0x25, 0x7F, /* Logical Maximum (127) */
0x75, 0x08, /* Report Size (8) */
0x95, 0x02, /* Report Count (2) */
0x81, 0x06, /* Input (Data,Var,Rel,No Wrap,Linear,Preferred State,No Null Position) */
0x09, 0x38, /* Usage (Wheel) */
0x15, 0x81, /* Logical Minimum (-127) */
0x25, 0x7F, /* Logical Maximum (127) */
0x75, 0x08, /* Report Size (8) */
0x95, 0x01, /* Report Count (1) */
0x81, 0x06, /* Input (Data,Var,Rel,No Wrap,Linear,Preferred State,No Null Position) */
0xC0, /* End Collection */
0xC0, /* End Collection */
0x05, 0x0C, /* Usage Page (Consumer) */
0x09, 0x01, /* Usage (Consumer Control) */
0xA1, 0x01, /* Collection (Application) */
0x85, HIDAPP_CONSUMER_REPORT_ID, /* Report ID (3) */
0x75, 0x10, /* Report Size (16) */
0x95, 0x01, /* Report Count (1) */
0x15, 0x01, /* Logical Minimum (1) */
0x26, 0x8C, 0x02, /* Logical Maximum (652) */
0x19, 0x01, /* Usage Minimum (Consumer Control) */
0x2A, 0x8C, 0x02, /* Usage Maximum (AC Send) */
0x81, 0x00, /* Input (Data,Array,Abs,No Wrap,Linear,Preferred State,No Null Position) */
0xC0, /* End Collection */
#if 0
0x05, 0x01, /* Usage Page (Generic Desktop Ctrls) */
0x09, 0x05, /* Usage (Game Pad) */
0xA1, 0x01, /* Collection (Application) */
0x85, 0x05, /* Report ID (5) */
0x05, 0x09, /* Usage Page (Button) */
0x19, 0x01, /* Usage Minimum (\x01) */
0x29, 0x10, /* Usage Maximum (\x10) */
0x15, 0x00, /* Logical Minimum (0) */
0x25, 0x01, /* Logical Maximum (1) */
0x75, 0x01, /* Report Size (1) */
0x95, 0x10, /* Report Count (16) */
0x81, 0x02, /* Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) */
0x05, 0x01, /* Usage Page (Generic Desktop Ctrls) */
0x15, 0x81, /* Logical Minimum (-127) */
0x25, 0x7F, /* Logical Maximum (127) */
0x09, 0x30, /* Usage (X) */
0x09, 0x31, /* Usage (Y) */
0x09, 0x32, /* Usage (Z) */
0x09, 0x35, /* Usage (Rz) */
0x75, 0x08, /* Report Size (8) */
0x95, 0x04, /* Report Count (4) */
0x81, 0x02, /* Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) */
0xC0, /* End Collection */
#endif
};
/* clang-format on */
const uint16_t hidReportMapLen = sizeof(hidReportMap);
/*! HID Report Type/ID and attribute handle map */
/* clang-format off */
static const hidReportIdMap_t hidAppReportIdSet[] =
{
/* type ID handle */
{HID_REPORT_TYPE_INPUT, HIDAPP_KEYBOARD_REPORT_ID, HID_INPUT_REPORT_1_HDL}, /* Keyboard Input Report */
{HID_REPORT_TYPE_OUTPUT, HIDAPP_KEYBOARD_REPORT_ID, HID_OUTPUT_REPORT_HDL}, /* Keyboard Output Report */
{HID_REPORT_TYPE_FEATURE, HIDAPP_KEYBOARD_REPORT_ID, HID_FEATURE_REPORT_HDL}, /* Keyboard Feature Report */
{HID_REPORT_TYPE_INPUT, HIDAPP_MOUSE_REPORT_ID, HID_INPUT_REPORT_2_HDL}, /* Mouse Input Report */
{HID_REPORT_TYPE_INPUT, HIDAPP_CONSUMER_REPORT_ID, HID_INPUT_REPORT_3_HDL}, /* Consumer Control Input Report */
{HID_REPORT_TYPE_INPUT, HID_KEYBOARD_BOOT_ID, HID_KEYBOARD_BOOT_IN_HDL}, /* Boot Keyboard Input Report */
{HID_REPORT_TYPE_OUTPUT, HID_KEYBOARD_BOOT_ID, HID_KEYBOARD_BOOT_OUT_HDL}, /* Boot Keyboard Output Report */
{HID_REPORT_TYPE_INPUT, HID_MOUSE_BOOT_ID, HID_MOUSE_BOOT_IN_HDL}, /* Boot Mouse Input Report */
};
/* clang-format on */
void hidAppOutputCback(
dmConnId_t connId, uint8_t id, uint16_t len, uint8_t *pReport
);
void hidAppFeatureCback(
dmConnId_t connId, uint8_t id, uint16_t len, uint8_t *pReport
);
void hidAppInfoCback(dmConnId_t connId, uint8_t type, uint8_t value);
/*! HID Profile Configuration */
/* clang-format off */
static const hidConfig_t hidAppHidConfig =
{
(hidReportIdMap_t*) hidAppReportIdSet, /* Report ID to Attribute Handle map */
sizeof(hidAppReportIdSet)/sizeof(hidReportIdMap_t), /* Size of Report ID to Attribute Handle map */
&hidAppOutputCback, /* Output Report Callback */
&hidAppFeatureCback, /* Feature Report Callback */
&hidAppInfoCback /* Info Callback */
};
/* clang-format on */
static void hidAppReportInit(void)
{
uint8_t iKeyboardBuffer[HIDAPP_KEYBOARD_INPUT_REPORT_LEN];
uint8_t iMouseBuffer[HIDAPP_MOUSE_INPUT_REPORT_LEN];
uint8_t iConsumerBuffer[HIDAPP_CONSUMER_INPUT_REPORT_LEN];
uint8_t oBuffer[HIDAPP_OUTPUT_REPORT_LEN];
uint8_t fBuffer[HIDAPP_FEATURE_REPORT_LEN];
/* Keyboard Input report */
memset(iKeyboardBuffer, 0, HIDAPP_KEYBOARD_INPUT_REPORT_LEN);
AttsSetAttr(
HID_INPUT_REPORT_1_HDL,
HIDAPP_KEYBOARD_INPUT_REPORT_LEN,
iKeyboardBuffer
);
/* Mouse Input report */
memset(iMouseBuffer, 0, HIDAPP_MOUSE_INPUT_REPORT_LEN);
AttsSetAttr(
HID_INPUT_REPORT_2_HDL,
HIDAPP_MOUSE_INPUT_REPORT_LEN,
iMouseBuffer
);
/* Consumer Control Input report */
memset(iConsumerBuffer, 0, HIDAPP_CONSUMER_INPUT_REPORT_LEN);
AttsSetAttr(
HID_INPUT_REPORT_3_HDL,
HIDAPP_CONSUMER_INPUT_REPORT_LEN,
iConsumerBuffer
);
/* Output report */
memset(oBuffer, 0, HIDAPP_OUTPUT_REPORT_LEN);
AttsSetAttr(HID_OUTPUT_REPORT_HDL, HIDAPP_OUTPUT_REPORT_LEN, oBuffer);
/* Feature report */
memset(fBuffer, 0, HIDAPP_FEATURE_REPORT_LEN);
AttsSetAttr(HID_FEATURE_REPORT_HDL, HIDAPP_FEATURE_REPORT_LEN, fBuffer);
}
void hid_work_init(void);
void hid_init(void)
{
SvcHidAddGroup();
SvcHidRegister(HidAttsWriteCback, NULL);
/* Initialize the HID profile */
HidInit(&hidAppHidConfig);
/* Initialize the report attributes */
hidAppReportInit();
hid_work_init();
}
#pragma once
#include "wsf_types.h"
#include "wsf_os.h"
/* The input report fits in one byte */
#define HIDAPP_KEYBOARD_INPUT_REPORT_LEN 8
#define HIDAPP_MOUSE_INPUT_REPORT_LEN 4
#define HIDAPP_CONSUMER_INPUT_REPORT_LEN 2
#define HIDAPP_OUTPUT_REPORT_LEN 1
#define HIDAPP_FEATURE_REPORT_LEN 1
/* HID Report IDs */
#define HIDAPP_KEYBOARD_REPORT_ID 1
#define HIDAPP_MOUSE_REPORT_ID 2
#define HIDAPP_CONSUMER_REPORT_ID 3
void hid_init(void);
void HidProcMsg(wsfMsgHdr_t *pMsg);
#include "hid.h"
#include "cccd.h"
#include "epicardium.h"
#include "os/core.h"
#include "dm_api.h"
#include "att_api.h"
#include "app_api.h"
#include "hid/hid_api.h"
#include "svc_hid.h"
#include "FreeRTOS.h"
#include "queue.h"
#include <stdint.h>
#include <stdio.h>
#include <string.h>
/* HidApp TX path flags */
#define HIDAPP_TX_FLAGS_READY 0x01
/*! application control block */
/* clang-format off */
struct
{
uint8_t txFlags; /* transmit flags */
uint8_t protocolMode; /* current protocol mode */
uint8_t hostSuspended; /* TRUE if host suspended */
} hidAppCb;
/* clang-format on */
struct report {
uint8_t report_id;
uint8_t data[8];
uint8_t len;
};
#define QUEUE_SIZE 10
static QueueHandle_t queue;
static uint8_t buffer[sizeof(struct report) * QUEUE_SIZE];
static StaticQueue_t queue_data;
static int hid_queue_data(uint8_t report_id, uint8_t *data, uint8_t len)
{
struct report report;
if (report_id < 1 || report_id > 3) {
return -EINVAL;
}
report.report_id = report_id;
if (len > sizeof(report.data)) {
return -EINVAL;
}
memcpy(report.data, data, len);
report.len = len;
if (xQueueSend(queue, &report, 0) != pdTRUE) {
/* Likely full */
return -EAGAIN;
}
return 0;
}
static bool hid_dequeue_data(dmConnId_t connId)
{
uint8_t cccHandle;
struct report report;
//lock();
if (!(hidAppCb.txFlags & HIDAPP_TX_FLAGS_READY)) {
//unlock();
return false;
}
// Loop until a CCC is enabled or the queue is empty
while (true) {
if (xQueueReceive(queue, &report, 0) != pdTRUE) {
break;
}
if (HidGetProtocolMode() == HID_PROTOCOL_MODE_BOOT) {
if (report.report_id == HIDAPP_KEYBOARD_REPORT_ID) {
report.report_id = HID_KEYBOARD_BOOT_ID;
cccHandle = HIDAPP_KBI_CCC_HDL;
} else if (report.report_id == HIDAPP_MOUSE_REPORT_ID) {
report.report_id = HID_MOUSE_BOOT_ID;
cccHandle = HIDAPP_MBI_CCC_HDL;
} else {
break;
}
} else {
if (report.report_id == HIDAPP_KEYBOARD_REPORT_ID) {
cccHandle = HIDAPP_IN_KEYBOARD_CCC_HDL;
} else if (report.report_id == HIDAPP_MOUSE_REPORT_ID) {
cccHandle = HIDAPP_IN_MOUSE_CCC_HDL;
} else if (report.report_id == HIDAPP_CONSUMER_REPORT_ID) {
cccHandle = HIDAPP_IN_CONSUMER_CCC_HDL;
} else {
break;
};
}
if (AttsCccEnabled(connId, cccHandle) || 1) {
hidAppCb.txFlags &= ~(HIDAPP_TX_FLAGS_READY);
/* Send the message */
HidSendInputReport(
connId,
report.report_id,
report.len,
report.data
);
break;
}
}
//unlock();
return true;
}
/*************************************************************************************************/
/*!
* \brief Callback to handle an output report from the host.
*
* \param connId The connection identifier.
* \param id The ID of the report.
* \param len The length of the report data in pReport.
* \param pReport A buffer containing the report.
*
* \return None.
*/
/*************************************************************************************************/
void hidAppOutputCback(
dmConnId_t connId, uint8_t id, uint16_t len, uint8_t *pReport
) {
/* TODO: process output reports */
}
/*************************************************************************************************/
/*!
* \brief Callback to handle a feature report from the host.
*
* \param connId The connection identifier.
* \param id The ID of the report.
* \param len The length of the report data in pReport.
* \param pReport A buffer containing the report.
*
* \return None.
*/
/*************************************************************************************************/
void hidAppFeatureCback(
dmConnId_t connId, uint8_t id, uint16_t len, uint8_t *pReport
) {
/* TODO: process feature reports */
}
/*************************************************************************************************/
/*!
* \brief Callback to handle a change in protocol mode or control point from the host.
*
* \param connId The connection identifier.
* \param mode The type of information (HID_INFO_CONTROL_POINT or HID_INFO_PROTOCOL_MODE)
* \param value The value of the information
*
* \return None.
*/
/*************************************************************************************************/
void hidAppInfoCback(dmConnId_t connId, uint8_t type, uint8_t value)
{
if (type == HID_INFO_PROTOCOL_MODE) {
LOG_INFO("hid", "protocol mode: %u\n", value);
hidAppCb.protocolMode = value;
} else if (type == HID_INFO_CONTROL_POINT) {
LOG_INFO("hid", "host suspended: %u\n", value);
hidAppCb.hostSuspended =
(value == HID_CONTROL_POINT_SUSPEND) ? TRUE : FALSE;
}
}
void HidProcMsg(wsfMsgHdr_t *pMsg)
{
if (!queue) {
/* Not initialized (yet). */
return;
}
if (pMsg->event == ATTS_HANDLE_VALUE_CNF) {
if (pMsg->status == ATT_SUCCESS) {
hidAppCb.txFlags |= HIDAPP_TX_FLAGS_READY;
hid_dequeue_data((dmConnId_t)pMsg->param);
}
}
if (pMsg->event == DM_CONN_OPEN_IND) {
hidAppCb.txFlags = HIDAPP_TX_FLAGS_READY;
struct report report;
while (xQueueReceive(queue, &report, 0) == pdTRUE)
;
/* Todo: At this point the CCC descriptors are not set up yet
* and things which get sent until then are discarded. */
}
}
int epic_ble_hid_send_report(uint8_t report_id, uint8_t *data, uint8_t len)
{
dmConnId_t connId = AppConnIsOpen();
if (connId == DM_CONN_ID_NONE) {
return -EIO;
}
if (!queue) {
return -EIO;
}
int ret;
ret = hid_queue_data(report_id, data, len);
if (ret < 0) {
return ret;
}
if (hid_dequeue_data(connId)) {
return 0;
} else {
return 1;
}
}
void hid_work_init(void)
{
queue = xQueueCreateStatic(
QUEUE_SIZE, sizeof(struct report), buffer, &queue_data
);
hidAppCb.txFlags = HIDAPP_TX_FLAGS_READY;
}
ble_sources = files(
'ble.c',
'epic_ble_api.c',
'epic_att_api.c',
'stack.c',
'ble_main.c',
'ble_adv.c',
'ble_attc.c',
'profiles/tipc_main.c',
'profiles/gap_main.c',
'svc_dis.c',
'svc_core.c',
'app/app_main.c',
'app/common/app_db.c',
'app/common/app_ui.c',
'bondings.c',
'uart.c',
'card10.c',
'ess.c',
'filetransfer.c',
'hid.c',
'hid_work.c',
)
/*************************************************************************************************/
/*!
* \file
*
* \brief GAP profile.
*
* Copyright (c) 2015-2018 Arm Ltd. All Rights Reserved.
* Arm Ltd. confidential and proprietary.
*
* IMPORTANT. Your use of this file is governed by a Software License Agreement
* ("Agreement") that must be accepted in order to download or otherwise receive a
* copy of this file. You may not use or copy this file for any purpose other than
* as described in the Agreement. If you do not agree to all of the terms of the
* Agreement do not use this file and delete all copies in your possession or control;
* if you do not have a copy of the Agreement, you must contact Arm Ltd. prior
* to any use, copying or further distribution of this software.
*/
/*************************************************************************************************/
#ifndef GAP_API_H
#define GAP_API_H
#include "att_api.h"
#ifdef __cplusplus
extern "C" {
#endif
/*! \addtogroup GAP_PROFILE
* \{ */
/**************************************************************************************************
Macros
**************************************************************************************************/
/*! \brief Enumeration of handle indexes of characteristics to be discovered */
enum
{
GAP_DN_HDL_IDX,
GAP_CAR_HDL_IDX, /*!< \brief Central Address Resolution */
GAP_RPAO_HDL_IDX, /*!< \brief Resolvable Private Address Only */
GAP_HDL_LIST_LEN /*!< \brief Handle list length */
};
/**************************************************************************************************
Function Declarations
**************************************************************************************************/
int GapGetDeviceName(char *buf, size_t buf_size);
void GapClearDeviceName(void);
/*************************************************************************************************/
/*!
* \brief Perform service and characteristic discovery for GAP service. Note that pHdlList
* must point to an array of handles of length \ref GAP_HDL_LIST_LEN. If discovery is
* successful the handles of discovered characteristics and descriptors will be set
* in pHdlList.
*
* \param connId Connection identifier.
* \param pHdlList Characteristic handle list.
*
* \return None.
*/
/*************************************************************************************************/
void GapDiscover(dmConnId_t connId, uint16_t *pHdlList);
/*************************************************************************************************/
/*!
* \brief Process a value received in an ATT read response, notification, or indication
* message. Parameter pHdlList must point to an array of length \ref GAP_HDL_LIST_LEN.
* If the attribute handle of the message matches a handle in the handle list the value
* is processed, otherwise it is ignored.
*
* \param pHdlList Characteristic handle list.
* \param pMsg ATT callback message.
*
* \return \ref ATT_SUCCESS if handle is found, \ref ATT_ERR_NOT_FOUND otherwise.
*/
/*************************************************************************************************/
uint8_t GapValueUpdate(uint16_t *pHdlList, attEvt_t *pMsg);
/*! \} */ /* GAP_PROFILE */
#ifdef __cplusplus
};
#endif
#endif /* GAP_API_H */
/*************************************************************************************************/
/*!
* \file
*
* \brief GAP profile.
*
* Copyright (c) 2015-2018 Arm Ltd. All Rights Reserved.
* ARM Ltd. confidential and proprietary.
*
* IMPORTANT. Your use of this file is governed by a Software License Agreement
* ("Agreement") that must be accepted in order to download or otherwise receive a
* copy of this file. You may not use or copy this file for any purpose other than
* as described in the Agreement. If you do not agree to all of the terms of the
* Agreement do not use this file and delete all copies in your possession or control;
* if you do not have a copy of the Agreement, you must contact ARM Ltd. prior
* to any use, copying or further distribution of this software.
*/
/*************************************************************************************************/
#include "wsf_types.h"
#include "wsf_assert.h"
#include "wsf_trace.h"
#include "app_db.h"
#include "app_api.h"
#include "gap_api.h"
#include <string.h>
/* card10:
* copied from lib/sdk/Libraries/BTLE/stack/ble-profiles/sources/profiles/gap/gap_main.c
*/
/* clang-format off */
/* clang-formet turned off for easier diffing against orginal file */
/**************************************************************************************************
Local Variables
**************************************************************************************************/
static char dn_value[ATT_DEFAULT_PAYLOAD_LEN + 1];
/*! GAP service characteristics for discovery */
static const attcDiscChar_t gapDn =
{
attDnChUuid,
0
};
/*! Central Address Resolution */
static const attcDiscChar_t gapCar =
{
attCarChUuid,
0
};
/*! Resolvable Private Address Only */
static const attcDiscChar_t gapRpao =
{
attRpaoChUuid,
0
};
/*! List of characteristics to be discovered; order matches handle index enumeration */
static const attcDiscChar_t *gapDiscCharList[] =
{
&gapDn,
&gapCar, /* Central Address Resolution */
&gapRpao /* Resolvable Private Address Only */
};
/* sanity check: make sure handle list length matches characteristic list length */
WSF_CT_ASSERT(GAP_HDL_LIST_LEN == ((sizeof(gapDiscCharList) / sizeof(attcDiscChar_t *))));
int GapGetDeviceName(char *buf, size_t buf_size)
{
memset(buf, 0, buf_size);
strncpy(buf, dn_value, buf_size - 1);
return 0;
}
void GapClearDeviceName(void)
{
dn_value[0] = 0;
}
/*************************************************************************************************/
/*!
* \brief Perform service and characteristic discovery for GAP service. Note that pHdlList
* must point to an array of handles of length GAP_HDL_LIST_LEN. If discovery is
* successful the handles of discovered characteristics and descriptors will be set
* in pHdlList.
*
* \param connId Connection identifier.
* \param pHdlList Characteristic handle list.
*
* \return None.
*/
/*************************************************************************************************/
void GapDiscover(dmConnId_t connId, uint16_t *pHdlList)
{
AppDiscFindService(connId, ATT_16_UUID_LEN, (uint8_t *) attGapSvcUuid,
GAP_HDL_LIST_LEN, (attcDiscChar_t **) gapDiscCharList, pHdlList);
}
/*************************************************************************************************/
/*!
* \brief Process a value received in an ATT read response, notification, or indication
* message. Parameter pHdlList must point to an array of length GAP_HDL_LIST_LEN.
* If the attribute handle of the message matches a handle in the handle list the value
* is processed, otherwise it is ignored.
*
* \param pHdlList Characteristic handle list.
* \param pMsg ATT callback message.
*
* \return ATT_SUCCESS if handle is found, ATT_ERR_NOT_FOUND otherwise.
*/
/*************************************************************************************************/
uint8_t GapValueUpdate(uint16_t *pHdlList, attEvt_t *pMsg)
{
uint8_t status = ATT_SUCCESS;
/* device name string */
if (pMsg->handle == pHdlList[GAP_DN_HDL_IDX])
{
int len = (pMsg->valueLen < (sizeof(dn_value) - 1)) ? pMsg->valueLen : (sizeof(dn_value) - 1);
memcpy(dn_value, pMsg->pValue, len);
dn_value[len] = '\0';
}
/* Central Address Resolution */
else if (pMsg->handle == pHdlList[GAP_CAR_HDL_IDX])
{
appDbHdl_t dbHdl;
/* if there's a device record */
if ((dbHdl = AppDbGetHdl((dmConnId_t)pMsg->hdr.param)) != APP_DB_HDL_NONE)
{
if ((pMsg->pValue[0] == FALSE) || (pMsg->pValue[0] == TRUE))
{
/* store value in device database */
AppDbSetPeerAddrRes(dbHdl, pMsg->pValue[0]);
}
else
{
/* invalid value */
status = ATT_ERR_RANGE;
}
APP_TRACE_INFO1("Central address resolution: %d", pMsg->pValue[0]);
}
}
/* handle not found in list */
else
{
status = ATT_ERR_NOT_FOUND;
}
return status;
}
/* clang-format on */
/*************************************************************************************************/
/*!
* \file
*
* \brief Time profile client.
*
* Copyright (c) 2011-2018 Arm Ltd. All Rights Reserved.
* ARM Ltd. confidential and proprietary.
*
* IMPORTANT. Your use of this file is governed by a Software License Agreement
* ("Agreement") that must be accepted in order to download or otherwise receive a
* copy of this file. You may not use or copy this file for any purpose other than
* as described in the Agreement. If you do not agree to all of the terms of the
* Agreement do not use this file and delete all copies in your possession or control;
* if you do not have a copy of the Agreement, you must contact ARM Ltd. prior
* to any use, copying or further distribution of this software.
*/
/*************************************************************************************************/
/* card10:
* copied from lib/sdk/Libraries/BTLE/stack/ble-profiles/sources/profiles/tipc/tipc_main.c
*/
/* clang-format off */
/* clang-formet turned off for easier diffing against orginal file */
#include "wsf_types.h"
#include "wsf_assert.h"
#include "wsf_trace.h"
#include "util/bstream.h"
#include "app_api.h"
#include "tipc/tipc_api.h"
#include "epicardium.h"
#include <time.h>
#include <stdio.h>
/**************************************************************************************************
Local Variables
**************************************************************************************************/
static time_t s_time;
/*!
* Current Time service
*/
/* Characteristics for discovery */
/*! Current time */
static const attcDiscChar_t tipcCtsCt =
{
attCtChUuid,
ATTC_SET_REQUIRED
};
/*! Current time client characteristic configuration descriptor */
static const attcDiscChar_t tipcCtsCtCcc =
{
attCliChCfgUuid,
ATTC_SET_REQUIRED | ATTC_SET_DESCRIPTOR
};
/*! Local time information */
static const attcDiscChar_t tipcCtsLti =
{
attLtiChUuid,
0
};
/*! Reference time information */
static const attcDiscChar_t tipcCtsRti =
{
attRtiChUuid,
0
};
/*! List of characteristics to be discovered; order matches handle index enumeration */
static const attcDiscChar_t *tipcCtsDiscCharList[] =
{
&tipcCtsCt, /* Current time */
&tipcCtsCtCcc, /* Current time client characteristic configuration descriptor */
&tipcCtsLti, /* Local time information */
&tipcCtsRti /* Reference time information */
};
/* sanity check: make sure handle list length matches characteristic list length */
WSF_CT_ASSERT(TIPC_CTS_HDL_LIST_LEN == ((sizeof(tipcCtsDiscCharList) / sizeof(attcDiscChar_t *))));
/*************************************************************************************************/
/*!
* \brief Perform service and characteristic discovery for Current Time service. Parameter
* pHdlList must point to an array of length TIPC_CTS_HDL_LIST_LEN. If discovery is
* successful the handles of discovered characteristics and descriptors will be set
* in pHdlList.
*
* \param connId Connection identifier.
* \param pHdlList Characteristic handle list.
*
* \return None.
*/
/*************************************************************************************************/
void TipcCtsDiscover(dmConnId_t connId, uint16_t *pHdlList)
{
AppDiscFindService(connId, ATT_16_UUID_LEN, (uint8_t *) attCtsSvcUuid,
TIPC_CTS_HDL_LIST_LEN, (attcDiscChar_t **) tipcCtsDiscCharList, pHdlList);
}
/*************************************************************************************************/
/*!
* \brief Process a value received in an ATT read response, notification, or indication
* message. Parameter pHdlList must point to an array of length TIPC_CTS_HDL_LIST_LEN.
* If the attribute handle of the message matches a handle in the handle list the value
* is processed, otherwise it is ignored.
*
* \param pHdlList Characteristic handle list.
* \param pMsg ATT callback message.
*
* \return ATT_SUCCESS if handle is found, ATT_ERR_NOT_FOUND otherwise.
*/
/*************************************************************************************************/
uint8_t TipcCtsValueUpdate(uint16_t *pHdlList, attEvt_t *pMsg)
{
uint8_t status = ATT_SUCCESS;
uint8_t *p;
uint16_t year;
uint8_t month, day, hour, min, sec, dayOfWeek, adjustReason;
uint8_t sec256 = 0;
int8_t timeZone;
uint8_t dstOffset, source, accuracy;
/* Suppress unused variable compile warning */
(void)month; (void)day; (void)hour; (void)min; (void)sec; (void)dayOfWeek; (void)adjustReason;
(void)year; (void)sec256; (void)dstOffset; (void)accuracy; (void)timeZone; (void)source;
/* current time */
if (pMsg->handle == pHdlList[TIPC_CTS_CT_HDL_IDX])
{
/* parse value */
p = pMsg->pValue;
BSTREAM_TO_UINT16(year, p);
BSTREAM_TO_UINT8(month, p);
BSTREAM_TO_UINT8(day, p);
BSTREAM_TO_UINT8(hour, p);
BSTREAM_TO_UINT8(min, p);
BSTREAM_TO_UINT8(sec, p);
BSTREAM_TO_UINT8(dayOfWeek, p);
BSTREAM_TO_UINT8(sec256, p);
BSTREAM_TO_UINT8(adjustReason, p);
struct tm t;
t.tm_year = year - 1900;
t.tm_mon = month - 1;
t.tm_mday = day;
t.tm_hour = hour;
t.tm_min = min;
t.tm_sec = sec;
t.tm_isdst = -1; /* unknown */
s_time = mktime(&t);
APP_TRACE_INFO3("Date: %d/%d/%d", month, day, year);
APP_TRACE_INFO3("Time: %02d:%02d:%02d", hour, min, sec);
APP_TRACE_INFO3("dayOfWeek:%d sec256:%d adjustReason:%d", dayOfWeek, sec256, adjustReason);
}
/* local time information */
else if (pMsg->handle == pHdlList[TIPC_CTS_LTI_HDL_IDX])
{
/* parse value */
p = pMsg->pValue;
BSTREAM_TO_UINT8(timeZone, p);
BSTREAM_TO_UINT8(dstOffset, p);
APP_TRACE_INFO2("timeZone:%d dstOffset:%d", timeZone, dstOffset);
if(s_time) {
char buf[32];
snprintf(buf, sizeof(buf), "%+05d", (timeZone +dstOffset) / 4 * 100);
epic_config_set_string("timezone", buf);
epic_rtc_set_milliseconds(s_time * 1000 + (1000 * sec256) / 256 - (timeZone + dstOffset) * 15 * 60 * 1000);
}
}
/* reference time information */
else if (pMsg->handle == pHdlList[TIPC_CTS_RTI_HDL_IDX])
{
/* parse value */
p = pMsg->pValue;
BSTREAM_TO_UINT8(source, p);
BSTREAM_TO_UINT8(accuracy, p);
BSTREAM_TO_UINT8(day, p);
BSTREAM_TO_UINT8(hour, p);
APP_TRACE_INFO2("Ref. time source:%d accuracy:%d", source, accuracy);
APP_TRACE_INFO2("Last update days:%d hours:%d", day, hour);
}
/* handle not found in list */
else
{
status = ATT_ERR_NOT_FOUND;
}
return status;
}
/* clang-format on */
......@@ -89,8 +89,8 @@ const LlRtCfg_t _ll_cfg = {
/*dtmRxSyncMs*/ 10000,
/* PHY */
/*phy2mSup*/ TRUE,
/*phyCodedSup*/ TRUE,
/*phy2mSup*/ FALSE,
/*phyCodedSup*/ FALSE,
/*stableModIdxTxSup*/ FALSE,
/*stableModIdxRxSup*/ FALSE
};
......@@ -98,22 +98,20 @@ const LlRtCfg_t _ll_cfg = {
const BbRtCfg_t _bb_cfg = {
/*clkPpm*/ 20,
/*rfSetupDelayUsec*/ BB_RF_SETUP_DELAY_US,
/*defaultTxPower*/ -10,
/*maxScanPeriodMsec*/ BB_MAX_SCAN_PERIOD_MS,
/*schSetupDelayUsec*/ BB_SCH_SETUP_DELAY_US
};
/*************************************************************************************************/
/*!
* \brief Initialize stack.
* \brief Initialize link layer part of the stack.
*
* \return None.
*/
/*************************************************************************************************/
void StackInit(void)
void LlStackInit()
{
wsfHandlerId_t handlerId;
/* card10: We do not use the SDMA HCI at the moment. The block below is not compiled. */
#ifndef ENABLE_SDMA
uint32_t memUsed;
......@@ -129,18 +127,31 @@ void StackInit(void)
.freeMemAvail = LL_MEMORY_FOOTPRINT
};
#ifdef DATS_APP_USE_LEGACY_API
memUsed = LlInitControllerExtInit(&ll_init_cfg);
#else /* DATS_APP_USE_LEGACY_API */
memUsed = LlInitControllerExtInit(&ll_init_cfg);
#endif /* DATS_APP_USE_LEGACY_API */
if(memUsed != LL_MEMORY_FOOTPRINT)
{
printf("Controller memory mismatch 0x%x != 0x%x\n", (unsigned int)memUsed,
printf("Controller memory mismatch 0x%x != 0x%x\n", (unsigned int)memUsed,
(unsigned int)LL_MEMORY_FOOTPRINT);
}
#endif
}
/*************************************************************************************************/
/*!
* \brief Initialize stack.
*
* \return None.
*/
/*************************************************************************************************/
void StackInit(void)
{
wsfHandlerId_t handlerId;
SecInit();
SecRandInit();
SecAesInit();
SecCmacInit();
SecEccInit();
/* card10:
* These calls register a queue for callbacks in the OS abstraction
......@@ -152,14 +163,10 @@ void StackInit(void)
handlerId = WsfOsSetNextHandler(HciHandler);
HciHandlerInit(handlerId);
SecInit();
SecAesInit();
SecCmacInit();
SecEccInit();
handlerId = WsfOsSetNextHandler(DmHandler);
DmDevVsInit(0);
DmAdvInit();
DmScanInit();
DmConnInit();
DmConnSlaveInit();
DmSecInit();
......@@ -177,6 +184,8 @@ void StackInit(void)
AttHandlerInit(handlerId);
AttsInit();
AttsIndInit();
AttsDynInit();
AttcInit();
handlerId = WsfOsSetNextHandler(SmpHandler);
SmpHandlerInit(handlerId);
......@@ -185,5 +194,8 @@ void StackInit(void)
/*TODO card10: Probably want to adjust this */
HciSetMaxRxAclLen(100);
handlerId = WsfOsSetNextHandler(AppHandler);
AppHandlerInit(handlerId);
}
/* clang-format off */
......@@ -24,13 +24,17 @@
*/
/* clang-format off */
/* clang-formet turned off for easier diffing against orginal file */
#include "card10-version.h"
#include "wsf_types.h"
#include "att_api.h"
#include "wsf_assert.h"
#include "wsf_trace.h"
#include "util/bstream.h"
#define DIS_MAXSIZE_FWR_ATT 32
#include "svc_dis.h"
#include "svc_cfg.h"
#include <string.h>
/**************************************************************************************************
Macros
......@@ -60,10 +64,10 @@
#define DIS_DEFAULT_SERIAL_NUM_LEN 1
/*! Default firmware revision */
#define DIS_DEFAULT_FW_REV "<git hash>"
#define DIS_DEFAULT_FW_REV CARD10_VERSION
/*! Length of default firmware revision */
#define DIS_DEFAULT_FW_REV_LEN 10
#define DIS_DEFAULT_FW_REV_LEN strlen(CARD10_VERSION)
/*! Default hardware revision */
#define DIS_DEFAULT_HW_REV "1"
......@@ -128,7 +132,7 @@ static const uint16_t disLenFwrCh = sizeof(disValFwrCh);
/* Firmware revision string */
static const uint8_t disUuFwr[] = {UINT16_TO_BYTES(ATT_UUID_FIRMWARE_REV)};
static uint8_t disValFwr[DIS_MAXSIZE_FWR_ATT] = DIS_DEFAULT_FW_REV;
static uint16_t disLenFwr = DIS_DEFAULT_FW_REV_LEN;
static uint16_t disLenFwr;
/* Hardware revision string characteristic */
static const uint8_t disValHwrCh[] = {ATT_PROP_READ, UINT16_TO_BYTES(DIS_HWR_HDL), UINT16_TO_BYTES(ATT_UUID_HARDWARE_REV)};
......@@ -319,6 +323,7 @@ WSF_CT_ASSERT(((sizeof(disList) / sizeof(disList[0])) == DIS_END_HDL - DIS_START
/*************************************************************************************************/
void SvcDisAddGroup(void)
{
disLenFwr = DIS_DEFAULT_FW_REV_LEN;
AttsAddGroup(&svcDisGroup);
}
......
#include "uart.h"
#include "cccd.h"
#include "modules/modules.h"
#include "drivers/drivers.h"
#include "wsf_types.h"
#include "util/bstream.h"
#include "att_api.h"
#include "dm_api.h"
#include "app_api.h"
#include "FreeRTOS.h"
#include "timers.h"
......@@ -11,85 +17,91 @@
#include <string.h>
#include <stdbool.h>
#define UART_START_HDL 0x800 /*!< \brief Service start handle. */
#define UART_END_HDL (UART_MAX_HDL - 1) /*!< \brief Service end handle. */
/**************************************************************************************************
Handles
**************************************************************************************************/
/*! \brief UART Service Handles */
enum { UART_SVC_HDL = UART_START_HDL, /*!< \brief UART service declaration */
UART_RX_CH_HDL, /*!< \brief UART rx characteristic */
UART_RX_HDL, /*!< \brief UART rx value */
UART_TX_CH_HDL, /*!< \brief UART tx characteristic */
UART_TX_HDL, /*!< \brief UART tx value */
UART_TX_CH_CCC_HDL, /*!< \brief UART tx CCCD */
UART_MAX_HDL /*!< \brief Maximum handle. */
};
/**@}*/
/* clang-format off */
static const uint8_t UARTSvc[] = {0x9E,0xCA,0xDC,0x24,0x0E,0xE5,0xA9,0xE0,0x93,0xF3,0xA3,0xB5,0x01,0x00,0x40,0x6E};
static const uint16_t UARTSvc_len = sizeof(UARTSvc);
static const uint8_t uartRxCh[] = {ATT_PROP_WRITE, UINT16_TO_BYTES(UART_RX_HDL), 0x9E,0xCA,0xDC,0x24,0x0E,0xE5,0xA9,0xE0,0x93,0xF3,0xA3,0xB5,0x02,0x00,0x40,0x6E};
const uint8_t attUartRxChUuid[] = {0x9E,0xCA,0xDC,0x24,0x0E,0xE5, 0xA9,0xE0,0x93,0xF3,0xA3,0xB5,0x02,0x00,0x40,0x6E};
static const uint16_t uartRxCh_len = sizeof(uartRxCh);
static const uint8_t attUartRxChUuid[] = {0x9E,0xCA,0xDC,0x24,0x0E,0xE5, 0xA9,0xE0,0x93,0xF3,0xA3,0xB5,0x02,0x00,0x40,0x6E};
static const uint8_t uartTxCh[] = {ATT_PROP_READ | ATT_PROP_NOTIFY, UINT16_TO_BYTES(UART_TX_HDL), 0x9E,0xCA,0xDC,0x24,0x0E,0xE5,0xA9,0xE0,0x93,0xF3,0xA3,0xB5,0x03,0x00,0x40,0x6E};
const uint8_t attUartTxChUuid[] = {0x9E,0xCA,0xDC,0x24,0x0E,0xE5, 0xA9,0xE0,0x93,0xF3,0xA3,0xB5,0x03,0x00,0x40,0x6E};
/* clang-format on */
static const uint16_t uartTxCh_len = sizeof(uartTxCh);
static const uint8_t attUartTxChUuid[] = {0x9E,0xCA,0xDC,0x24,0x0E,0xE5, 0xA9,0xE0,0x93,0xF3,0xA3,0xB5,0x03,0x00,0x40,0x6E};
static void *SvcUARTAddGroupDyn(void)
{
void *pSHdl;
uint8_t initCcc[] = { UINT16_TO_BYTES(0x0000) };
/* Create the service */
pSHdl = AttsDynCreateGroup(UART_START_HDL, UART_END_HDL);
if (pSHdl != NULL) {
/* clang-format off */
/* Primary service */
AttsDynAddAttrConst( pSHdl, attPrimSvcUuid, UARTSvc, sizeof(UARTSvc),
0, ATTS_PERMIT_READ);
/* UART rx characteristic */
AttsDynAddAttrConst( pSHdl, attChUuid, uartRxCh, sizeof(uartRxCh),
0, ATTS_PERMIT_READ);
// XXX: attUartRxChUuid is 16 bytes but nothing says so....
/* UART rx value */
// XXX: not sure if max value of 128 is fine...
AttsDynAddAttr( pSHdl, attUartRxChUuid, NULL, 0, 128,
ATTS_SET_WRITE_CBACK | ATTS_SET_VARIABLE_LEN, ATTS_PERMIT_WRITE);
/* UART tx characteristic */
AttsDynAddAttrConst( pSHdl, attChUuid, uartTxCh, sizeof(uartTxCh),
0, ATTS_PERMIT_READ);
/* UART tx value */
/* TODO: do we need ATTS_SET_READ_CBACK ? */
AttsDynAddAttr( pSHdl, attUartTxChUuid, NULL, 0, sizeof(uint8_t),
ATTS_SET_READ_CBACK, ATTS_PERMIT_READ);
/* UART tx CCC descriptor */
AttsDynAddAttr( pSHdl, attCliChCfgUuid, initCcc, sizeof(uint16_t), sizeof(uint16_t),
ATTS_SET_CCC, ATTS_PERMIT_READ | ATTS_PERMIT_WRITE);
/* clang-format on */
}
static uint8_t uartValTxChCcc[] = {UINT16_TO_BYTES(0x0000)};
static const uint16_t uartLenTxChCcc = sizeof(uartValTxChCcc);
return pSHdl;
}
static uint8_t ble_uart_tx_buf[20];
static uint16_t ble_uart_buf_tx_fill = 0;
/* clang-format on */
dmConnId_t active_connection = 0;
/* Attribute list for uriCfg group */
static const attsAttr_t uartAttrCfgList[] = {
/* Primary service */
{
.pUuid = attPrimSvcUuid,
.pValue = (uint8_t *)UARTSvc,
.pLen = (uint16_t *)&UARTSvc_len,
.maxLen = sizeof(UARTSvc),
.settings = 0,
.permissions = ATTS_PERMIT_READ,
},
/* UART rx characteristic */
{
.pUuid = attChUuid,
.pValue = (uint8_t *)uartRxCh,
.pLen = (uint16_t *)&uartRxCh_len,
.maxLen = sizeof(uartRxCh),
.settings = 0,
.permissions = ATTS_PERMIT_READ,
},
/* UART rx value */
{
.pUuid = attUartRxChUuid,
.pValue = NULL,
.pLen = NULL,
.maxLen = 128,
.settings = ATTS_SET_WRITE_CBACK | ATTS_SET_VARIABLE_LEN,
.permissions = ATTS_PERMIT_WRITE | ATTS_PERMIT_WRITE_ENC |
ATTS_PERMIT_WRITE_AUTH,
},
/* UART tx characteristic */
{
.pUuid = attChUuid,
.pValue = (uint8_t *)uartTxCh,
.pLen = (uint16_t *)&uartTxCh_len,
.maxLen = sizeof(uartTxCh),
.settings = 0,
.permissions = ATTS_PERMIT_READ,
},
/* UART tx value */
{
.pUuid = attUartTxChUuid,
.pValue = ble_uart_tx_buf,
.pLen = &ble_uart_buf_tx_fill,
.maxLen = sizeof(ble_uart_tx_buf),
.settings = 0,
.permissions = ATTS_PERMIT_READ | ATTS_PERMIT_READ_ENC |
ATTS_PERMIT_READ_AUTH,
},
/* UART tx CCC descriptor */
{
.pUuid = attCliChCfgUuid,
.pValue = uartValTxChCcc,
.pLen = (uint16_t *)&uartLenTxChCcc,
.maxLen = sizeof(uartValTxChCcc),
.settings = ATTS_SET_CCC,
.permissions =
(ATTS_PERMIT_READ |
ATTS_PERMIT_WRITE) // How about security?
},
static uint8_t UARTReadCback(
dmConnId_t connId,
uint16_t handle,
uint8_t operation,
uint16_t offset,
attsAttr_t *pAttr
) {
printf("read callback\n");
return ATT_SUCCESS;
}
};
static uint8_t UARTWriteCback(
dmConnId_t connId,
......@@ -100,76 +112,107 @@ static uint8_t UARTWriteCback(
uint8_t *pValue,
attsAttr_t *pAttr
) {
active_connection = connId;
static bool was_r = false;
//printf("UARTWriteCback %d: ", len);
int i;
for (i = 0; i < len; i++) {
//printf("%c", pValue[i]);
if (pValue[i] == '\n' && !was_r) {
serial_enqueue_char('\r');
}
was_r = pValue[i] == '\r';
serial_enqueue_char(pValue[i]);
}
serial_enqueue_char('\r');
//printf("\n");
#if 0
AttsSetAttr(UART_TX_HDL, len, pValue);
AttsHandleValueNtf(connId, UART_TX_HDL, len, pValue);
#endif
return ATT_SUCCESS;
}
uint8_t ble_uart_tx_buf[129];
uint8_t ble_uart_buf_tx_fill;
int ble_uart_lasttick = 0;
static bool done;
static bool again;
void ble_uart_write(uint8_t *pValue, uint8_t len)
static void ble_uart_flush(void)
{
int i;
for (i = 0; i < len; i++) {
if (pValue[i] >= 0x20 && pValue[i] < 0x7f) {
ble_uart_tx_buf[ble_uart_buf_tx_fill] = pValue[i];
ble_uart_buf_tx_fill++;
} else if (pValue[i] == '\r' || pValue[i] == '\n') {
if (ble_uart_buf_tx_fill > 0) {
AttsSetAttr(
UART_TX_HDL,
ble_uart_buf_tx_fill,
ble_uart_tx_buf
);
if (active_connection) {
int x = xTaskGetTickCount() -
ble_uart_lasttick;
if (x < 100) {
// Ugly hack if we already send something recently.
// TODO: figure out how fast we can send or use indications
vTaskDelay(100 - x);
}
//printf("notify: ");
//int j;
//for(j=0;j<ble_uart_buf_tx_fill;j++) {
// printf("%02x ", ble_uart_tx_buf[j]);
//}
//printf("\n");
if (ble_uart_buf_tx_fill == 0) {
return;
}
dmConnId_t connId = AppConnIsOpen();
if (connId != DM_CONN_ID_NONE) {
if (AttsCccEnabled(connId, UART_TX_CH_CCC_IDX)) {
done = false;
again = true;
int t0 = xTaskGetTickCount();
while (!done && ((xTaskGetTickCount() - t0) < 1000)) {
if (again) {
again = false;
AttsHandleValueNtf(
active_connection,
connId,
UART_TX_HDL,
ble_uart_buf_tx_fill,
ble_uart_tx_buf
);
ble_uart_lasttick = xTaskGetTickCount();
}
ble_uart_buf_tx_fill = 0;
/* This function is supposed to only be called
* from the API scheduler with lowest priority.
*
* If that is not the case anymore, use a delay
* instead of the yield. Ideally refactor to avoid
* the delay.
*/
//vTaskDelay(5);
taskYIELD();
}
}
}
ble_uart_buf_tx_fill = 0;
}
static void ble_uart_write_char(uint8_t c)
{
ble_uart_tx_buf[ble_uart_buf_tx_fill] = c;
ble_uart_buf_tx_fill++;
// TODO: increase buffer if configured MTU allows it
if (ble_uart_buf_tx_fill == sizeof(ble_uart_tx_buf)) {
ble_uart_flush();
}
}
void ble_uart_write(uint8_t *pValue, uint8_t len)
{
for (int i = 0; i < len; i++) {
ble_uart_write_char(pValue[i]);
}
// TODO schedule timer in a few ms to flush the buffer
ble_uart_flush();
}
static attsGroup_t uartCfgGroup = {
.pAttr = (attsAttr_t *)uartAttrCfgList,
.writeCback = UARTWriteCback,
.startHandle = UART_START_HDL,
.endHandle = UART_END_HDL,
};
void bleuart_init(void)
{
/* Add the UART service dynamically */
void *pSHdl;
pSHdl = SvcUARTAddGroupDyn();
AttsDynRegister(pSHdl, UARTReadCback, UARTWriteCback);
//AttsDynRegister(pSHdl, NULL, UARTWriteCback);
/* Add the UART service */
AttsAddGroup(&uartCfgGroup);
}
void UartProcMsg(bleMsg_t *pMsg)
{
if (pMsg->hdr.event == ATTS_HANDLE_VALUE_CNF) {
if (pMsg->att.handle == UART_TX_HDL) {
if (pMsg->hdr.status == ATT_SUCCESS) {
done = true;
} else if (pMsg->hdr.status == ATT_ERR_OVERFLOW) {
again = true;
}
}
}
if (pMsg->hdr.event == DM_CONN_OPEN_IND) {
ble_uart_buf_tx_fill = 0;
}
}
#pragma once
#include "ble_api.h"
#define UART_START_HDL 0x800 /*!< \brief Service start handle. */
#define UART_END_HDL (UART_MAX_HDL - 1) /*!< \brief Service end handle. */
/*! \brief UART Service Handles */
enum { UART_SVC_HDL = UART_START_HDL, /*!< \brief UART service declaration */
UART_RX_CH_HDL, /*!< \brief UART rx characteristic */
UART_RX_HDL, /*!< \brief UART rx value */
UART_TX_CH_HDL, /*!< \brief UART tx characteristic */
UART_TX_HDL, /*!< \brief UART tx value */
UART_TX_CH_CCC_HDL, /*!< \brief UART tx CCCD */
UART_MAX_HDL /*!< \brief Maximum handle. */
};
void UartProcMsg(bleMsg_t *pMsg);
/*******************************************************************************
* Copyright (C) 2016 Maxim Integrated Products, Inc., All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL MAXIM INTEGRATED BE LIABLE FOR ANY CLAIM, DAMAGES
* OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* Except as contained in this notice, the name of Maxim Integrated
* Products, Inc. shall not be used except as stated in the Maxim Integrated
* Products, Inc. Branding Policy.
*
* The mere transfer of this software does not imply any licenses
* of trade secrets, proprietary technology, copyrights, patents,
* trademarks, maskwork rights, or any other form of intellectual
* property whatsoever. Maxim Integrated Products, Inc. retains all
* ownership rights.
*
* $Id: main.c 32120 2017-11-28 23:51:11Z lorne.smith $
*
*******************************************************************************
*/
/**
* @file main.c
* @brief USB CDC-ACM example
* @details This project creates a virtual COM port, which loops back data sent to it.
* Load the project, connect a cable from the PC to the USB connector
* on the Evaluation Kit, and observe that the PC now recognizes a new COM port.
* A driver for the COM port, if needed, is located in the Driver/ subdirectory.
*
*/
#include <stdio.h>
#include <stddef.h>
#include "mxc_config.h"
#include "mxc_sys.h"
#include "mxc_delay.h"
#include "board.h"
#include "led.h"
#include "usb.h"
#include "usb_event.h"
#include "enumerate.h"
#include "cdc_acm.h"
#include "descriptors.h"
#include "modules/log.h"
#include <errno.h>
/***** Definitions *****/
#define EVENT_ENUM_COMP MAXUSB_NUM_EVENTS
#define BUFFER_SIZE 64
#define STRINGIFY(x) #x
#define TOSTRING(x) STRINGIFY(x)
/***** Global Data *****/
//SWYM: rename to CDC_xy or put into struct CDC_state
volatile int configured; //SWYM: actually unused...
volatile int suspended;
volatile unsigned int event_flags;
int remote_wake_en;
/***** Function Prototypes *****/
static int setconfig_callback(usb_setup_pkt *sud, void *cbdata);
static int setfeature_callback(usb_setup_pkt *sud, void *cbdata);
static int clrfeature_callback(usb_setup_pkt *sud, void *cbdata);
static int event_callback(maxusb_event_t evt, void *data);
static void usb_app_sleep(void);
static void usb_app_wakeup(void);
static int usb_read_callback(void);
//static void echo_usb(void);
/***** File Scope Variables *****/
/* This EP assignment must match the Configuration Descriptor */
static const acm_cfg_t acm_cfg = {
1, /* EP OUT */
MXC_USBHS_MAX_PACKET, /* OUT max packet size */
2, /* EP IN */
MXC_USBHS_MAX_PACKET, /* IN max packet size */
3, /* EP Notify */
MXC_USBHS_MAX_PACKET, /* Notify max packet size */
};
static volatile int usb_read_complete;
int usb_startup_cb()
{
const sys_cfg_usbhs_t sys_usbhs_cfg = NULL;
return SYS_USBHS_Init(&sys_usbhs_cfg);
}
int usb_shutdown_cb()
{
return SYS_USBHS_Shutdown();
}
/* User-supplied function to delay usec micro-seconds */
void delay_us(unsigned int usec)
{
/* mxc_delay() takes unsigned long, so can't use it directly */
mxc_delay(usec);
}
/******************************************************************************/
int cdcacm_init(void)
{
maxusb_cfg_options_t usb_opts;
/* Initialize state */
configured = 0;
suspended = 0;
event_flags = 0;
remote_wake_en = 0;
/* Start out in full speed */
usb_opts.enable_hs = 0;
usb_opts.delay_us =
delay_us; /* Function which will be used for delays */
usb_opts.init_callback = usb_startup_cb;
usb_opts.shutdown_callback = usb_shutdown_cb;
/* Initialize the usb module */
if (usb_init(&usb_opts) != 0) {
LOG_ERR("cdcacm", "usb_init() failed");
return -EIO;
}
/* Initialize the enumeration module */
if (enum_init() != 0) {
LOG_ERR("cdcacm", "enum_init() failed");
return -EIO;
}
/* Register enumeration data */
enum_register_descriptor(
ENUM_DESC_DEVICE, (uint8_t *)&device_descriptor, 0
);
enum_register_descriptor(
ENUM_DESC_CONFIG, (uint8_t *)&config_descriptor, 0
);
enum_register_descriptor(ENUM_DESC_STRING, lang_id_desc, 0);
enum_register_descriptor(ENUM_DESC_STRING, mfg_id_desc, 1);
enum_register_descriptor(ENUM_DESC_STRING, prod_id_desc, 2);
/* Handle configuration */
enum_register_callback(ENUM_SETCONFIG, setconfig_callback, NULL);
/* Handle feature set/clear */
enum_register_callback(ENUM_SETFEATURE, setfeature_callback, NULL);
enum_register_callback(ENUM_CLRFEATURE, clrfeature_callback, NULL);
/* Initialize the class driver */
if (acm_init(&config_descriptor.comm_interface_descriptor) != 0) {
LOG_ERR("cdcacm", "acm_init() failed");
return -EIO;
}
/* Register callbacks */
usb_event_enable(MAXUSB_EVENT_NOVBUS, event_callback, NULL);
usb_event_enable(MAXUSB_EVENT_VBUS, event_callback, NULL);
acm_register_callback(ACM_CB_READ_READY, usb_read_callback);
usb_read_complete = 0;
/* Start with USB in low power mode */
usb_app_sleep();
/* TODO: Fix priority */
NVIC_SetPriority(USB_IRQn, 6);
NVIC_EnableIRQ(USB_IRQn);
return 0;
}
int cdcacm_num_read_avail(void)
{
return acm_canread();
}
uint8_t cdcacm_read(void)
{
while (acm_canread() <= 0) {
}
uint8_t buf;
acm_read(&buf, 1);
return buf;
}
void cdcacm_write(uint8_t *data, int len)
{
static int lockup_disable = 0;
if (acm_present() && !lockup_disable) {
int ret = acm_write(data, len);
if (ret < 0) {
LOG_ERR("cdcacm", "fifo lockup detected");
lockup_disable = 1;
} else if (ret != len) {
LOG_WARN(
"cdcacm", "write length mismatch, got %d", ret
);
}
}
}
/******************************************************************************/
#if 0
static void echo_usb(void)
{
int chars;
uint8_t buffer[BUFFER_SIZE];
if ((chars = acm_canread()) > 0) {
if (chars > BUFFER_SIZE) {
chars = BUFFER_SIZE;
}
// Read the data from USB
if (acm_read(buffer, chars) != chars) {
printf("acm_read() failed\n");
return;
}
// Echo it back
if (acm_present()) {
if (acm_write(buffer, chars) != chars) {
printf("acm_write() failed\n");
}
}
}
}
#endif
/******************************************************************************/
static int setconfig_callback(usb_setup_pkt *sud, void *cbdata)
{
/* Confirm the configuration value */
if (sud->wValue ==
config_descriptor.config_descriptor.bConfigurationValue) {
configured = 1;
MXC_SETBIT(&event_flags, EVENT_ENUM_COMP);
return acm_configure(&acm_cfg); /* Configure the device class */
} else if (sud->wValue == 0) {
configured = 0;
return acm_deconfigure();
}
return -1;
}
/******************************************************************************/
static int setfeature_callback(usb_setup_pkt *sud, void *cbdata)
{
if (sud->wValue == FEAT_REMOTE_WAKE) {
remote_wake_en = 1;
} else {
// Unknown callback
return -1;
}
return 0;
}
/******************************************************************************/
static int clrfeature_callback(usb_setup_pkt *sud, void *cbdata)
{
if (sud->wValue == FEAT_REMOTE_WAKE) {
remote_wake_en = 0;
} else {
// Unknown callback
return -1;
}
return 0;
}
/******************************************************************************/
static void usb_app_sleep(void)
{
/* TODO: Place low-power code here */
suspended = 1;
}
/******************************************************************************/
static void usb_app_wakeup(void)
{
/* TODO: Place low-power code here */
suspended = 0;
}
/******************************************************************************/
static int event_callback(maxusb_event_t evt, void *data)
{
/* Set event flag */
MXC_SETBIT(&event_flags, evt);
switch (evt) {
case MAXUSB_EVENT_NOVBUS:
usb_event_disable(MAXUSB_EVENT_BRST);
usb_event_disable(MAXUSB_EVENT_SUSP);
usb_event_disable(MAXUSB_EVENT_DPACT);
usb_disconnect();
configured = 0;
enum_clearconfig();
acm_deconfigure();
usb_app_sleep();
break;
case MAXUSB_EVENT_VBUS:
usb_event_clear(MAXUSB_EVENT_BRST);
usb_event_enable(MAXUSB_EVENT_BRST, event_callback, NULL);
usb_event_clear(MAXUSB_EVENT_SUSP);
usb_event_enable(MAXUSB_EVENT_SUSP, event_callback, NULL);
usb_connect();
usb_app_sleep();
break;
case MAXUSB_EVENT_BRST:
usb_app_wakeup();
enum_clearconfig();
acm_deconfigure();
configured = 0;
suspended = 0;
break;
case MAXUSB_EVENT_SUSP:
usb_app_sleep();
break;
case MAXUSB_EVENT_DPACT:
usb_app_wakeup();
break;
default:
break;
}
return 0;
}
/******************************************************************************/
static int usb_read_callback(void)
{
usb_read_complete = 1;
return 0;
}
/******************************************************************************/
#include "FreeRTOS.h"
#include "task.h"
extern TaskHandle_t serial_task_id;
void USB_IRQHandler(void)
{
usb_event_handler();
if (serial_task_id != NULL) {
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
vTaskNotifyGiveFromISR(
serial_task_id, &xHigherPriorityTaskWoken
);
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}
}
/******************************************************************************/
/* TODO: We probably need to fix something related to this */
#if 0
void SysTick_Handler(void)
{
mxc_delay_handler();
}
#endif /* 0 */
#ifndef CDCACM_H
#define CDCACM_H
#include <stdint.h>
int cdcacm_init(void);
int cdcacm_num_read_avail(void);
uint8_t cdcacm_read(void);
void cdcacm_write(uint8_t *data, int len);
#endif