Skip to content
Snippets Groups Projects

Compare revisions

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

Source

Select target project
No results found

Target

Select target project
  • card10/firmware
  • annejan/firmware
  • astro/firmware
  • fpletz/firmware
  • gerd/firmware
  • fleur/firmware
  • swym/firmware
  • l/firmware
  • uberardy/firmware
  • wink/firmware
  • madonius/firmware
  • mot/firmware
  • filid/firmware
  • q3k/firmware
  • hauke/firmware
  • Woazboat/firmware
  • pink/firmware
  • mossmann/firmware
  • omniskop/firmware
  • zenox/firmware
  • trilader/firmware
  • Danukeru/firmware
  • shoragan/firmware
  • zlatko/firmware
  • sistason/firmware
  • datenwolf/firmware
  • bene/firmware
  • amedee/firmware
  • martinling/firmware
  • griffon/firmware
  • chris007/firmware
  • adisbladis/firmware
  • dbrgn/firmware
  • jelly/firmware
  • rnestler/firmware
  • mh/firmware
  • ln/firmware
  • penguineer/firmware
  • monkeydom/firmware
  • jens/firmware
  • jnaulty/firmware
  • jeffmakes/firmware
  • marekventur/firmware
  • pete/firmware
  • h2obrain/firmware
  • DooMMasteR/firmware
  • jackie/firmware
  • prof_r/firmware
  • Draradech/firmware
  • Kartoffel/firmware
  • hinerk/firmware
  • abbradar/firmware
  • JustTB/firmware
  • LuKaRo/firmware
  • iggy/firmware
  • ente/firmware
  • flgr/firmware
  • Lorphos/firmware
  • matejo/firmware
  • ceddral7/firmware
  • danb/firmware
  • joshi/firmware
  • melle/firmware
  • fitch/firmware
  • deurknop/firmware
  • sargon/firmware
  • markus/firmware
  • kloenk/firmware
  • lucaswerkmeister/firmware
  • derf/firmware
  • meh/firmware
  • dx/card10-firmware
  • torben/firmware
  • yuvadm/firmware
  • AndyBS/firmware
  • klausdieter1/firmware
  • katzenparadoxon/firmware
  • xiretza/firmware
  • ole/firmware
  • techy/firmware
  • thor77/firmware
  • TilCreator/firmware
  • fuchsi/firmware
  • dos/firmware
  • yrlf/firmware
  • PetePriority/firmware
  • SuperVirus/firmware
  • sur5r/firmware
  • tazz/firmware
  • Alienmaster/firmware
  • flo_h/firmware
  • baldo/firmware
  • mmu_man/firmware
  • Foaly/firmware
  • sodoku/firmware
  • Guinness/firmware
  • ssp/firmware
  • led02/firmware
  • Stormwind/firmware
  • arist/firmware
  • coon/firmware
  • mdik/firmware
  • pippin/firmware
  • royrobotiks/firmware
  • zigot83/firmware
  • mo_k/firmware
