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
Select Git revision
  • add_menu_vibration
  • blinkisync-as-preload
  • ch3/api-speed-eval2
  • ch3/dual-core
  • ch3/genapi-refactor
  • ch3/leds-api
  • ch3/splashscreen
  • dualcore
  • dx/flatten-config-module
  • dx/meh-bdf-to-stm
  • freertos-btle
  • genofire/ble-follow-py
  • koalo/bhi160-works-but-dirty
  • koalo/factory-reset
  • koalo/wip/i2c-for-python
  • master
  • msgctl/faultscreen
  • msgctl/textbuffer_api
  • plaetzchen/ios-workaround
  • rahix/bhi
  • rahix/bluetooth-app-favorite
  • rahix/bma
  • rahix/user-space-ctx
  • renze/hatchery_apps
  • renze/safe_mode
  • schleicher-test
  • schneider/212-reset-hardware-when-entering-repl
  • schneider/ancs
  • schneider/ble-buffers
  • schneider/ble-central
  • schneider/ble-ecg-stream-visu
  • schneider/ble-fixes-2020-3
  • schneider/ble-mini-demo
  • schneider/ble-stability
  • schneider/ble-stability-new-phy
  • schneider/bonding
  • schneider/bonding-fail-if-full
  • schneider/bootloader-update-9a0d158
  • schneider/deepsleep
  • schneider/deepsleep2
  • schneider/deepsleep4
  • schneider/default-main
  • schneider/freertos-list-debug
  • schneider/fundamental-test
  • schneider/iaq-python
  • schneider/ir
  • schneider/max30001
  • schneider/max30001-epicaridum
  • schneider/max30001-pycardium
  • schneider/maxim-sdk-update
  • schneider/mp-exception-print
  • schneider/mp-for-old-bl
  • schneider/png
  • schneider/schleicher-test
  • schneider/sdk-0.2.1-11
  • schneider/sdk-0.2.1-7
  • schneider/sleep-display
  • schneider/spo2-playground
  • schneider/stream-locks
  • schneider/v1.17-changelog
  • bootloader-v1
  • release-1
  • v0.0
  • v1.0
  • v1.1
  • v1.10
  • v1.11
  • v1.12
  • v1.13
  • v1.14
  • v1.15
  • v1.16
  • v1.17
  • v1.18
  • v1.2
  • v1.3
  • v1.4
  • v1.5
  • v1.6
  • v1.7
  • v1.8
  • v1.9
82 results

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
Select Git revision
  • ch3/api-speed-eval2
  • ch3/dual-core
  • ch3/genapi-refactor
  • ch3/leds-api
  • color-2
  • color-html
  • color-html-2
  • color-html-3
  • dualcore
  • freertos-btle
  • master
  • micro-modules
  • patch-1
  • patch-2
  • patch-3
  • rahix/bma
  • schneider/mp-for-old-bl
  • wink/trng
18 results
Show changes
Showing
with 3814 additions and 611 deletions
#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
* functionality so we implemented our own service.
* The service uses the UUID in fileTransSvc
* The characteristic uses the UUID in attTxChConfigUuid
* 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
* * Directory creation
* * Directory listing
*/
#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 <epicardium.h>
#include "os/core.h"
#include "util/bstream.h"
#include "att_api.h"
#include "FreeRTOS.h"
#include "crc32.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
#include <machine/endian.h>
/*!< \brief Service start handle. */
#define FILE_TRANS_START_HDL 0x820
/*!< \brief Service end handle. */
#define FILE_TRANS_END_HDL (FILE_TRANS_MAX_HDL - 1)
#define FILE_TRANS_UUID_SUFFIX \
0x42, 0x23, 0x42, 0x23, 0x42, 0x23, 0x42, 0x23, 0x42, 0x23, 0x42, 0x23
#define FILE_TRANS_UUID_PREFIX 0x01, 0x23, 0x42
/*
* This has to match in order and number of members to the functions
* called in fileTransAddGroupDyn() otherwise the stack breaks.
*/
enum {
/*!< \brief File transfer service declaration */
FILE_TRANS_SVC_HDL = FILE_TRANS_START_HDL,
/*!< \brief File transfer Central tx characteristic */
FILE_TRANS_CENTRAL_TX_CH_HDL,
/*!< \brief File transfer Central tx value */
FILE_TRANS_CENTRAL_TX_VAL_HDL,
/*!< \brief File transfer Central rx characteristic */
FILE_TRANS_CENTRAL_RX_CH_HDL,
/*!< \brief File transfer Central rx value */
FILE_TRANS_CENTRAL_RX_VAL_HDL,
/*!< \brief File transfer Central rx notification characteristic */
FILE_TRANS_CENTRAL_RX_CCC_HDL,
/*!< \brief Maximum handle. */
FILE_TRANS_MAX_HDL
};
/* BLE File transfer Service UUID */
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 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 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;
/* 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.
*/
static void sendCrcResponse(
dmConnId_t connId,
char code,
uint16_t crcLen,
uint8_t *crcData,
const char *msg
) {
uint32_t crc;
uint16_t len = 1;
uint8_t answer[24];
answer[0] = code;
if (crcData) {
crc = __htonl(CalcCrc32(0xffffffff, crcLen, crcData));
memcpy(answer + len, &crc, sizeof(crc));
len += 4;
}
if (msg) {
if (strlen(msg) < sizeof(answer) - len) {
strncpy((char *)answer + len,
msg,
sizeof(answer) - len);
len += strlen(msg);
LOG_ERR("filetrans", "%s\n", msg);
} else {
LOG_ERR("filetrans",
"error message \"%s\" too long\n",
msg);
}
}
AttsSetAttr(FILE_TRANS_CENTRAL_RX_VAL_HDL, len, answer);
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];
if (len > sizeof(filepath)) {
sendCrcResponse(connId, 'e', 0, NULL, "path too long");
return ATT_ERR_RESOURCES;
}
/* 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 = bleFileCreateOrOpen(filepath);
if (file_fd < 0) {
sendCrcResponse(connId, 'e', 0, NULL, "open failed");
return ATT_ERR_RESOURCES;
}
sendCrcResponse(connId, 'S', len, pValue, NULL);
return ATT_SUCCESS;
}
static uint8_t bleFileWrite(dmConnId_t connId, uint8_t *pValue, uint16_t len)
{
uint32_t fileOffsetNet;
uint32_t fileOffset;
int res;
if (len < 6) {
sendCrcResponse(connId, 'e', 0, NULL, "msg too short");
return ATT_ERR_LENGTH;
}
memcpy(&fileOffsetNet, pValue + 1, sizeof(fileOffsetNet));
fileOffset = __ntohl(fileOffsetNet);
res = epic_file_seek(file_fd, fileOffset, SEEK_SET);
if (res) {
sendCrcResponse(connId, 'e', 0, NULL, "seek failed");
return ATT_ERR_RESOURCES;
}
res = epic_file_write(file_fd, pValue + 5, len - 5);
if (res != (len - 5)) {
sendCrcResponse(connId, 'e', 0, NULL, "write failed");
return ATT_ERR_RESOURCES;
}
sendCrcResponse(connId, 'C', len, pValue, NULL);
return ATT_SUCCESS;
}
/*
* Handle the Central TX characteristic which is used by the central to
* issues file transfer commands on this peripheral.
*/
static uint8_t handleCentralTX(
dmConnId_t connId,
uint16_t handle,
uint8_t operation,
uint16_t offset,
uint16_t len,
uint8_t *pValue,
attsAttr_t *pAttr
) {
if (len < 1) {
sendCrcResponse(connId, 'e', 0, NULL, "msg too short");
return ATT_ERR_LENGTH;
}
if (operation == ATT_PDU_PREP_WRITE_REQ) {
/* In case of relaiable write, just return success and wait for exec. */
return ATT_SUCCESS;
} else if (
operation != ATT_PDU_EXEC_WRITE_REQ &&
operation != ATT_PDU_WRITE_CMD) {
LOG_ERR("filetrans",
"operation 0x%x not supported, try normal write\n",
operation);
return ATT_ERR_INVALID_PDU;
}
switch (pValue[0]) {
case 's':
return bleFileOpen(connId, pValue, len);
case 'c':
return bleFileWrite(connId, pValue, len);
case 'f':
if (file_fd != -1) {
epic_file_close(file_fd);
file_fd = -1;
}
sendCrcResponse(connId, 'F', 0, NULL, NULL);
return ATT_SUCCESS;
case 'e':
if (file_fd != -1) {
epic_file_close(file_fd);
file_fd = -1;
}
sendCrcResponse(connId, 'E', 0, NULL, NULL);
return ATT_SUCCESS;
case 'E':
LOG_ERR("filetrans", "Error was acked");
return ATT_SUCCESS;
default:
sendCrcResponse(connId, 'e', 0, NULL, "unkown command");
return ATT_ERR_NOT_SUP;
}
return ATT_ERR_NOT_SUP;
}
/*
* 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.
*/
static uint8_t writeCallback(
dmConnId_t connId,
uint16_t handle,
uint8_t operation,
uint16_t offset,
uint16_t len,
uint8_t *pValue,
attsAttr_t *pAttr
) {
switch (handle) {
case FILE_TRANS_CENTRAL_TX_VAL_HDL:
return handleCentralTX(
connId, handle, operation, offset, len, pValue, pAttr
);
default:
LOG_ERR("filetrans", "unsupported handle: %x\n", handle);
return ATT_ERR_HANDLE;
}
}
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)
{
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',
'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 */
/*************************************************************************************************/
/*!
* \file
*
* \brief Stack initialization
*
* Copyright (c) 2016-2017 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.
*/
/*************************************************************************************************/
/*
* This file initializes the different components of the whole BLE stack. This inlucdes link level,
* HCI, security, etc...
*
* This file has been copied from lib/sdk/Applications/EvKitExamples/BLE_fit/stack_fit.c
*
* NOTE: Different stack_*.c files in the SDK initialize different components. We have to
* be very carefull to intitialize all needed components here. Think e.g. SecRandInit() ...
*
* Many components are related to the role of the device. Different components need to be
* initialized for central and peripheral roles.
*/
/* clang-format off */
/* clang-formet turned off for easier diffing against orginal file */
#include <stdio.h>
#include <string.h>
#include "wsf_types.h"
#include "wsf_os.h"
#include "util/bstream.h"
#include "ble_api.h"
#include "hci_handler.h"
#include "dm_handler.h"
#include "l2c_handler.h"
#include "att_handler.h"
#include "smp_handler.h"
#include "l2c_api.h"
#include "att_api.h"
#include "smp_api.h"
#include "app_api.h"
#include "svc_dis.h"
#include "svc_core.h"
#include "sec_api.h"
#include "ll_init_api.h"
/* TODO: card10: Where does this number come from? Is there any documentation? */
#define LL_IMPL_REV 0x2303
/* TODO: card10: Where does this number come from? Is there any documentation? */
#define LL_MEMORY_FOOTPRINT 0xC152
uint8_t LlMem[LL_MEMORY_FOOTPRINT];
const LlRtCfg_t _ll_cfg = {
/* Device */
/*compId*/ LL_COMP_ID_ARM,
/*implRev*/ LL_IMPL_REV,
/*btVer*/ LL_VER_BT_CORE_SPEC_5_0,
/*_align32 */ 0, // padding for alignment
/* Advertiser */
/*maxAdvSets*/ 4, // 4 Extended Advertising Sets
/*maxAdvReports*/ 8,
/*maxExtAdvDataLen*/ LL_MAX_ADV_DATA_LEN,
/*defExtAdvDataFrag*/ 64,
/*auxDelayUsec*/ 0,
/* Scanner */
/*maxScanReqRcvdEvt*/ 4,
/*maxExtScanDataLen*/ LL_MAX_ADV_DATA_LEN,
/* Connection */
/*maxConn*/ 2,
/*numTxBufs*/ 16,
/*numRxBufs*/ 16,
/*maxAclLen*/ 512,
/*defTxPwrLvl*/ 0,
/*ceJitterUsec*/ 0,
/* DTM */
/*dtmRxSyncMs*/ 10000,
/* PHY */
/*phy2mSup*/ FALSE,
/*phyCodedSup*/ FALSE,
/*stableModIdxTxSup*/ FALSE,
/*stableModIdxRxSup*/ FALSE
};
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 link layer part of the stack.
*
* \return None.
*/
/*************************************************************************************************/
void LlStackInit()
{
#ifndef ENABLE_SDMA
uint32_t memUsed;
/* Initialize link layer. */
LlInitRtCfg_t ll_init_cfg =
{
.pBbRtCfg = &_bb_cfg,
.wlSizeCfg = 4,
.rlSizeCfg = 4,
.plSizeCfg = 4,
.pLlRtCfg = &_ll_cfg,
.pFreeMem = LlMem,
.freeMemAvail = LL_MEMORY_FOOTPRINT
};
memUsed = LlInitControllerExtInit(&ll_init_cfg);
if(memUsed != LL_MEMORY_FOOTPRINT)
{
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
* and then pass a handle down to modules which uses them to
* internally handle callbacks.
*
* No idea why the modules don't call WsfOsSetNextHandler()
* internally ... */
handlerId = WsfOsSetNextHandler(HciHandler);
HciHandlerInit(handlerId);
handlerId = WsfOsSetNextHandler(DmHandler);
DmDevVsInit(0);
DmAdvInit();
DmScanInit();
DmConnInit();
DmConnSlaveInit();
DmSecInit();
DmSecLescInit();
DmPrivInit();
DmPhyInit();
DmHandlerInit(handlerId);
handlerId = WsfOsSetNextHandler(L2cSlaveHandler);
L2cSlaveHandlerInit(handlerId);
L2cInit();
L2cSlaveInit();
handlerId = WsfOsSetNextHandler(AttHandler);
AttHandlerInit(handlerId);
AttsInit();
AttsIndInit();
AttsDynInit();
AttcInit();
handlerId = WsfOsSetNextHandler(SmpHandler);
SmpHandlerInit(handlerId);
SmprInit();
SmprScInit();
/*TODO card10: Probably want to adjust this */
HciSetMaxRxAclLen(100);
handlerId = WsfOsSetNextHandler(AppHandler);
AppHandlerInit(handlerId);
}
/* clang-format off */
/*************************************************************************************************/
/*!
* \file
*
* \brief Example GATT and GAP service implementations.
*
* Copyright (c) 2009-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/services/svc_core.c
*/
/* clang-format off */
/* clang-formet turned off for easier diffing against orginal file */
#include "wsf_types.h"
#include "att_api.h"
#include "att_uuid.h"
#include "util/bstream.h"
#include "svc_core.h"
#include "svc_ch.h"
#include "svc_cfg.h"
#include "wsf_assert.h"
/**************************************************************************************************
Macros
**************************************************************************************************/
/*! Characteristic read permissions */
#ifndef CORE_SEC_PERMIT_READ
#define CORE_SEC_PERMIT_READ SVC_SEC_PERMIT_READ
#endif
/*! Characteristic write permissions */
#ifndef CORE_SEC_PERMIT_WRITE
#define CORE_SEC_PERMIT_WRITE SVC_SEC_PERMIT_WRITE
#endif
/*! Default device name */
#define CORE_DEFAULT_DEV_NAME "card10"
/*! Length of default device name */
#define CORE_DEFAULT_DEV_NAME_LEN 6
/**************************************************************************************************
GAP group
**************************************************************************************************/
/* service */
static const uint8_t gapValSvc[] = {UINT16_TO_BYTES(ATT_UUID_GAP_SERVICE)};
static const uint16_t gapLenSvc = sizeof(gapValSvc);
/* device name characteristic */
static const uint8_t gapValDnCh[] = {ATT_PROP_READ | ATT_PROP_WRITE, UINT16_TO_BYTES(GAP_DN_HDL), UINT16_TO_BYTES(ATT_UUID_DEVICE_NAME)};
static const uint16_t gapLenDnCh = sizeof(gapValDnCh);
/* device name */
static uint8_t gapValDn[ATT_DEFAULT_PAYLOAD_LEN] = CORE_DEFAULT_DEV_NAME;
static uint16_t gapLenDn = CORE_DEFAULT_DEV_NAME_LEN;
/* appearance characteristic */
static const uint8_t gapValApCh[] = {ATT_PROP_READ, UINT16_TO_BYTES(GAP_AP_HDL), UINT16_TO_BYTES(ATT_UUID_APPEARANCE)};
static const uint16_t gapLenApCh = sizeof(gapValApCh);
/* appearance */
static uint8_t gapValAp[] = {UINT16_TO_BYTES(CH_APPEAR_UNKNOWN)};
static const uint16_t gapLenAp = sizeof(gapValAp);
/* central address resolution characteristic */
static const uint8_t gapValCarCh[] = {ATT_PROP_READ, UINT16_TO_BYTES(GAP_CAR_HDL), UINT16_TO_BYTES(ATT_UUID_CAR)};
static const uint16_t gapLenCarCh = sizeof(gapValCarCh);
/* central address resolution */
static uint8_t gapValCar[] = {FALSE};
static const uint16_t gapLenCar = sizeof(gapValCar);
#if 0
/* TODO card10:
* Enable these if "privacy" is enabled. See svc_core.h lien 38 */
/* resolvable private address only characteristic */
static const uint8_t gapValRpaoCh[] = {ATT_PROP_READ, UINT16_TO_BYTES(GAP_RPAO_HDL), UINT16_TO_BYTES(ATT_UUID_RPAO)};
static const uint16_t gapLenRpaoCh = sizeof(gapValRpaoCh);
/* resolvable private address only */
static uint8_t gapValRpao[] = {0};
static const uint16_t gapLenRpao = sizeof(gapValRpao);
#endif
/* Attribute list for GAP group */
static const attsAttr_t gapList[] =
{
{
attPrimSvcUuid,
(uint8_t *) gapValSvc,
(uint16_t *) &gapLenSvc,
sizeof(gapValSvc),
0,
ATTS_PERMIT_READ
},
{
attChUuid,
(uint8_t *) gapValDnCh,
(uint16_t *) &gapLenDnCh,
sizeof(gapValDnCh),
0,
ATTS_PERMIT_READ
},
{
attDnChUuid,
gapValDn,
&gapLenDn,
sizeof(gapValDn),
(ATTS_SET_VARIABLE_LEN | ATTS_SET_WRITE_CBACK),
(ATTS_PERMIT_READ | CORE_SEC_PERMIT_WRITE)
},
{
attChUuid,
(uint8_t *) gapValApCh,
(uint16_t *) &gapLenApCh,
sizeof(gapValApCh),
0,
ATTS_PERMIT_READ
},
{
attApChUuid,
gapValAp,
(uint16_t *) &gapLenAp,
sizeof(gapValAp),
0,
ATTS_PERMIT_READ
},
{
attChUuid,
(uint8_t *) gapValCarCh,
(uint16_t *) &gapLenCarCh,
sizeof(gapValCarCh),
0,
ATTS_PERMIT_READ
},
{
attCarChUuid,
gapValCar,
(uint16_t *) &gapLenCar,
sizeof(gapValCar),
0,
ATTS_PERMIT_READ
},
#if 0
/* TODO card10:
* Enable these if "privacy" is enabled. See svc_core.h lien 38 */
{
attChUuid,
(uint8_t *) gapValRpaoCh,
(uint16_t *) &gapLenRpaoCh,
sizeof(gapValRpaoCh),
0,
ATTS_PERMIT_READ
},
{
attRpaoChUuid,
gapValRpao,
(uint16_t *) &gapLenRpao,
sizeof(gapValRpao),
0,
ATTS_PERMIT_READ
}
#endif
};
/* GAP group structure */
static attsGroup_t svcGapGroup =
{
NULL,
(attsAttr_t *) gapList,
NULL,
NULL,
GAP_START_HDL,
GAP_END_HDL
};
WSF_CT_ASSERT(((sizeof(gapList) / sizeof(gapList[0])) == GAP_END_HDL - GAP_START_HDL + 1));
/**************************************************************************************************
GATT group
**************************************************************************************************/
/* service */
static const uint8_t gattValSvc[] = {UINT16_TO_BYTES(ATT_UUID_GATT_SERVICE)};
static const uint16_t gattLenSvc = sizeof(gattValSvc);
/* service changed characteristic */
static const uint8_t gattValScCh[] = {ATT_PROP_INDICATE, UINT16_TO_BYTES(GATT_SC_HDL), UINT16_TO_BYTES(ATT_UUID_SERVICE_CHANGED)};
static const uint16_t gattLenScCh = sizeof(gattValScCh);
/* service changed */
static const uint8_t gattValSc[] = {UINT16_TO_BYTES(0x0001), UINT16_TO_BYTES(0xFFFF)};
static const uint16_t gattLenSc = sizeof(gattValSc);
/* service changed client characteristic configuration */
static uint8_t gattValScChCcc[] = {UINT16_TO_BYTES(0x0000)};
static const uint16_t gattLenScChCcc = sizeof(gattValScChCcc);
/* Attribute list for GATT group */
static const attsAttr_t gattList[] =
{
{
attPrimSvcUuid,
(uint8_t *) gattValSvc,
(uint16_t *) &gattLenSvc,
sizeof(gattValSvc),
0,
ATTS_PERMIT_READ
},
{
attChUuid,
(uint8_t *) gattValScCh,
(uint16_t *) &gattLenScCh,
sizeof(gattValScCh),
0,
ATTS_PERMIT_READ
},
{
attScChUuid,
(uint8_t *) gattValSc,
(uint16_t *) &gattLenSc,
sizeof(gattValSc),
0,
0
},
{
attCliChCfgUuid,
gattValScChCcc,
(uint16_t *) &gattLenScChCcc,
sizeof(gattValScChCcc),
ATTS_SET_CCC,
(ATTS_PERMIT_READ | CORE_SEC_PERMIT_WRITE)
},
};
/* GATT group structure */
static attsGroup_t svcGattGroup =
{
NULL,
(attsAttr_t *) gattList,
NULL,
NULL,
GATT_START_HDL,
GATT_END_HDL
};
WSF_CT_ASSERT(((sizeof(gattList) / sizeof(gattList[0])) == GATT_END_HDL - GATT_START_HDL + 1));
/*************************************************************************************************/
/*!
* \brief Add the services to the attribute server.
*
* \return None.
*/
/*************************************************************************************************/
void SvcCoreAddGroup(void)
{
AttsAddGroup(&svcGapGroup);
AttsAddGroup(&svcGattGroup);
}
/*************************************************************************************************/
/*!
* \brief Remove the services from the attribute server.
*
* \return None.
*/
/*************************************************************************************************/
void SvcCoreRemoveGroup(void)
{
AttsRemoveGroup(GAP_START_HDL);
AttsRemoveGroup(GATT_START_HDL);
}
/*************************************************************************************************/
/*!
* \brief Register callbacks for the service.
*
* \param readCback Read callback function.
* \param writeCback Write callback function.
*
* \return None.
*/
/*************************************************************************************************/
void SvcCoreGapCbackRegister(attsReadCback_t readCback, attsWriteCback_t writeCback)
{
svcGapGroup.readCback = readCback;
svcGapGroup.writeCback = writeCback;
}
/*************************************************************************************************/
/*!
* \brief Register callbacks for the service.
*
* \param readCback Read callback function.
* \param writeCback Write callback function.
*
* \return None.
*/
/*************************************************************************************************/
void SvcCoreGattCbackRegister(attsReadCback_t readCback, attsWriteCback_t writeCback)
{
svcGattGroup.readCback = readCback;
svcGattGroup.writeCback = writeCback;
}
/*************************************************************************************************/
/*!
* \brief Update the central address resolution attribute value.
*
* \param value New value.
*
* \return None.
*/
/*************************************************************************************************/
void SvcCoreGapCentAddrResUpdate(bool_t value)
{
gapValCar[0] = value;
}
/*************************************************************************************************/
/*!
* \brief Add the Resolvable Private Address Only (RPAO) characteristic to the GAP service.
* The RPAO characteristic should be added only when DM Privacy is enabled.
*
* \return None.
*/
/*************************************************************************************************/
void SvcCoreGapAddRpaoCh(void)
{
/* if RPAO characteristic not already in GAP service */
if (svcGapGroup.endHandle < GAP_RPAO_HDL)
{
svcGapGroup.endHandle = GAP_RPAO_HDL;
}
}
/* clang-format on */
/*************************************************************************************************/
/*!
* \file
*
* \brief Example Device Information Service implementation.
*
* 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/services/svc_dis.c
*
* Contains adaptions for the card10 (e.g. manufacturer name)
*/
/* 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
**************************************************************************************************/
/*! Characteristic read permissions */
#ifndef DIS_SEC_PERMIT_READ
#define DIS_SEC_PERMIT_READ SVC_SEC_PERMIT_READ
#endif
/*! Default manufacturer name */
#define DIS_DEFAULT_MFR_NAME "CCC"
/*! Length of default manufacturer name */
#define DIS_DEFAULT_MFR_NAME_LEN 3
/*! Default model number */
#define DIS_DEFAULT_MODEL_NUM "1"
/*! Length of default model number */
#define DIS_DEFAULT_MODEL_NUM_LEN 1
/*! Default serial number */
#define DIS_DEFAULT_SERIAL_NUM "1"
/*! Length of default serial number */
#define DIS_DEFAULT_SERIAL_NUM_LEN 1
/*! Default firmware revision */
#define DIS_DEFAULT_FW_REV CARD10_VERSION
/*! Length of default firmware revision */
#define DIS_DEFAULT_FW_REV_LEN strlen(CARD10_VERSION)
/*! Default hardware revision */
#define DIS_DEFAULT_HW_REV "1"
/*! Length of default hardware revision */
#define DIS_DEFAULT_HW_REV_LEN 1
/*! Default software revision */
#define DIS_DEFAULT_SW_REV "1"
/*! Length of default software revision */
#define DIS_DEFAULT_SW_REV_LEN 1
/**************************************************************************************************
Service variables
**************************************************************************************************/
/* Device information service declaration */
static const uint8_t disValSvc[] = {UINT16_TO_BYTES(ATT_UUID_DEVICE_INFO_SERVICE)};
static const uint16_t disLenSvc = sizeof(disValSvc);
/* Manufacturer name string characteristic */
static const uint8_t disValMfrCh[] = {ATT_PROP_READ, UINT16_TO_BYTES(DIS_MFR_HDL), UINT16_TO_BYTES(ATT_UUID_MANUFACTURER_NAME)};
static const uint16_t disLenMfrCh = sizeof(disValMfrCh);
/* Manufacturer name string */
static const uint8_t disUuMfr[] = {UINT16_TO_BYTES(ATT_UUID_MANUFACTURER_NAME)};
static uint8_t disValMfr[DIS_MAXSIZE_MFR_ATT] = DIS_DEFAULT_MFR_NAME;
static uint16_t disLenMfr = DIS_DEFAULT_MFR_NAME_LEN;
/* System ID characteristic */
static const uint8_t disValSidCh[] = {ATT_PROP_READ, UINT16_TO_BYTES(DIS_SID_HDL), UINT16_TO_BYTES(ATT_UUID_SYSTEM_ID)};
static const uint16_t disLenSidCh = sizeof(disValSidCh);
/* System ID */
static const uint8_t disUuSid[] = {UINT16_TO_BYTES(ATT_UUID_SYSTEM_ID)};
static uint8_t disValSid[DIS_SIZE_SID_ATT] = {0x01, 0x02, 0x03, 0x04, 0x05, UINT16_TO_BYTE0(HCI_ID_ARM), UINT16_TO_BYTE1(HCI_ID_ARM), 0x00};
static const uint16_t disLenSid = sizeof(disValSid);
/* Model number string characteristic */
static const uint8_t disValMnCh[] = {ATT_PROP_READ, UINT16_TO_BYTES(DIS_MN_HDL), UINT16_TO_BYTES(ATT_UUID_MODEL_NUMBER)};
static const uint16_t disLenMnCh = sizeof(disValMnCh);
/* Model number string */
static const uint8_t disUuMn[] = {UINT16_TO_BYTES(ATT_UUID_MODEL_NUMBER)};
static uint8_t disValMn[DIS_MAXSIZE_MN_ATT] = DIS_DEFAULT_MODEL_NUM;
static uint16_t disLenMn = DIS_DEFAULT_MODEL_NUM_LEN;
/* Serial number string characteristic */
static const uint8_t disValSnCh[] = {ATT_PROP_READ, UINT16_TO_BYTES(DIS_SN_HDL), UINT16_TO_BYTES(ATT_UUID_SERIAL_NUMBER)};
static const uint16_t disLenSnCh = sizeof(disValSnCh);
/* Serial number string */
static const uint8_t disUuSn[] = {UINT16_TO_BYTES(ATT_UUID_SERIAL_NUMBER)};
static uint8_t disValSn[DIS_MAXSIZE_SN_ATT] = DIS_DEFAULT_SERIAL_NUM;
static uint16_t disLenSn = DIS_DEFAULT_SERIAL_NUM_LEN;
/* Firmware revision string characteristic */
static const uint8_t disValFwrCh[] = {ATT_PROP_READ, UINT16_TO_BYTES(DIS_FWR_HDL), UINT16_TO_BYTES(ATT_UUID_FIRMWARE_REV)};
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;
/* 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)};
static const uint16_t disLenHwrCh = sizeof(disValHwrCh);
/* Hardware revision string */
static const uint8_t disUuHwr[] = {UINT16_TO_BYTES(ATT_UUID_HARDWARE_REV)};
static uint8_t disValHwr[DIS_MAXSIZE_HWR_ATT] = DIS_DEFAULT_HW_REV;
static uint16_t disLenHwr = DIS_DEFAULT_HW_REV_LEN;
/* Software revision string characteristic */
static const uint8_t disValSwrCh[] = {ATT_PROP_READ, UINT16_TO_BYTES(DIS_SWR_HDL), UINT16_TO_BYTES(ATT_UUID_SOFTWARE_REV)};
static const uint16_t disLenSwrCh = sizeof(disValSwrCh);
/* Software revision string */
static const uint8_t disUuSwr[] = {UINT16_TO_BYTES(ATT_UUID_SOFTWARE_REV)};
static uint8_t disValSwr[DIS_MAXSIZE_SWR_ATT] = DIS_DEFAULT_SW_REV;
static uint16_t disLenSwr = DIS_DEFAULT_SW_REV_LEN;
/* Registration certificate data characteristic */
static const uint8_t disValRcdCh[] = {ATT_PROP_READ, UINT16_TO_BYTES(DIS_RCD_HDL), UINT16_TO_BYTES(ATT_UUID_11073_CERT_DATA)};
static const uint16_t disLenRcdCh = sizeof(disValRcdCh);
/* Registration certificate data */
static const uint8_t disUuRcd[] = {UINT16_TO_BYTES(ATT_UUID_11073_CERT_DATA)};
static uint8_t disValRcd[DIS_SIZE_RCD_ATT] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
static const uint16_t disLenRcd = sizeof(disValRcd);
/* Attribute list for dis group */
static const attsAttr_t disList[] =
{
{
attPrimSvcUuid,
(uint8_t *) disValSvc,
(uint16_t *) &disLenSvc,
sizeof(disValSvc),
0,
ATTS_PERMIT_READ
},
{
attChUuid,
(uint8_t *) disValMfrCh,
(uint16_t *) &disLenMfrCh,
sizeof(disValMfrCh),
0,
ATTS_PERMIT_READ
},
{
disUuMfr,
(uint8_t *) disValMfr,
(uint16_t *) &disLenMfr,
sizeof(disValMfr),
ATTS_SET_VARIABLE_LEN,
DIS_SEC_PERMIT_READ
},
{
attChUuid,
(uint8_t *) disValSidCh,
(uint16_t *) &disLenSidCh,
sizeof(disValSidCh),
0,
ATTS_PERMIT_READ
},
{
disUuSid,
disValSid,
(uint16_t *) &disLenSid,
sizeof(disValSid),
0,
DIS_SEC_PERMIT_READ
},
{
attChUuid,
(uint8_t *) disValMnCh,
(uint16_t *) &disLenMnCh,
sizeof(disValMnCh),
0,
ATTS_PERMIT_READ
},
{
disUuMn,
(uint8_t *) disValMn,
(uint16_t *) &disLenMn,
sizeof(disValMn),
ATTS_SET_VARIABLE_LEN,
DIS_SEC_PERMIT_READ
},
{
attChUuid,
(uint8_t *) disValSnCh,
(uint16_t *) &disLenSnCh,
sizeof(disValSnCh),
0,
ATTS_PERMIT_READ
},
{
disUuSn,
(uint8_t *) disValSn,
(uint16_t *) &disLenSn,
sizeof(disValSn),
ATTS_SET_VARIABLE_LEN,
DIS_SEC_PERMIT_READ
},
{
attChUuid,
(uint8_t *) disValFwrCh,
(uint16_t *) &disLenFwrCh,
sizeof(disValFwrCh),
0,
ATTS_PERMIT_READ
},
{
disUuFwr,
(uint8_t *) disValFwr,
(uint16_t *) &disLenFwr,
sizeof(disValFwr),
ATTS_SET_VARIABLE_LEN,
DIS_SEC_PERMIT_READ
},
{
attChUuid,
(uint8_t *) disValHwrCh,
(uint16_t *) &disLenHwrCh,
sizeof(disValHwrCh),
0,
ATTS_PERMIT_READ
},
{
disUuHwr,
(uint8_t *) disValHwr,
(uint16_t *) &disLenHwr,
sizeof(disValHwr),
ATTS_SET_VARIABLE_LEN,
DIS_SEC_PERMIT_READ
},
{
attChUuid,
(uint8_t *) disValSwrCh,
(uint16_t *) &disLenSwrCh,
sizeof(disValSwrCh),
0,
ATTS_PERMIT_READ
},
{
disUuSwr,
(uint8_t *) disValSwr,
(uint16_t *) &disLenSwr,
sizeof(disValSwr),
ATTS_SET_VARIABLE_LEN,
DIS_SEC_PERMIT_READ
},
{
attChUuid,
(uint8_t *) disValRcdCh,
(uint16_t *) &disLenRcdCh,
sizeof(disValRcdCh),
0,
ATTS_PERMIT_READ
},
{
disUuRcd,
(uint8_t *) disValRcd,
(uint16_t *) &disLenRcd,
sizeof(disValRcd),
0,
DIS_SEC_PERMIT_READ
},
};
/* DIS group structure */
static attsGroup_t svcDisGroup =
{
NULL,
(attsAttr_t *) disList,
NULL,
NULL,
DIS_START_HDL,
DIS_END_HDL
};
WSF_CT_ASSERT(((sizeof(disList) / sizeof(disList[0])) == DIS_END_HDL - DIS_START_HDL + 1));
/*************************************************************************************************/
/*!
* \brief Add the services to the attribute server.
*
* \return None.
*/
/*************************************************************************************************/
void SvcDisAddGroup(void)
{
disLenFwr = DIS_DEFAULT_FW_REV_LEN;
AttsAddGroup(&svcDisGroup);
}
/*************************************************************************************************/
/*!
* \brief Remove the services from the attribute server.
*
* \return None.
*/
/*************************************************************************************************/
void SvcDisRemoveGroup(void)
{
AttsRemoveGroup(DIS_START_HDL);
}
/* clang-format on */
#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"
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
/**************************************************************************************************
Handles
**************************************************************************************************/
/* 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};
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};
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 uint8_t uartValTxChCcc[] = {UINT16_TO_BYTES(0x0000)};
static const uint16_t uartLenTxChCcc = sizeof(uartValTxChCcc);
static uint8_t ble_uart_tx_buf[20];
static uint16_t ble_uart_buf_tx_fill = 0;
/* clang-format on */
/* 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 UARTWriteCback(
dmConnId_t connId,
uint16_t handle,
uint8_t operation,
uint16_t offset,
uint16_t len,
uint8_t *pValue,
attsAttr_t *pAttr
) {
static bool was_r = false;
int i;
for (i = 0; i < len; i++) {
if (pValue[i] == '\n' && !was_r) {
serial_enqueue_char('\r');
}
was_r = pValue[i] == '\r';
serial_enqueue_char(pValue[i]);
}
return ATT_SUCCESS;
}
static bool done;
static bool again;
static void ble_uart_flush(void)
{
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(
connId,
UART_TX_HDL,
ble_uart_buf_tx_fill,
ble_uart_tx_buf
);
}
/* 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 */
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
/*******************************************************************************
* 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.
*
* Description: Communications Device Class ACM (Serial Port) over USB
* $Id: descriptors.h 36682 2018-08-06 21:14:03Z michael.bayern $
*
*******************************************************************************
*/
#ifndef _DESCRIPTORS_H_
#define _DESCRIPTORS_H_
#include <stdint.h>
#include "usb.h"
#include "hid_kbd.h"
usb_device_descriptor_t __attribute__((aligned(4))) device_descriptor = {
0x12, /* bLength = 18 */
0x01, /* bDescriptorType = Device */
0x0110, /* bcdUSB USB spec rev (BCD) */
0x02, /* bDeviceClass = comm class (2) */
0x00, /* bDeviceSubClass */
0x00, /* bDeviceProtocol */
0x40, /* bMaxPacketSize0 is 64 bytes */
0x0B6A, /* idVendor (Maxim Integrated) */
0x003C, /* idProduct */
0x0100, /* bcdDevice */
0x01, /* iManufacturer Descriptor ID */
0x02, /* iProduct Descriptor ID */
0x00, /* iSerialNumber = (0) No string */
0x01 /* bNumConfigurations */
};
__attribute__((aligned(4)))
struct __attribute__((packed)) {
usb_configuration_descriptor_t config_descriptor;
usb_interface_descriptor_t comm_interface_descriptor;
uint8_t header_functional_descriptor[5];
uint8_t call_management_descriptor[5];
uint8_t acm_functional_descriptor[4];
uint8_t union_functional_descriptor[5];
usb_endpoint_descriptor_t endpoint_descriptor_3;
usb_interface_descriptor_t data_interface_descriptor;
usb_endpoint_descriptor_t endpoint_descriptor_1;
usb_endpoint_descriptor_t endpoint_descriptor_2;
} config_descriptor =
{
{
0x09, /* bLength = 9 */
0x02, /* bDescriptorType = Config (2) */
0x0043, /* wTotalLength(L/H) */
0x02, /* bNumInterfaces */
0x01, /* bConfigValue */
0x00, /* iConfiguration */
0xE0, /* bmAttributes (self-powered, remote wakeup) */
0x01, /* MaxPower is 2ma (units are 2ma/bit) */
},
{ /* First Interface Descriptor For Comm Class Interface */
0x09, /* bLength = 9 */
0x04, /* bDescriptorType = Interface (4) */
0x00, /* bInterfaceNumber */
0x00, /* bAlternateSetting */
0x01, /* bNumEndpoints (one for OUT) */
0x02, /* bInterfaceClass = Communications Interface Class (2) */
0x02, /* bInterfaceSubClass = Abstract Control Model (2) */
0x01, /* bInterfaceProtocol = Common "AT" commands (1), no class specific protocol (0) */
0x00, /* iInterface */
},
{ /* Header Functional Descriptor */
0x05, /* bFunctionalLength = 5 */
0x24, /* bDescriptorType */
0x00, /* bDescriptorSubtype */
0x10, 0x01, /* bcdCDC */
},
{ /* Call Management Descriptor */
0x05, /* bFunctionalLength = 5 */
0x24, /* bDescriptorType */
0x01, /* bDescriptorSubtype */
0x03, /* bmCapabilities = Device handles call management itself (0x01), management over data class (0x02) */
0x01, /* bmDataInterface */
},
{ /* Abstract Control Management Functional Descriptor */
0x04, /* bFunctionalLength = 4 */
0x24, /* bDescriptorType */
0x02, /* bDescriptorSubtype */
0x02, /* bmCapabilities */
},
{ /* Union Functional Descriptor */
0x05, /* bFunctionalLength = 5 */
0x24, /* bDescriptorType */
0x06, /* bDescriptorSubtype */
0x00, /* bmMasterInterface */
0x01, /* bmSlaveInterface0 */
},
{ /* IN Endpoint 3 (Descriptor #1) */
0x07, /* bLength */
0x05, /* bDescriptorType (Endpoint) */
0x83, /* bEndpointAddress (EP3-IN) */
0x03, /* bmAttributes (interrupt) */
0x0040, /* wMaxPacketSize */
0xff, /* bInterval (milliseconds) */
},
{ /* Second Interface Descriptor For Data Interface */
0x09, /* bLength */
0x04, /* bDescriptorType (Interface) */
0x01, /* bInterfaceNumber */
0x00, /* bAlternateSetting */
0x02, /* bNumEndpoints */
0x0a, /* bInterfaceClass = Data Interface (10) */
0x00, /* bInterfaceSubClass = none (0) */
0x00, /* bInterfaceProtocol = No class specific protocol (0) */
0x00, /* biInterface = No Text String (0) */
},
{ /* OUT Endpoint 1 (Descriptor #2) */
0x07, /* bLength */
0x05, /* bDescriptorType (Endpoint) */
0x01, /* bEndpointAddress (EP1-OUT) */
0x02, /* bmAttributes (bulk) */
0x0040, /* wMaxPacketSize */
0x00, /* bInterval (N/A) */
},
{ /* IN Endpoint 2 (Descriptor #3) */
0x07, /* bLength */
0x05, /* bDescriptorType (Endpoint) */
0x82, /* bEndpointAddress (EP2-IN) */
0x02, /* bmAttributes (bulk) */
0x0040, /* wMaxPacketSize */
0x00 /* bInterval (N/A) */
}
};
__attribute__((aligned(4)))
uint8_t lang_id_desc[] = {
0x04, /* bLength */
0x03, /* bDescriptorType */
0x09, 0x04 /* bString = wLANGID (see usb_20.pdf 9.6.7 String) */
};
__attribute__((aligned(4)))
uint8_t mfg_id_desc[] = {
0x22, /* bLength */
0x03, /* bDescriptorType */
'M', 0,
'a', 0,
'x', 0,
'i', 0,
'm', 0,
' ', 0,
'I', 0,
'n', 0,
't', 0,
'e', 0,
'g', 0,
'r', 0,
'a', 0,
't', 0,
'e', 0,
'd', 0,
};
__attribute__((aligned(4)))
uint8_t prod_id_desc[] = {
0x22, /* bLength */
0x03, /* bDescriptorType */
'M', 0,
'A', 0,
'X', 0,
'3', 0,
'2', 0,
'6', 0,
'6', 0,
'5', 0,
' ', 0,
'C', 0,
'D', 0,
'C', 0,
'-', 0,
'A', 0,
'C', 0,
'M', 0,
};
/* Not currently used (see device descriptor), but could be enabled if desired */
__attribute__((aligned(4)))
uint8_t serial_id_desc[] = {
0x14, /* bLength */
0x03, /* bDescriptorType */
'0', 0,
'0', 0,
'0', 0,
'0', 0,
'0', 0,
'0', 0,
'0', 0,
'0', 0,
'1', 0
};
#endif /* _DESCRIPTORS_H_ */
/* clang-format off */
/*******************************************************************************
* Copyright (C) 2017 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.
*******************************************************************************
*/
#ifndef _MAX30003_H_
#define _MAX30003_H_
///MAX30003 Registers
enum Registers_e
{
NO_OP = 0x00,
STATUS = 0x01,
EN_INT = 0x02,
EN_INT2 = 0x03,
MNGR_INT = 0x04,
MNGR_DYN = 0x05,
SW_RST = 0x08,
SYNCH = 0x09,
FIFO_RST = 0x0A,
INFO = 0x0F,
CNFG_GEN = 0x10,
CNFG_ALL = 0x12,
CNFG_EMUX = 0x14,
CNFG_ECG = 0x15,
CNFG_RTOR1 = 0x1D,
CNFG_RTOR2 = 0x1E,
ECG_FIFO_BURST = 0x20,
ECG_FIFO = 0x21,
RTOR = 0x25,
NO_OP2 = 0x7F
};
///Status register bits
union Status_u
{
///Access all bits
uint32_t all;
///Access individual bits
struct
{
uint32_t loff_nl : 1;
uint32_t loff_nh : 1;
uint32_t loff_pl : 1;
uint32_t loff_ph : 1;
uint32_t reserved1 : 4;
uint32_t pllint : 1;
uint32_t samp : 1;
uint32_t rrint : 1;
uint32_t lonint : 1;
uint32_t reserved2 : 8;
uint32_t dcloffint : 1;
uint32_t fstint : 1;
uint32_t eovf : 1;
uint32_t eint : 1;
uint32_t reserved3 : 8;
}bits;
};
///Enable Interrupt registers bits
union EnableInterrupts_u
{
///Access all bits
uint32_t all;
///Access individual bits
struct
{
uint32_t intb_type : 2;
uint32_t reserved1 : 6;
uint32_t en_pllint : 1;
uint32_t en_samp : 1;
uint32_t en_rrint : 1;
uint32_t en_loint : 1;
uint32_t reserved2 : 8;
uint32_t en_dcloffint : 1;
uint32_t en_fstint : 1;
uint32_t en_eovf : 1;
uint32_t en_eint : 1;
uint32_t reserved3 : 8;
}bits;
};
///Manage Interrupt register bits
union ManageInterrupts_u
{
///Access all bits
uint32_t all;
///Access individual bits
struct
{
uint32_t samp_it : 4;
uint32_t clr_samp : 1;
uint32_t reserved1 : 1;
uint32_t clr_rrint : 2;
uint32_t clr_fast : 1;
uint32_t reserved2 : 12;
uint32_t efit : 5;
uint32_t reserved3 : 8;
}bits;
};
///Manage Dynamic Modes register bits
union ManageDynamicModes_u
{
///Access all bits
uint32_t all;
///Access individual bits
struct
{
uint32_t reserved1 : 16;
uint32_t fast_th : 6;
uint32_t fast : 2;
uint32_t reserved2 : 8;
}bits;
};
///General Configuration bits
union GeneralConfiguration_u
{
///Access all bits
uint32_t all;
///Access individual bits
struct
{
uint32_t rbiasn : 1;
uint32_t rbiasp : 1;
uint32_t rbiasv : 2;
uint32_t en_rbias : 2;
uint32_t vth : 2;
uint32_t imag : 3;
uint32_t ipol : 1;
uint32_t en_dcloff : 2;
uint32_t reserved1 : 5;
uint32_t en_ecg : 1;
uint32_t fmstr : 2;
uint32_t en_ulp_lon : 2;
uint32_t reserved2 : 8;
}bits;
};
///Cal Configuration bits
union CalConfiguration_u
{
///Access all bits
uint32_t all;
///Access individual bits
struct
{
uint32_t thigh : 11;
uint32_t fifty : 1;
uint32_t fcal : 3;
uint32_t reserved1 : 5;
uint32_t vmag : 1;
uint32_t vmode : 1;
uint32_t en_vcal : 1;
uint32_t reserved2 : 9;
}bits;
};
///Mux Configuration bits
union MuxConfiguration_u
{
///Access all bits
uint32_t all;
///Access individual bits
struct
{
uint32_t reserved1 : 16;
uint32_t caln_sel : 2;
uint32_t calp_sel : 2;
uint32_t openn : 1;
uint32_t openp : 1;
uint32_t reserved2 : 1;
uint32_t pol : 1;
uint32_t reserved3 : 8;
}bits;
};
///ECG Configuration bits
union ECGConfiguration_u
{
///Access all bits
uint32_t all;
///Access individual bits
struct
{
uint32_t reserved1 : 12;
uint32_t dlpf : 2;
uint32_t dhpf : 1;
uint32_t reserved2 : 1;
uint32_t gain : 2;
uint32_t reserved3 : 4;
uint32_t rate : 2;
uint32_t reserved4 : 8;
}bits;
};
///RtoR1 Configuration bits
union RtoR1Configuration_u
{
///Access all bits
uint32_t all;
///Access individual bits
struct
{
uint32_t reserved1 : 8;
uint32_t ptsf : 4;
uint32_t pavg : 2;
uint32_t reserved2 : 1;
uint32_t en_rtor : 1;
uint32_t rgain : 4;
uint32_t wndw : 4;
uint32_t reserved3 : 8;
}bits;
};
///RtoR2 Configuration bits
union RtoR2Configuration_u
{
///Access all bits
uint32_t all;
///Access individual bits
struct
{
uint32_t reserved1 : 8;
uint32_t rhsf : 3;
uint32_t reserved2 : 1;
uint32_t ravg : 2;
uint32_t reserved3 : 2;
uint32_t hoff : 6;
uint32_t reserved4 : 10;
}bits;
};
#endif /* _MAX30003_H_ */
/* clang-format on */
#include <assert.h>
#include <stdio.h>
#include <string.h>
#include "gpio.h"
#include "bhy_uc_driver.h"
#include "bhy.h"
#include "pmic.h"
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "epicardium.h"
#include "os/core.h"
#include "modules/modules.h"
#include "drivers/drivers.h"
#include "modules/stream.h"
#include "user_core/interrupts.h"
/* BHI160 Firmware Blob. Contents are defined in libcard10. */
extern uint8_t bhy1_fw[];
/* Interrupt Pin */
static const gpio_cfg_t bhi160_interrupt_pin = {
PORT_0, PIN_13, GPIO_FUNC_IN, GPIO_PAD_PULL_UP
};
/* clang-format off */
/* Axis remapping matrices */
static const int8_t bhi160_mapping_matrix[3 * 3] = {
0, -1, 0,
1, 0, 0,
0, 0, 1,
};
static const int8_t bmm150_mapping_matrix[3 * 3] = {
-1, 0, 0,
0, 1, 0,
0, 0, -1,
};
/*
* From the official docs:
*
* The sic matrix should be calculated for customer platform by logging
* uncalibrated magnetometer data. The sic matrix here is only an example
* array (identity matrix). Customer should generate their own matrix. This
* affects magnetometer fusion performance.
*
* TODO: Get data for card10
*/
static const float bhi160_sic_array[3 * 3] = {
1.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 1.0f,
};
/* clang-format on */
/* BHI160 Task ID */
static TaskHandle_t bhi160_task_id = NULL;
/* BHI160 Mutex */
static struct mutex bhi160_mutex = { 0 };
/* Streams */
static struct stream_info bhi160_streams[10];
/* Active */
static bool bhi160_sensor_active[10] = { 0 };
/*
* Driver State: A flag that is set when an unrecoverable error occurred.
* Effectively, this means the sensor will be disabled until next reboot and any
* API calls will fail immediately.
*/
static bool bhi160_driver_b0rked = false;
/* -- Utilities -------------------------------------------------------- {{{ */
/*
* Retrieve the data size for a sensor. This value is needed for the creation
* of the sensor's sample queue.
*/
static size_t bhi160_lookup_data_size(enum bhi160_sensor_type type)
{
switch (type) {
case BHI160_ACCELEROMETER:
case BHI160_MAGNETOMETER:
case BHI160_ORIENTATION:
case BHI160_GYROSCOPE:
return sizeof(struct bhi160_data_vector);
default:
return 0;
}
}
/*
* Map a sensor type to the virtual sensor ID used by BHy1.
*/
static bhy_virtual_sensor_t bhi160_lookup_vs_id(enum bhi160_sensor_type type)
{
switch (type) {
case BHI160_ACCELEROMETER:
return VS_ID_ACCELEROMETER;
case BHI160_MAGNETOMETER:
return VS_ID_MAGNETOMETER;
case BHI160_ORIENTATION:
return VS_ID_ORIENTATION;
case BHI160_GYROSCOPE:
return VS_ID_GYROSCOPE;
default:
return -1;
}
}
/*
* Map a sensor type to its stream descriptor.
*/
static int bhi160_lookup_sd(enum bhi160_sensor_type type)
{
switch (type) {
case BHI160_ACCELEROMETER:
return SD_BHI160_ACCELEROMETER;
case BHI160_MAGNETOMETER:
return SD_BHI160_MAGNETOMETER;
case BHI160_ORIENTATION:
return SD_BHI160_ORIENTATION;
case BHI160_GYROSCOPE:
return SD_BHI160_GYROSCOPE;
default:
return -1;
}
}
/* }}} */
/* -- API -------------------------------------------------------------- {{{ */
int epic_bhi160_enable_sensor(
enum bhi160_sensor_type sensor_type,
struct bhi160_sensor_config *config
) {
int result = 0;
bhy_virtual_sensor_t vs_id = bhi160_lookup_vs_id(sensor_type);
if (vs_id == (bhy_virtual_sensor_t)-1) {
return -ENODEV;
}
mutex_lock(&bhi160_mutex);
hwlock_acquire(HWLOCK_I2C);
if (bhi160_driver_b0rked) {
result = -ENODEV;
goto out_free;
}
struct stream_info *stream = &bhi160_streams[sensor_type];
stream->item_size = bhi160_lookup_data_size(sensor_type);
/* TODO: Sanity check length */
stream->queue =
xQueueCreate(config->sample_buffer_len, stream->item_size);
if (stream->queue == NULL) {
result = -ENOMEM;
goto out_free;
}
result = stream_register(bhi160_lookup_sd(sensor_type), stream);
if (result < 0) {
goto out_free;
}
result = bhy_enable_virtual_sensor(
vs_id,
VS_WAKEUP,
config->sample_rate,
0,
VS_FLUSH_NONE,
0,
config->dynamic_range /* dynamic range is sensor dependent */
);
if (result != BHY_SUCCESS) {
goto out_free;
}
bhi160_sensor_active[sensor_type] = true;
/* Return the sensor stream descriptor */
result = bhi160_lookup_sd(sensor_type);
out_free:
hwlock_release(HWLOCK_I2C);
mutex_unlock(&bhi160_mutex);
return result;
}
int epic_bhi160_disable_sensor(enum bhi160_sensor_type sensor_type)
{
int result = 0;
bhy_virtual_sensor_t vs_id = bhi160_lookup_vs_id(sensor_type);
if (vs_id == (bhy_virtual_sensor_t)-1) {
return -ENODEV;
}
mutex_lock(&bhi160_mutex);
hwlock_acquire(HWLOCK_I2C);
if (bhi160_driver_b0rked) {
result = -ENODEV;
goto out_free;
}
struct stream_info *stream = &bhi160_streams[sensor_type];
result = stream_deregister(bhi160_lookup_sd(sensor_type), stream);
if (result < 0) {
goto out_free;
}
vQueueDelete(stream->queue);
stream->queue = NULL;
result = bhy_disable_virtual_sensor(vs_id, VS_WAKEUP);
if (result < 0) {
goto out_free;
}
bhi160_sensor_active[sensor_type] = false;
result = 0;
out_free:
hwlock_release(HWLOCK_I2C);
mutex_unlock(&bhi160_mutex);
return result;
}
void epic_bhi160_disable_all_sensors()
{
for (size_t i = 0; i < sizeof(bhi160_sensor_active); i++) {
if (bhi160_sensor_active[i]) {
epic_bhi160_disable_sensor(i);
}
}
}
/* }}} */
/* -- Driver ----------------------------------------------------------- {{{ */
/*
* Handle a single packet from the FIFO. For most sensors this means pushing
* the sample into its sample queue.
*/
static void
bhi160_handle_packet(bhy_data_type_t data_type, bhy_data_generic_t *sensor_data)
{
uint8_t sensor_id = sensor_data->data_vector.sensor_id;
struct bhi160_data_vector data_vector;
/*
* Timestamp of the next samples, counting at 32 kHz.
* Currently unused.
*/
static uint32_t timestamp = 0;
enum bhi160_sensor_type sensor_type = 0;
int epic_int = 0;
bool wakeup = false;
switch (sensor_id) {
case VS_ID_TIMESTAMP_MSW_WAKEUP:
wakeup = true;
/* fall through */
case VS_ID_TIMESTAMP_MSW:
assert(data_type == BHY_DATA_TYPE_SCALAR_U16);
timestamp = sensor_data->data_scalar_u16.data << 16;
break;
case VS_ID_TIMESTAMP_LSW_WAKEUP:
wakeup = true;
/* fall through */
case VS_ID_TIMESTAMP_LSW:
assert(data_type == BHY_DATA_TYPE_SCALAR_U16);
timestamp = (timestamp & 0xFFFF0000) |
sensor_data->data_scalar_u16.data;
break;
case VS_ID_ACCELEROMETER_WAKEUP:
case VS_ID_MAGNETOMETER_WAKEUP:
case VS_ID_ORIENTATION_WAKEUP:
case VS_ID_GYROSCOPE_WAKEUP:
wakeup = true;
/* fall through */
case VS_ID_ACCELEROMETER:
case VS_ID_MAGNETOMETER:
case VS_ID_ORIENTATION:
case VS_ID_GYROSCOPE:
switch (sensor_id) {
case VS_ID_ACCELEROMETER_WAKEUP:
case VS_ID_ACCELEROMETER:
sensor_type = BHI160_ACCELEROMETER;
epic_int = EPIC_INT_BHI160_ACCELEROMETER;
break;
case VS_ID_MAGNETOMETER_WAKEUP:
case VS_ID_MAGNETOMETER:
sensor_type = BHI160_MAGNETOMETER;
epic_int = EPIC_INT_BHI160_MAGNETOMETER;
break;
case VS_ID_ORIENTATION_WAKEUP:
case VS_ID_ORIENTATION:
sensor_type = BHI160_ORIENTATION;
epic_int = EPIC_INT_BHI160_ORIENTATION;
break;
case VS_ID_GYROSCOPE_WAKEUP:
case VS_ID_GYROSCOPE:
sensor_type = BHI160_GYROSCOPE;
epic_int = EPIC_INT_BHI160_GYROSCOPE;
break;
}
assert(data_type == BHY_DATA_TYPE_VECTOR);
if (bhi160_streams[sensor_type].queue == NULL) {
break;
}
data_vector.data_type = BHI160_DATA_TYPE_VECTOR;
data_vector.x = sensor_data->data_vector.x;
data_vector.y = sensor_data->data_vector.y;
data_vector.z = sensor_data->data_vector.z;
data_vector.status = sensor_data->data_vector.status;
/* Discard overflow. See discussion in !316. */
if (xQueueSend(
bhi160_streams[sensor_type].queue,
&data_vector,
0) != pdTRUE) {
if (!bhi160_streams[sensor_type].was_full) {
LOG_WARN(
"bhi160",
"queue full for %d",
sensor_type
);
}
bhi160_streams[sensor_type].was_full = true;
} else {
bhi160_streams[sensor_type].was_full = false;
}
if (wakeup) {
interrupt_trigger(epic_int);
}
break;
default:
break;
}
}
/*
* Fetch all data available from BHI160's FIFO buffer and handle all packets
* contained in it.
*/
static void bhi160_fetch_fifo(void)
{
/*
* Warning: The code from the BHy1 docs has some issues. This
* implementation looks similar, but has a few important differences.
* You'll probably be best of leaving it as it is ...
*/
/*
* FIFO buffer. Access to this static variable is safe because this
* function is guarded by the bhi160_mutex.
*/
static uint8_t bhi160_fifo[BHI160_FIFO_SIZE];
static size_t start_index = 0;
BHY_RETURN_FUNCTION_TYPE result = 0;
/* Number of bytes left in BHI160's FIFO buffer */
uint16_t bytes_left_in_fifo = 1;
mutex_lock(&bhi160_mutex);
hwlock_acquire(HWLOCK_I2C);
while (bytes_left_in_fifo) {
/* Fill local FIFO buffer with as many bytes as possible */
uint16_t bytes_read;
result = bhy_read_fifo(
&bhi160_fifo[start_index],
BHI160_FIFO_SIZE - start_index,
&bytes_read,
&bytes_left_in_fifo
);
if (result != BHY_SUCCESS) {
/*
* Honestly not sure how we should handle these errors.
*
* Needs more digging to find out how bhy_read_fifo()
* behaves in error situations (a quick glance at the
* code shows that the function might have written
* _somethings_ into our buffer so we can't just retry
* blindly ...)
*
* For now, just abort everything and disable the
* sensor. Won't cause havoc at least ...
*/
LOG_ERR("bhi160",
"Error while reading fifo: %d. Disabling.",
result);
bhi160_driver_b0rked = true;
break;
}
/* Add the bytes left from the last transfer on top */
bytes_read += start_index;
/* Handle all full packets received in this transfer */
uint8_t *fifo_ptr = bhi160_fifo;
uint16_t bytes_left = bytes_read;
while (bytes_left > 0) {
bhy_data_generic_t sensor_data;
bhy_data_type_t data_type;
result = bhy_parse_next_fifo_packet(
&fifo_ptr,
&bytes_left,
&sensor_data,
&data_type
);
if (result == BHY_SUCCESS) {
bhi160_handle_packet(data_type, &sensor_data);
} else {
LOG_WARN(
"bhi160",
"Error in fifo packet: %d. Ignoring.",
result
);
break;
}
}
/* Shift the remaining bytes to the beginning */
for (int i = 0; i < bytes_left; i++) {
bhi160_fifo[i] =
bhi160_fifo[bytes_read - bytes_left + i];
}
start_index = bytes_left;
}
hwlock_release(HWLOCK_I2C);
mutex_unlock(&bhi160_mutex);
}
/*
* Callback for the BHI160 interrupt pin. This callback is called from the
* SDK's GPIO interrupt driver, in interrupt context.
*/
static void bhi160_interrupt_callback(void *_)
{
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
if (bhi160_task_id != NULL && !bhi160_driver_b0rked) {
vTaskNotifyGiveFromISR(
bhi160_task_id, &xHigherPriorityTaskWoken
);
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}
}
/* }}} */
void vBhi160Task(void *pvParameters)
{
int ret;
bhi160_task_id = xTaskGetCurrentTaskHandle();
mutex_create(&bhi160_mutex);
mutex_lock(&bhi160_mutex);
hwlock_acquire(HWLOCK_I2C);
memset(bhi160_streams, 0x00, sizeof(bhi160_streams));
/*
* The BHI160, coming out of power-on-reset will hold its interrupt line
* high until a firmware image is loaded. Once that firmware is loaded
* and running, the interrupt line is deasserted and from then on,
* interrupts are signaled using a rising edge.
*
* So, initially we need to configure the IRQ for a falling edge, load
* the firmware and then reconfigure for a rising edge.
*/
GPIO_Config(&bhi160_interrupt_pin);
GPIO_RegisterCallback(
&bhi160_interrupt_pin, bhi160_interrupt_callback, NULL
);
GPIO_IntConfig(&bhi160_interrupt_pin, GPIO_INT_EDGE, GPIO_INT_FALLING);
GPIO_IntEnable(&bhi160_interrupt_pin);
NVIC_SetPriority(
(IRQn_Type)MXC_GPIO_GET_IRQ(bhi160_interrupt_pin.port), 2
);
NVIC_EnableIRQ((IRQn_Type)MXC_GPIO_GET_IRQ(bhi160_interrupt_pin.port));
/* Upload firmware */
ret = bhy_driver_init(bhy1_fw);
if (ret) {
LOG_CRIT("bhi160", "BHy1 init failed! Disabling.");
/* Disable BHI160 until next reboot */
bhi160_driver_b0rked = true;
hwlock_release(HWLOCK_I2C);
mutex_unlock(&bhi160_mutex);
vTaskDelete(NULL);
}
/* Wait for first interrupt, a falling edge */
hwlock_release(HWLOCK_I2C);
if (ulTaskNotifyTake(pdTRUE, pdMS_TO_TICKS(2000)) == 0) {
LOG_CRIT(
"bhi160",
"Sensor firmware was not loaded correctly. Disabling."
);
/* Disable BHI160 until next reboot */
bhi160_driver_b0rked = true;
mutex_unlock(&bhi160_mutex);
vTaskDelete(NULL);
}
hwlock_acquire(HWLOCK_I2C);
/*
* The firmware is now loaded; as stated above, we now need to
* reconfigure the IRQ for a rising edge.
*/
GPIO_IntConfig(&bhi160_interrupt_pin, GPIO_INT_EDGE, GPIO_INT_RISING);
/*
* Remap axes to match card10 layout.
*
* TODO: We set the matrix for the accelerometer twice because on some
* badges, the axis mapping is not applied properly the first time. We
* should fix this properly at some point.
*/
bhy_mapping_matrix_set(
PHYSICAL_SENSOR_INDEX_ACC, (int8_t *)bhi160_mapping_matrix
);
bhy_mapping_matrix_set(
PHYSICAL_SENSOR_INDEX_ACC, (int8_t *)bhi160_mapping_matrix
);
bhy_mapping_matrix_set(
PHYSICAL_SENSOR_INDEX_MAG, (int8_t *)bmm150_mapping_matrix
);
bhy_mapping_matrix_set(
PHYSICAL_SENSOR_INDEX_GYRO, (int8_t *)bhi160_mapping_matrix
);
/* Set "SIC" matrix. TODO: Find out what this is about */
bhy_set_sic_matrix((float *)bhi160_sic_array);
hwlock_release(HWLOCK_I2C);
mutex_unlock(&bhi160_mutex);
/* ----------------------------------------- */
while (1) {
bhi160_fetch_fifo();
/*
* Wait for interrupt. After two seconds, fetch FIFO anyway in
* case there are any diagnostics or errors.
*
* In the future, reads using epic_stream_read() might also
* trigger a FIFO fetch, from outside this task.
*/
ulTaskNotifyTake(pdTRUE, pdMS_TO_TICKS(2000));
}
}