106 results
Show changes
Showing
with 4361 additions and 794 deletions
......@@ -13,66 +13,46 @@
*/
/* clang-format off */
/* clang-formet turned off for easier diffing against orginal file */
#include <stdio.h>
#include <string.h>
#include "wsf_types.h"
#include "util/bstream.h"
#include "wsf_msg.h"
#include "wsf_trace.h"
#include "hci_api.h"
#include "l2c_api.h"
#include "dm_api.h"
#include "att_api.h"
#include "gatt/gatt_api.h"
#include "smp_api.h"
#include "app_api.h"
#include "app_db.h"
#include "app_ui.h"
#include "app_hw.h"
#include "svc_ch.h"
#include "svc_core.h"
#include "svc_hrs.h"
#include "svc_dis.h"
#include "svc_batt.h"
#include "svc_rscs.h"
#include "bas/bas_api.h"
#include "hrps/hrps_api.h"
#include "rscp/rscp_api.h"
#include "svc_hid.h"
#include "profiles/gap_api.h"
#include "cccd.h"
#include "ess.h"
#include "hid.h"
#include "uart.h"
/**************************************************************************************************
Macros
**************************************************************************************************/
/*! WSF message event starting value */
#define BLE_MSG_START 0xA0
/*! WSF message event enumeration */
enum
{
BLE_BATT_TIMER_IND = BLE_MSG_START, /*! Battery measurement timer expired */
};
#include "ble_api.h"
#include "epicardium.h"
#include "os/core.h"
#include "os/config.h"
/**************************************************************************************************
Data Types
**************************************************************************************************/
#define SCAN_REPORTS_NUM 16
/*! Application message type */
typedef union
{
wsfMsgHdr_t hdr;
dmEvt_t dm;
attsCccEvt_t ccc;
attEvt_t att;
} bleMsg_t;
static bool active;
static struct epic_scan_report scan_reports[SCAN_REPORTS_NUM];
static int scan_reports_head;
static int scan_reports_tail;
/**************************************************************************************************
Configurable Parameters
**************************************************************************************************/
/*! configurable parameters for advertising */
static const appAdvCfg_t bleAdvCfg =
{
{0, 0, 0}, /*! Advertising durations in ms */
{500/0.625, 4000/0.625, 0} /*! Advertising intervals in 0.625 ms units */
};
/*! configurable parameters for slave */
static const appSlaveCfg_t bleSlaveCfg =
{
......@@ -82,11 +62,11 @@ static const appSlaveCfg_t bleSlaveCfg =
/*! configurable parameters for security */
static const appSecCfg_t bleSecCfg =
{
DM_AUTH_BOND_FLAG | DM_AUTH_SC_FLAG, /*! Authentication and bonding flags */
0, /*! Initiator key distribution flags */
DM_KEY_DIST_LTK, /*! Responder key distribution flags */
FALSE, /*! TRUE if Out-of-band pairing data is present */
TRUE /*! TRUE to initiate security upon connection */
.auth = DM_AUTH_MITM_FLAG | DM_AUTH_BOND_FLAG | DM_AUTH_SC_FLAG, /*! Authentication and bonding flags */
.iKeyDist = 0, /*! Initiator key distribution flags */
.rKeyDist = DM_KEY_DIST_LTK, /*! Responder key distribution flags */
.oob=FALSE, /*! TRUE if Out-of-band pairing data is present */
.initiateSec = TRUE /*! TRUE to initiate security upon connection */
};
/*! configurable parameters for connection parameter update */
......@@ -94,30 +74,32 @@ static const appUpdateCfg_t bleUpdateCfg =
{
6000, /*! Connection idle period in ms before attempting
connection parameter update; set to zero to disable */
800/1.25, /*! Minimum connection interval in 1.25ms units */
1000/1.25, /*! Maximum connection interval in 1.25ms units */
30/1.25, /*! Minimum connection interval in 1.25ms units.
Values < 8 didn't work with my Tinkpad T470 */
40/1.25, /*! Maximum connection interval in 1.25ms units */
0, /*! Connection latency */
9000/10, /*! Supervision timeout in 10ms units */
5 /*! Number of update attempts before giving up */
};
/*! battery measurement configuration */
static const basCfg_t bleBasCfg =
/*! SMP security parameter configuration */
static const smpCfg_t bleSmpCfg =
{
30, /*! Battery measurement timer expiration period in seconds */
1, /*! Perform battery measurement after this many timer periods */
100 /*! Send battery level notification to peer when below this level. */
.attemptTimeout = 3000, /*! 'Repeated attempts' timeout in msec */
.ioCap = SMP_IO_DISP_YES_NO, /*! I/O Capability */
.minKeyLen = 16, /*! Minimum encryption key length */
.maxKeyLen = 16, /*! Maximum encryption key length */
.maxAttempts = 3, /*! Attempts to trigger 'repeated attempts' timeout */
.auth = DM_AUTH_MITM_FLAG | DM_AUTH_SC_FLAG, /*! Device authentication requirements */
.maxAttemptTimeout = 64000, /*! Maximum 'Repeated attempts' timeout in msec */
.attemptDecTimeout = 64000, /*! Time msec before attemptExp decreases */
.attemptExp = 2, /*! Exponent to raise attemptTimeout on maxAttempts */
};
/*! SMP security parameter configuration */
static const smpCfg_t bleSmpCfg =
/*! Configurable parameters for service and characteristic discovery */
static const appDiscCfg_t bleDiscCfg =
{
3000, /*! 'Repeated attempts' timeout in msec */
SMP_IO_DISP_YES_NO, /*! I/O Capability */
7, /*! Minimum encryption key length */
16, /*! Maximum encryption key length */
3, /*! Attempts to trigger 'repeated attempts' timeout */
DM_AUTH_MITM_FLAG, /*! Device authentication requirements */
FALSE /*! TRUE to wait for a secure connection before initiating discovery */
};
/* Configuration structure */
......@@ -129,58 +111,36 @@ static const attCfg_t bleAttCfg =
1 /* number of queued prepare writes supported by server */
};
/**************************************************************************************************
Advertising Data
**************************************************************************************************/
/*! advertising data, discoverable mode */
static const uint8_t bleAdvDataDisc[] =
{
/*! flags */
2, /*! length */
DM_ADV_TYPE_FLAGS, /*! AD type */
DM_FLAG_LE_GENERAL_DISC | /*! flags */
DM_FLAG_LE_BREDR_NOT_SUP,
/*! tx power */
2, /*! length */
DM_ADV_TYPE_TX_POWER, /*! AD type */
0, /*! tx power */
/*! service UUID list */
5, /*! length */
DM_ADV_TYPE_16_UUID, /*! AD type */
UINT16_TO_BYTES(ATT_UUID_DEVICE_INFO_SERVICE),
UINT16_TO_BYTES(ATT_UUID_BATTERY_SERVICE)
};
/*! scan data, discoverable mode */
static const uint8_t bleScanDataDisc[] =
static const appMasterCfg_t scannerMasterCfg =
{
/*! device name */
7, /*! length */
DM_ADV_TYPE_LOCAL_NAME, /*! AD type */
'c','a','r','d','1','0'
420, /*! The scan interval, in 0.625 ms units */
420, /*! The scan window, in 0.625 ms units */
0, /*! The scan duration in ms */
DM_DISC_MODE_NONE, /*! The GAP discovery mode */
DM_SCAN_TYPE_PASSIVE
/*!< The scan type (active or passive) */
};
/**************************************************************************************************
Client Characteristic Configuration Descriptors
**************************************************************************************************/
/*! enumeration of client characteristic configuration descriptors */
enum
{
BLE_GATT_SC_CCC_IDX, /*! GATT service, service changed characteristic */
BLE_BATT_LVL_CCC_IDX, /*! Battery service, battery level characteristic */
BLE_NUM_CCC_IDX
};
/*! client characteristic configuration descriptors settings, indexed by above enumeration */
static const attsCccSet_t bleCccSet[BLE_NUM_CCC_IDX] =
{
/* cccd handle value range security level */
{GATT_SC_CH_CCC_HDL, ATT_CLIENT_CFG_INDICATE, DM_SEC_LEVEL_NONE}, /* BLE_GATT_SC_CCC_IDX */
{BATT_LVL_CH_CCC_HDL, ATT_CLIENT_CFG_NOTIFY, DM_SEC_LEVEL_NONE}, /* BLE_BATT_LVL_CCC_IDX */
{ESS_TEMP_CH_CCC_HDL, ATT_CLIENT_CFG_NOTIFY, DM_SEC_LEVEL_NONE}, /* BLE_ESS_TEMP_CCC_IDX */
{ESS_HUMI_CH_CCC_HDL, ATT_CLIENT_CFG_NOTIFY, DM_SEC_LEVEL_NONE}, /* BLE_ESS_HUMI_CCC_IDX */
{ESS_PRES_CH_CCC_HDL, ATT_CLIENT_CFG_NOTIFY, DM_SEC_LEVEL_NONE}, /* BLE_ESS_PRES_CCC_IDX */
{HID_MOUSE_BOOT_IN_CH_CCC_HDL, ATT_CLIENT_CFG_NOTIFY, DM_SEC_LEVEL_NONE}, /* HIDAPP_MBI_CCC_HDL */
{HID_KEYBOARD_BOOT_IN_CH_CCC_HDL, ATT_CLIENT_CFG_NOTIFY, DM_SEC_LEVEL_NONE}, /* HIDAPP_KBI_CCC_HDL */
{HID_INPUT_REPORT_1_CH_CCC_HDL, ATT_CLIENT_CFG_NOTIFY, DM_SEC_LEVEL_NONE}, /* HIDAPP_IN_KEYBOARD_CCC_HDL */
{HID_INPUT_REPORT_2_CH_CCC_HDL, ATT_CLIENT_CFG_NOTIFY, DM_SEC_LEVEL_NONE}, /* HIDAPP_IN_MOUSE_CCC_HDL */
{HID_INPUT_REPORT_3_CH_CCC_HDL, ATT_CLIENT_CFG_NOTIFY, DM_SEC_LEVEL_NONE}, /* HIDAPP_IN_CONSUMER_CCC_HDL */
{ESS_IAQ_CH_CCC_HDL, ATT_CLIENT_CFG_NOTIFY, DM_SEC_LEVEL_NONE}, /* BLE_ESS_IAQ_CCC_IDX */
{UART_TX_CH_CCC_HDL, ATT_CLIENT_CFG_NOTIFY, DM_SEC_LEVEL_NONE}, /* BLE_ESS_IAQ_CCC_IDX */
};
/**************************************************************************************************
......@@ -190,8 +150,114 @@ static const attsCccSet_t bleCccSet[BLE_NUM_CCC_IDX] =
/*! WSF handler ID */
wsfHandlerId_t bleHandlerId;
static dmConnId_t pair_connId = DM_CONN_ID_NONE;
static uint32_t pair_confirm_value;
static appDbHdl_t last_pairing = NULL;
static void BleHandler(wsfEventMask_t event, wsfMsgHdr_t *pMsg);
static const char * const att_events[] = {
"ATTC_FIND_INFO_RSP",
"ATTC_FIND_BY_TYPE_VALUE_RSP",
"ATTC_READ_BY_TYPE_RSP",
"ATTC_READ_RSP",
"ATTC_READ_LONG_RSP",
"ATTC_READ_MULTIPLE_RSP",
"ATTC_READ_BY_GROUP_TYPE_RSP",
"ATTC_WRITE_RSP",
"ATTC_WRITE_CMD_RSP",
"ATTC_PREPARE_WRITE_RSP",
"ATTC_EXECUTE_WRITE_RSP",
"ATTC_HANDLE_VALUE_NTF",
"ATTC_HANDLE_VALUE_IND",
/* ATT server callback events */
"ATTS_HANDLE_VALUE_CNF",
"ATTS_CCC_STATE_IND",
"ATTS_DB_HASH_CALC_CMPL_IND",
/* ATT common callback events */
"ATT_MTU_UPDATE_IND"
};
static const char * const dm_events[] = {
"DM_RESET_CMPL_IND",
"DM_ADV_START_IND",
"DM_ADV_STOP_IND",
"DM_ADV_NEW_ADDR_IND",
"DM_SCAN_START_IND",
"DM_SCAN_STOP_IND",
"DM_SCAN_REPORT_IND",
"DM_CONN_OPEN_IND",
"DM_CONN_CLOSE_IND",
"DM_CONN_UPDATE_IND",
"DM_SEC_PAIR_CMPL_IND",
"DM_SEC_PAIR_FAIL_IND",
"DM_SEC_ENCRYPT_IND",
"DM_SEC_ENCRYPT_FAIL_IND",
"DM_SEC_AUTH_REQ_IND",
"DM_SEC_KEY_IND",
"DM_SEC_LTK_REQ_IND",
"DM_SEC_PAIR_IND",
"DM_SEC_SLAVE_REQ_IND",
"DM_SEC_CALC_OOB_IND",
"DM_SEC_ECC_KEY_IND",
"DM_SEC_COMPARE_IND",
"DM_SEC_KEYPRESS_IND",
"DM_PRIV_RESOLVED_ADDR_IND",
"DM_PRIV_GENERATE_ADDR_IND",
"DM_CONN_READ_RSSI_IND",
"DM_PRIV_ADD_DEV_TO_RES_LIST_IND",
"DM_PRIV_REM_DEV_FROM_RES_LIST_IND",
"DM_PRIV_CLEAR_RES_LIST_IND",
"DM_PRIV_READ_PEER_RES_ADDR_IND",
"DM_PRIV_READ_LOCAL_RES_ADDR_IND",
"DM_PRIV_SET_ADDR_RES_ENABLE_IND",
"DM_REM_CONN_PARAM_REQ_IND",
"DM_CONN_DATA_LEN_CHANGE_IND",
"DM_CONN_WRITE_AUTH_TO_IND",
"DM_CONN_AUTH_TO_EXPIRED_IND",
"DM_PHY_READ_IND",
"DM_PHY_SET_DEF_IND",
"DM_PHY_UPDATE_IND",
"DM_ADV_SET_START_IND",
"DM_ADV_SET_STOP_IND",
"DM_SCAN_REQ_RCVD_IND",
"DM_EXT_SCAN_START_IND",
"DM_EXT_SCAN_STOP_IND",
"DM_EXT_SCAN_REPORT_IND",
"DM_PER_ADV_SET_START_IND",
"DM_PER_ADV_SET_STOP_IND",
"DM_PER_ADV_SYNC_EST_IND",
"DM_PER_ADV_SYNC_EST_FAIL_IND",
"DM_PER_ADV_SYNC_LOST_IND",
"DM_PER_ADV_SYNC_TRSF_EST_IND",
"DM_PER_ADV_SYNC_TRSF_EST_FAIL_IND",
"DM_PER_ADV_SYNC_TRSF_IND",
"DM_PER_ADV_SET_INFO_TRSF_IND",
"DM_PER_ADV_REPORT_IND",
"DM_REMOTE_FEATURES_IND",
"DM_READ_REMOTE_VER_INFO_IND",
"DM_CONN_IQ_REPORT_IND",
"DM_CTE_REQ_FAIL_IND",
"DM_CONN_CTE_RX_SAMPLE_START_IND",
"DM_CONN_CTE_RX_SAMPLE_STOP_IND",
"DM_CONN_CTE_TX_CFG_IND",
"DM_CONN_CTE_REQ_START_IND",
"DM_CONN_CTE_REQ_STOP_IND",
"DM_CONN_CTE_RSP_START_IND",
"DM_CONN_CTE_RSP_STOP_IND",
"DM_READ_ANTENNA_INFO_IND",
"DM_L2C_CMD_REJ_IND",
"DM_ERROR_IND",
"DM_HW_ERROR_IND",
"DM_VENDOR_SPEC_IND"
};
static const char * const l2c_coc_events[] = {
"L2C_COC_CONNECT_IND",
"L2C_COC_DISCONNECT_IND",
"L2C_COC_DATA_IND",
"L2C_COC_DATA_CNF"
};
/*************************************************************************************************/
/*!
* \brief Application DM callback.
......@@ -257,6 +323,7 @@ static void bleCccCback(attsCccEvt_t *pEvt)
{
/* store value in device database */
AppDbSetCccTblValue(dbHdl, pEvt->idx, pEvt->value);
AppDbNvmStoreCccTbl(dbHdl);
}
if ((pMsg = WsfMsgAlloc(sizeof(attsCccEvt_t))) != NULL)
......@@ -266,8 +333,6 @@ static void bleCccCback(attsCccEvt_t *pEvt)
}
}
/*************************************************************************************************/
/*!
* \brief Process CCC state change.
......@@ -282,18 +347,14 @@ static void bleProcCccState(bleMsg_t *pMsg)
APP_TRACE_INFO3("ccc state ind value:%d handle:%d idx:%d", pMsg->ccc.value, pMsg->ccc.handle, pMsg->ccc.idx);
/* handle battery level CCC */
if (pMsg->ccc.idx == BLE_BATT_LVL_CCC_IDX)
{
if (pMsg->ccc.value == ATT_CLIENT_CFG_NOTIFY)
{
BasMeasBattStart((dmConnId_t) pMsg->ccc.hdr.param, BLE_BATT_TIMER_IND, BLE_BATT_LVL_CCC_IDX);
}
else
{
BasMeasBattStop((dmConnId_t) pMsg->ccc.hdr.param);
}
return;
}
switch(pMsg->ccc.idx) {
case BLE_ESS_TEMP_CCC_IDX:
case BLE_ESS_HUMI_CCC_IDX:
case BLE_ESS_PRES_CCC_IDX:
case BLE_ESS_IAQ_CCC_IDX:
bleESS_ccc_update();
break;
};
}
/*************************************************************************************************/
......@@ -308,7 +369,8 @@ static void bleProcCccState(bleMsg_t *pMsg)
static void bleClose(bleMsg_t *pMsg)
{
/* stop battery measurement */
BasMeasBattStop((dmConnId_t) pMsg->hdr.param);
bleESS_ccc_update();
GapClearDeviceName();
}
/*************************************************************************************************/
......@@ -323,25 +385,143 @@ static void bleClose(bleMsg_t *pMsg)
/*************************************************************************************************/
static void bleSetup(bleMsg_t *pMsg)
{
/* set advertising and scan response data for discoverable mode */
AppAdvSetData(APP_ADV_DATA_DISCOVERABLE, sizeof(bleAdvDataDisc), (uint8_t *) bleAdvDataDisc);
AppAdvSetData(APP_SCAN_DATA_DISCOVERABLE, sizeof(bleScanDataDisc), (uint8_t *) bleScanDataDisc);
/* set advertising and scan response data for connectable mode */
AppAdvSetData(APP_ADV_DATA_CONNECTABLE, 0, NULL);
AppAdvSetData(APP_SCAN_DATA_CONNECTABLE, 0, NULL);
#if 0
/* TODO: card10: until we have an BLE dialog, be discoverable and bondable always */
/* start advertising; automatically set connectable/discoverable mode and bondable mode */
AppAdvStart(APP_MODE_AUTO_INIT);
#else
/* enter discoverable and bondable mode mode by default */
AppSetBondable(TRUE);
AppAdvStart(APP_MODE_DISCOVERABLE);
#endif
ble_adv_setup();
active = true;
/* TODO: Sadly, not advertising leads to a higher current consumption... */
epic_ble_set_mode(false, false);
}
void epic_ble_set_mode(bool bondable, bool scanner)
{
if(!active) {
return;
}
if(scanner && bondable) {
/* TODO: return error */
return;
}
if(scanner) {
ble_adv_stop();
dmConnId_t connId;
if ((connId = AppConnIsOpen()) != DM_CONN_ID_NONE) {
AppConnClose(connId);
}
/* Normal scanning filters out duplicates. We don't
* want that for now... */
//AppScanStart(scannerMasterCfg.discMode, scannerMasterCfg.scanType, scannerMasterCfg.scanDuration);
DmScanSetInterval(HCI_SCAN_PHY_LE_1M_BIT, &pAppMasterCfg->scanInterval,
&pAppMasterCfg->scanWindow);
DmScanStart(HCI_SCAN_PHY_LE_1M_BIT, scannerMasterCfg.discMode,
&scannerMasterCfg.scanType, FALSE, scannerMasterCfg.scanDuration, 0);
return;
} else {
AppScanStop();
}
if(bondable) {
AppSetBondable(TRUE);
ble_adv_discoverable(true);
} else {
AppSetBondable(FALSE);
ble_adv_discoverable(false);
}
}
uint32_t epic_ble_get_compare_value(void)
{
return pair_confirm_value;
}
int epic_ble_get_peer_device_name(char *buf, size_t buf_size)
{
if (AppConnIsOpen() != DM_CONN_ID_NONE) {
return GapGetDeviceName(buf, buf_size);
} else {
return -ENOENT;
}
}
int epic_ble_get_last_pairing_name(char *buf, size_t buf_size)
{
if(last_pairing == NULL) {
return -ENOENT;
}
return AppDbGetPairingName(last_pairing, buf, buf_size);
}
void epic_ble_compare_response(bool confirmed)
{
if(!active) {
return;
}
if(pair_connId != DM_CONN_ID_NONE) {
LOG_INFO("ble", "Value confirmed: %u", confirmed);
DmSecCompareRsp(pair_connId, confirmed);
} else {
/* error condition */
}
}
static void bleHandleNumericComparison(dmSecCnfIndEvt_t *pCnfInd)
{
if(!active) {
return;
}
pair_connId = (dmConnId_t)pCnfInd->hdr.param;
pair_confirm_value = DmSecGetCompareValue(pCnfInd->confirm);
LOG_INFO("ble", "Confirm Value: %ld", pair_confirm_value);
ble_epic_ble_api_trigger_event(BLE_EVENT_HANDLE_NUMERIC_COMPARISON, NULL);
}
int epic_ble_get_scan_report(struct epic_scan_report *rpt)
{
if(scan_reports_head == scan_reports_tail) {
return -ENOENT;
}
int new_tail = (scan_reports_tail + 1) % SCAN_REPORTS_NUM;
*rpt = scan_reports[new_tail];
scan_reports_tail = new_tail;
return 0;
}
static void scannerScanReport(dmEvt_t *pMsg)
{
struct epic_scan_report *scan_report;
int next_head = (scan_reports_head + 1) % SCAN_REPORTS_NUM;
if(next_head == scan_reports_tail) {
ble_epic_ble_api_trigger_event(BLE_EVENT_SCAN_REPORT, NULL);
return;
}
scan_reports_head = next_head;
scan_report = &scan_reports[scan_reports_head];
memset(scan_report->data, 0, sizeof(scan_report->data));
memccpy(scan_report->data, pMsg->scanReport.pData, pMsg->scanReport.len, sizeof(scan_report->data));
scan_report->len = pMsg->scanReport.len;
scan_report->rssi = pMsg->scanReport.rssi;
scan_report->eventType = pMsg->scanReport.eventType;
scan_report->addrType = pMsg->scanReport.addrType;
memcpy(scan_report->addr, pMsg->scanReport.addr, BDA_ADDR_LEN);
scan_report->directAddrType = pMsg->scanReport.directAddrType;
memcpy(scan_report->directAddr, pMsg->scanReport.directAddr, BDA_ADDR_LEN);
ble_epic_ble_api_trigger_event(BLE_EVENT_SCAN_REPORT, NULL);
if((scan_reports_head + 1) % SCAN_REPORTS_NUM == scan_reports_tail) {
LOG_WARN("ble", "Application missing scan results");
}
}
/*************************************************************************************************/
/*!
......@@ -354,16 +534,18 @@ static void bleSetup(bleMsg_t *pMsg)
/*************************************************************************************************/
static void bleProcMsg(bleMsg_t *pMsg)
{
uint8_t uiEvent = APP_UI_NONE;
hciLeConnCmplEvt_t *connOpen;
switch(pMsg->hdr.event)
{
case BLE_BATT_TIMER_IND:
BasProcMsg(&pMsg->hdr);
case ATTC_READ_RSP:
case ATTC_HANDLE_VALUE_IND:
bleValueUpdate((attEvt_t *) pMsg);
break;
case ATTS_HANDLE_VALUE_CNF:
BasProcMsg(&pMsg->hdr);
HidProcMsg(&pMsg->hdr);
UartProcMsg(pMsg);
break;
case ATTS_CCC_STATE_IND:
......@@ -373,41 +555,102 @@ static void bleProcMsg(bleMsg_t *pMsg)
case DM_RESET_CMPL_IND:
DmSecGenerateEccKeyReq();
bleSetup(pMsg);
uiEvent = APP_UI_RESET_CMPL;
break;
case DM_ADV_START_IND:
uiEvent = APP_UI_ADV_START;
ble_adv_proc_msg(pMsg);
break;
case DM_ADV_STOP_IND:
uiEvent = APP_UI_ADV_STOP;
ble_adv_proc_msg(pMsg);
break;
case DM_CONN_OPEN_IND:
BasProcMsg(&pMsg->hdr);
uiEvent = APP_UI_CONN_OPEN;
connOpen = &pMsg->dm.connOpen;
LOG_INFO("ble", "connection from %02X:%02X:%02X:%02X:%02X:%02X opened",
connOpen->peerAddr[5], connOpen->peerAddr[4],
connOpen->peerAddr[3], connOpen->peerAddr[2],
connOpen->peerAddr[1], connOpen->peerAddr[0]);
bleESS_ccc_update();
HidProcMsg(&pMsg->hdr);
UartProcMsg(pMsg);
break;
case DM_CONN_CLOSE_IND:
switch (pMsg->dm.connClose.reason)
{
case HCI_ERR_CONN_TIMEOUT:
LOG_INFO("ble", "Connection closed (0x%02X), Connection timeout",
pMsg->dm.connClose.reason);
break;
case HCI_ERR_LOCAL_TERMINATED:
LOG_INFO("ble", "Connection closed (0x%02X), Connection terminated by local host",
pMsg->dm.connClose.reason);
break;
case HCI_ERR_REMOTE_TERMINATED:
LOG_INFO("ble", "Connection closed (0x%02X), Remote user terminated connection",
pMsg->dm.connClose.reason);
break;
case HCI_ERR_CONN_FAIL:
LOG_INFO("ble", "Connection closed (0x%02X), Connection failed to be established",
pMsg->dm.connClose.reason);
break;
case HCI_ERR_MIC_FAILURE:
LOG_INFO("ble", "Connection closed (0x%02X), Connection terminated due to MIC failure",
pMsg->dm.connClose.reason);
break;
default:
LOG_INFO("ble", "Connection closed (0x%02X)",
pMsg->dm.connClose.reason);
break;
}
ble_adv_proc_msg(pMsg);
bleClose(pMsg);
uiEvent = APP_UI_CONN_CLOSE;
break;
case DM_SEC_PAIR_CMPL_IND:
uiEvent = APP_UI_SEC_PAIR_CMPL;
LOG_INFO("ble", "Secure pairing successful, auth: 0x%02X",
pMsg->dm.pairCmpl.auth);
DmSecGenerateEccKeyReq();
last_pairing = AppDbGetHdl((dmConnId_t) pMsg->hdr.param);
AppDbNvmStoreBond(last_pairing);
pair_connId = DM_CONN_ID_NONE;
ble_epic_ble_api_trigger_event(BLE_EVENT_PAIRING_COMPLETE, NULL);
/* After a successful pairing, bonding is disabled again.
* We don't want that for now. */
AppSetBondable(TRUE);
break;
case DM_SEC_PAIR_FAIL_IND:
uiEvent = APP_UI_SEC_PAIR_FAIL;
switch (pMsg->hdr.status) {
case SMP_ERR_TIMEOUT:
LOG_INFO("ble", "Secure pairing failed (0x%02X), Transaction timeout",
pMsg->hdr.status);
break;
case SMP_ERR_ATTEMPTS:
LOG_INFO("ble", "Secure pairing failed (0x%02X), Repeated attempts",
pMsg->hdr.status);
break;
default:
LOG_INFO("ble", "Secure pairing failed (0x%02X)",
pMsg->hdr.status);
break;
}
DmSecGenerateEccKeyReq();
pair_connId = DM_CONN_ID_NONE;
ble_epic_ble_api_trigger_event(BLE_EVENT_PAIRING_FAILED, NULL);
break;
case DM_SEC_ENCRYPT_IND:
uiEvent = APP_UI_SEC_ENCRYPT;
LOG_INFO("ble", "Encrypted handshake successful");
break;
case DM_SEC_ENCRYPT_FAIL_IND:
uiEvent = APP_UI_SEC_ENCRYPT_FAIL;
LOG_INFO("ble", "Encrypted handshake failed");
break;
case DM_SEC_AUTH_REQ_IND:
......@@ -419,21 +662,20 @@ static void bleProcMsg(bleMsg_t *pMsg)
break;
case DM_SEC_COMPARE_IND:
AppHandleNumericComparison(&pMsg->dm.cnfInd);
bleHandleNumericComparison(&pMsg->dm.cnfInd);
break;
case DM_SCAN_REPORT_IND:
scannerScanReport((dmEvt_t *)pMsg);
break;
case DM_HW_ERROR_IND:
uiEvent = APP_UI_HW_ERROR;
LOG_ERR("ble", "HW Error");
break;
default:
break;
}
if (uiEvent != APP_UI_NONE)
{
AppUiAction(uiEvent);
}
}
/*************************************************************************************************/
......@@ -453,20 +695,21 @@ static void BleHandlerInit(void)
bleHandlerId =WsfOsSetNextHandler(BleHandler);
/* Set configuration pointers */
pAppAdvCfg = (appAdvCfg_t *) &bleAdvCfg;
pAppSlaveCfg = (appSlaveCfg_t *) &bleSlaveCfg;
pAppSecCfg = (appSecCfg_t *) &bleSecCfg;
pAppUpdateCfg = (appUpdateCfg_t *) &bleUpdateCfg;
pAppDiscCfg = (appDiscCfg_t *) &bleDiscCfg;
pAppMasterCfg = (appMasterCfg_t *) &scannerMasterCfg;
ble_adv_init();
/* Initialize application framework */
AppSlaveInit();
AppDiscInit();
/* Set stack configuration pointers */
pSmpCfg = (smpCfg_t *) &bleSmpCfg;
pAttCfg = (attCfg_t *) &bleAttCfg;
/* initialize battery service server */
BasInit(bleHandlerId, (basCfg_t *) &bleBasCfg);
}
/*************************************************************************************************/
......@@ -487,11 +730,37 @@ static void BleHandler(wsfEventMask_t event, wsfMsgHdr_t *pMsg)
if (pMsg->event >= DM_CBACK_START && pMsg->event <= DM_CBACK_END)
{
if(pMsg->event != DM_SCAN_REPORT_IND) LOG_INFO("ble", "Ble got evt %d: %s", pMsg->event, dm_events[pMsg->event - DM_CBACK_START]);
/* process advertising and connection-related messages */
AppSlaveProcDmMsg((dmEvt_t *) pMsg);
/* process security-related messages */
AppSlaveSecProcDmMsg((dmEvt_t *) pMsg);
/* Needs to be before AppDiscProcDmMsg, to get DM_CONN_OPEN_IND first */
ble_epic_dm_api_event((dmEvt_t *)pMsg);
/* process discovery-related messages */
AppDiscProcDmMsg((dmEvt_t *) pMsg);
}
else if (pMsg->event >= ATT_CBACK_START && pMsg->event <= ATT_CBACK_END)
{
/* Don't spam the console with successful notfication/indications */
if (!(pMsg->event == ATTS_HANDLE_VALUE_CNF && pMsg->status == ATT_SUCCESS)) {
LOG_INFO("ble", "Ble got evt %d (%s): %d %d", pMsg->event, att_events[pMsg->event - ATT_CBACK_START], ((bleMsg_t *)pMsg)->att.handle, pMsg->status);
}
/* process discovery-related ATT messages */
AppDiscProcAttMsg((attEvt_t *) pMsg);
ble_epic_att_api_event((attEvt_t *)pMsg);
}
else if (pMsg->event >= L2C_COC_CBACK_START && pMsg->event <= L2C_COC_CBACK_CBACK_END)
{
LOG_INFO("ble", "Ble got evt %d: %s", pMsg->event, l2c_coc_events[pMsg->event - L2C_COC_CBACK_START]);
}
else
{
LOG_INFO("ble", "Ble got evt %d", pMsg->event);
}
/* perform profile and user interface-related operations */
......@@ -508,7 +777,6 @@ static void BleHandler(wsfEventMask_t event, wsfMsgHdr_t *pMsg)
/*************************************************************************************************/
void BleStart(void)
{
BleHandlerInit();
/* Register for stack callbacks */
......@@ -518,13 +786,24 @@ void BleStart(void)
AttConnRegister(AppServerConnCback);
AttsCccRegister(BLE_NUM_CCC_IDX, (attsCccSet_t *) bleCccSet, bleCccCback);
/* Register for app framework discovery callbacks */
AppDiscRegister(bleDiscCback);
/* Initialize attribute server database */
SvcCoreAddGroup();
SvcDisAddGroup(); // Device Information Service
SvcBattCbackRegister(BasReadCback, NULL);
SvcBattAddGroup();
if(config_get_boolean_with_default("ble_hid_enable", false)) {
hid_init();
}
ble_epic_ble_api_init();
/* Set Service Changed CCCD index. */
GattSetSvcChangedIdx(BLE_GATT_SC_CCC_IDX);
/* Reset the device */
DmDevReset();
}
/* clang-format on */
/*************************************************************************************************/
/*!
* \file
*
* \brief Application framework device database example, using simple RAM-based storage.
*
* 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/apps/app/common/app_db.c
*
* Reason: we need to implement persistent storage for bondings
*/
/* clang-format off */
/* clang-formet turned off for easier diffing against orginal file */
#include "wsf_types.h"
#include "wsf_assert.h"
#include "util/bda.h"
#include "app_api.h"
#include "app_main.h"
#include "app_db.h"
#include "app_cfg.h"
#include "epicardium.h"
#include "os/core.h"
#include "FreeRTOS.h"
#include "timers.h"
#include "mxc_sys.h"
#include "wdt.h"
#include <string.h>
#include <stdio.h>
/**************************************************************************************************
Macros
**************************************************************************************************/
/* App DB NVM record parameter indicies from upstream:
* https://github.com/packetcraft-inc/stacks/blob/master/ble-profiles/sources/af/common/app_db.c#L46
*/
#define APP_DB_NVM_IN_USE_ID 0
#define APP_DB_NVM_PEER_ADDR_ID 1
#define APP_DB_NVM_ADDR_TYPE_ID 2
#define APP_DB_NVM_PEER_IRK_ID 3
#define APP_DB_NVM_PEER_CSRK_ID 4
#define APP_DB_NVM_KV_MASK_ID 5
#define APP_DB_NVM_VALID_ID 6
#define APP_DB_NVM_PEER_RAPO_ID 7
#define APP_DB_NVM_LOCAL_LTK_ID 8
#define APP_DB_NVM_LOCAL_SEC_LVL_ID 9
#define APP_DB_NVM_PEER_ADDR_RES_ID 10
#define APP_DB_NVM_PEER_LTK_ID 11
#define APP_DB_NVM_PEER_SEC_LVL_ID 12
#define APP_DB_NVM_CCC_TBL_ID 13
#define APP_DB_NVM_PEER_SIGN_CTR_ID 14
#define APP_DB_NVM_CAS_ID 15
#define APP_DB_NVM_CSF_ID 16
#define APP_DB_NVM_CACHE_HASH_ID 17
#define APP_DB_NVM_HASH_ID 18
#define APP_DB_NVM_HDL_LIST_ID 19
#define APP_DB_NVM_DISC_STATUS_ID 20
#define APP_DB_NVM_SEQUENCE_NUMBER_ID 100
/**************************************************************************************************
Data Types
**************************************************************************************************/
/*! Database record */
typedef struct
{
/*! Common for all roles */
bdAddr_t peerAddr; /*! Peer address */
uint8_t addrType; /*! Peer address type */
dmSecIrk_t peerIrk; /*! Peer IRK */
dmSecCsrk_t peerCsrk; /*! Peer CSRK */
uint8_t keyValidMask; /*! Valid keys in this record */
bool_t inUse; /*! TRUE if record in use */
bool_t valid; /*! TRUE if record is valid */
bool_t peerAddedToRl; /*! TRUE if peer device's been added to resolving list */
bool_t peerRpao; /*! TRUE if RPA Only attribute's present on peer device */
/*! For slave local device */
dmSecLtk_t localLtk; /*! Local LTK */
uint8_t localLtkSecLevel; /*! Local LTK security level */
bool_t peerAddrRes; /*! TRUE if address resolution's supported on peer device (master) */
/*! For master local device */
dmSecLtk_t peerLtk; /*! Peer LTK */
uint8_t peerLtkSecLevel; /*! Peer LTK security level */
/*! for ATT server local device */
uint16_t cccTbl[APP_DB_NUM_CCCD]; /*! Client characteristic configuration descriptors */
uint32_t peerSignCounter; /*! Peer Sign Counter */
/*! for ATT client */
uint16_t hdlList[APP_DB_HDL_LIST_LEN]; /*! Cached handle list */
uint8_t discStatus; /*! Service discovery and configuration status */
uint32_t sequenceNumber;
} appDbRec_t;
/**************************************************************************************************
Local Variables
**************************************************************************************************/
/*! Database */
static appDbRec_t records[APP_DB_NUM_RECS];
/* clang-format on */
/* Translate a pointer to a record into the filename to be used for it. */
static int record_to_filename(appDbRec_t *record, char *buf, size_t buf_size)
{
int id = record - records;
int ret = snprintf(buf, buf_size, "pairings/pairing%d.bin", id + 1);
if (ret >= (int)buf_size) {
ret = -1;
}
return ret;
}
static appDbRec_t *record_with_highest_seq_number()
{
appDbRec_t *r = &records[0];
for (int i = 0; i < APP_DB_NUM_RECS; i++) {
if (records[i].sequenceNumber > r->sequenceNumber) {
r = &records[i];
}
}
return r;
}
static appDbRec_t *record_with_lowest_seq_number()
{
appDbRec_t *r = &records[0];
for (int i = 0; i < APP_DB_NUM_RECS; i++) {
if (records[i].sequenceNumber < r->sequenceNumber) {
r = &records[i];
}
}
return r;
}
/* Write a TLV to a file. */
static int write_tlv(int fd, uint32_t t, uint32_t l, void *v)
{
int ret;
ret = epic_file_write(fd, &t, sizeof(t));
if (ret != sizeof(t))
return ret;
ret = epic_file_write(fd, &l, sizeof(l));
if (ret != sizeof(l))
return ret;
ret = epic_file_write(fd, v, l);
if (ret != (int)l)
return ret;
return 0;
}
/* Read a TLV from a file.
*
* Super naive implementation assuming that the next TLV is
* the expected one. */
static int read_tlv(int fd, uint32_t t, uint32_t l, void *v)
{
int ret;
uint32_t t_r;
ret = epic_file_read(fd, &t_r, sizeof(t_r));
if (ret != sizeof(t))
return ret;
if (t != t_r)
return -ENOENT;
uint32_t l_r;
ret = epic_file_read(fd, &l_r, sizeof(l_r));
if (ret != sizeof(l_r))
return ret;
if (l_r > l)
return -EINVAL;
memset(v, 0, l);
ret = epic_file_read(fd, v, l_r);
if (ret != (int)l_r)
return ret;
return 0;
}
static int write_bond_to_file(appDbRec_t *r, char *filename)
{
if (!r->inUse) {
return -EINVAL;
}
int fd = epic_file_open(filename, "w");
int ret;
if (fd < 0) {
return fd;
}
static const uint8_t version = 1;
ret = epic_file_write(fd, &version, sizeof(version));
if (ret != sizeof(version))
goto out;
#define write_element(t, x) \
if ((ret = write_tlv(fd, t, sizeof(r->x), &r->x))) \
goto out;
write_element(APP_DB_NVM_PEER_ADDR_ID, peerAddr);
write_element(APP_DB_NVM_ADDR_TYPE_ID, addrType);
write_element(APP_DB_NVM_PEER_IRK_ID, peerIrk);
write_element(APP_DB_NVM_PEER_CSRK_ID, peerCsrk);
write_element(APP_DB_NVM_KV_MASK_ID, keyValidMask);
/* peerAddedToRl not persisted by upstream */
/* write_element(, peerAddedToRl); */
write_element(APP_DB_NVM_PEER_RAPO_ID, peerRpao);
write_element(APP_DB_NVM_LOCAL_LTK_ID, localLtk);
write_element(APP_DB_NVM_LOCAL_SEC_LVL_ID, localLtkSecLevel);
write_element(APP_DB_NVM_PEER_ADDR_RES_ID, peerAddrRes);
write_element(APP_DB_NVM_PEER_LTK_ID, peerLtk);
write_element(APP_DB_NVM_PEER_SEC_LVL_ID, peerLtkSecLevel);
write_element(APP_DB_NVM_CCC_TBL_ID, cccTbl);
write_element(APP_DB_NVM_PEER_SIGN_CTR_ID, peerSignCounter);
write_element(APP_DB_NVM_HDL_LIST_ID, hdlList);
write_element(APP_DB_NVM_DISC_STATUS_ID, discStatus);
write_element(APP_DB_NVM_SEQUENCE_NUMBER_ID, sequenceNumber);
write_element(APP_DB_NVM_VALID_ID, valid);
#undef write_element
out:
epic_file_close(fd);
return ret;
}
static int read_bond_from_file(appDbRec_t *r, char *filename)
{
int fd = epic_file_open(filename, "r");
if (fd < 0) {
return fd;
}
uint8_t version;
int ret = epic_file_read(fd, &version, sizeof(version));
if (ret != sizeof(version)) {
goto out;
}
if (version != 1) {
ret = -EINVAL;
goto out;
}
#define read_element(t, x) \
if ((ret = read_tlv(fd, t, sizeof(r->x), &r->x))) \
goto out;
read_element(APP_DB_NVM_PEER_ADDR_ID, peerAddr);
read_element(APP_DB_NVM_ADDR_TYPE_ID, addrType);
read_element(APP_DB_NVM_PEER_IRK_ID, peerIrk);
read_element(APP_DB_NVM_PEER_CSRK_ID, peerCsrk);
read_element(APP_DB_NVM_KV_MASK_ID, keyValidMask);
/* peerAddedToRl not persisted by upstream */
/* read_element(, peerAddedToRl); */
read_element(APP_DB_NVM_PEER_RAPO_ID, peerRpao);
read_element(APP_DB_NVM_LOCAL_LTK_ID, localLtk);
read_element(APP_DB_NVM_LOCAL_SEC_LVL_ID, localLtkSecLevel);
read_element(APP_DB_NVM_PEER_ADDR_RES_ID, peerAddrRes);
read_element(APP_DB_NVM_PEER_LTK_ID, peerLtk);
read_element(APP_DB_NVM_PEER_SEC_LVL_ID, peerLtkSecLevel);
read_element(APP_DB_NVM_CCC_TBL_ID, cccTbl);
read_element(APP_DB_NVM_PEER_SIGN_CTR_ID, peerSignCounter);
read_element(APP_DB_NVM_HDL_LIST_ID, hdlList);
read_element(APP_DB_NVM_DISC_STATUS_ID, discStatus);
read_element(APP_DB_NVM_SEQUENCE_NUMBER_ID, sequenceNumber);
read_element(APP_DB_NVM_VALID_ID, valid);
#undef read_element
r->inUse = true;
out:
epic_file_close(fd);
return ret;
}
static int delete_bond(char *filename)
{
return epic_file_unlink(filename);
}
/*************************************************************************************************/
/*!
* \brief Initialize the device database.
*
* \return None.
*/
/*************************************************************************************************/
void AppDbInit(void)
{
memset(&records, 0, sizeof(records));
char filename[32];
for (int i = 0; i < APP_DB_NUM_RECS; i++) {
record_to_filename(&records[i], filename, sizeof(filename));
int ret = read_bond_from_file(&records[i], filename);
if (ret < 0) {
if (ret != -ENOENT) {
LOG_WARN(
"bondings",
"Reading pairing '%s' failed: %d",
filename,
ret
);
}
memset(&records[i], 0, sizeof(records[i]));
}
}
}
/* clang-format off */
/*************************************************************************************************/
/*!
* \brief Create a new device database record.
*
* \param addrType Address type.
* \param pAddr Peer device address.
*
* \return Database record handle.
*/
/*************************************************************************************************/
appDbHdl_t AppDbNewRecord(uint8_t addrType, uint8_t *pAddr)
{
appDbRec_t *pRec = records;
uint8_t i;
/* find a free record */
for (i = APP_DB_NUM_RECS; i > 0; i--, pRec++)
{
if (!pRec->inUse)
{
break;
}
}
/* if all records were allocated */
if (i == 0)
{
/* overwrite the oldest record */
pRec = record_with_lowest_seq_number();
}
/* initialize record */
memset(pRec, 0, sizeof(appDbRec_t));
pRec->inUse = TRUE;
pRec->addrType = addrType;
BdaCpy(pRec->peerAddr, pAddr);
pRec->peerAddedToRl = FALSE;
pRec->peerRpao = FALSE;
pRec->sequenceNumber = record_with_highest_seq_number()->sequenceNumber + 1;
return (appDbHdl_t) pRec;
}
/*************************************************************************************************/
/*!
* \brief Get the next database record for a given record. For the first record, the function
* should be called with 'hdl' set to 'APP_DB_HDL_NONE'.
*
* \param hdl Database record handle.
*
* \return Next record handle found. APP_DB_HDL_NONE, otherwise.
*/
/*************************************************************************************************/
appDbHdl_t AppDbGetNextRecord(appDbHdl_t hdl)
{
appDbRec_t *pRec;
/* if first record is requested */
if (hdl == APP_DB_HDL_NONE)
{
pRec = records;
}
/* if valid record passed in */
else if (AppDbRecordInUse(hdl))
{
pRec = (appDbRec_t *)hdl;
pRec++;
}
/* invalid record passed in */
else
{
return APP_DB_HDL_NONE;
}
/* look for next valid record */
while (pRec < &records[APP_DB_NUM_RECS])
{
/* if record is in use */
if (pRec->inUse && pRec->valid)
{
/* record found */
return (appDbHdl_t)pRec;
}
/* look for next record */
pRec++;
}
/* end of records */
return APP_DB_HDL_NONE;
}
/*************************************************************************************************/
/*!
* \brief Delete a new device database record.
*
* \param hdl Database record handle.
*
* \return None.
*/
/*************************************************************************************************/
void AppDbDeleteRecord(appDbHdl_t hdl)
{
((appDbRec_t *) hdl)->inUse = FALSE;
char filename[32];
record_to_filename((appDbRec_t *) hdl, filename, sizeof(filename));
delete_bond(filename);
}
/*************************************************************************************************/
/*!
* \brief Validate a new device database record. This function is called when pairing is
* successful and the devices are bonded.
*
* \param hdl Database record handle.
* \param keyMask Bitmask of keys to validate.
*
* \return None.
*/
/*************************************************************************************************/
void AppDbValidateRecord(appDbHdl_t hdl, uint8_t keyMask)
{
((appDbRec_t *) hdl)->valid = TRUE;
((appDbRec_t *) hdl)->keyValidMask = keyMask;
}
/*************************************************************************************************/
/*!
* \brief Check if a record has been validated. If it has not, delete it. This function
* is typically called when the connection is closed.
*
* \param hdl Database record handle.
*
* \return None.
*/
/*************************************************************************************************/
void AppDbCheckValidRecord(appDbHdl_t hdl)
{
if (((appDbRec_t *) hdl)->valid == FALSE)
{
AppDbDeleteRecord(hdl);
}
}
/*************************************************************************************************/
/*!
* \brief Check if a database record is in use.
* \param hdl Database record handle.
*
* \return TURE if record in use. FALSE, otherwise.
*/
/*************************************************************************************************/
bool_t AppDbRecordInUse(appDbHdl_t hdl)
{
appDbRec_t *pRec = records;
uint8_t i;
/* see if record is in database record list */
for (i = APP_DB_NUM_RECS; i > 0; i--, pRec++)
{
if (pRec->inUse && pRec->valid && (pRec == ((appDbRec_t *)hdl)))
{
return TRUE;
}
}
return FALSE;
}
/*************************************************************************************************/
/*!
* \brief Check if there is a stored bond with any device.
*
* \param hdl Database record handle.
*
* \return TRUE if a bonded device is found, FALSE otherwise.
*/
/*************************************************************************************************/
bool_t AppDbCheckBonded(void)
{
appDbRec_t *pRec = records;
uint8_t i;
/* find a record */
for (i = APP_DB_NUM_RECS; i > 0; i--, pRec++)
{
if (pRec->inUse)
{
return TRUE;
}
}
return FALSE;
}
/*************************************************************************************************/
/*!
* \brief Delete all database records.
*
* \return None.
*/
/*************************************************************************************************/
void AppDbDeleteAllRecords(void)
{
appDbRec_t *pRec = records;
uint8_t i;
/* set in use to false for all records */
for (i = APP_DB_NUM_RECS; i > 0; i--, pRec++)
{
pRec->inUse = FALSE;
}
}
/*************************************************************************************************/
/*!
* \brief Find a device database record by peer address.
*
* \param addrType Address type.
* \param pAddr Peer device address.
*
* \return Database record handle or APP_DB_HDL_NONE if not found.
*/
/*************************************************************************************************/
appDbHdl_t AppDbFindByAddr(uint8_t addrType, uint8_t *pAddr)
{
appDbRec_t *pRec = records;
uint8_t peerAddrType = DmHostAddrType(addrType);
uint8_t i;
/* find matching record */
for (i = APP_DB_NUM_RECS; i > 0; i--, pRec++)
{
if (pRec->inUse && (pRec->addrType == peerAddrType) && BdaCmp(pRec->peerAddr, pAddr))
{
return (appDbHdl_t) pRec;
}
}
return APP_DB_HDL_NONE;
}
/*************************************************************************************************/
/*!
* \brief Find a device database record by data in an LTK request.
*
* \param encDiversifier Encryption diversifier associated with key.
* \param pRandNum Pointer to random number associated with key.
*
* \return Database record handle or APP_DB_HDL_NONE if not found.
*/
/*************************************************************************************************/
appDbHdl_t AppDbFindByLtkReq(uint16_t encDiversifier, uint8_t *pRandNum)
{
appDbRec_t *pRec = records;
uint8_t i;
/* find matching record */
for (i = APP_DB_NUM_RECS; i > 0; i--, pRec++)
{
if (pRec->inUse && (pRec->localLtk.ediv == encDiversifier) &&
(memcmp(pRec->localLtk.rand, pRandNum, SMP_RAND8_LEN) == 0))
{
return (appDbHdl_t) pRec;
}
}
return APP_DB_HDL_NONE;
}
/*************************************************************************************************/
/*!
* \brief Get a key from a device database record.
*
* \param hdl Database record handle.
* \param type Type of key to get.
* \param pSecLevel If the key is valid, the security level of the key.
*
* \return Pointer to key if key is valid or NULL if not valid.
*/
/*************************************************************************************************/
dmSecKey_t *AppDbGetKey(appDbHdl_t hdl, uint8_t type, uint8_t *pSecLevel)
{
dmSecKey_t *pKey = NULL;
/* if key valid */
if ((type & ((appDbRec_t *) hdl)->keyValidMask) != 0)
{
switch(type)
{
case DM_KEY_LOCAL_LTK:
*pSecLevel = ((appDbRec_t *) hdl)->localLtkSecLevel;
pKey = (dmSecKey_t *) &((appDbRec_t *) hdl)->localLtk;
break;
case DM_KEY_PEER_LTK:
*pSecLevel = ((appDbRec_t *) hdl)->peerLtkSecLevel;
pKey = (dmSecKey_t *) &((appDbRec_t *) hdl)->peerLtk;
break;
case DM_KEY_IRK:
pKey = (dmSecKey_t *)&((appDbRec_t *)hdl)->peerIrk;
break;
case DM_KEY_CSRK:
pKey = (dmSecKey_t *)&((appDbRec_t *)hdl)->peerCsrk;
break;
default:
break;
}
}
return pKey;
}
/*************************************************************************************************/
/*!
* \brief Set a key in a device database record.
*
* \param hdl Database record handle.
* \param pKey Key data.
*
* \return None.
*/
/*************************************************************************************************/
void AppDbSetKey(appDbHdl_t hdl, dmSecKeyIndEvt_t *pKey)
{
switch(pKey->type)
{
case DM_KEY_LOCAL_LTK:
((appDbRec_t *) hdl)->localLtkSecLevel = pKey->secLevel;
((appDbRec_t *) hdl)->localLtk = pKey->keyData.ltk;
break;
case DM_KEY_PEER_LTK:
((appDbRec_t *) hdl)->peerLtkSecLevel = pKey->secLevel;
((appDbRec_t *) hdl)->peerLtk = pKey->keyData.ltk;
break;
case DM_KEY_IRK:
((appDbRec_t *)hdl)->peerIrk = pKey->keyData.irk;
/* make sure peer record is stored using its identity address */
((appDbRec_t *)hdl)->addrType = pKey->keyData.irk.addrType;
BdaCpy(((appDbRec_t *)hdl)->peerAddr, pKey->keyData.irk.bdAddr);
break;
case DM_KEY_CSRK:
((appDbRec_t *)hdl)->peerCsrk = pKey->keyData.csrk;
/* sign counter must be initialized to zero when CSRK is generated */
((appDbRec_t *)hdl)->peerSignCounter = 0;
break;
default:
break;
}
}
/*************************************************************************************************/
/*!
* \brief Get the client characteristic configuration descriptor table.
*
* \param hdl Database record handle.
*
* \return Pointer to client characteristic configuration descriptor table.
*/
/*************************************************************************************************/
uint16_t *AppDbGetCccTbl(appDbHdl_t hdl)
{
return ((appDbRec_t *) hdl)->cccTbl;
}
/*************************************************************************************************/
/*!
* \brief Set a value in the client characteristic configuration table.
*
* \param hdl Database record handle.
* \param idx Table index.
* \param value client characteristic configuration value.
*
* \return None.
*/
/*************************************************************************************************/
void AppDbSetCccTblValue(appDbHdl_t hdl, uint16_t idx, uint16_t value)
{
WSF_ASSERT(idx < APP_DB_NUM_CCCD);
((appDbRec_t *) hdl)->cccTbl[idx] = value;
}
/*************************************************************************************************/
/*!
* \brief Get the discovery status.
*
* \param hdl Database record handle.
*
* \return Discovery status.
*/
/*************************************************************************************************/
uint8_t AppDbGetDiscStatus(appDbHdl_t hdl)
{
return ((appDbRec_t *) hdl)->discStatus;
}
/*************************************************************************************************/
/*!
* \brief Set the discovery status.
*
* \param hdl Database record handle.
* \param state Discovery status.
*
* \return None.
*/
/*************************************************************************************************/
void AppDbSetDiscStatus(appDbHdl_t hdl, uint8_t status)
{
((appDbRec_t *) hdl)->discStatus = status;
}
/*************************************************************************************************/
/*!
* \brief Get the cached handle list.
*
* \param hdl Database record handle.
*
* \return Pointer to handle list.
*/
/*************************************************************************************************/
uint16_t *AppDbGetHdlList(appDbHdl_t hdl)
{
return ((appDbRec_t *) hdl)->hdlList;
}
/*************************************************************************************************/
/*!
* \brief Set the cached handle list.
*
* \param hdl Database record handle.
* \param pHdlList Pointer to handle list.
*
* \return None.
*/
/*************************************************************************************************/
void AppDbSetHdlList(appDbHdl_t hdl, uint16_t *pHdlList)
{
memcpy(((appDbRec_t *) hdl)->hdlList, pHdlList, sizeof(((appDbRec_t *) hdl)->hdlList));
}
/*************************************************************************************************/
/*!
* \brief Get address resolution attribute value read from a peer device.
*
* \param hdl Database record handle.
*
* \return TRUE if address resolution is supported in peer device. FALSE, otherwise.
*/
/*************************************************************************************************/
bool_t AppDbGetPeerAddrRes(appDbHdl_t hdl)
{
return ((appDbRec_t *)hdl)->peerAddrRes;
}
/*************************************************************************************************/
/*!
* \brief Set address resolution attribute value for a peer device.
*
* \param hdl Database record handle.
* \param addrRes Address resolution attribue value.
*
* \return None.
*/
/*************************************************************************************************/
void AppDbSetPeerAddrRes(appDbHdl_t hdl, uint8_t addrRes)
{
((appDbRec_t *)hdl)->peerAddrRes = addrRes;
}
/*************************************************************************************************/
/*!
* \brief Get sign counter for a peer device.
*
* \param hdl Database record handle.
*
* \return Sign counter for peer device.
*/
/*************************************************************************************************/
uint32_t AppDbGetPeerSignCounter(appDbHdl_t hdl)
{
return ((appDbRec_t *)hdl)->peerSignCounter;
}
/*************************************************************************************************/
/*!
* \brief Set sign counter for a peer device.
*
* \param hdl Database record handle.
* \param signCounter Sign counter for peer device.
*
* \return None.
*/
/*************************************************************************************************/
void AppDbSetPeerSignCounter(appDbHdl_t hdl, uint32_t signCounter)
{
if(((appDbRec_t *)hdl)->peerSignCounter != signCounter) {
((appDbRec_t *)hdl)->peerSignCounter = signCounter;
}
}
/*************************************************************************************************/
/*!
* \brief Get the peer device added to resolving list flag value.
*
* \param hdl Database record handle.
*
* \return TRUE if peer device's been added to resolving list. FALSE, otherwise.
*/
/*************************************************************************************************/
bool_t AppDbGetPeerAddedToRl(appDbHdl_t hdl)
{
return ((appDbRec_t *)hdl)->peerAddedToRl;
}
/*************************************************************************************************/
/*!
* \brief Set the peer device added to resolving list flag to a given value.
*
* \param hdl Database record handle.
* \param peerAddedToRl Peer device added to resolving list flag value.
*
* \return None.
*/
/*************************************************************************************************/
void AppDbSetPeerAddedToRl(appDbHdl_t hdl, bool_t peerAddedToRl)
{
((appDbRec_t *)hdl)->peerAddedToRl = peerAddedToRl;
}
/*************************************************************************************************/
/*!
* \brief Get the resolvable private address only attribute flag for a given peer device.
*
* \param hdl Database record handle.
*
* \return TRUE if RPA Only attribute is present on peer device. FALSE, otherwise.
*/
/*************************************************************************************************/
bool_t AppDbGetPeerRpao(appDbHdl_t hdl)
{
return ((appDbRec_t *)hdl)->peerRpao;
}
/*************************************************************************************************/
/*!
* \brief Set the resolvable private address only attribute flag for a given peer device.
*
* \param hdl Database record handle.
* \param peerRpao Resolvable private address only attribute flag value.
*
* \return None.
*/
/*************************************************************************************************/
void AppDbSetPeerRpao(appDbHdl_t hdl, bool_t peerRpao)
{
((appDbRec_t *)hdl)->peerRpao = peerRpao;
}
/*************************************************************************************************/
/*!
* \brief Store the client characteristic configuration table for a device record in NVM.
*
* \param hdl Database record handle.
*
* \return None.
*/
/*************************************************************************************************/
void AppDbNvmStoreCccTbl(appDbHdl_t hdl)
{
/* We take a short cut and simply write the whole file again. */
AppDbNvmStoreBond(hdl);
}
/*************************************************************************************************/
/*!
* \brief Store bonding information for device record in NVM.
*
* \param hdl Database record handle.
*
* \return None.
*/
/*************************************************************************************************/
void AppDbNvmStoreBond(appDbHdl_t hdl)
{
appDbRec_t *pRec = (appDbRec_t *) hdl;
if (pRec->inUse && pRec->valid) {
char filename[32];
record_to_filename(pRec, filename, sizeof(filename));
/* Directory might exist already. Call will fail silently in that case. */
epic_file_mkdir("pairings");
int ret = write_bond_to_file(pRec, filename);
if(ret < 0) {
LOG_WARN(
"bondings",
"Writing pairing '%s' failed: %d",
filename,
ret
);
}
}
}
/* clang-format on */
int AppDbGetPairingName(appDbHdl_t hdl, char *buf, size_t buf_size)
{
appDbRec_t *pRec = (appDbRec_t *)hdl;
return record_to_filename(pRec, buf, buf_size);
}
#include "wsf_types.h"
#include "wsf_os.h"
#include "wsf_buf.h"
#include "wsf_timer.h"
#include "wsf_trace.h"
#include "app_ui.h"
#include "fit/fit_api.h"
#include "hci_vs.h"
#include "ble_api.h"
#include "ff.h"
#include "epicardium.h"
#include "wsf_types.h"
#include "util/bstream.h"
#include "wsf_assert.h"
#include "att_api.h"
#include "FreeRTOS.h"
#include "crc32.h"
#include "epicardium.h"
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
#include <machine/endian.h>
#define CARD10_UUID_SUFFIX \
0x42, 0x23, 0x42, 0x23, 0x42, 0x23, 0x42, 0x23, 0x42, 0x23, 0x42, 0x23
#define CARD10_UUID_PREFIX 0x02, 0x23, 0x42
/*!< \brief Service start handle. */
#define CARD10_START_HDL 0x920
/*!< \brief Service end handle. */
......@@ -39,8 +25,8 @@ enum {
/*!< \brief card10 service declaration */
CARD10_SVC_HDL = CARD10_START_HDL,
/*!< \brief time update characteristic */
CARD10_TIME_UPDATE_CH_HDL,
CARD10_TIME_UPDATE_VAL_HDL,
CARD10_TIME_CH_HDL,
CARD10_TIME_VAL_HDL,
/*!< \brief vibra characteristic */
CARD10_VIRBA_CH_HDL,
CARD10_VIBRA_VAL_HDL,
......@@ -71,6 +57,9 @@ enum {
/*!< \brief flashlight characteristic */
CARD10_FLASHLIGHT_CH_HDL,
CARD10_FLASHLIGHT_VAL_HDL,
/*!< \brief flashlight characteristic */
CARD10_PERSONAL_STATE_CH_HDL,
CARD10_PERSONAL_STATE_VAL_HDL,
/*!< \brief leds above characteristic */
CARD10_LEDS_ABOVE_CH_HDL,
CARD10_LEDS_ABOVE_VAL_HDL,
......@@ -85,24 +74,31 @@ enum {
/* BLE UUID for card10 service*/
static const uint8_t UUID_svc[] = { CARD10_UUID_SUFFIX, 0x0, CARD10_UUID_PREFIX };
// works vor everyone?
static const uint16_t UUID_len = sizeof(UUID_svc);
// starting at 0x01 with write (non visual) charateristics
/* BLE UUID for card10 time update */
/* BLE UUID for card10 time */
static const uint8_t UUID_char_time[] = {
ATT_PROP_WRITE,
UINT16_TO_BYTES(CARD10_TIME_UPDATE_VAL_HDL),
(ATT_PROP_READ | ATT_PROP_WRITE_NO_RSP),
UINT16_TO_BYTES(CARD10_TIME_VAL_HDL),
CARD10_UUID_SUFFIX, 0x01, CARD10_UUID_PREFIX
};
static uint8_t timeValue[] = { UINT32_TO_BYTES(0), UINT32_TO_BYTES(0) };
static uint16_t timeLen = sizeof(timeValue);
// works vor everyone?
static const uint16_t UUID_char_len = sizeof(UUID_char_time);
static const uint8_t UUID_attChar_time[] = {
CARD10_UUID_SUFFIX, 0x01, CARD10_UUID_PREFIX
};
/* BLE UUID for card10 char vibra */
static const uint8_t UUID_char_vibra[] = {
ATT_PROP_WRITE,
ATT_PROP_WRITE_NO_RSP,
UINT16_TO_BYTES(CARD10_VIBRA_VAL_HDL),
CARD10_UUID_SUFFIX, 0x0f, CARD10_UUID_PREFIX
};
......@@ -116,7 +112,7 @@ static const uint8_t UUID_attChar_vibra[] = {
/* BLE UUID for card10 char rockets */
static const uint8_t UUID_char_rockets[] = {
ATT_PROP_WRITE,
ATT_PROP_READ | ATT_PROP_WRITE_NO_RSP,
UINT16_TO_BYTES(CARD10_ROCKETS_VAL_HDL),
CARD10_UUID_SUFFIX, 0x10, CARD10_UUID_PREFIX
};
......@@ -125,9 +121,12 @@ static const uint8_t UUID_attChar_rockets[] = {
CARD10_UUID_SUFFIX, 0x10, CARD10_UUID_PREFIX
};
static uint8_t rocketsValue[] = { 0, 0, 0 };
static uint16_t rocketsLen = sizeof(rocketsValue);
/* BLE UUID for card10 led background bottom left */
static const uint8_t UUID_char_led_bg_bottom_left[] = {
ATT_PROP_WRITE,
ATT_PROP_READ | ATT_PROP_WRITE_NO_RSP,
UINT16_TO_BYTES(CARD10_LED_BG_BOTTOM_LEFT_VAL_HDL),
CARD10_UUID_SUFFIX, 0x11, CARD10_UUID_PREFIX
};
......@@ -136,9 +135,13 @@ static const uint8_t UUID_attChar_led_bg_bottom_left[] = {
CARD10_UUID_SUFFIX, 0x11, CARD10_UUID_PREFIX
};
static uint8_t ledBGBottomLeftValue[] = { 0,0,0 };
// works vor everyone?
static uint16_t rgbLen = sizeof(ledBGBottomLeftValue);
/* BLE UUID for card10 led background bottom right */
static const uint8_t UUID_char_led_bg_bottom_right[] = {
ATT_PROP_WRITE,
ATT_PROP_READ | ATT_PROP_WRITE_NO_RSP,
UINT16_TO_BYTES(CARD10_LED_BG_BOTTOM_RIGHT_VAL_HDL),
CARD10_UUID_SUFFIX, 0x12, CARD10_UUID_PREFIX
};
......@@ -147,9 +150,11 @@ static const uint8_t UUID_attChar_led_bg_bottom_right[] = {
CARD10_UUID_SUFFIX, 0x12, CARD10_UUID_PREFIX
};
static uint8_t ledBGBottomRightValue[] = { 0,0,0 };
/* BLE UUID for card10 led background top right */
static const uint8_t UUID_char_led_bg_top_right[] = {
ATT_PROP_WRITE,
ATT_PROP_READ | ATT_PROP_WRITE_NO_RSP,
UINT16_TO_BYTES(CARD10_LED_BG_TOP_RIGHT_VAL_HDL),
CARD10_UUID_SUFFIX, 0x13, CARD10_UUID_PREFIX
};
......@@ -158,9 +163,11 @@ static const uint8_t UUID_attChar_led_bg_top_right[] = {
CARD10_UUID_SUFFIX, 0x13, CARD10_UUID_PREFIX
};
static uint8_t ledBGTopRightValue[] = { 0,0,0 };
/* BLE UUID for card10 led background top left */
static const uint8_t UUID_char_led_bg_top_left[] = {
ATT_PROP_WRITE,
ATT_PROP_READ | ATT_PROP_WRITE_NO_RSP,
UINT16_TO_BYTES(CARD10_LED_BG_TOP_LEFT_VAL_HDL),
CARD10_UUID_SUFFIX, 0x14, CARD10_UUID_PREFIX
};
......@@ -169,6 +176,8 @@ static const uint8_t UUID_attChar_led_bg_top_left[] = {
CARD10_UUID_SUFFIX, 0x14, CARD10_UUID_PREFIX
};
static uint8_t ledBGTopLeftValue[] = { 0,0,0 };
/* BLE UUID for card10 dim leds on bottom */
static const uint8_t UUID_char_leds_bottom_dim[] = {
ATT_PROP_WRITE,
......@@ -193,7 +202,7 @@ static const uint8_t UUID_attChar_leds_top_dim[] = {
/* BLE UUID for card10 powersafe */
static const uint8_t UUID_char_led_powersafe[] = {
ATT_PROP_WRITE,
ATT_PROP_WRITE_NO_RSP,
UINT16_TO_BYTES(CARD10_LED_POWERSAFE_VAL_HDL),
CARD10_UUID_SUFFIX, 0x17, CARD10_UUID_PREFIX
};
......@@ -204,7 +213,7 @@ static const uint8_t UUID_attChar_led_powersafe[] = {
/* BLE UUID for card10 flashlight */
static const uint8_t UUID_char_flashlight[] = {
ATT_PROP_WRITE,
ATT_PROP_WRITE_NO_RSP,
UINT16_TO_BYTES(CARD10_FLASHLIGHT_VAL_HDL),
CARD10_UUID_SUFFIX, 0x18, CARD10_UUID_PREFIX
};
......@@ -213,9 +222,23 @@ static const uint8_t UUID_attChar_flashlight[] = {
CARD10_UUID_SUFFIX, 0x18, CARD10_UUID_PREFIX
};
/* BLE UUID for card10 personal state */
static const uint8_t UUID_char_personal_state[] = {
ATT_PROP_READ | ATT_PROP_WRITE,
UINT16_TO_BYTES(CARD10_PERSONAL_STATE_VAL_HDL),
CARD10_UUID_SUFFIX, 0x19, CARD10_UUID_PREFIX
};
static const uint8_t UUID_attChar_personal_state[] = {
CARD10_UUID_SUFFIX, 0x19, CARD10_UUID_PREFIX
};
static uint8_t personalStateValue = 0;
static uint16_t personalStateLen = sizeof(personalStateValue);
/* BLE UUID for card10 above leds */
static const uint8_t UUID_char_leds_above[] = {
ATT_PROP_WRITE,
ATT_PROP_READ | ATT_PROP_WRITE_NO_RSP,
UINT16_TO_BYTES(CARD10_LEDS_ABOVE_VAL_HDL),
CARD10_UUID_SUFFIX, 0x20, CARD10_UUID_PREFIX
};
......@@ -223,6 +246,21 @@ static const uint8_t UUID_char_leds_above[] = {
static const uint8_t UUID_attChar_leds_above[] = {
CARD10_UUID_SUFFIX, 0x20, CARD10_UUID_PREFIX
};
static uint8_t aboveLEDsValue[] = {
0,0,0, // 0
0,0,0, // 1
0,0,0, // 2
0,0,0, // 3
0,0,0, // 4
0,0,0, // 5
0,0,0, // 6
0,0,0, // 7
0,0,0, // 8
0,0,0, // 9
0,0,0, // 10
};
static uint16_t aboveLEDsLen = sizeof(aboveLEDsValue);
// starting at 0xf0 with read only characteristics
/* BLE UUID for card10 char light sensor */
......@@ -234,308 +272,299 @@ static const uint8_t UUID_char_light_sensor[] = {
static const uint8_t UUID_attChar_light_sensor[] = {
CARD10_UUID_SUFFIX, 0xf0, CARD10_UUID_PREFIX
};
static uint8_t initLightSensorValue[] = { UINT16_TO_BYTES(0) };
static uint16_t initLightSensorLen = sizeof(initLightSensorValue);
/* clang-format on */
/*
* Create the BLE service description.
* Create the BLE service description.
*/
static void *addCard10GroupDyn(void)
{
void *pSHdl;
uint8_t initLightSensorValue[] = { UINT16_TO_BYTES(0) };
/* Create the service */
pSHdl = AttsDynCreateGroup(CARD10_START_HDL, CARD10_END_HDL);
if (pSHdl != NULL) {
/* Primary service */
AttsDynAddAttrConst(
pSHdl,
attPrimSvcUuid,
UUID_svc,
sizeof(UUID_svc),
0,
ATTS_PERMIT_READ
);
// TIME UPDTAE
AttsDynAddAttrConst(
pSHdl,
attChUuid,
UUID_char_time,
sizeof(UUID_char_time),
0,
ATTS_PERMIT_READ
);
AttsDynAddAttr(
pSHdl,
UUID_attChar_time,
NULL,
0,
sizeof(uint64_t),
ATTS_SET_WRITE_CBACK,
ATTS_PERMIT_WRITE | ATTS_PERMIT_WRITE_ENC |
ATTS_PERMIT_WRITE_AUTH);
// VIBRA
AttsDynAddAttrConst(
pSHdl,
attChUuid,
UUID_char_vibra,
sizeof(UUID_char_vibra),
0,
ATTS_PERMIT_READ
);
AttsDynAddAttr(
pSHdl,
UUID_attChar_vibra,
NULL,
0,
sizeof(uint16_t),
ATTS_SET_WRITE_CBACK,
ATTS_PERMIT_WRITE | ATTS_PERMIT_WRITE_ENC |
ATTS_PERMIT_WRITE_AUTH);
// ROCKETS
AttsDynAddAttrConst(
pSHdl,
attChUuid,
UUID_char_rockets,
sizeof(UUID_char_rockets),
0,
ATTS_PERMIT_READ
);
AttsDynAddAttr(
pSHdl,
UUID_attChar_rockets,
NULL,
0,
3 * sizeof(uint8_t),
ATTS_SET_WRITE_CBACK,
ATTS_PERMIT_WRITE | ATTS_PERMIT_WRITE_ENC |
ATTS_PERMIT_WRITE_AUTH);
// BG LED Bottom left
AttsDynAddAttrConst(
pSHdl,
attChUuid,
UUID_char_led_bg_bottom_left,
sizeof(UUID_char_led_bg_bottom_left),
0,
ATTS_PERMIT_READ
);
AttsDynAddAttr(
pSHdl,
UUID_attChar_led_bg_bottom_left,
NULL,
0,
3 * sizeof(uint8_t),
ATTS_SET_WRITE_CBACK,
ATTS_PERMIT_WRITE | ATTS_PERMIT_WRITE_ENC |
ATTS_PERMIT_WRITE_AUTH);
// BG LED Bottom right
AttsDynAddAttrConst(
pSHdl,
attChUuid,
UUID_char_led_bg_bottom_right,
sizeof(UUID_char_led_bg_bottom_right),
0,
ATTS_PERMIT_READ
);
AttsDynAddAttr(
pSHdl,
UUID_attChar_led_bg_bottom_right,
NULL,
0,
3 * sizeof(uint8_t),
ATTS_SET_WRITE_CBACK,
ATTS_PERMIT_WRITE | ATTS_PERMIT_WRITE_ENC |
ATTS_PERMIT_WRITE_AUTH);
// BG LED top right
AttsDynAddAttrConst(
pSHdl,
attChUuid,
UUID_char_led_bg_top_right,
sizeof(UUID_char_led_bg_top_right),
0,
ATTS_PERMIT_READ
);
AttsDynAddAttr(
pSHdl,
UUID_attChar_led_bg_top_right,
NULL,
0,
3 * sizeof(uint8_t),
ATTS_SET_WRITE_CBACK,
ATTS_PERMIT_WRITE | ATTS_PERMIT_WRITE_ENC |
ATTS_PERMIT_WRITE_AUTH);
// BG LED top left
AttsDynAddAttrConst(
pSHdl,
attChUuid,
UUID_char_led_bg_top_left,
sizeof(UUID_char_led_bg_top_left),
0,
ATTS_PERMIT_READ
);
AttsDynAddAttr(
pSHdl,
UUID_attChar_led_bg_top_left,
NULL,
0,
3 * sizeof(uint8_t),
ATTS_SET_WRITE_CBACK,
ATTS_PERMIT_WRITE | ATTS_PERMIT_WRITE_ENC |
ATTS_PERMIT_WRITE_AUTH);
// Dim bottom module
AttsDynAddAttrConst(
pSHdl,
attChUuid,
UUID_char_leds_bottom_dim,
sizeof(UUID_char_leds_bottom_dim),
0,
ATTS_PERMIT_READ
);
AttsDynAddAttr(
pSHdl,
UUID_attChar_leds_bottom_dim,
NULL,
0,
sizeof(uint8_t),
ATTS_SET_WRITE_CBACK,
ATTS_PERMIT_WRITE | ATTS_PERMIT_WRITE_ENC |
ATTS_PERMIT_WRITE_AUTH);
// Dim top module
AttsDynAddAttrConst(
pSHdl,
attChUuid,
UUID_char_leds_top_dim,
sizeof(UUID_char_leds_top_dim),
0,
ATTS_PERMIT_READ
);
static const attsAttr_t card10SvcAttrList[] = {
{
.pUuid = attPrimSvcUuid,
.pValue = (uint8_t *)UUID_svc,
.pLen = (uint16_t *)&UUID_len,
.maxLen = sizeof(UUID_svc),
.permissions = ATTS_PERMIT_READ,
},
// TIME
{
.pUuid = attChUuid,
.pValue = (uint8_t *)UUID_char_time,
.pLen = (uint16_t *)&UUID_char_len,
.maxLen = sizeof(UUID_char_time),
.permissions = ATTS_PERMIT_READ,
},
{
.pUuid = UUID_attChar_time,
.pValue = timeValue,
.pLen = &timeLen,
.maxLen = sizeof(uint64_t),
.settings = ATTS_SET_WRITE_CBACK | ATTS_SET_READ_CBACK,
.permissions = ATTS_PERMIT_WRITE | ATTS_PERMIT_WRITE_ENC |
ATTS_PERMIT_WRITE_AUTH | ATTS_PERMIT_READ |
ATTS_PERMIT_READ_ENC | ATTS_PERMIT_READ_AUTH,
},
// VIBRA
{
.pUuid = attChUuid,
.pValue = (uint8_t *)UUID_char_vibra,
.pLen = (uint16_t *)&UUID_char_len,
.maxLen = sizeof(UUID_char_vibra),
.permissions = ATTS_PERMIT_READ,
},
{
.pUuid = UUID_attChar_vibra,
.pValue = NULL,
.maxLen = sizeof(uint16_t),
.settings = ATTS_SET_WRITE_CBACK,
.permissions = ATTS_PERMIT_WRITE | ATTS_PERMIT_WRITE_ENC |
ATTS_PERMIT_WRITE_AUTH,
},
// ROCKETS
{
.pUuid = attChUuid,
.pValue = (uint8_t *)UUID_char_rockets,
.pLen = (uint16_t *)&UUID_char_len,
.maxLen = sizeof(UUID_char_rockets),
.permissions = ATTS_PERMIT_READ,
},
{
.pUuid = UUID_attChar_rockets,
.pValue = rocketsValue,
.pLen = &rocketsLen,
.maxLen = 3 * sizeof(uint8_t),
.settings = ATTS_SET_WRITE_CBACK | ATTS_SET_READ_CBACK,
.permissions = ATTS_PERMIT_WRITE | ATTS_PERMIT_WRITE_ENC |
ATTS_PERMIT_WRITE_AUTH | ATTS_PERMIT_READ |
ATTS_PERMIT_READ_ENC | ATTS_PERMIT_READ_AUTH,
},
// BG LED Bottom left
{
.pUuid = attChUuid,
.pValue = (uint8_t *)UUID_char_led_bg_bottom_left,
.pLen = (uint16_t *)&UUID_char_len,
.maxLen = sizeof(UUID_char_led_bg_bottom_left),
.permissions = ATTS_PERMIT_READ,
},
{
.pUuid = UUID_attChar_led_bg_bottom_left,
.pValue = ledBGBottomLeftValue,
.pLen = &rgbLen,
.maxLen = 3 * sizeof(uint8_t),
.settings = ATTS_SET_WRITE_CBACK | ATTS_SET_READ_CBACK,
.permissions = ATTS_PERMIT_WRITE | ATTS_PERMIT_WRITE_ENC |
ATTS_PERMIT_WRITE_AUTH | ATTS_PERMIT_READ |
ATTS_PERMIT_READ_ENC | ATTS_PERMIT_READ_AUTH,
},
// BG LED Bottom right
{
.pUuid = attChUuid,
.pValue = (uint8_t *)UUID_char_led_bg_bottom_right,
.pLen = (uint16_t *)&UUID_char_len,
.maxLen = sizeof(UUID_char_led_bg_bottom_right),
.permissions = ATTS_PERMIT_READ,
},
{
.pUuid = UUID_attChar_led_bg_bottom_right,
.pValue = ledBGBottomRightValue,
.pLen = &rgbLen,
.maxLen = 3 * sizeof(uint8_t),
.settings = ATTS_SET_WRITE_CBACK | ATTS_SET_READ_CBACK,
.permissions = ATTS_PERMIT_WRITE | ATTS_PERMIT_WRITE_ENC |
ATTS_PERMIT_WRITE_AUTH | ATTS_PERMIT_READ |
ATTS_PERMIT_READ_ENC | ATTS_PERMIT_READ_AUTH,
},
// BG LED top right
{
.pUuid = attChUuid,
.pValue = (uint8_t *)UUID_char_led_bg_top_right,
.pLen = (uint16_t *)&UUID_char_len,
.maxLen = sizeof(UUID_char_led_bg_top_right),
.settings = 0,
.permissions = ATTS_PERMIT_READ,
},
{
.pUuid = UUID_attChar_led_bg_top_right,
.pValue = ledBGTopRightValue,
.pLen = &rgbLen,
.maxLen = 3 * sizeof(uint8_t),
.settings = ATTS_SET_WRITE_CBACK | ATTS_SET_READ_CBACK,
.permissions = ATTS_PERMIT_WRITE | ATTS_PERMIT_WRITE_ENC |
ATTS_PERMIT_WRITE_AUTH | ATTS_PERMIT_READ |
ATTS_PERMIT_READ_ENC | ATTS_PERMIT_READ_AUTH,
},
// BG LED top left
{
.pUuid = attChUuid,
.pValue = (uint8_t *)UUID_char_led_bg_top_left,
.pLen = (uint16_t *)&UUID_char_len,
.maxLen = sizeof(UUID_char_led_bg_top_left),
.permissions = ATTS_PERMIT_READ,
},
{
.pUuid = UUID_attChar_led_bg_top_left,
.pValue = ledBGTopLeftValue,
.pLen = &rgbLen,
.maxLen = 3 * sizeof(uint8_t),
.settings = ATTS_SET_WRITE_CBACK | ATTS_SET_READ_CBACK,
.permissions = ATTS_PERMIT_WRITE | ATTS_PERMIT_WRITE_ENC |
ATTS_PERMIT_WRITE_AUTH | ATTS_PERMIT_READ |
ATTS_PERMIT_READ_ENC | ATTS_PERMIT_READ_AUTH,
},
// Dim bottom module
{
.pUuid = attChUuid,
.pValue = (uint8_t *)UUID_char_leds_bottom_dim,
.pLen = (uint16_t *)&UUID_char_len,
.maxLen = sizeof(UUID_char_leds_bottom_dim),
.permissions = ATTS_PERMIT_READ,
},
{
.pUuid = UUID_attChar_leds_bottom_dim,
.pValue = NULL,
.pLen = 0,
.maxLen = sizeof(uint8_t),
.settings = ATTS_SET_WRITE_CBACK,
.permissions = ATTS_PERMIT_WRITE | ATTS_PERMIT_WRITE_ENC |
ATTS_PERMIT_WRITE_AUTH,
},
// Dim top module
{
.pUuid = attChUuid,
.pValue = (uint8_t *)UUID_char_leds_top_dim,
.pLen = (uint16_t *)&UUID_char_len,
.maxLen = sizeof(UUID_char_leds_top_dim),
.permissions = ATTS_PERMIT_READ,
},
{
.pUuid = UUID_attChar_leds_top_dim,
.pValue = NULL,
.maxLen = sizeof(uint8_t),
.settings = ATTS_SET_WRITE_CBACK,
.permissions = ATTS_PERMIT_WRITE | ATTS_PERMIT_WRITE_ENC |
ATTS_PERMIT_WRITE_AUTH,
},
AttsDynAddAttr(
pSHdl,
UUID_attChar_leds_top_dim,
NULL,
0,
sizeof(uint8_t),
ATTS_SET_WRITE_CBACK,
ATTS_PERMIT_WRITE | ATTS_PERMIT_WRITE_ENC |
ATTS_PERMIT_WRITE_AUTH);
// led powersafe
AttsDynAddAttrConst(
pSHdl,
attChUuid,
UUID_char_led_powersafe,
sizeof(UUID_char_led_powersafe),
0,
ATTS_PERMIT_READ
);
AttsDynAddAttr(
pSHdl,
UUID_attChar_led_powersafe,
NULL,
0,
sizeof(uint8_t),
ATTS_SET_WRITE_CBACK,
ATTS_PERMIT_WRITE | ATTS_PERMIT_WRITE_ENC |
ATTS_PERMIT_WRITE_AUTH);
// flashlight
AttsDynAddAttrConst(
pSHdl,
attChUuid,
UUID_char_flashlight,
sizeof(UUID_char_flashlight),
0,
ATTS_PERMIT_READ
);
// led powersafe
AttsDynAddAttr(
pSHdl,
UUID_attChar_flashlight,
NULL,
0,
sizeof(uint8_t),
ATTS_SET_WRITE_CBACK,
ATTS_PERMIT_WRITE | ATTS_PERMIT_WRITE_ENC |
ATTS_PERMIT_WRITE_AUTH);
// ABOVE LEDS
AttsDynAddAttrConst(
pSHdl,
attChUuid,
UUID_char_leds_above,
sizeof(UUID_char_leds_above),
0,
ATTS_PERMIT_READ
);
{
.pUuid = attChUuid,
.pValue = (uint8_t *)UUID_char_led_powersafe,
.pLen = (uint16_t *)&UUID_char_len,
.maxLen = sizeof(UUID_char_led_powersafe),
.permissions = ATTS_PERMIT_READ,
},
{
.pUuid = UUID_attChar_led_powersafe,
.pValue = NULL,
.maxLen = sizeof(uint8_t),
.settings = ATTS_SET_WRITE_CBACK,
.permissions = ATTS_PERMIT_WRITE | ATTS_PERMIT_WRITE_ENC |
ATTS_PERMIT_WRITE_AUTH,
},
AttsDynAddAttr(
pSHdl,
UUID_attChar_leds_above,
NULL,
0,
11 * 3 * sizeof(uint8_t),
ATTS_SET_WRITE_CBACK,
ATTS_PERMIT_WRITE | ATTS_PERMIT_WRITE_ENC |
ATTS_PERMIT_WRITE_AUTH);
// LIGHT_SENSOR
AttsDynAddAttrConst(
pSHdl,
attChUuid,
UUID_char_light_sensor,
sizeof(UUID_char_light_sensor),
0,
ATTS_PERMIT_READ
);
// flashlight
AttsDynAddAttr(
pSHdl,
UUID_attChar_light_sensor,
initLightSensorValue,
sizeof(uint8_t),
sizeof(uint8_t),
ATTS_SET_READ_CBACK,
ATTS_PERMIT_READ | ATTS_PERMIT_READ_ENC |
ATTS_PERMIT_READ_AUTH);
APP_TRACE_INFO0("ble-card10: services bound\n");
}
return pSHdl;
}
{
.pUuid = attChUuid,
.pValue = (uint8_t *)UUID_char_flashlight,
.pLen = (uint16_t *)&UUID_char_len,
.maxLen = sizeof(UUID_char_flashlight),
.permissions = ATTS_PERMIT_READ,
},
{
.pUuid = UUID_attChar_flashlight,
.pValue = NULL,
.maxLen = sizeof(uint8_t),
.settings = ATTS_SET_WRITE_CBACK,
.permissions = ATTS_PERMIT_WRITE | ATTS_PERMIT_WRITE_ENC |
ATTS_PERMIT_WRITE_AUTH,
},
// personal state
{
.pUuid = attChUuid,
.pValue = (uint8_t *)UUID_char_personal_state,
.pLen = (uint16_t *)&UUID_char_len,
.maxLen = sizeof(UUID_char_personal_state),
.permissions = ATTS_PERMIT_READ,
},
{
.pUuid = UUID_attChar_personal_state,
.pValue = &personalStateValue,
.pLen = &personalStateLen,
.maxLen = sizeof(uint16_t),
.settings = ATTS_SET_WRITE_CBACK | ATTS_SET_READ_CBACK,
.permissions = ATTS_PERMIT_WRITE | ATTS_PERMIT_WRITE_ENC |
ATTS_PERMIT_WRITE_AUTH | ATTS_PERMIT_READ |
ATTS_PERMIT_READ_ENC | ATTS_PERMIT_READ_AUTH,
},
// ABOVE LEDS
{
.pUuid = attChUuid,
.pValue = (uint8_t *)UUID_char_leds_above,
.pLen = (uint16_t *)&UUID_char_len,
.maxLen = sizeof(UUID_char_leds_above),
.permissions = ATTS_PERMIT_READ,
},
{
.pUuid = UUID_attChar_leds_above,
.pValue = aboveLEDsValue,
.pLen = &aboveLEDsLen,
.maxLen = 11 * 3 * sizeof(uint8_t),
.settings = ATTS_SET_WRITE_CBACK | ATTS_SET_READ_CBACK,
.permissions = ATTS_PERMIT_WRITE | ATTS_PERMIT_WRITE_ENC |
ATTS_PERMIT_WRITE_AUTH | ATTS_PERMIT_READ |
ATTS_PERMIT_READ_ENC | ATTS_PERMIT_READ_AUTH,
},
// Light sensor
{
.pUuid = attChUuid,
.pValue = (uint8_t *)UUID_char_light_sensor,
.pLen = (uint16_t *)&UUID_char_len,
.maxLen = sizeof(UUID_char_light_sensor),
.permissions = ATTS_PERMIT_READ,
},
{
.pUuid = UUID_attChar_light_sensor,
.pValue = initLightSensorValue,
.pLen = &initLightSensorLen,
.maxLen = sizeof(uint8_t),
.settings = ATTS_SET_READ_CBACK,
.permissions = ATTS_PERMIT_READ | ATTS_PERMIT_READ_ENC |
ATTS_PERMIT_READ_AUTH,
},
};
// validating, that the service really get all charateristics
WSF_CT_ASSERT(
((sizeof(card10SvcAttrList) / sizeof(card10SvcAttrList[0])) ==
CARD10_END_HDL - CARD10_START_HDL + 1));
/*
* Set the time given in milliseconds since 1.1.1970 as 64 bit integer.
*/
......@@ -548,7 +577,39 @@ static uint8_t setTime(uint8_t *pValue)
time = __bswap64(timeNet);
epic_rtc_set_milliseconds(time);
APP_TRACE_INFO1("set time to: %d\n", time);
APP_TRACE_INFO0("ble-card10: set time");
return ATT_SUCCESS;
}
/*
* Set a rgb led
*/
static uint8_t setRGBLed(uint8_t led, uint8_t *pValue)
{
epic_leds_set(led, pValue[0], pValue[1], pValue[2]);
APP_TRACE_INFO4(
"ble-card10: set rgb led %d: #%02x%02x%02x\n",
led,
pValue[0],
pValue[1],
pValue[2]
);
return ATT_SUCCESS;
}
/*
* Get value of a rgb led
*/
static uint8_t getRGBLed(uint8_t led, attsAttr_t *pAttr)
{
epic_leds_get_rgb(led, pAttr->pValue);
APP_TRACE_INFO4(
"ble-card10: set rgb led %d: #%02x%02x%02x\n",
led,
pAttr->pValue[0],
pAttr->pValue[1],
pAttr->pValue[2]
);
return ATT_SUCCESS;
}
......@@ -570,7 +631,7 @@ static uint8_t writeCard10CB(
switch (handle) {
// time
case CARD10_TIME_UPDATE_VAL_HDL:
case CARD10_TIME_VAL_HDL:
return setTime(pValue);
// vibra
case CARD10_VIBRA_VAL_HDL:
......@@ -592,60 +653,80 @@ static uint8_t writeCard10CB(
return ATT_SUCCESS;
// bg leds
case CARD10_LED_BG_BOTTOM_LEFT_VAL_HDL:
epic_leds_set(11, pValue[0], pValue[1], pValue[2]);
APP_TRACE_INFO3(
"ble-card10: set bg bottom left: #%02x%02x%02x\n",
pValue[0],
pValue[1],
pValue[2]
);
return ATT_SUCCESS;
return setRGBLed(11, pValue);
case CARD10_LED_BG_BOTTOM_RIGHT_VAL_HDL:
epic_leds_set(12, pValue[0], pValue[1], pValue[2]);
APP_TRACE_INFO3(
"ble-card10: set bg bottom right: #%02x%02x%02x\n",
pValue[0],
pValue[1],
pValue[2]
);
return ATT_SUCCESS;
return setRGBLed(12, pValue);
case CARD10_LED_BG_TOP_RIGHT_VAL_HDL:
epic_leds_set(13, pValue[0], pValue[1], pValue[2]);
APP_TRACE_INFO3(
"ble-card10: set bg top right: #%02x%02x%02x\n",
pValue[0],
pValue[1],
pValue[2]
);
return ATT_SUCCESS;
return setRGBLed(13, pValue);
case CARD10_LED_BG_TOP_LEFT_VAL_HDL:
epic_leds_set(14, pValue[0], pValue[1], pValue[2]);
APP_TRACE_INFO3(
"ble-card10: set bg top left: #%02x%02x%02x\n",
pValue[0],
pValue[1],
pValue[2]
);
return ATT_SUCCESS;
return setRGBLed(14, pValue);
// dim
case CARD10_LEDS_BOTTOM_DIM_VAL_HDL:
ui8 = pValue[0];
if (ui8 >= 1 && ui8 <= 8) {
epic_leds_dim_bottom(pValue[0]);
APP_TRACE_INFO1("dim bottom to: %d\n", pValue[0]);
return ATT_SUCCESS;
if (operation == ATT_PDU_WRITE_CMD ||
operation == ATT_PDU_SIGNED_WRITE_CMD ||
operation == ATT_PDU_WRITE_REQ ||
operation == ATT_PDU_EXEC_WRITE_REQ) {
epic_leds_dim_bottom(pValue[0]);
APP_TRACE_INFO1(
"ble-card10: dim bottom to: %d\n",
pValue[0]
);
return ATT_SUCCESS;
} else if (operation == ATT_PDU_PREP_WRITE_REQ) {
APP_TRACE_INFO1(
"ble_card10: value for dim bottom would be okay: %d\n",
pValue[0]
);
return ATT_SUCCESS;
} else {
APP_TRACE_INFO1(
"ble-card10: dim bottom with unknown operation: %d\n",
operation
);
return ATT_ERR_INVALID_PDU;
}
} else {
APP_TRACE_INFO1(
"ble-card: prep dim bottom invalid value (1-8): %d\n",
ui8
);
return ATT_ERR_RANGE;
}
APP_TRACE_INFO1("dim bottom invalid value (1-8): %d\n", ui8);
return ATT_ERR_RANGE;
case CARD10_LEDS_TOP_DIM_VAL_HDL:
ui8 = pValue[0];
if (ui8 >= 1 && ui8 <= 8) {
epic_leds_dim_top(ui8);
APP_TRACE_INFO1("dim top to: %d\n", ui8);
return ATT_SUCCESS;
if (operation == ATT_PDU_WRITE_CMD ||
operation == ATT_PDU_SIGNED_WRITE_CMD ||
operation == ATT_PDU_WRITE_REQ ||
operation == ATT_PDU_EXEC_WRITE_REQ) {
epic_leds_dim_top(pValue[0]);
APP_TRACE_INFO1(
"ble-card10: dim top to: %d\n",
pValue[0]
);
return ATT_SUCCESS;
} else if (operation == ATT_PDU_PREP_WRITE_REQ) {
APP_TRACE_INFO1(
"ble_card10: value for dim top would be okay: %d\n",
pValue[0]
);
return ATT_SUCCESS;
} else {
APP_TRACE_INFO1(
"ble-card10: dim top with unknown operation: %d\n",
operation
);
return ATT_ERR_INVALID_PDU;
}
} else {
APP_TRACE_INFO1(
"ble-card: prep dim top invalid value (1-8): %d\n",
ui8
);
return ATT_ERR_RANGE;
}
APP_TRACE_INFO1("dim top invalid value (1-8): %d\n", ui8);
return ATT_ERR_RANGE;
// led powersafe
case CARD10_LED_POWERSAFE_VAL_HDL:
epic_leds_set_powersave(pValue[0]);
......@@ -656,27 +737,57 @@ static uint8_t writeCard10CB(
epic_set_flashlight(pValue[0]);
APP_TRACE_INFO1("set flashlight to: %d\n", pValue[0]);
return ATT_SUCCESS;
// personal state
case CARD10_PERSONAL_STATE_VAL_HDL:
BYTES_TO_UINT16(ui16, pValue);
if (ui16 <= STATE_MAX) {
if (operation == ATT_PDU_WRITE_CMD ||
operation == ATT_PDU_SIGNED_WRITE_CMD ||
operation == ATT_PDU_WRITE_REQ ||
operation == ATT_PDU_EXEC_WRITE_REQ) {
epic_personal_state_set(ui16, true);
APP_TRACE_INFO1(
"ble-card10: set personal state to: %d\n",
ui16
);
return ATT_SUCCESS;
} else if (operation == ATT_PDU_PREP_WRITE_REQ) {
APP_TRACE_INFO1(
"ble_card10: personal state would be okay: %d\n",
ui16
);
return ATT_SUCCESS;
} else {
APP_TRACE_INFO1(
"ble-card10: personal state with unknown operation: %d\n",
operation
);
return ATT_ERR_INVALID_PDU;
}
} else {
APP_TRACE_INFO2(
"ble-card: personal state invalid value (0-%d): %d\n",
STATE_MAX - 1,
ui16
);
return ATT_ERR_RANGE;
}
// leds above
case CARD10_LEDS_ABOVE_VAL_HDL:
APP_TRACE_INFO0("ble-card10: update LEDs above");
for (ui16 = 0; ui16 < 11; ui16++) {
epic_leds_set(
ui16,
pValue[ui16 * 3],
pValue[ui16 * 3 + 1],
pValue[ui16 * 3 + 2]
);
APP_TRACE_INFO4(
"ble-card10: set led %ld above to #%02x%02x%02x\n",
epic_leds_prep(
ui16,
pValue[ui16 * 3],
pValue[ui16 * 3 + 1],
pValue[ui16 * 3 + 2]
);
}
epic_leds_update();
return ATT_SUCCESS;
default:
APP_TRACE_INFO1(
"ble-card10: unsupported characteristic: %c\n", handle
);
APP_TRACE_INFO1("ble-card10: unsupported handle: %x\n", handle);
return ATT_ERR_HANDLE;
}
}
......@@ -693,8 +804,63 @@ static uint8_t readCard10CB(
attsAttr_t *pAttr
) {
uint16_t ui16 = 0;
uint64_t ui64 = 0;
uint8_t rgb[] = { 0, 0, 0 };
switch (handle) {
// time
case CARD10_TIME_VAL_HDL:
ui64 = epic_rtc_get_milliseconds();
uint64_t time;
time = __bswap64(ui64);
memcpy(pAttr->pValue, &time, sizeof(time));
APP_TRACE_INFO0("ble-card10: read time\n");
return ATT_SUCCESS;
case CARD10_ROCKETS_VAL_HDL:
pAttr->pValue[0] = epic_leds_get_rocket(0);
pAttr->pValue[1] = epic_leds_get_rocket(1);
pAttr->pValue[2] = epic_leds_get_rocket(2);
APP_TRACE_INFO3(
"ble-card10: get rockets 0:%d, 1:%d, 2:%d\n",
pAttr->pValue[0],
pAttr->pValue[1],
pAttr->pValue[2]
);
return ATT_SUCCESS;
// background leds
case CARD10_LED_BG_BOTTOM_LEFT_VAL_HDL:
return getRGBLed(11, pAttr);
case CARD10_LED_BG_BOTTOM_RIGHT_VAL_HDL:
return getRGBLed(12, pAttr);
case CARD10_LED_BG_TOP_RIGHT_VAL_HDL:
return getRGBLed(13, pAttr);
case CARD10_LED_BG_TOP_LEFT_VAL_HDL:
return getRGBLed(14, pAttr);
// personal state
case CARD10_PERSONAL_STATE_VAL_HDL:
ui16 = epic_personal_state_get();
*pAttr->pValue = ui16;
APP_TRACE_INFO1("ble-card10: read personal state: %d\n", ui16);
return ATT_SUCCESS;
// leds above
case CARD10_LEDS_ABOVE_VAL_HDL:
for (ui16 = 0; ui16 < 11; ui16++) {
epic_leds_get_rgb(ui16, rgb);
pAttr->pValue[ui16 * 3] = rgb[0];
pAttr->pValue[ui16 * 3 + 1] = rgb[1];
pAttr->pValue[ui16 * 3 + 2] = rgb[2];
APP_TRACE_INFO4(
"ble-card10: get led %ld above to #%02x%02x%02x\n",
ui16,
pAttr->pValue[ui16 * 3],
pAttr->pValue[ui16 * 3 + 1],
pAttr->pValue[ui16 * 3 + 2]
);
}
return ATT_SUCCESS;
// light sensor
case CARD10_LIGHT_SENSOR_VAL_HDL:
epic_light_sensor_get(&ui16);
*pAttr->pValue = ui16;
......@@ -706,12 +872,20 @@ static uint8_t readCard10CB(
}
}
static attsGroup_t svcCard10Group = {
.pNext = NULL,
.pAttr = (attsAttr_t *)card10SvcAttrList,
.readCback = readCard10CB,
.writeCback = writeCard10CB,
.startHandle = CARD10_START_HDL,
.endHandle = CARD10_END_HDL,
};
/*
* This registers and starts the BLE card10 service.
*/
void bleCard10_init(void)
{
void *pSHdl = addCard10GroupDyn();
AttsDynRegister(pSHdl, readCard10CB, writeCard10CB);
AttsAddGroup(&svcCard10Group);
}
#pragma once
/*! enumeration of client characteristic configuration descriptors */
enum
{
BLE_GATT_SC_CCC_IDX, /*! GATT service, service changed characteristic */
BLE_BATT_LVL_CCC_IDX, /*! Battery service, battery level characteristic */
BLE_ESS_TEMP_CCC_IDX, /*! Environmental sensing service, temperature characteristic */
BLE_ESS_HUMI_CCC_IDX, /*! Environmental sensing service, humidity characteristic */
BLE_ESS_PRES_CCC_IDX, /*! Environmental sensing service, pressure characteristic */
HIDAPP_MBI_CCC_HDL, /*! HID Boot Mouse Input characteristic */
HIDAPP_KBI_CCC_HDL, /*! HID Boot Keyboard Input characteristic */
HIDAPP_IN_KEYBOARD_CCC_HDL, /*! HID Input Report characteristic for keyboard inputs */
HIDAPP_IN_MOUSE_CCC_HDL, /*! HID Input Report characteristic for mouse inputs */
HIDAPP_IN_CONSUMER_CCC_HDL, /*! HID Input Report characteristic for consumer control inputs */
BLE_ESS_IAQ_CCC_IDX, /*! Environmental sensing service, IAQ characteristic */
UART_TX_CH_CCC_IDX,
BLE_NUM_CCC_IDX
};
#include "ble_api.h"
#include "epicardium.h"
#include "os/core.h"
#include "wsf_types.h"
#include "util/bstream.h"
#include "wsf_msg.h"
#include "att_api.h"
#include "wsf_buf.h"
#include "gatt/gatt_api.h"
#include "FreeRTOS.h"
#include "queue.h"
#include <stdio.h>
#include <string.h>
/* We allow up to 10 dynamically defined services */
#define ATTS_DYN_GROUP_COUNT 10
#define ATTS_DYN_START_HANDLE 0x200
static void *dyn_groups[ATTS_DYN_GROUP_COUNT] = {};
static int next_dyn_group = 0;
static int next_handle = ATTS_DYN_START_HANDLE;
void ble_epic_att_api_event(attEvt_t *att_event)
{
if (att_event->hdr.event != ATTS_HANDLE_VALUE_CNF ||
(att_event->handle >= ATTS_DYN_START_HANDLE &&
att_event->handle < next_handle)) {
size_t value_len = 0;
if (att_event->hdr.event == ATTC_READ_BY_GROUP_TYPE_RSP ||
att_event->hdr.event == ATTC_READ_BY_TYPE_RSP ||
att_event->hdr.event == ATTC_FIND_INFO_RSP ||
att_event->hdr.event == ATTC_HANDLE_VALUE_NTF ||
att_event->hdr.event == ATTC_HANDLE_VALUE_IND) {
value_len = att_event->valueLen;
}
attEvt_t *e = WsfBufAlloc(sizeof(*e) + value_len);
if (e) {
memcpy(e, att_event, sizeof(*e));
memcpy(e + 1, att_event->pValue, value_len);
e->pValue = (uint8_t *)(e + 1);
ble_epic_ble_api_trigger_event(BLE_EVENT_ATT_EVENT, e);
} else {
LOG_WARN(
"ble",
"could not allocate att event of size %d",
sizeof(*e) + att_event->valueLen
);
}
}
}
void ble_epic_att_api_free_att_write_data(struct epic_att_write *w)
{
WsfBufFree(w->buffer);
WsfBufFree(w);
}
static uint8_t DynAttsWriteCback(
dmConnId_t connId,
uint16_t handle,
uint8_t operation,
uint16_t offset,
uint16_t len,
uint8_t *pValue,
attsAttr_t *pAttr
) {
struct epic_att_write *att_write = WsfBufAlloc(sizeof(*att_write));
if (att_write) {
att_write->hdr.param = connId;
att_write->handle = handle;
att_write->valueLen = len;
att_write->operation = operation;
att_write->offset = offset;
att_write->buffer = WsfBufAlloc(len);
if (att_write->buffer) {
memcpy(att_write->buffer, pValue, len);
ble_epic_ble_api_trigger_event(
BLE_EVENT_ATT_WRITE, att_write
);
} else {
LOG_WARN("ble", "could allocate att write data");
WsfBufFree(att_write);
}
} else {
LOG_WARN("ble", "could allocate att write");
}
// TODO: handle offset != 0
return AttsSetAttr(handle, len, pValue);
}
int epic_atts_dyn_create_service(
const uint8_t *uuid,
uint8_t uuid_len,
uint16_t group_size,
void **pSvcHandle
) {
uint16_t start_handle = next_handle;
uint16_t end_handle = start_handle + group_size - 1;
*pSvcHandle = AttsDynCreateGroup(start_handle, end_handle);
dyn_groups[next_dyn_group++] = *pSvcHandle;
AttsDynRegister(*pSvcHandle, NULL, DynAttsWriteCback);
AttsDynAddAttr(
*pSvcHandle,
attPrimSvcUuid,
uuid,
uuid_len,
uuid_len,
0,
ATTS_PERMIT_READ
);
next_handle++;
// TODO: validate parameters and pointer and current service count
return 0;
}
int epic_atts_dyn_add_characteristic(
void *pSvcHandle,
const uint8_t *uuid,
uint8_t uuid_len,
uint8_t flags,
uint16_t maxLen,
uint16_t *value_handle
) {
uint8_t settings = ATTS_SET_VARIABLE_LEN;
if (flags & ATT_PROP_WRITE) {
settings |= ATTS_SET_WRITE_CBACK;
}
uint8_t permissions = 0;
if (flags & ATT_PROP_READ) {
permissions |= ATTS_PERMIT_READ;
}
if (flags & ATT_PROP_WRITE) {
permissions |= ATTS_PERMIT_WRITE;
}
uint8_t characteristic[1 + 2 + 16] = {
flags, UINT16_TO_BYTES(next_handle + 1)
};
memcpy(characteristic + 3, uuid, uuid_len);
uint8_t characteristic_len = 1 + 2 + uuid_len;
/* Characteristic */
AttsDynAddAttr(
pSvcHandle,
attChUuid,
characteristic,
characteristic_len,
characteristic_len,
0,
ATTS_PERMIT_READ
);
next_handle++;
/* Value */
*value_handle = next_handle;
if ((flags & ATT_PROP_READ) == 0) {
AttsDynAddAttrDynConst(
pSvcHandle,
uuid,
uuid_len,
NULL,
0,
maxLen,
settings,
permissions
);
} else {
AttsDynAddAttrDyn(
pSvcHandle,
uuid,
uuid_len,
NULL,
0,
maxLen,
settings,
permissions
);
}
next_handle++;
return 0;
}
int epic_ble_atts_dyn_add_descriptor(
void *pSvcHandle,
const uint8_t *uuid,
uint8_t uuid_len,
uint8_t flags,
const uint8_t *value,
uint16_t value_len,
uint16_t maxLen,
uint16_t *descriptor_handle
) {
uint8_t settings = 0;
if (flags & ATT_PROP_WRITE) {
settings |= ATTS_SET_WRITE_CBACK;
}
uint8_t permissions = 0;
if (flags & ATT_PROP_READ) {
permissions |= ATTS_PERMIT_READ;
}
if (flags & ATT_PROP_WRITE) {
permissions |= ATTS_PERMIT_WRITE;
}
*descriptor_handle = next_handle;
AttsDynAddAttrDyn(
pSvcHandle,
uuid,
uuid_len,
value,
value_len,
maxLen,
settings,
permissions
);
next_handle++;
return 0;
}
int epic_atts_dyn_send_service_changed_ind(void)
{
/* Indicate to the server that our GATT DB changed.
* TODO: Handling of CCCDs in pairings might still be broken:
* See https://git.card10.badge.events.ccc.de/card10/firmware/-/issues/197 */
GattSendServiceChangedInd(
DM_CONN_ID_NONE, ATT_HANDLE_START, ATT_HANDLE_MAX
);
return 0;
}
int epic_ble_atts_dyn_delete_groups(void)
{
for (int i = 0; i < ATTS_DYN_GROUP_COUNT; i++) {
if (dyn_groups[i]) {
AttsDynDeleteGroup(dyn_groups[i]);
dyn_groups[i] = NULL;
}
}
next_dyn_group = 0;
next_handle = ATTS_DYN_START_HANDLE;
return 0;
}
void ble_epic_att_api_init(void)
{
}
int epic_ble_atts_handle_value_ntf(
uint8_t connId, uint16_t handle, uint16_t valueLen, uint8_t *pValue
) {
if (!DmConnInUse(connId)) {
return -EIO;
}
AttsHandleValueNtf(connId, handle, valueLen, pValue);
return 0;
}
int epic_ble_atts_handle_value_ind(
uint8_t connId, uint16_t handle, uint16_t valueLen, uint8_t *pValue
) {
if (!DmConnInUse(connId)) {
return -EIO;
}
AttsHandleValueInd(connId, handle, valueLen, pValue);
return 0;
}
int epic_ble_atts_set_buffer(uint16_t value_handle, size_t len, bool append)
{
return AttsDynResize(value_handle, len);
}
int epic_ble_atts_set_attr(
uint16_t handle, const uint8_t *value, uint16_t value_len
) {
uint8_t ret = AttsSetAttr(handle, value_len, (uint8_t *)value);
return ret;
}
int epic_ble_attc_discover_primary_services(
uint8_t connId, const uint8_t *uuid, uint8_t uuid_len
) {
if (uuid_len == 0 || uuid == NULL) {
AttcReadByGroupTypeReq(
connId, 1, 0xFFFF, 2, (uint8_t *)attPrimSvcUuid, TRUE
);
} else {
AttcFindByTypeValueReq(
connId,
ATT_HANDLE_START,
ATT_HANDLE_MAX,
ATT_UUID_PRIMARY_SERVICE,
uuid_len,
(uint8_t *)uuid,
FALSE
);
}
return 0;
}
int epic_ble_attc_discover_characteristics(
uint8_t connId, uint16_t start_handle, uint16_t end_handle
) {
AttcReadByTypeReq(
connId,
start_handle,
end_handle,
ATT_16_UUID_LEN,
(uint8_t *)attChUuid,
TRUE
);
return 0;
}
int epic_ble_attc_discover_descriptors(
uint8_t connId, uint16_t start_handle, uint16_t end_handle
) {
AttcFindInfoReq(connId, start_handle, end_handle, TRUE);
return 0;
}
int epic_ble_attc_read(uint8_t connId, uint16_t value_handle)
{
AttcReadReq(connId, value_handle);
return 0;
}
int epic_ble_attc_write_no_rsp(
uint8_t connId,
uint16_t value_handle,
const uint8_t *value,
uint16_t value_len
) {
AttcWriteCmd(connId, value_handle, value_len, (uint8_t *)value);
return 0;
}
int epic_ble_attc_write(
uint8_t connId,
uint16_t value_handle,
const uint8_t *value,
uint16_t value_len
) {
AttcWriteReq(connId, value_handle, value_len, (uint8_t *)value);
return 0;
}
#include "ble_api.h"
#include "epicardium.h"
#include "os/core.h"
#include "modules/modules.h"
#include "user_core/interrupts.h"
#include "wsf_buf.h"
#include "app_api.h"
#include "svc_core.h"
#include "FreeRTOS.h"
#include "queue.h"
#include "timers.h"
#include <stdint.h>
#include <string.h>
#include <stdio.h>
#define BLE_EVENT_QUEUE_SIZE 10
static QueueHandle_t ble_event_queue;
static uint8_t ble_event_queue_buffer
[sizeof(struct epic_ble_event) * BLE_EVENT_QUEUE_SIZE];
static StaticQueue_t ble_event_queue_data;
static uint8_t adv_data_buf[HCI_ADV_DATA_LEN];
static uint8_t sr_data_buf[HCI_ADV_DATA_LEN];
static TimerHandle_t dm_timer;
static StaticTimer_t dm_timer_data;
static dmEvt_t connection_open_event;
static bool connection_open;
int epic_ble_free_event(struct epic_ble_event *e)
{
if (e->data) {
if (e->type == BLE_EVENT_ATT_WRITE) {
ble_epic_att_api_free_att_write_data(e->att_write);
} else {
// Generic free
WsfBufFree(e->data);
}
}
return 0;
}
void ble_epic_ble_api_trigger_event(enum epic_ble_event_type type, void *data)
{
bool enabled;
epic_interrupt_is_enabled(EPIC_INT_BLE, &enabled);
struct epic_ble_event e = { .type = type, .data = data };
if (type == BLE_EVENT_DM_EVENT) {
dmEvt_t *dm_event = data;
if (dm_event->hdr.event == DM_CONN_OPEN_IND) {
connection_open = true;
}
}
if (!connection_open &&
(type == BLE_EVENT_ATT_EVENT || type == BLE_EVENT_DM_EVENT)) {
// Don't forward DM and ATT events until epicardium is done setting up
// the connection
epic_ble_free_event(&e);
return;
}
if (enabled) {
if (xQueueSend(ble_event_queue, &e, 0) != pdTRUE) {
/* Print a warning if the app is missing events. Missing scan results
* is considered OK though, as they are queued and periodic. */
if (type != BLE_EVENT_SCAN_REPORT) {
LOG_WARN(
"ble",
"Application missed event %u",
type
);
}
epic_ble_free_event(&e);
}
interrupt_trigger(EPIC_INT_BLE);
} else {
epic_ble_free_event(&e);
}
if (type == BLE_EVENT_DM_EVENT) {
dmEvt_t *dm_event = data;
if (dm_event->hdr.event == DM_CONN_CLOSE_IND) {
connection_open = false;
}
}
}
int epic_ble_get_event(struct epic_ble_event *e)
{
if (!ble_is_enabled()) {
return -EIO;
}
if (xQueueReceive(ble_event_queue, e, 0) != pdTRUE) {
return -ENOENT;
}
return uxQueueMessagesWaiting(ble_event_queue);
}
void ble_epic_ble_api_init(void)
{
ble_event_queue = xQueueCreateStatic(
BLE_EVENT_QUEUE_SIZE,
sizeof(struct epic_ble_event),
ble_event_queue_buffer,
&ble_event_queue_data
);
ble_epic_att_api_init();
}
static void send_dm_event(dmEvt_t *dm_event)
{
dmEvt_t *e = WsfBufAlloc(sizeof(*e));
if (e) {
memcpy(e, dm_event, sizeof(*e));
ble_epic_ble_api_trigger_event(BLE_EVENT_DM_EVENT, e);
} else {
LOG_WARN("ble", "could not allocate dm event");
}
}
void ble_epic_dm_api_event(dmEvt_t *dm_event)
{
if (dm_event->hdr.event == DM_CONN_OPEN_IND) {
/* Cache the connection open indication until
* epicardium is done dicovering services. */
memcpy(&connection_open_event,
dm_event,
sizeof(connection_open_event));
} else {
send_dm_event(dm_event);
}
}
void ble_epic_disc_cfg_complete(void)
{
send_dm_event(&connection_open_event);
}
void epic_ble_close_connection(uint8_t connId)
{
AppConnClose(connId);
}
int epic_ble_is_connection_open(void)
{
return AppConnIsOpen();
}
void vDmTimerCallback()
{
send_dm_event(&connection_open_event);
}
int epic_ble_init(void)
{
if (!ble_is_enabled()) {
return -EIO;
}
if (dm_timer == NULL) {
dm_timer = xTimerCreateStatic(
"dmtimer",
1,
pdFALSE, /* one-shot */
0,
vDmTimerCallback,
&dm_timer_data);
}
epic_interrupt_enable(EPIC_INT_BLE);
if (connection_open) {
// Give pycardium a bit of time and then let it
// know that there already is an open connection
int millis = 100;
int ticks = millis * (configTICK_RATE_HZ / 1000);
xTimerChangePeriod(dm_timer, ticks, 0);
}
return 0;
}
int epic_ble_deinit(void)
{
xTimerStop(dm_timer, 0);
epic_interrupt_disable(EPIC_INT_BLE);
return 0;
}
int epic_ble_set_device_name(const uint8_t *buf, uint16_t len)
{
return epic_ble_atts_set_attr(GAP_DN_HDL, buf, len);
}
int epic_ble_get_device_name(uint8_t **buf, uint16_t *len)
{
uint8_t ret = AttsGetAttr(GAP_DN_HDL, len, buf);
return ret;
}
int epic_ble_advertise(
int interval_us,
const uint8_t *adv_data,
size_t adv_data_len,
const uint8_t *sr_data,
size_t sr_data_len,
bool connectable
) {
if (adv_data_len > sizeof(adv_data_buf)) {
adv_data_len = sizeof(adv_data_buf);
}
if (sr_data_len > sizeof(sr_data_buf)) {
sr_data_len = sizeof(sr_data_buf);
}
memcpy(adv_data_buf, adv_data, adv_data_len);
memcpy(sr_data_buf, sr_data, sr_data_len);
ble_adv_set_interval(interval_us);
if (connectable) {
AppAdvSetData(
APP_ADV_DATA_CONNECTABLE, adv_data_len, adv_data_buf
);
AppAdvSetData(
APP_SCAN_DATA_CONNECTABLE, sr_data_len, sr_data_buf
);
ble_adv_start(APP_MODE_CONNECTABLE);
} else {
AppAdvSetData(
APP_ADV_DATA_DISCOVERABLE, adv_data_len, adv_data_buf
);
AppAdvSetData(
APP_SCAN_DATA_DISCOVERABLE, sr_data_len, sr_data_buf
);
ble_adv_start(APP_MODE_DISCOVERABLE);
}
return 0;
}
int epic_ble_advertise_stop(void)
{
ble_adv_stop();
return 0;
}
#include "ess.h"
#include "cccd.h"
#include "wsf_types.h"
#include "util/bstream.h"
#include "wsf_assert.h"
#include "att_api.h"
#include "app_api.h"
#include "epicardium.h"
#include "os/core.h"
#include "os/work_queue.h"
#include "modules/modules.h"
#include "drivers/drivers.h"
#include "ble/ble_api.h"
#include "FreeRTOS.h"
#include "timers.h"
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
#include <machine/endian.h>
/* clang-format off */
/* BLE UUID for ESS service*/
static const uint8_t UUID_svc[] = { UINT16_TO_BYTES(ATT_UUID_ENVIRONMENTAL_SENSING_SERVICE) };
static const uint16_t UUID_svc_len = sizeof(UUID_svc);
/* BLE UUID for temperature */
static const uint8_t UUID_char_temperature[] = {
ATT_PROP_READ | ATT_PROP_NOTIFY,
UINT16_TO_BYTES(ESS_TEMPERATURE_VAL_HDL),
UINT16_TO_BYTES(ATT_UUID_TEMPERATURE)
};
/* BLE UUID for humidity */
static const uint8_t UUID_char_humidity[] = {
ATT_PROP_READ | ATT_PROP_NOTIFY,
UINT16_TO_BYTES(ESS_HUMIDITY_VAL_HDL),
UINT16_TO_BYTES(ATT_UUID_HUMIDITY)
};
/* BLE UUID for pressure */
static const uint8_t UUID_char_pressure[] = {
ATT_PROP_READ | ATT_PROP_NOTIFY,
UINT16_TO_BYTES(ESS_PRESSURE_VAL_HDL),
UINT16_TO_BYTES(ATT_UUID_PRESSURE)
};
/* BLE UUID for IAQ */
static const uint8_t UUID_char_IAQ[] = {
ATT_PROP_READ | ATT_PROP_NOTIFY,
UINT16_TO_BYTES(ESS_IAQ_VAL_HDL),
CARD10_UUID_SUFFIX, 0xf1, CARD10_UUID_PREFIX
};
static const uint8_t UUID_attChar_IAQ[] = {
CARD10_UUID_SUFFIX, 0xf1, CARD10_UUID_PREFIX
};
static const uint16_t UUID_char_len = sizeof(UUID_char_temperature);
static const uint16_t UUID_char_IAQ_len = sizeof(UUID_char_IAQ);
static uint8_t initTemperatureValue[] = { UINT16_TO_BYTES(0) };
static uint16_t initTemperatureLen = sizeof(initTemperatureValue);
static uint8_t initHumidityValue[] = { UINT16_TO_BYTES(0) };
static uint16_t initHumidityLen = sizeof(initHumidityValue);
static uint8_t initPressureValue[] = { UINT32_TO_BYTES(0) };
static uint16_t initPressureLen = sizeof(initPressureValue);
static uint8_t initIAQValue[] = {0x00, UINT16_TO_BYTES(0), UINT16_TO_BYTES(0)};
static uint16_t initIAQLen = sizeof(initIAQValue);
/* Temperature client characteristic configuration */
static uint8_t essValTempChCcc[] = {UINT16_TO_BYTES(0x0000)};
static const uint16_t essLenTempChCcc = sizeof(essValTempChCcc);
/* Humidity client characteristic configuration */
static uint8_t essValHumidityChCcc[] = {UINT16_TO_BYTES(0x0000)};
static const uint16_t essLenHumidityChCcc = sizeof(essValHumidityChCcc);
/* Pressure client characteristic configuration */
static uint8_t essValPressureChCcc[] = {UINT16_TO_BYTES(0x0000)};
static const uint16_t essLenPressureChCcc = sizeof(essValPressureChCcc);
/* IAQ client characteristic configuration */
static uint8_t essValIAQChCcc[] = {UINT16_TO_BYTES(0x0000)};
static const uint16_t essLenIAQChCcc = sizeof(essValIAQChCcc);
/* clang-format on */
/*
* BLE service description
*/
static const attsAttr_t ESSSvcAttrList[] = {
{
.pUuid = attPrimSvcUuid,
.pValue = (uint8_t *)UUID_svc,
.pLen = (uint16_t *)&UUID_svc_len,
.maxLen = sizeof(UUID_svc),
.permissions = ATTS_PERMIT_READ,
},
// Temperature
{
.pUuid = attChUuid,
.pValue = (uint8_t *)UUID_char_temperature,
.pLen = (uint16_t *)&UUID_char_len,
.maxLen = sizeof(UUID_char_temperature),
.permissions = ATTS_PERMIT_READ,
},
{
.pUuid = attTemperatureChUuid,
.pValue = initTemperatureValue,
.pLen = &initTemperatureLen,
.maxLen = sizeof(initTemperatureValue),
.settings = ATTS_SET_READ_CBACK,
.permissions = ATTS_PERMIT_READ | ATTS_PERMIT_READ_ENC |
ATTS_PERMIT_READ_AUTH,
},
/* Characteristic CCC descriptor */
{
.pUuid = attCliChCfgUuid,
.pValue = essValTempChCcc,
.pLen = (uint16_t *)&essLenTempChCcc,
.maxLen = sizeof(essValTempChCcc),
.settings = ATTS_SET_CCC,
.permissions =
(ATTS_PERMIT_READ |
ATTS_PERMIT_WRITE) // How about security?
},
// Humidity
{
.pUuid = attChUuid,
.pValue = (uint8_t *)UUID_char_humidity,
.pLen = (uint16_t *)&UUID_char_len,
.maxLen = sizeof(UUID_char_humidity),
.permissions = ATTS_PERMIT_READ,
},
{
.pUuid = attHumidityChUuid,
.pValue = initHumidityValue,
.pLen = &initHumidityLen,
.maxLen = sizeof(initHumidityValue),
.settings = ATTS_SET_READ_CBACK,
.permissions = ATTS_PERMIT_READ | ATTS_PERMIT_READ_ENC |
ATTS_PERMIT_READ_AUTH,
},
/* Characteristic CCC descriptor */
{
.pUuid = attCliChCfgUuid,
.pValue = essValHumidityChCcc,
.pLen = (uint16_t *)&essLenHumidityChCcc,
.maxLen = sizeof(essValHumidityChCcc),
.settings = ATTS_SET_CCC,
.permissions =
(ATTS_PERMIT_READ |
ATTS_PERMIT_WRITE) // How about security?
},
// Pressure
{
.pUuid = attChUuid,
.pValue = (uint8_t *)UUID_char_pressure,
.pLen = (uint16_t *)&UUID_char_len,
.maxLen = sizeof(UUID_char_pressure),
.permissions = ATTS_PERMIT_READ,
},
{
.pUuid = attPressureChUuid,
.pValue = initPressureValue,
.pLen = &initPressureLen,
.maxLen = sizeof(initPressureValue),
.settings = ATTS_SET_READ_CBACK,
.permissions = ATTS_PERMIT_READ | ATTS_PERMIT_READ_ENC |
ATTS_PERMIT_READ_AUTH,
},
/* Characteristic CCC descriptor */
{
.pUuid = attCliChCfgUuid,
.pValue = essValPressureChCcc,
.pLen = (uint16_t *)&essLenPressureChCcc,
.maxLen = sizeof(essValPressureChCcc),
.settings = ATTS_SET_CCC,
.permissions =
(ATTS_PERMIT_READ |
ATTS_PERMIT_WRITE) // How about security?
},
// IAQ
{
.pUuid = attChUuid,
.pValue = (uint8_t *)UUID_char_IAQ,
.pLen = (uint16_t *)&UUID_char_IAQ_len,
.maxLen = sizeof(UUID_char_IAQ),
.permissions = ATTS_PERMIT_READ,
},
{
.pUuid = UUID_attChar_IAQ,
.pValue = initIAQValue,
.pLen = &initIAQLen,
.maxLen = sizeof(initIAQValue),
.settings = ATTS_SET_READ_CBACK,
.permissions = ATTS_PERMIT_READ | ATTS_PERMIT_READ_ENC |
ATTS_PERMIT_READ_AUTH,
},
/* Characteristic CCC descriptor */
{
.pUuid = attCliChCfgUuid,
.pValue = essValIAQChCcc,
.pLen = (uint16_t *)&essLenIAQChCcc,
.maxLen = sizeof(essValIAQChCcc),
.settings = ATTS_SET_CCC,
.permissions =
(ATTS_PERMIT_READ |
ATTS_PERMIT_WRITE) // How about security?
},
};
// validating that the service really has all charateristics
WSF_CT_ASSERT(
((sizeof(ESSSvcAttrList) / sizeof(ESSSvcAttrList[0])) ==
ESS_END_HDL - ESS_START_HDL + 1));
static TimerHandle_t poll_timer;
static StaticTimer_t poll_timer_buffer;
static void update_from_bme680(struct bme680_sensor_data *data);
static void workpoll(void *data)
{
struct bme680_sensor_data sensor_data;
if (epic_bme680_read_sensors(&sensor_data) == 0) {
update_from_bme680(&sensor_data);
}
}
static void poll(TimerHandle_t xTimer)
{
workqueue_schedule(workpoll, NULL);
}
static void periodic(int period)
{
if (poll_timer == NULL) {
poll_timer = xTimerCreateStatic(
"bme680_poll",
1,
pdTRUE,
NULL,
poll,
&poll_timer_buffer
);
}
if (period < 1) {
xTimerStop(poll_timer, 0);
} else {
xTimerChangePeriod(poll_timer, pdMS_TO_TICKS(period), 0);
}
}
static void
sendNotification(dmConnId_t connId, uint16_t handle, uint16_t cccidx)
{
if (connId != DM_CONN_ID_NONE) {
uint16_t len;
uint8_t *value;
uint8_t ret = AttsGetAttr(handle, &len, &value);
if (ret == ATT_SUCCESS) {
if (AttsCccEnabled(connId, cccidx)) {
AttsHandleValueNtf(connId, handle, len, value);
}
}
}
}
static void setAttrFromBME680(struct bme680_sensor_data *data)
{
int16_t temperature;
uint16_t humidity;
uint32_t pressure;
temperature = data->temperature * 100;
AttsSetAttr(
ESS_TEMPERATURE_VAL_HDL,
sizeof(temperature),
(uint8_t *)&temperature
);
humidity = data->humidity * 100;
AttsSetAttr(
ESS_HUMIDITY_VAL_HDL, sizeof(humidity), (uint8_t *)&humidity
);
pressure = data->pressure * 1000;
AttsSetAttr(
ESS_PRESSURE_VAL_HDL, sizeof(pressure), (uint8_t *)&pressure
);
}
static void setAttrFromBSEC(struct bsec_sensor_data *data)
{
setAttrFromBME680((struct bme680_sensor_data *)data);
uint16_t iaq = data->indoor_air_quality;
uint8_t accuracy = data->accuracy;
uint16_t co2_equivalent = data->co2_equivalent;
uint8_t IAQValue[] = { accuracy,
UINT16_TO_BYTES(iaq),
UINT16_TO_BYTES(co2_equivalent) };
AttsSetAttr(ESS_IAQ_VAL_HDL, sizeof(IAQValue), IAQValue);
}
/*
* BLE ESS read callback.
*
*/
static uint8_t readESSCB(
dmConnId_t connId,
uint16_t handle,
uint8_t operation,
uint16_t offset,
attsAttr_t *pAttr
) {
struct bme680_sensor_data data;
int ret = epic_bme680_read_sensors(&data);
if (ret != 0) {
return ATT_ERR_UNDEFINED;
}
setAttrFromBME680(&data);
/* Send notifications (if enabled) for the _other_ characteristics. */
switch (handle) {
case ESS_TEMPERATURE_VAL_HDL:
sendNotification(
connId, ESS_HUMIDITY_VAL_HDL, BLE_ESS_HUMI_CCC_IDX
);
sendNotification(
connId, ESS_PRESSURE_VAL_HDL, BLE_ESS_PRES_CCC_IDX
);
APP_TRACE_INFO1("ble-ess: read temperature: %d\n", temperature);
return ATT_SUCCESS;
case ESS_HUMIDITY_VAL_HDL:
sendNotification(
connId, ESS_TEMPERATURE_VAL_HDL, BLE_ESS_TEMP_CCC_IDX
);
sendNotification(
connId, ESS_PRESSURE_VAL_HDL, BLE_ESS_PRES_CCC_IDX
);
APP_TRACE_INFO1("ble-ess: read humidity: %u\n", humidity);
return ATT_SUCCESS;
case ESS_PRESSURE_VAL_HDL:
sendNotification(
connId, ESS_TEMPERATURE_VAL_HDL, BLE_ESS_TEMP_CCC_IDX
);
sendNotification(
connId, ESS_HUMIDITY_VAL_HDL, BLE_ESS_HUMI_CCC_IDX
);
APP_TRACE_INFO1("ble-ess: read pressure: %u\n", pressure);
return ATT_SUCCESS;
case ESS_IAQ_VAL_HDL:
return ATT_SUCCESS;
default:
APP_TRACE_INFO0("ble-card10: read no handler found\n");
return ATT_ERR_HANDLE;
}
}
static attsGroup_t svcESSGroup = {
.pNext = NULL,
.pAttr = (attsAttr_t *)ESSSvcAttrList,
.readCback = readESSCB,
.writeCback = NULL,
.startHandle = ESS_START_HDL,
.endHandle = ESS_END_HDL,
};
static void update_from_bme680(struct bme680_sensor_data *data)
{
setAttrFromBME680(data);
/* Send notifications (if enabled) for all characteristics. */
dmConnId_t connId = AppConnIsOpen();
sendNotification(connId, ESS_TEMPERATURE_VAL_HDL, BLE_ESS_TEMP_CCC_IDX);
sendNotification(connId, ESS_HUMIDITY_VAL_HDL, BLE_ESS_HUMI_CCC_IDX);
sendNotification(connId, ESS_PRESSURE_VAL_HDL, BLE_ESS_PRES_CCC_IDX);
}
void bleESS_update_from_bsec_data(struct bsec_sensor_data *data)
{
setAttrFromBSEC(data);
/* Send notifications (if enabled) for all characteristics. */
dmConnId_t connId = AppConnIsOpen();
sendNotification(connId, ESS_TEMPERATURE_VAL_HDL, BLE_ESS_TEMP_CCC_IDX);
sendNotification(connId, ESS_HUMIDITY_VAL_HDL, BLE_ESS_HUMI_CCC_IDX);
sendNotification(connId, ESS_PRESSURE_VAL_HDL, BLE_ESS_PRES_CCC_IDX);
sendNotification(connId, ESS_IAQ_VAL_HDL, BLE_ESS_IAQ_CCC_IDX);
}
/*
* This registers and starts the ESS service.
*/
void bleESS_init(void)
{
AttsAddGroup(&svcESSGroup);
}
/*
* Instruct the ESS service to check if any characterstics have
* notifications enabled and enable/disable periodic measurements.
*/
void bleESS_ccc_update(void)
{
if (bsec_active()) {
return;
}
dmConnId_t connId = AppConnIsOpen();
if (connId != DM_CONN_ID_NONE &&
(AttsCccEnabled(connId, BLE_ESS_TEMP_CCC_IDX) ||
AttsCccEnabled(connId, BLE_ESS_HUMI_CCC_IDX) ||
AttsCccEnabled(connId, BLE_ESS_PRES_CCC_IDX) ||
AttsCccEnabled(connId, BLE_ESS_IAQ_CCC_IDX))) {
LOG_INFO("ess", "enable periodic measurement");
periodic(3000);
} else {
LOG_INFO("ess", "disable periodic measurement");
periodic(0);
}
}
#pragma once
#include "epicardium.h"
/*!< \brief Service start handle. */
#define ESS_START_HDL 0x1000
/*!< \brief Service end handle. */
#define ESS_END_HDL (ESS_MAX_HDL - 1)
enum {
/*!< \brief environmental sensing services service declaration */
ESS_SVC_HDL = ESS_START_HDL,
/*!< \brief temperature characteristic */
ESS_TEMPERATURE_CH_HDL,
ESS_TEMPERATURE_VAL_HDL,
ESS_TEMP_CH_CCC_HDL, /*!< Temperature CCCD */
/*!< \brief humidity characteristic */
ESS_HUMIDITY_CH_HDL,
ESS_HUMIDITY_VAL_HDL,
ESS_HUMI_CH_CCC_HDL, /*!< Humidity CCCD */
/*!< \brief pressure characteristic */
ESS_PRESSURE_CH_HDL,
ESS_PRESSURE_VAL_HDL,
ESS_PRES_CH_CCC_HDL, /*!< Pressure CCCD */
/*!< \brief IAQ/CO2 characteristic */
ESS_IAQ_CH_HDL,
ESS_IAQ_VAL_HDL,
ESS_IAQ_CH_CCC_HDL, /*!< IAQ CCCD */
/*!< \brief Maximum handle. */
ESS_MAX_HDL
};
void bleESS_ccc_update(void);
void bleESS_update_from_bsec_data(struct bsec_sensor_data *data);
/*
* BLE (Bluetooth Low energy) file transfer service.
*
* This file provides a BLE service and a characteristic which allows to
* write the content of the mytest.py file over BLE into the file system.
* We haven't found any existing BLE service which provides such a
* This file provides a BLE service and a characteristic which allows to
* write the content of the mytest.py file over BLE into the file system.
* We haven't found any existing BLE service which provides such a
* functionality so we implemented our own service.
* The service uses the UUID in fileTransSvc
......@@ -11,11 +11,11 @@
* BLE point to point links use a pretty small MTU (min 23 bytes, max 244
* bytes) which is negotiated between the central and the peripheral (card10).
*
*
* The first byte of each message is the type of message being send,
* different types are supported and based on this type the next bytes
* have different meaning.
*
*
* TODOs:
* * File deletion
* * File read
......@@ -33,6 +33,7 @@
#include "hci_vs.h"
#include <epicardium.h>
#include "os/core.h"
#include "util/bstream.h"
#include "att_api.h"
......@@ -40,6 +41,7 @@
#include "FreeRTOS.h"
#include "crc32.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
......@@ -76,119 +78,109 @@ enum {
};
/* BLE File transfer Service UUID */
static const uint8_t fileTransSvc[] = { FILE_TRANS_UUID_SUFFIX,
0x00,
FILE_TRANS_UUID_PREFIX };
static const uint8_t fileTransSvc[] = { FILE_TRANS_UUID_SUFFIX,
0x00,
FILE_TRANS_UUID_PREFIX };
static const uint16_t fileTransSvc_len = sizeof(fileTransSvc);
/* BLE File transfer Central TX configuration */
static const uint8_t txChConfig[] = { ATT_PROP_WRITE_NO_RSP,
UINT16_TO_BYTES(
FILE_TRANS_CENTRAL_TX_VAL_HDL),
FILE_TRANS_UUID_SUFFIX,
0x01,
FILE_TRANS_UUID_PREFIX };
static const uint8_t txChConfig[] = { ATT_PROP_WRITE_NO_RSP,
UINT16_TO_BYTES(
FILE_TRANS_CENTRAL_TX_VAL_HDL),
FILE_TRANS_UUID_SUFFIX,
0x01,
FILE_TRANS_UUID_PREFIX };
static const uint16_t txChConfig_len = sizeof(txChConfig);
/* BLE File transfer Central TX UUID */
static const uint8_t attTxChConfigUuid[] = { FILE_TRANS_UUID_SUFFIX,
0x01,
FILE_TRANS_UUID_PREFIX };
/* BLE File transfer Central RX configuration */
static const uint8_t rxChConfig[] = { ATT_PROP_READ | ATT_PROP_NOTIFY,
UINT16_TO_BYTES(
FILE_TRANS_CENTRAL_RX_VAL_HDL),
FILE_TRANS_UUID_SUFFIX,
0x02,
FILE_TRANS_UUID_PREFIX };
static const uint8_t rxChConfig[] = { ATT_PROP_READ | ATT_PROP_NOTIFY,
UINT16_TO_BYTES(
FILE_TRANS_CENTRAL_RX_VAL_HDL),
FILE_TRANS_UUID_SUFFIX,
0x02,
FILE_TRANS_UUID_PREFIX };
static const uint16_t rxChConfig_len = sizeof(rxChConfig);
/* BLE File transfer Central RX UUID */
static const uint8_t attRxChConfigUuid[] = { FILE_TRANS_UUID_SUFFIX,
0x02,
FILE_TRANS_UUID_PREFIX };
static uint8_t attRxChConfigValue[64];
static uint16_t attRxChConfigValue_len = 0;
/* File descriptor of the currently transferred file */
static int file_fd = -1;
/*
* Create the BLE service description.
*/
static void *fileTransAddGroupDyn(void)
{
void *pSHdl;
uint8_t initCcc[] = { UINT16_TO_BYTES(0x0000) };
/* Create the service */
pSHdl = AttsDynCreateGroup(FILE_TRANS_START_HDL, FILE_TRANS_END_HDL);
if (pSHdl != NULL) {
/* Primary service */
AttsDynAddAttrConst(
pSHdl,
attPrimSvcUuid,
fileTransSvc,
sizeof(fileTransSvc),
0,
ATTS_PERMIT_READ
);
/* File transfer Central TX characteristic */
AttsDynAddAttrConst(
pSHdl,
attChUuid,
txChConfig,
sizeof(txChConfig),
0,
ATTS_PERMIT_READ
);
/* File transfer Central TX, this contains information about the real data */
AttsDynAddAttr(
pSHdl,
attTxChConfigUuid,
NULL,
0,
128,
ATTS_SET_WRITE_CBACK | ATTS_SET_VARIABLE_LEN,
ATTS_PERMIT_WRITE
);
/* File transfer Central RX characteristic */
AttsDynAddAttrConst(
pSHdl,
attChUuid,
rxChConfig,
sizeof(rxChConfig),
0,
ATTS_PERMIT_READ
);
/* File transfer Central RX, this contains information about the real data */
AttsDynAddAttr(
pSHdl,
attRxChConfigUuid,
NULL,
0,
64,
ATTS_SET_READ_CBACK,
ATTS_PERMIT_READ
);
/* File transfer Central RX notification channel */
AttsDynAddAttr(
pSHdl,
attCliChCfgUuid,
initCcc,
sizeof(uint16_t),
sizeof(uint16_t),
ATTS_SET_CCC,
ATTS_PERMIT_READ | ATTS_PERMIT_WRITE
);
}
return pSHdl;
}
/* Attribute list for uriCfg group */
static const attsAttr_t fileTransCfgList[] = {
/* Service declaration */
{
.pUuid = attPrimSvcUuid,
.pValue = (uint8_t *)fileTransSvc,
.pLen = (uint16_t *)&fileTransSvc_len,
.maxLen = sizeof(fileTransSvc),
.settings = 0,
.permissions = ATTS_PERMIT_READ,
},
/* File transfer Central TX characteristic */
{
.pUuid = attChUuid,
.pValue = (uint8_t *)txChConfig,
.pLen = (uint16_t *)&txChConfig_len,
.maxLen = sizeof(txChConfig),
.settings = 0,
.permissions = ATTS_PERMIT_READ,
},
/* File transfer Central TX, this contains information about the real data */
{
.pUuid = attTxChConfigUuid,
.pValue = NULL,
.pLen = NULL,
.maxLen = 128,
.settings = ATTS_SET_WRITE_CBACK | ATTS_SET_VARIABLE_LEN,
.permissions = ATTS_PERMIT_WRITE | ATTS_PERMIT_WRITE_ENC |
ATTS_PERMIT_WRITE_AUTH,
},
/* File transfer Central RX characteristic */
{
.pUuid = attChUuid,
.pValue = (uint8_t *)rxChConfig,
.pLen = (uint16_t *)&rxChConfig_len,
.maxLen = sizeof(rxChConfig),
.settings = 0,
.permissions = ATTS_PERMIT_READ,
},
/* File transfer Central RX, this contains information about the real data */
{
.pUuid = attRxChConfigUuid,
.pValue = attRxChConfigValue,
.pLen = &attRxChConfigValue_len,
.maxLen = sizeof(attRxChConfigValue),
.settings = ATTS_SET_VARIABLE_LEN,
.permissions = ATTS_PERMIT_READ | ATTS_PERMIT_READ_ENC |
ATTS_PERMIT_READ_AUTH,
},
/* File transfer Central RX notification channel */
{
.pUuid = attCliChCfgUuid,
.pValue = attRxChConfigValue,
.pLen = &attRxChConfigValue_len,
.maxLen = sizeof(attRxChConfigValue),
.settings = ATTS_SET_CCC,
.permissions = ATTS_PERMIT_READ | ATTS_PERMIT_READ_ENC |
ATTS_PERMIT_READ_AUTH | ATTS_PERMIT_WRITE |
ATTS_PERMIT_WRITE_ENC | ATTS_PERMIT_WRITE_AUTH,
},
};
/**
* Send a repose with an optional CRC.
*
*
* The Central RX characteristic is filled and a notification is send in addition.
* If msg is given message is added in addition.
*/
......@@ -217,9 +209,11 @@ static void sendCrcResponse(
msg,
sizeof(answer) - len);
len += strlen(msg);
printf("BLE file transfer: %s\n", msg);
LOG_ERR("filetrans", "%s\n", msg);
} else {
printf("error message \"%s\" too long\n", msg);
LOG_ERR("filetrans",
"error message \"%s\" too long\n",
msg);
}
}
......@@ -227,6 +221,41 @@ static void sendCrcResponse(
AttsHandleValueNtf(connId, FILE_TRANS_CENTRAL_RX_VAL_HDL, len, answer);
}
/*
* This function splits the path into the folders and the file name and
* creates all the missing folders.
*/
static int bleFileCreateOrOpen(char *filepath)
{
char *pathEnd;
int pos = 0;
int ret;
while (true) {
pathEnd = strchr(filepath + pos, '/');
if (!pathEnd)
return epic_file_open(filepath, "w");
pathEnd[0] = '\00';
pos = pathEnd - filepath + 1;
if (strlen(filepath)) {
ret = epic_file_stat(filepath, NULL);
if (ret == -ENOENT) {
ret = epic_file_mkdir(filepath);
if (ret) {
LOG_ERR("filetrans",
"mkdir failed: %s, ret: %i\n",
filepath,
ret);
return ret;
}
}
}
pathEnd[0] = '/';
}
}
static uint8_t bleFileOpen(dmConnId_t connId, uint8_t *pValue, uint16_t len)
{
char filepath[100];
......@@ -238,11 +267,12 @@ static uint8_t bleFileOpen(dmConnId_t connId, uint8_t *pValue, uint16_t len)
/* Copy only file path and not type, make sure this is NULL terminated */
strncpy(filepath, (char *)pValue + 1, len - 1);
filepath[len - 1] = 0;
if (file_fd != -1)
epic_file_close(file_fd);
file_fd = epic_file_open(filepath, "w");
file_fd = bleFileCreateOrOpen(filepath);
if (file_fd < 0) {
sendCrcResponse(connId, 'e', 0, NULL, "open failed");
return ATT_ERR_RESOURCES;
......@@ -305,8 +335,9 @@ static uint8_t handleCentralTX(
} else if (
operation != ATT_PDU_EXEC_WRITE_REQ &&
operation != ATT_PDU_WRITE_CMD) {
printf("operation 0x%x not supported, try normal write\n",
operation);
LOG_ERR("filetrans",
"operation 0x%x not supported, try normal write\n",
operation);
return ATT_ERR_INVALID_PDU;
}
......@@ -334,7 +365,7 @@ static uint8_t handleCentralTX(
return ATT_SUCCESS;
case 'E':
printf("Error was acked");
LOG_ERR("filetrans", "Error was acked");
return ATT_SUCCESS;
default:
......@@ -346,7 +377,7 @@ static uint8_t handleCentralTX(
/*
* BLE file transfer write callback.
*
*
* This gets called when data is written by a BLE central to our BLE
* peripheral. Here we take care of handling the received data.
*/
......@@ -365,27 +396,22 @@ static uint8_t writeCallback(
connId, handle, operation, offset, len, pValue, pAttr
);
default:
printf("unsupported characteristic: %c\n", handle);
LOG_ERR("filetrans", "unsupported handle: %x\n", handle);
return ATT_ERR_HANDLE;
}
}
static uint8_t readCallback(
dmConnId_t connId,
uint16_t handle,
uint8_t operation,
uint16_t offset,
attsAttr_t *pAttr
) {
printf("read callback\n");
return ATT_SUCCESS;
}
static attsGroup_t fileTransCfgGroup = {
.pAttr = (attsAttr_t *)fileTransCfgList,
.writeCback = writeCallback,
.startHandle = FILE_TRANS_START_HDL,
.endHandle = FILE_TRANS_END_HDL,
};
/*
* This registers and starts the BLE file transfer service.
*/
void bleFileTransfer_init(void)
{
void *pSHdl = fileTransAddGroupDyn();
AttsDynRegister(pSHdl, readCallback, writeCallback);
AttsAddGroup(&fileTransCfgGroup);
}
/*
* Based on ble-profiles/sources/apps/hidapp/hidapp_main.c
*/
#include "cccd.h"
#include "hid.h"
#include "wsf_types.h"
#include "dm_api.h"
#include "att_api.h"
#include "svc_hid.h"
#include "hid/hid_api.h"
#include "os/core.h"
#include <stdio.h>
#include <string.h>
#include <stdint.h>
/*! HidApp Report Map (Descriptor) */
/* clang-format off */
/* Based on https://github.com/adafruit/Adafruit_CircuitPython_BLE/blob/master/adafruit_ble/services/standard/hid.py */
const uint8_t hidReportMap[] =
{
0x05, 0x01, /* Usage Page (Generic Desktop Ctrls) */
0x09, 0x06, /* Usage (Keyboard) */
0xA1, 0x01, /* Collection (Application) */
0x85, HIDAPP_KEYBOARD_REPORT_ID, /* Report ID (1) */
0x05, 0x07, /* Usage Page (Kbrd/Keypad) */
0x19, 0xE0, /* Usage Minimum (\xE0) */
0x29, 0xE7, /* Usage Maximum (\xE7) */
0x15, 0x00, /* Logical Minimum (0) */
0x25, 0x01, /* Logical Maximum (1) */
0x75, 0x01, /* Report Size (1) */
0x95, 0x08, /* Report Count (8) */
0x81, 0x02, /* Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) */
0x81, 0x01, /* Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position) */
0x19, 0x00, /* Usage Minimum (\x00) */
0x29, 0x89, /* Usage Maximum (\x89) */
0x15, 0x00, /* Logical Minimum (0) */
0x25, 0x89, /* Logical Maximum (137) */
0x75, 0x08, /* Report Size (8) */
0x95, 0x06, /* Report Count (6) */
0x81, 0x00, /* Input (Data,Array,Abs,No Wrap,Linear,Preferred State,No Null Position) */
0x05, 0x08, /* Usage Page (LEDs) */
0x19, 0x01, /* Usage Minimum (Num Lock) */
0x29, 0x05, /* Usage Maximum (Kana) */
0x15, 0x00, /* Logical Minimum (0) */
0x25, 0x01, /* Logical Maximum (1) */
0x75, 0x01, /* Report Size (1) */
0x95, 0x05, /* Report Count (5) */
0x91, 0x02, /* Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) */
0x95, 0x03, /* Report Count (3) */
0x91, 0x01, /* Output (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) */
0xC0, /* End Collection */
0x05, 0x01, /* Usage Page (Generic Desktop Ctrls) */
0x09, 0x02, /* Usage (Mouse) */
0xA1, 0x01, /* Collection (Application) */
0x09, 0x01, /* Usage (Pointer) */
0xA1, 0x00, /* Collection (Physical) */
0x85, HIDAPP_MOUSE_REPORT_ID, /* Report ID (2) */
0x05, 0x09, /* Usage Page (Button) */
0x19, 0x01, /* Usage Minimum (\x01) */
0x29, 0x05, /* Usage Maximum (\x05) */
0x15, 0x00, /* Logical Minimum (0) */
0x25, 0x01, /* Logical Maximum (1) */
0x95, 0x05, /* Report Count (5) */
0x75, 0x01, /* Report Size (1) */
0x81, 0x02, /* Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) */
0x95, 0x01, /* Report Count (1) */
0x75, 0x03, /* Report Size (3) */
0x81, 0x01, /* Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position) */
0x05, 0x01, /* Usage Page (Generic Desktop Ctrls) */
0x09, 0x30, /* Usage (X) */
0x09, 0x31, /* Usage (Y) */
0x15, 0x81, /* Logical Minimum (-127) */
0x25, 0x7F, /* Logical Maximum (127) */
0x75, 0x08, /* Report Size (8) */
0x95, 0x02, /* Report Count (2) */
0x81, 0x06, /* Input (Data,Var,Rel,No Wrap,Linear,Preferred State,No Null Position) */
0x09, 0x38, /* Usage (Wheel) */
0x15, 0x81, /* Logical Minimum (-127) */
0x25, 0x7F, /* Logical Maximum (127) */
0x75, 0x08, /* Report Size (8) */
0x95, 0x01, /* Report Count (1) */
0x81, 0x06, /* Input (Data,Var,Rel,No Wrap,Linear,Preferred State,No Null Position) */
0xC0, /* End Collection */
0xC0, /* End Collection */
0x05, 0x0C, /* Usage Page (Consumer) */
0x09, 0x01, /* Usage (Consumer Control) */
0xA1, 0x01, /* Collection (Application) */
0x85, HIDAPP_CONSUMER_REPORT_ID, /* Report ID (3) */
0x75, 0x10, /* Report Size (16) */
0x95, 0x01, /* Report Count (1) */
0x15, 0x01, /* Logical Minimum (1) */
0x26, 0x8C, 0x02, /* Logical Maximum (652) */
0x19, 0x01, /* Usage Minimum (Consumer Control) */
0x2A, 0x8C, 0x02, /* Usage Maximum (AC Send) */
0x81, 0x00, /* Input (Data,Array,Abs,No Wrap,Linear,Preferred State,No Null Position) */
0xC0, /* End Collection */
#if 0
0x05, 0x01, /* Usage Page (Generic Desktop Ctrls) */
0x09, 0x05, /* Usage (Game Pad) */
0xA1, 0x01, /* Collection (Application) */
0x85, 0x05, /* Report ID (5) */
0x05, 0x09, /* Usage Page (Button) */
0x19, 0x01, /* Usage Minimum (\x01) */
0x29, 0x10, /* Usage Maximum (\x10) */
0x15, 0x00, /* Logical Minimum (0) */
0x25, 0x01, /* Logical Maximum (1) */
0x75, 0x01, /* Report Size (1) */
0x95, 0x10, /* Report Count (16) */
0x81, 0x02, /* Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) */
0x05, 0x01, /* Usage Page (Generic Desktop Ctrls) */
0x15, 0x81, /* Logical Minimum (-127) */
0x25, 0x7F, /* Logical Maximum (127) */
0x09, 0x30, /* Usage (X) */
0x09, 0x31, /* Usage (Y) */
0x09, 0x32, /* Usage (Z) */
0x09, 0x35, /* Usage (Rz) */
0x75, 0x08, /* Report Size (8) */
0x95, 0x04, /* Report Count (4) */
0x81, 0x02, /* Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) */
0xC0, /* End Collection */
#endif
};
/* clang-format on */
const uint16_t hidReportMapLen = sizeof(hidReportMap);
/*! HID Report Type/ID and attribute handle map */
/* clang-format off */
static const hidReportIdMap_t hidAppReportIdSet[] =
{
/* type ID handle */
{HID_REPORT_TYPE_INPUT, HIDAPP_KEYBOARD_REPORT_ID, HID_INPUT_REPORT_1_HDL}, /* Keyboard Input Report */
{HID_REPORT_TYPE_OUTPUT, HIDAPP_KEYBOARD_REPORT_ID, HID_OUTPUT_REPORT_HDL}, /* Keyboard Output Report */
{HID_REPORT_TYPE_FEATURE, HIDAPP_KEYBOARD_REPORT_ID, HID_FEATURE_REPORT_HDL}, /* Keyboard Feature Report */
{HID_REPORT_TYPE_INPUT, HIDAPP_MOUSE_REPORT_ID, HID_INPUT_REPORT_2_HDL}, /* Mouse Input Report */
{HID_REPORT_TYPE_INPUT, HIDAPP_CONSUMER_REPORT_ID, HID_INPUT_REPORT_3_HDL}, /* Consumer Control Input Report */
{HID_REPORT_TYPE_INPUT, HID_KEYBOARD_BOOT_ID, HID_KEYBOARD_BOOT_IN_HDL}, /* Boot Keyboard Input Report */
{HID_REPORT_TYPE_OUTPUT, HID_KEYBOARD_BOOT_ID, HID_KEYBOARD_BOOT_OUT_HDL}, /* Boot Keyboard Output Report */
{HID_REPORT_TYPE_INPUT, HID_MOUSE_BOOT_ID, HID_MOUSE_BOOT_IN_HDL}, /* Boot Mouse Input Report */
};
/* clang-format on */
void hidAppOutputCback(
dmConnId_t connId, uint8_t id, uint16_t len, uint8_t *pReport
);
void hidAppFeatureCback(
dmConnId_t connId, uint8_t id, uint16_t len, uint8_t *pReport
);
void hidAppInfoCback(dmConnId_t connId, uint8_t type, uint8_t value);
/*! HID Profile Configuration */
/* clang-format off */
static const hidConfig_t hidAppHidConfig =
{
(hidReportIdMap_t*) hidAppReportIdSet, /* Report ID to Attribute Handle map */
sizeof(hidAppReportIdSet)/sizeof(hidReportIdMap_t), /* Size of Report ID to Attribute Handle map */
&hidAppOutputCback, /* Output Report Callback */
&hidAppFeatureCback, /* Feature Report Callback */
&hidAppInfoCback /* Info Callback */
};
/* clang-format on */
static void hidAppReportInit(void)
{
uint8_t iKeyboardBuffer[HIDAPP_KEYBOARD_INPUT_REPORT_LEN];
uint8_t iMouseBuffer[HIDAPP_MOUSE_INPUT_REPORT_LEN];
uint8_t iConsumerBuffer[HIDAPP_CONSUMER_INPUT_REPORT_LEN];
uint8_t oBuffer[HIDAPP_OUTPUT_REPORT_LEN];
uint8_t fBuffer[HIDAPP_FEATURE_REPORT_LEN];
/* Keyboard Input report */
memset(iKeyboardBuffer, 0, HIDAPP_KEYBOARD_INPUT_REPORT_LEN);
AttsSetAttr(
HID_INPUT_REPORT_1_HDL,
HIDAPP_KEYBOARD_INPUT_REPORT_LEN,
iKeyboardBuffer
);
/* Mouse Input report */
memset(iMouseBuffer, 0, HIDAPP_MOUSE_INPUT_REPORT_LEN);
AttsSetAttr(
HID_INPUT_REPORT_2_HDL,
HIDAPP_MOUSE_INPUT_REPORT_LEN,
iMouseBuffer
);
/* Consumer Control Input report */
memset(iConsumerBuffer, 0, HIDAPP_CONSUMER_INPUT_REPORT_LEN);
AttsSetAttr(
HID_INPUT_REPORT_3_HDL,
HIDAPP_CONSUMER_INPUT_REPORT_LEN,
iConsumerBuffer
);
/* Output report */
memset(oBuffer, 0, HIDAPP_OUTPUT_REPORT_LEN);
AttsSetAttr(HID_OUTPUT_REPORT_HDL, HIDAPP_OUTPUT_REPORT_LEN, oBuffer);
/* Feature report */
memset(fBuffer, 0, HIDAPP_FEATURE_REPORT_LEN);
AttsSetAttr(HID_FEATURE_REPORT_HDL, HIDAPP_FEATURE_REPORT_LEN, fBuffer);
}
void hid_work_init(void);
void hid_init(void)
{
SvcHidAddGroup();
SvcHidRegister(HidAttsWriteCback, NULL);
/* Initialize the HID profile */
HidInit(&hidAppHidConfig);
/* Initialize the report attributes */
hidAppReportInit();
hid_work_init();
}
#pragma once
#include "wsf_types.h"
#include "wsf_os.h"
/* The input report fits in one byte */
#define HIDAPP_KEYBOARD_INPUT_REPORT_LEN 8
#define HIDAPP_MOUSE_INPUT_REPORT_LEN 4
#define HIDAPP_CONSUMER_INPUT_REPORT_LEN 2
#define HIDAPP_OUTPUT_REPORT_LEN 1
#define HIDAPP_FEATURE_REPORT_LEN 1
/* HID Report IDs */
#define HIDAPP_KEYBOARD_REPORT_ID 1
#define HIDAPP_MOUSE_REPORT_ID 2
#define HIDAPP_CONSUMER_REPORT_ID 3
void hid_init(void);
void HidProcMsg(wsfMsgHdr_t *pMsg);
#include "hid.h"
#include "cccd.h"
#include "epicardium.h"
#include "os/core.h"
#include "dm_api.h"
#include "att_api.h"
#include "app_api.h"
#include "hid/hid_api.h"
#include "svc_hid.h"
#include "FreeRTOS.h"
#include "queue.h"
#include <stdint.h>
#include <stdio.h>
#include <string.h>
/* HidApp TX path flags */
#define HIDAPP_TX_FLAGS_READY 0x01
/*! application control block */
/* clang-format off */
struct
{
uint8_t txFlags; /* transmit flags */
uint8_t protocolMode; /* current protocol mode */
uint8_t hostSuspended; /* TRUE if host suspended */
} hidAppCb;
/* clang-format on */
struct report {
uint8_t report_id;
uint8_t data[8];
uint8_t len;
};
#define QUEUE_SIZE 10
static QueueHandle_t queue;
static uint8_t buffer[sizeof(struct report) * QUEUE_SIZE];
static StaticQueue_t queue_data;
static int hid_queue_data(uint8_t report_id, uint8_t *data, uint8_t len)
{
struct report report;
if (report_id < 1 || report_id > 3) {
return -EINVAL;
}
report.report_id = report_id;
if (len > sizeof(report.data)) {
return -EINVAL;
}
memcpy(report.data, data, len);
report.len = len;
if (xQueueSend(queue, &report, 0) != pdTRUE) {
/* Likely full */
return -EAGAIN;
}
return 0;
}
static bool hid_dequeue_data(dmConnId_t connId)
{
uint8_t cccHandle;
struct report report;
//lock();
if (!(hidAppCb.txFlags & HIDAPP_TX_FLAGS_READY)) {
//unlock();
return false;
}
// Loop until a CCC is enabled or the queue is empty
while (true) {
if (xQueueReceive(queue, &report, 0) != pdTRUE) {
break;
}
if (HidGetProtocolMode() == HID_PROTOCOL_MODE_BOOT) {
if (report.report_id == HIDAPP_KEYBOARD_REPORT_ID) {
report.report_id = HID_KEYBOARD_BOOT_ID;
cccHandle = HIDAPP_KBI_CCC_HDL;
} else if (report.report_id == HIDAPP_MOUSE_REPORT_ID) {
report.report_id = HID_MOUSE_BOOT_ID;
cccHandle = HIDAPP_MBI_CCC_HDL;
} else {
break;
}
} else {
if (report.report_id == HIDAPP_KEYBOARD_REPORT_ID) {
cccHandle = HIDAPP_IN_KEYBOARD_CCC_HDL;
} else if (report.report_id == HIDAPP_MOUSE_REPORT_ID) {
cccHandle = HIDAPP_IN_MOUSE_CCC_HDL;
} else if (report.report_id == HIDAPP_CONSUMER_REPORT_ID) {
cccHandle = HIDAPP_IN_CONSUMER_CCC_HDL;
} else {
break;
};
}
if (AttsCccEnabled(connId, cccHandle) || 1) {
hidAppCb.txFlags &= ~(HIDAPP_TX_FLAGS_READY);
/* Send the message */
HidSendInputReport(
connId,
report.report_id,
report.len,
report.data
);
break;
}
}
//unlock();
return true;
}
/*************************************************************************************************/
/*!
* \brief Callback to handle an output report from the host.
*
* \param connId The connection identifier.
* \param id The ID of the report.
* \param len The length of the report data in pReport.
* \param pReport A buffer containing the report.
*
* \return None.
*/
/*************************************************************************************************/
void hidAppOutputCback(
dmConnId_t connId, uint8_t id, uint16_t len, uint8_t *pReport
) {
/* TODO: process output reports */
}
/*************************************************************************************************/
/*!
* \brief Callback to handle a feature report from the host.
*
* \param connId The connection identifier.
* \param id The ID of the report.
* \param len The length of the report data in pReport.
* \param pReport A buffer containing the report.
*
* \return None.
*/
/*************************************************************************************************/
void hidAppFeatureCback(
dmConnId_t connId, uint8_t id, uint16_t len, uint8_t *pReport
) {
/* TODO: process feature reports */
}
/*************************************************************************************************/
/*!
* \brief Callback to handle a change in protocol mode or control point from the host.
*
* \param connId The connection identifier.
* \param mode The type of information (HID_INFO_CONTROL_POINT or HID_INFO_PROTOCOL_MODE)
* \param value The value of the information
*
* \return None.
*/
/*************************************************************************************************/
void hidAppInfoCback(dmConnId_t connId, uint8_t type, uint8_t value)
{
if (type == HID_INFO_PROTOCOL_MODE) {
LOG_INFO("hid", "protocol mode: %u\n", value);
hidAppCb.protocolMode = value;
} else if (type == HID_INFO_CONTROL_POINT) {
LOG_INFO("hid", "host suspended: %u\n", value);
hidAppCb.hostSuspended =
(value == HID_CONTROL_POINT_SUSPEND) ? TRUE : FALSE;
}
}
void HidProcMsg(wsfMsgHdr_t *pMsg)
{
if (!queue) {
/* Not initialized (yet). */
return;
}
if (pMsg->event == ATTS_HANDLE_VALUE_CNF) {
if (pMsg->status == ATT_SUCCESS) {
hidAppCb.txFlags |= HIDAPP_TX_FLAGS_READY;
hid_dequeue_data((dmConnId_t)pMsg->param);
}
}
if (pMsg->event == DM_CONN_OPEN_IND) {
hidAppCb.txFlags = HIDAPP_TX_FLAGS_READY;
struct report report;
while (xQueueReceive(queue, &report, 0) == pdTRUE)
;
/* Todo: At this point the CCC descriptors are not set up yet
* and things which get sent until then are discarded. */
}
}
int epic_ble_hid_send_report(uint8_t report_id, uint8_t *data, uint8_t len)
{
dmConnId_t connId = AppConnIsOpen();
if (connId == DM_CONN_ID_NONE) {
return -EIO;
}
if (!queue) {
return -EIO;
}
int ret;
ret = hid_queue_data(report_id, data, len);
if (ret < 0) {
return ret;
}
if (hid_dequeue_data(connId)) {
return 0;
} else {
return 1;
}
}
void hid_work_init(void)
{
queue = xQueueCreateStatic(
QUEUE_SIZE, sizeof(struct report), buffer, &queue_data
);
hidAppCb.txFlags = HIDAPP_TX_FLAGS_READY;
}
ble_sources = files(
'ble.c',
'epic_ble_api.c',
'epic_att_api.c',
'stack.c',
'ble_main.c',
'ble_adv.c',
'ble_attc.c',
'profiles/tipc_main.c',
'profiles/gap_main.c',
'svc_dis.c',
'svc_core.c',
'app/app_main.c',
'app/common/app_db.c',
'app/common/app_ui.c',
'bondings.c',
'uart.c',
'card10.c',
'ess.c',
'filetransfer.c',
'hid.c',
'hid_work.c',
)
/*************************************************************************************************/
/*!
* \file
*
* \brief GAP profile.
*
* Copyright (c) 2015-2018 Arm Ltd. All Rights Reserved.
* Arm Ltd. confidential and proprietary.
*
* IMPORTANT. Your use of this file is governed by a Software License Agreement
* ("Agreement") that must be accepted in order to download or otherwise receive a
* copy of this file. You may not use or copy this file for any purpose other than
* as described in the Agreement. If you do not agree to all of the terms of the
* Agreement do not use this file and delete all copies in your possession or control;
* if you do not have a copy of the Agreement, you must contact Arm Ltd. prior
* to any use, copying or further distribution of this software.
*/
/*************************************************************************************************/
#ifndef GAP_API_H
#define GAP_API_H
#include "att_api.h"
#ifdef __cplusplus
extern "C" {
#endif
/*! \addtogroup GAP_PROFILE
* \{ */
/**************************************************************************************************
Macros
**************************************************************************************************/
/*! \brief Enumeration of handle indexes of characteristics to be discovered */
enum
{
GAP_DN_HDL_IDX,
GAP_CAR_HDL_IDX, /*!< \brief Central Address Resolution */
GAP_RPAO_HDL_IDX, /*!< \brief Resolvable Private Address Only */
GAP_HDL_LIST_LEN /*!< \brief Handle list length */
};
/**************************************************************************************************
Function Declarations
**************************************************************************************************/
int GapGetDeviceName(char *buf, size_t buf_size);
void GapClearDeviceName(void);
/*************************************************************************************************/
/*!
* \brief Perform service and characteristic discovery for GAP service. Note that pHdlList
* must point to an array of handles of length \ref GAP_HDL_LIST_LEN. If discovery is
* successful the handles of discovered characteristics and descriptors will be set
* in pHdlList.
*
* \param connId Connection identifier.
* \param pHdlList Characteristic handle list.
*
* \return None.
*/
/*************************************************************************************************/
void GapDiscover(dmConnId_t connId, uint16_t *pHdlList);
/*************************************************************************************************/
/*!
* \brief Process a value received in an ATT read response, notification, or indication
* message. Parameter pHdlList must point to an array of length \ref GAP_HDL_LIST_LEN.
* If the attribute handle of the message matches a handle in the handle list the value
* is processed, otherwise it is ignored.
*
* \param pHdlList Characteristic handle list.
* \param pMsg ATT callback message.
*
* \return \ref ATT_SUCCESS if handle is found, \ref ATT_ERR_NOT_FOUND otherwise.
*/
/*************************************************************************************************/
uint8_t GapValueUpdate(uint16_t *pHdlList, attEvt_t *pMsg);
/*! \} */ /* GAP_PROFILE */
#ifdef __cplusplus
};
#endif
#endif /* GAP_API_H */
/*************************************************************************************************/
/*!
* \file
*
* \brief GAP profile.
*
* Copyright (c) 2015-2018 Arm Ltd. All Rights Reserved.
* ARM Ltd. confidential and proprietary.
*
* IMPORTANT. Your use of this file is governed by a Software License Agreement
* ("Agreement") that must be accepted in order to download or otherwise receive a
* copy of this file. You may not use or copy this file for any purpose other than
* as described in the Agreement. If you do not agree to all of the terms of the
* Agreement do not use this file and delete all copies in your possession or control;
* if you do not have a copy of the Agreement, you must contact ARM Ltd. prior
* to any use, copying or further distribution of this software.
*/
/*************************************************************************************************/
#include "wsf_types.h"
#include "wsf_assert.h"
#include "wsf_trace.h"
#include "app_db.h"
#include "app_api.h"
#include "gap_api.h"
#include <string.h>
/* card10:
* copied from lib/sdk/Libraries/BTLE/stack/ble-profiles/sources/profiles/gap/gap_main.c
*/
/* clang-format off */
/* clang-formet turned off for easier diffing against orginal file */
/**************************************************************************************************
Local Variables
**************************************************************************************************/
static char dn_value[ATT_DEFAULT_PAYLOAD_LEN + 1];
/*! GAP service characteristics for discovery */
static const attcDiscChar_t gapDn =
{
attDnChUuid,
0
};
/*! Central Address Resolution */
static const attcDiscChar_t gapCar =
{
attCarChUuid,
0
};
/*! Resolvable Private Address Only */
static const attcDiscChar_t gapRpao =
{
attRpaoChUuid,
0
};
/*! List of characteristics to be discovered; order matches handle index enumeration */
static const attcDiscChar_t *gapDiscCharList[] =
{
&gapDn,
&gapCar, /* Central Address Resolution */
&gapRpao /* Resolvable Private Address Only */
};
/* sanity check: make sure handle list length matches characteristic list length */
WSF_CT_ASSERT(GAP_HDL_LIST_LEN == ((sizeof(gapDiscCharList) / sizeof(attcDiscChar_t *))));
int GapGetDeviceName(char *buf, size_t buf_size)
{
memset(buf, 0, buf_size);
strncpy(buf, dn_value, buf_size - 1);
return 0;
}
void GapClearDeviceName(void)
{
dn_value[0] = 0;
}
/*************************************************************************************************/
/*!
* \brief Perform service and characteristic discovery for GAP service. Note that pHdlList
* must point to an array of handles of length GAP_HDL_LIST_LEN. If discovery is
* successful the handles of discovered characteristics and descriptors will be set
* in pHdlList.
*
* \param connId Connection identifier.
* \param pHdlList Characteristic handle list.
*
* \return None.
*/
/*************************************************************************************************/
void GapDiscover(dmConnId_t connId, uint16_t *pHdlList)
{
AppDiscFindService(connId, ATT_16_UUID_LEN, (uint8_t *) attGapSvcUuid,
GAP_HDL_LIST_LEN, (attcDiscChar_t **) gapDiscCharList, pHdlList);
}
/*************************************************************************************************/
/*!
* \brief Process a value received in an ATT read response, notification, or indication
* message. Parameter pHdlList must point to an array of length GAP_HDL_LIST_LEN.
* If the attribute handle of the message matches a handle in the handle list the value
* is processed, otherwise it is ignored.
*
* \param pHdlList Characteristic handle list.
* \param pMsg ATT callback message.
*
* \return ATT_SUCCESS if handle is found, ATT_ERR_NOT_FOUND otherwise.
*/
/*************************************************************************************************/
uint8_t GapValueUpdate(uint16_t *pHdlList, attEvt_t *pMsg)
{
uint8_t status = ATT_SUCCESS;
/* device name string */
if (pMsg->handle == pHdlList[GAP_DN_HDL_IDX])
{
int len = (pMsg->valueLen < (sizeof(dn_value) - 1)) ? pMsg->valueLen : (sizeof(dn_value) - 1);
memcpy(dn_value, pMsg->pValue, len);
dn_value[len] = '\0';
}
/* Central Address Resolution */
else if (pMsg->handle == pHdlList[GAP_CAR_HDL_IDX])
{
appDbHdl_t dbHdl;
/* if there's a device record */
if ((dbHdl = AppDbGetHdl((dmConnId_t)pMsg->hdr.param)) != APP_DB_HDL_NONE)
{
if ((pMsg->pValue[0] == FALSE) || (pMsg->pValue[0] == TRUE))
{
/* store value in device database */
AppDbSetPeerAddrRes(dbHdl, pMsg->pValue[0]);
}
else
{
/* invalid value */
status = ATT_ERR_RANGE;
}
APP_TRACE_INFO1("Central address resolution: %d", pMsg->pValue[0]);
}
}
/* handle not found in list */
else
{
status = ATT_ERR_NOT_FOUND;
}
return status;
}
/* clang-format on */
/*************************************************************************************************/
/*!
* \file
*
* \brief Time profile client.
*
* Copyright (c) 2011-2018 Arm Ltd. All Rights Reserved.
* ARM Ltd. confidential and proprietary.
*
* IMPORTANT. Your use of this file is governed by a Software License Agreement
* ("Agreement") that must be accepted in order to download or otherwise receive a
* copy of this file. You may not use or copy this file for any purpose other than
* as described in the Agreement. If you do not agree to all of the terms of the
* Agreement do not use this file and delete all copies in your possession or control;
* if you do not have a copy of the Agreement, you must contact ARM Ltd. prior
* to any use, copying or further distribution of this software.
*/
/*************************************************************************************************/
/* card10:
* copied from lib/sdk/Libraries/BTLE/stack/ble-profiles/sources/profiles/tipc/tipc_main.c
*/
/* clang-format off */
/* clang-formet turned off for easier diffing against orginal file */
#include "wsf_types.h"
#include "wsf_assert.h"
#include "wsf_trace.h"
#include "util/bstream.h"
#include "app_api.h"
#include "tipc/tipc_api.h"
#include "epicardium.h"
#include <time.h>
#include <stdio.h>
/**************************************************************************************************
Local Variables
**************************************************************************************************/
static time_t s_time;
/*!
* Current Time service
*/
/* Characteristics for discovery */
/*! Current time */
static const attcDiscChar_t tipcCtsCt =
{
attCtChUuid,
ATTC_SET_REQUIRED
};
/*! Current time client characteristic configuration descriptor */
static const attcDiscChar_t tipcCtsCtCcc =
{
attCliChCfgUuid,
ATTC_SET_REQUIRED | ATTC_SET_DESCRIPTOR
};
/*! Local time information */
static const attcDiscChar_t tipcCtsLti =
{
attLtiChUuid,
0
};
/*! Reference time information */
static const attcDiscChar_t tipcCtsRti =
{
attRtiChUuid,
0
};
/*! List of characteristics to be discovered; order matches handle index enumeration */
static const attcDiscChar_t *tipcCtsDiscCharList[] =
{
&tipcCtsCt, /* Current time */
&tipcCtsCtCcc, /* Current time client characteristic configuration descriptor */
&tipcCtsLti, /* Local time information */
&tipcCtsRti /* Reference time information */
};
/* sanity check: make sure handle list length matches characteristic list length */
WSF_CT_ASSERT(TIPC_CTS_HDL_LIST_LEN == ((sizeof(tipcCtsDiscCharList) / sizeof(attcDiscChar_t *))));
/*************************************************************************************************/
/*!
* \brief Perform service and characteristic discovery for Current Time service. Parameter
* pHdlList must point to an array of length TIPC_CTS_HDL_LIST_LEN. If discovery is
* successful the handles of discovered characteristics and descriptors will be set
* in pHdlList.
*
* \param connId Connection identifier.
* \param pHdlList Characteristic handle list.
*
* \return None.
*/
/*************************************************************************************************/
void TipcCtsDiscover(dmConnId_t connId, uint16_t *pHdlList)
{
AppDiscFindService(connId, ATT_16_UUID_LEN, (uint8_t *) attCtsSvcUuid,
TIPC_CTS_HDL_LIST_LEN, (attcDiscChar_t **) tipcCtsDiscCharList, pHdlList);
}
/*************************************************************************************************/
/*!
* \brief Process a value received in an ATT read response, notification, or indication
* message. Parameter pHdlList must point to an array of length TIPC_CTS_HDL_LIST_LEN.
* If the attribute handle of the message matches a handle in the handle list the value
* is processed, otherwise it is ignored.
*
* \param pHdlList Characteristic handle list.
* \param pMsg ATT callback message.
*
* \return ATT_SUCCESS if handle is found, ATT_ERR_NOT_FOUND otherwise.
*/
/*************************************************************************************************/
uint8_t TipcCtsValueUpdate(uint16_t *pHdlList, attEvt_t *pMsg)
{
uint8_t status = ATT_SUCCESS;
uint8_t *p;
uint16_t year;
uint8_t month, day, hour, min, sec, dayOfWeek, adjustReason;
uint8_t sec256 = 0;
int8_t timeZone;
uint8_t dstOffset, source, accuracy;
/* Suppress unused variable compile warning */
(void)month; (void)day; (void)hour; (void)min; (void)sec; (void)dayOfWeek; (void)adjustReason;
(void)year; (void)sec256; (void)dstOffset; (void)accuracy; (void)timeZone; (void)source;
/* current time */
if (pMsg->handle == pHdlList[TIPC_CTS_CT_HDL_IDX])
{
/* parse value */
p = pMsg->pValue;
BSTREAM_TO_UINT16(year, p);
BSTREAM_TO_UINT8(month, p);
BSTREAM_TO_UINT8(day, p);
BSTREAM_TO_UINT8(hour, p);
BSTREAM_TO_UINT8(min, p);
BSTREAM_TO_UINT8(sec, p);
BSTREAM_TO_UINT8(dayOfWeek, p);
BSTREAM_TO_UINT8(sec256, p);
BSTREAM_TO_UINT8(adjustReason, p);
struct tm t;
t.tm_year = year - 1900;
t.tm_mon = month - 1;
t.tm_mday = day;
t.tm_hour = hour;
t.tm_min = min;
t.tm_sec = sec;
t.tm_isdst = -1; /* unknown */
s_time = mktime(&t);
APP_TRACE_INFO3("Date: %d/%d/%d", month, day, year);
APP_TRACE_INFO3("Time: %02d:%02d:%02d", hour, min, sec);
APP_TRACE_INFO3("dayOfWeek:%d sec256:%d adjustReason:%d", dayOfWeek, sec256, adjustReason);
}
/* local time information */
else if (pMsg->handle == pHdlList[TIPC_CTS_LTI_HDL_IDX])
{
/* parse value */
p = pMsg->pValue;
BSTREAM_TO_UINT8(timeZone, p);
BSTREAM_TO_UINT8(dstOffset, p);
APP_TRACE_INFO2("timeZone:%d dstOffset:%d", timeZone, dstOffset);
if(s_time) {
char buf[32];
snprintf(buf, sizeof(buf), "%+05d", (timeZone +dstOffset) / 4 * 100);
epic_config_set_string("timezone", buf);
epic_rtc_set_milliseconds(s_time * 1000 + (1000 * sec256) / 256 - (timeZone + dstOffset) * 15 * 60 * 1000);
}
}
/* reference time information */
else if (pMsg->handle == pHdlList[TIPC_CTS_RTI_HDL_IDX])
{
/* parse value */
p = pMsg->pValue;
BSTREAM_TO_UINT8(source, p);
BSTREAM_TO_UINT8(accuracy, p);
BSTREAM_TO_UINT8(day, p);
BSTREAM_TO_UINT8(hour, p);
APP_TRACE_INFO2("Ref. time source:%d accuracy:%d", source, accuracy);
APP_TRACE_INFO2("Last update days:%d hours:%d", day, hour);
}
/* handle not found in list */
else
{
status = ATT_ERR_NOT_FOUND;
}
return status;
}
/* clang-format on */
......@@ -89,8 +89,8 @@ const LlRtCfg_t _ll_cfg = {
/*dtmRxSyncMs*/ 10000,
/* PHY */
/*phy2mSup*/ TRUE,
/*phyCodedSup*/ TRUE,
/*phy2mSup*/ FALSE,
/*phyCodedSup*/ FALSE,
/*stableModIdxTxSup*/ FALSE,
/*stableModIdxRxSup*/ FALSE
};
......@@ -98,22 +98,20 @@ const LlRtCfg_t _ll_cfg = {
const BbRtCfg_t _bb_cfg = {
/*clkPpm*/ 20,
/*rfSetupDelayUsec*/ BB_RF_SETUP_DELAY_US,
/*defaultTxPower*/ -10,
/*maxScanPeriodMsec*/ BB_MAX_SCAN_PERIOD_MS,
/*schSetupDelayUsec*/ BB_SCH_SETUP_DELAY_US
};
/*************************************************************************************************/
/*!
* \brief Initialize stack.
* \brief Initialize link layer part of the stack.
*
* \return None.
*/
/*************************************************************************************************/
void StackInit(void)
void LlStackInit()
{
wsfHandlerId_t handlerId;
/* card10: We do not use the SDMA HCI at the moment. The block below is not compiled. */
#ifndef ENABLE_SDMA
uint32_t memUsed;
......@@ -129,18 +127,31 @@ void StackInit(void)
.freeMemAvail = LL_MEMORY_FOOTPRINT
};
#ifdef DATS_APP_USE_LEGACY_API
memUsed = LlInitControllerExtInit(&ll_init_cfg);
#else /* DATS_APP_USE_LEGACY_API */
memUsed = LlInitControllerExtInit(&ll_init_cfg);
#endif /* DATS_APP_USE_LEGACY_API */
if(memUsed != LL_MEMORY_FOOTPRINT)
{
printf("Controller memory mismatch 0x%x != 0x%x\n", (unsigned int)memUsed,
printf("Controller memory mismatch 0x%x != 0x%x\n", (unsigned int)memUsed,
(unsigned int)LL_MEMORY_FOOTPRINT);
}
#endif
}
/*************************************************************************************************/
/*!
* \brief Initialize stack.
*
* \return None.
*/
/*************************************************************************************************/
void StackInit(void)
{
wsfHandlerId_t handlerId;
SecInit();
SecRandInit();
SecAesInit();
SecCmacInit();
SecEccInit();
/* card10:
* These calls register a queue for callbacks in the OS abstraction
......@@ -152,14 +163,10 @@ void StackInit(void)
handlerId = WsfOsSetNextHandler(HciHandler);
HciHandlerInit(handlerId);
SecInit();
SecAesInit();
SecCmacInit();
SecEccInit();
handlerId = WsfOsSetNextHandler(DmHandler);
DmDevVsInit(0);
DmAdvInit();
DmScanInit();
DmConnInit();
DmConnSlaveInit();
DmSecInit();
......@@ -177,6 +184,8 @@ void StackInit(void)
AttHandlerInit(handlerId);
AttsInit();
AttsIndInit();
AttsDynInit();
AttcInit();
handlerId = WsfOsSetNextHandler(SmpHandler);
SmpHandlerInit(handlerId);
......@@ -185,5 +194,8 @@ void StackInit(void)
/*TODO card10: Probably want to adjust this */
HciSetMaxRxAclLen(100);
handlerId = WsfOsSetNextHandler(AppHandler);
AppHandlerInit(handlerId);
}
/* clang-format off */
......@@ -24,13 +24,17 @@
*/
/* clang-format off */
/* clang-formet turned off for easier diffing against orginal file */
#include "card10-version.h"
#include "wsf_types.h"
#include "att_api.h"
#include "wsf_assert.h"
#include "wsf_trace.h"
#include "util/bstream.h"
#define DIS_MAXSIZE_FWR_ATT 32
#include "svc_dis.h"
#include "svc_cfg.h"
#include <string.h>
/**************************************************************************************************
Macros
......@@ -60,10 +64,10 @@
#define DIS_DEFAULT_SERIAL_NUM_LEN 1
/*! Default firmware revision */
#define DIS_DEFAULT_FW_REV "<git hash>"
#define DIS_DEFAULT_FW_REV CARD10_VERSION
/*! Length of default firmware revision */
#define DIS_DEFAULT_FW_REV_LEN 10
#define DIS_DEFAULT_FW_REV_LEN strlen(CARD10_VERSION)
/*! Default hardware revision */
#define DIS_DEFAULT_HW_REV "1"
......@@ -128,7 +132,7 @@ static const uint16_t disLenFwrCh = sizeof(disValFwrCh);
/* Firmware revision string */
static const uint8_t disUuFwr[] = {UINT16_TO_BYTES(ATT_UUID_FIRMWARE_REV)};
static uint8_t disValFwr[DIS_MAXSIZE_FWR_ATT] = DIS_DEFAULT_FW_REV;
static uint16_t disLenFwr = DIS_DEFAULT_FW_REV_LEN;
static uint16_t disLenFwr;
/* Hardware revision string characteristic */
static const uint8_t disValHwrCh[] = {ATT_PROP_READ, UINT16_TO_BYTES(DIS_HWR_HDL), UINT16_TO_BYTES(ATT_UUID_HARDWARE_REV)};
......@@ -319,6 +323,7 @@ WSF_CT_ASSERT(((sizeof(disList) / sizeof(disList[0])) == DIS_END_HDL - DIS_START
/*************************************************************************************************/
void SvcDisAddGroup(void)
{
disLenFwr = DIS_DEFAULT_FW_REV_LEN;
AttsAddGroup(&svcDisGroup);
}
......
#include "uart.h"
#include "cccd.h"
#include "modules/modules.h"
#include "drivers/drivers.h"
#include "wsf_types.h"
#include "util/bstream.h"
#include "att_api.h"
#include "dm_api.h"
#include "app_api.h"
#include "FreeRTOS.h"
#include "timers.h"
......@@ -11,85 +17,91 @@
#include <string.h>
#include <stdbool.h>
#define UART_START_HDL 0x800 /*!< \brief Service start handle. */
#define UART_END_HDL (UART_MAX_HDL - 1) /*!< \brief Service end handle. */
/**************************************************************************************************
Handles
**************************************************************************************************/
/*! \brief UART Service Handles */
enum { UART_SVC_HDL = UART_START_HDL, /*!< \brief UART service declaration */
UART_RX_CH_HDL, /*!< \brief UART rx characteristic */
UART_RX_HDL, /*!< \brief UART rx value */
UART_TX_CH_HDL, /*!< \brief UART tx characteristic */
UART_TX_HDL, /*!< \brief UART tx value */
UART_TX_CH_CCC_HDL, /*!< \brief UART tx CCCD */
UART_MAX_HDL /*!< \brief Maximum handle. */
};
/**@}*/
/* clang-format off */
static const uint8_t UARTSvc[] = {0x9E,0xCA,0xDC,0x24,0x0E,0xE5,0xA9,0xE0,0x93,0xF3,0xA3,0xB5,0x01,0x00,0x40,0x6E};
static const uint16_t UARTSvc_len = sizeof(UARTSvc);
static const uint8_t uartRxCh[] = {ATT_PROP_WRITE, UINT16_TO_BYTES(UART_RX_HDL), 0x9E,0xCA,0xDC,0x24,0x0E,0xE5,0xA9,0xE0,0x93,0xF3,0xA3,0xB5,0x02,0x00,0x40,0x6E};
const uint8_t attUartRxChUuid[] = {0x9E,0xCA,0xDC,0x24,0x0E,0xE5, 0xA9,0xE0,0x93,0xF3,0xA3,0xB5,0x02,0x00,0x40,0x6E};
static const uint16_t uartRxCh_len = sizeof(uartRxCh);
static const uint8_t attUartRxChUuid[] = {0x9E,0xCA,0xDC,0x24,0x0E,0xE5, 0xA9,0xE0,0x93,0xF3,0xA3,0xB5,0x02,0x00,0x40,0x6E};
static const uint8_t uartTxCh[] = {ATT_PROP_READ | ATT_PROP_NOTIFY, UINT16_TO_BYTES(UART_TX_HDL), 0x9E,0xCA,0xDC,0x24,0x0E,0xE5,0xA9,0xE0,0x93,0xF3,0xA3,0xB5,0x03,0x00,0x40,0x6E};
const uint8_t attUartTxChUuid[] = {0x9E,0xCA,0xDC,0x24,0x0E,0xE5, 0xA9,0xE0,0x93,0xF3,0xA3,0xB5,0x03,0x00,0x40,0x6E};
/* clang-format on */
static const uint16_t uartTxCh_len = sizeof(uartTxCh);
static const uint8_t attUartTxChUuid[] = {0x9E,0xCA,0xDC,0x24,0x0E,0xE5, 0xA9,0xE0,0x93,0xF3,0xA3,0xB5,0x03,0x00,0x40,0x6E};
static void *SvcUARTAddGroupDyn(void)
{
void *pSHdl;
uint8_t initCcc[] = { UINT16_TO_BYTES(0x0000) };
/* Create the service */
pSHdl = AttsDynCreateGroup(UART_START_HDL, UART_END_HDL);
if (pSHdl != NULL) {
/* clang-format off */
/* Primary service */
AttsDynAddAttrConst( pSHdl, attPrimSvcUuid, UARTSvc, sizeof(UARTSvc),
0, ATTS_PERMIT_READ);
/* UART rx characteristic */
AttsDynAddAttrConst( pSHdl, attChUuid, uartRxCh, sizeof(uartRxCh),
0, ATTS_PERMIT_READ);
// XXX: attUartRxChUuid is 16 bytes but nothing says so....
/* UART rx value */
// XXX: not sure if max value of 128 is fine...
AttsDynAddAttr( pSHdl, attUartRxChUuid, NULL, 0, 128,
ATTS_SET_WRITE_CBACK | ATTS_SET_VARIABLE_LEN, ATTS_PERMIT_WRITE);
/* UART tx characteristic */
AttsDynAddAttrConst( pSHdl, attChUuid, uartTxCh, sizeof(uartTxCh),
0, ATTS_PERMIT_READ);
/* UART tx value */
/* TODO: do we need ATTS_SET_READ_CBACK ? */
AttsDynAddAttr( pSHdl, attUartTxChUuid, NULL, 0, sizeof(uint8_t),
ATTS_SET_READ_CBACK, ATTS_PERMIT_READ);
/* UART tx CCC descriptor */
AttsDynAddAttr( pSHdl, attCliChCfgUuid, initCcc, sizeof(uint16_t), sizeof(uint16_t),
ATTS_SET_CCC, ATTS_PERMIT_READ | ATTS_PERMIT_WRITE);
/* clang-format on */
}
static uint8_t uartValTxChCcc[] = {UINT16_TO_BYTES(0x0000)};
static const uint16_t uartLenTxChCcc = sizeof(uartValTxChCcc);
return pSHdl;
}
static uint8_t ble_uart_tx_buf[20];
static uint16_t ble_uart_buf_tx_fill = 0;
/* clang-format on */
dmConnId_t active_connection = 0;
/* Attribute list for uriCfg group */
static const attsAttr_t uartAttrCfgList[] = {
/* Primary service */
{
.pUuid = attPrimSvcUuid,
.pValue = (uint8_t *)UARTSvc,
.pLen = (uint16_t *)&UARTSvc_len,
.maxLen = sizeof(UARTSvc),
.settings = 0,
.permissions = ATTS_PERMIT_READ,
},
/* UART rx characteristic */
{
.pUuid = attChUuid,
.pValue = (uint8_t *)uartRxCh,
.pLen = (uint16_t *)&uartRxCh_len,
.maxLen = sizeof(uartRxCh),
.settings = 0,
.permissions = ATTS_PERMIT_READ,
},
/* UART rx value */
{
.pUuid = attUartRxChUuid,
.pValue = NULL,
.pLen = NULL,
.maxLen = 128,
.settings = ATTS_SET_WRITE_CBACK | ATTS_SET_VARIABLE_LEN,
.permissions = ATTS_PERMIT_WRITE | ATTS_PERMIT_WRITE_ENC |
ATTS_PERMIT_WRITE_AUTH,
},
/* UART tx characteristic */
{
.pUuid = attChUuid,
.pValue = (uint8_t *)uartTxCh,
.pLen = (uint16_t *)&uartTxCh_len,
.maxLen = sizeof(uartTxCh),
.settings = 0,
.permissions = ATTS_PERMIT_READ,
},
/* UART tx value */
{
.pUuid = attUartTxChUuid,
.pValue = ble_uart_tx_buf,
.pLen = &ble_uart_buf_tx_fill,
.maxLen = sizeof(ble_uart_tx_buf),
.settings = 0,
.permissions = ATTS_PERMIT_READ | ATTS_PERMIT_READ_ENC |
ATTS_PERMIT_READ_AUTH,
},
/* UART tx CCC descriptor */
{
.pUuid = attCliChCfgUuid,
.pValue = uartValTxChCcc,
.pLen = (uint16_t *)&uartLenTxChCcc,
.maxLen = sizeof(uartValTxChCcc),
.settings = ATTS_SET_CCC,
.permissions =
(ATTS_PERMIT_READ |
ATTS_PERMIT_WRITE) // How about security?
},
static uint8_t UARTReadCback(
dmConnId_t connId,
uint16_t handle,
uint8_t operation,
uint16_t offset,
attsAttr_t *pAttr
) {
printf("read callback\n");
return ATT_SUCCESS;
}
};
static uint8_t UARTWriteCback(
dmConnId_t connId,
......@@ -100,76 +112,107 @@ static uint8_t UARTWriteCback(
uint8_t *pValue,
attsAttr_t *pAttr
) {
active_connection = connId;
static bool was_r = false;
//printf("UARTWriteCback %d: ", len);
int i;
for (i = 0; i < len; i++) {
//printf("%c", pValue[i]);
if (pValue[i] == '\n' && !was_r) {
serial_enqueue_char('\r');
}
was_r = pValue[i] == '\r';
serial_enqueue_char(pValue[i]);
}
serial_enqueue_char('\r');
//printf("\n");
#if 0
AttsSetAttr(UART_TX_HDL, len, pValue);
AttsHandleValueNtf(connId, UART_TX_HDL, len, pValue);
#endif
return ATT_SUCCESS;
}
uint8_t ble_uart_tx_buf[129];
uint8_t ble_uart_buf_tx_fill;
int ble_uart_lasttick = 0;
static bool done;
static bool again;
void ble_uart_write(uint8_t *pValue, uint8_t len)
static void ble_uart_flush(void)
{
int i;
for (i = 0; i < len; i++) {
if (pValue[i] >= 0x20 && pValue[i] < 0x7f) {
ble_uart_tx_buf[ble_uart_buf_tx_fill] = pValue[i];
ble_uart_buf_tx_fill++;
} else if (pValue[i] == '\r' || pValue[i] == '\n') {
if (ble_uart_buf_tx_fill > 0) {
AttsSetAttr(
UART_TX_HDL,
ble_uart_buf_tx_fill,
ble_uart_tx_buf
);
if (active_connection) {
int x = xTaskGetTickCount() -
ble_uart_lasttick;
if (x < 100) {
// Ugly hack if we already send something recently.
// TODO: figure out how fast we can send or use indications
vTaskDelay(100 - x);
}
//printf("notify: ");
//int j;
//for(j=0;j<ble_uart_buf_tx_fill;j++) {
// printf("%02x ", ble_uart_tx_buf[j]);
//}
//printf("\n");
if (ble_uart_buf_tx_fill == 0) {
return;
}
dmConnId_t connId = AppConnIsOpen();
if (connId != DM_CONN_ID_NONE) {
if (AttsCccEnabled(connId, UART_TX_CH_CCC_IDX)) {
done = false;
again = true;
int t0 = xTaskGetTickCount();
while (!done && ((xTaskGetTickCount() - t0) < 1000)) {
if (again) {
again = false;
AttsHandleValueNtf(
active_connection,
connId,
UART_TX_HDL,
ble_uart_buf_tx_fill,
ble_uart_tx_buf
);
ble_uart_lasttick = xTaskGetTickCount();
}
ble_uart_buf_tx_fill = 0;
/* This function is supposed to only be called
* from the API scheduler with lowest priority.
*
* If that is not the case anymore, use a delay
* instead of the yield. Ideally refactor to avoid
* the delay.
*/
//vTaskDelay(5);
taskYIELD();
}
}
}
ble_uart_buf_tx_fill = 0;
}
static void ble_uart_write_char(uint8_t c)
{
ble_uart_tx_buf[ble_uart_buf_tx_fill] = c;
ble_uart_buf_tx_fill++;
// TODO: increase buffer if configured MTU allows it
if (ble_uart_buf_tx_fill == sizeof(ble_uart_tx_buf)) {
ble_uart_flush();
}
}
void ble_uart_write(uint8_t *pValue, uint8_t len)
{
for (int i = 0; i < len; i++) {
ble_uart_write_char(pValue[i]);
}
// TODO schedule timer in a few ms to flush the buffer
ble_uart_flush();
}
static attsGroup_t uartCfgGroup = {
.pAttr = (attsAttr_t *)uartAttrCfgList,
.writeCback = UARTWriteCback,
.startHandle = UART_START_HDL,
.endHandle = UART_END_HDL,
};
void bleuart_init(void)
{
/* Add the UART service dynamically */
void *pSHdl;
pSHdl = SvcUARTAddGroupDyn();
AttsDynRegister(pSHdl, UARTReadCback, UARTWriteCback);
//AttsDynRegister(pSHdl, NULL, UARTWriteCback);
/* Add the UART service */
AttsAddGroup(&uartCfgGroup);
}
void UartProcMsg(bleMsg_t *pMsg)
{
if (pMsg->hdr.event == ATTS_HANDLE_VALUE_CNF) {
if (pMsg->att.handle == UART_TX_HDL) {
if (pMsg->hdr.status == ATT_SUCCESS) {
done = true;
} else if (pMsg->hdr.status == ATT_ERR_OVERFLOW) {
again = true;
}
}
}
if (pMsg->hdr.event == DM_CONN_OPEN_IND) {
ble_uart_buf_tx_fill = 0;
}
}
#pragma once
#include "ble_api.h"
#define UART_START_HDL 0x800 /*!< \brief Service start handle. */
#define UART_END_HDL (UART_MAX_HDL - 1) /*!< \brief Service end handle. */
/*! \brief UART Service Handles */
enum { UART_SVC_HDL = UART_START_HDL, /*!< \brief UART service declaration */
UART_RX_CH_HDL, /*!< \brief UART rx characteristic */
UART_RX_HDL, /*!< \brief UART rx value */
UART_TX_CH_HDL, /*!< \brief UART tx characteristic */
UART_TX_HDL, /*!< \brief UART tx value */
UART_TX_CH_CCC_HDL, /*!< \brief UART tx CCCD */
UART_MAX_HDL /*!< \brief Maximum handle. */
};
void UartProcMsg(bleMsg_t *pMsg);