From a6f4e1966d7f00a88978f2acaac72ec0c5d1e803 Mon Sep 17 00:00:00 2001 From: schneider <schneider@blinkenlichts.net> Date: Sat, 16 Jan 2021 03:32:57 +0100 Subject: [PATCH] feat(mp-ble): More MicroPython BLE hacks --- epicardium/ble/ble.c | 1 - epicardium/ble/ble_api.h | 4 + epicardium/ble/ble_main.c | 41 +-- epicardium/ble/epic_att_api.c | 126 ++++++++ epicardium/ble/hid.c | 10 +- epicardium/ble/meson.build | 1 + epicardium/ble/stack.c | 1 + epicardium/epicardium.h | 54 +++- lib/micropython/meson.build | 1 + pycardium/modules/modbluetooth_card10.c | 412 +++++++++++++++++++----- pycardium/mpconfigport.h | 10 + 11 files changed, 542 insertions(+), 119 deletions(-) create mode 100644 epicardium/ble/epic_att_api.c diff --git a/epicardium/ble/ble.c b/epicardium/ble/ble.c index bfb33db01..2ef81b930 100644 --- a/epicardium/ble/ble.c +++ b/epicardium/ble/ble.c @@ -428,7 +428,6 @@ void vBleTask(void *pvParameters) setAddress(); BleStart(); - AttsDynInit(); bleuart_init(); bleFileTransfer_init(); diff --git a/epicardium/ble/ble_api.h b/epicardium/ble/ble_api.h index 93261a58d..baad5eaf1 100644 --- a/epicardium/ble/ble_api.h +++ b/epicardium/ble/ble_api.h @@ -1,5 +1,7 @@ #pragma once +#include "epicardium.h" + #include <stdint.h> #include "wsf_types.h" @@ -25,3 +27,5 @@ void BleStart(void); /* ATT client module interface. Used by main BLE module */ void bleValueUpdate(attEvt_t *pMsg); void bleDiscCback(dmConnId_t connId, uint8_t status); +void ble_trigger_event(enum ble_event_type event); +void ble_epic_att_api_init(void); diff --git a/epicardium/ble/ble_main.c b/epicardium/ble/ble_main.c index 05e1da53c..c3f7d86a0 100644 --- a/epicardium/ble/ble_main.c +++ b/epicardium/ble/ble_main.c @@ -17,28 +17,19 @@ #include <string.h> #include "wsf_types.h" #include "util/bstream.h" -#include "fs/fs_util.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 "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_hid.h" -#include "svc_rscs.h" -#include "bas/bas_api.h" -#include "hrps/hrps_api.h" -#include "rscp/rscp_api.h" #include "profiles/gap_api.h" #include "cccd.h" #include "ess.h" @@ -435,8 +426,6 @@ static void bleCccCback(attsCccEvt_t *pEvt) } } - - /*************************************************************************************************/ /*! * \brief Process CCC state change. @@ -642,7 +631,7 @@ void epic_ble_compare_response(bool confirmed) /* error condition */ } } -static void trigger_event(enum ble_event_type event) +void ble_trigger_event(enum ble_event_type event) { bool enabled; epic_interrupt_is_enabled(EPIC_INT_BLE, &enabled); @@ -673,7 +662,7 @@ static void bleHandleNumericComparison(dmSecCnfIndEvt_t *pCnfInd) pair_connId = (dmConnId_t)pCnfInd->hdr.param; pair_confirm_value = DmSecGetCompareValue(pCnfInd->confirm); LOG_INFO("ble", "Confirm Value: %ld", pair_confirm_value); - trigger_event(BLE_EVENT_HANDLE_NUMERIC_COMPARISON); + ble_trigger_event(BLE_EVENT_HANDLE_NUMERIC_COMPARISON); } int epic_ble_get_scan_report(struct epic_scan_report *rpt) @@ -694,7 +683,7 @@ static void scannerScanReport(dmEvt_t *pMsg) int next_head = (scan_reports_head + 1) % SCAN_REPORTS_NUM; if(next_head == scan_reports_tail) { - trigger_event(BLE_EVENT_SCAN_REPORT); + ble_trigger_event(BLE_EVENT_SCAN_REPORT); return; } scan_reports_head = next_head; @@ -710,7 +699,7 @@ static void scannerScanReport(dmEvt_t *pMsg) scan_report->directAddrType = pMsg->scanReport.directAddrType; memcpy(scan_report->directAddr, pMsg->scanReport.directAddr, BDA_ADDR_LEN); - trigger_event(BLE_EVENT_SCAN_REPORT); + ble_trigger_event(BLE_EVENT_SCAN_REPORT); if((scan_reports_head + 1) % SCAN_REPORTS_NUM == scan_reports_tail) { LOG_WARN("ble", "Application missing scan results"); @@ -726,9 +715,13 @@ static void scannerScanReport(dmEvt_t *pMsg) * \return None. */ /*************************************************************************************************/ +#define ATT_CONNECTION_OPENED 0x81 +#define ATT_CONNECTION_CLOSED 0x82 +void send_att_event(attEvt_t *att_event); static void bleProcMsg(bleMsg_t *pMsg) { hciLeConnCmplEvt_t *connOpen; + attEvt_t e; switch(pMsg->hdr.event) { @@ -773,6 +766,8 @@ static void bleProcMsg(bleMsg_t *pMsg) connOpen->peerAddr[1], connOpen->peerAddr[0]); bleESS_ccc_update(); HidProcMsg(&pMsg->hdr); + e.hdr.event = ATT_CONNECTION_OPENED; e.hdr.param = connOpen->hdr.param; + send_att_event(&e); break; case DM_CONN_CLOSE_IND: @@ -808,7 +803,8 @@ static void bleProcMsg(bleMsg_t *pMsg) */ advertising_mode = APP_MODE_NONE; AppAdvStop(); - + e.hdr.param = pMsg->dm.connClose.hdr.param; e.hdr.event = ATT_CONNECTION_CLOSED; + send_att_event(&e); bleClose(pMsg); break; @@ -821,7 +817,7 @@ static void bleProcMsg(bleMsg_t *pMsg) AppDbNvmStoreBond(last_pairing); pair_connId = DM_CONN_ID_NONE; - trigger_event(BLE_EVENT_PAIRING_COMPLETE); + ble_trigger_event(BLE_EVENT_PAIRING_COMPLETE); /* After a successful pairing, bonding is disabled again. * We don't want that for now. */ AppSetBondable(TRUE); @@ -846,7 +842,7 @@ static void bleProcMsg(bleMsg_t *pMsg) DmSecGenerateEccKeyReq(); pair_connId = DM_CONN_ID_NONE; - trigger_event(BLE_EVENT_PAIRING_FAILED); + ble_trigger_event(BLE_EVENT_PAIRING_FAILED); break; case DM_SEC_ENCRYPT_IND: @@ -945,9 +941,11 @@ static void BleHandler(wsfEventMask_t event, wsfMsgHdr_t *pMsg) } else if (pMsg->event >= ATT_CBACK_START && pMsg->event <= ATT_CBACK_END) { - LOG_INFO("ble", "Ble got evt %d: %s", pMsg->event, att_events[pMsg->event - ATT_CBACK_START]); + //if(!(pMsg->event == ATTS_HANDLE_VALUE_CNF && pMsg->status == ATT_SUCCESS) ) + LOG_INFO("ble", "Ble got evt %d (%s): %d", pMsg->event, att_events[pMsg->event - ATT_CBACK_START], pMsg->status); /* process discovery-related ATT messages */ AppDiscProcAttMsg((attEvt_t *) pMsg); + send_att_event((attEvt_t *)pMsg); } else if (pMsg->event >= L2C_COC_CBACK_START && pMsg->event <= L2C_COC_CBACK_CBACK_END) { @@ -972,7 +970,6 @@ static void BleHandler(wsfEventMask_t event, wsfMsgHdr_t *pMsg) /*************************************************************************************************/ void BleStart(void) { - BleHandlerInit(); /* Register for stack callbacks */ @@ -992,7 +989,11 @@ void BleStart(void) if(config_get_boolean_with_default("ble_hid_enable", false)) { hid_init(); } + + ble_epic_att_api_init(); + /* Reset the device */ DmDevReset(); } + /* clang-format on */ diff --git a/epicardium/ble/epic_att_api.c b/epicardium/ble/epic_att_api.c new file mode 100644 index 000000000..b492d32fb --- /dev/null +++ b/epicardium/ble/epic_att_api.c @@ -0,0 +1,126 @@ +#include "ble_api.h" +#include "epicardium.h" +#include "modules/log.h" + +#include "wsf_types.h" +#include "util/bstream.h" +#include "wsf_msg.h" +#include "att_api.h" + +#include "FreeRTOS.h" +#include "queue.h" + +#include <stdio.h> +#include <string.h> + +#define ATT_QUEUE_SIZE 10 +static QueueHandle_t att_queue; +static uint8_t att_queue_buffer[sizeof(attEvt_t) * ATT_QUEUE_SIZE]; +static StaticQueue_t att_queue_data; + +int epic_ble_get_att_event(struct epic_att_event *e) +{ + if (xQueueReceive(att_queue, e, 0) != pdTRUE) { + return -ENOENT; + } + return uxQueueMessagesWaiting(att_queue); +} + +// TODO: make static again once ble_main does not use it anymore +void send_att_event(attEvt_t *att_event) +{ + if (xQueueSend(att_queue, att_event, 0) != pdTRUE) { + LOG_WARN("ble", "could not queue att event"); + } + ble_trigger_event(BLE_EVENT_ATT_EVENT); +} + +#define ATT_WRITE_CALLBACK 0x80 + +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 +) { + if (AttsSetAttr(handle, len, pValue) == ATT_SUCCESS) { + attEvt_t att_event; + att_event.hdr.event = ATT_WRITE_CALLBACK; + att_event.hdr.param = connId; + att_event.handle = handle; + att_event.valueLen = len; + send_att_event(&att_event); + } + + return ATT_SUCCESS; +} + +#define ATTS_DYN_GROUP_COUNT 8 +#define ATTS_DYN_START_HANDLE 0x200 +static int next_start_handle = ATTS_DYN_START_HANDLE; +static void *dyn_groups[ATTS_DYN_GROUP_COUNT] = {}; +static int next_dyn_group = 0; + +int epic_atts_dyn_create_group( + uint16_t group_size, void **pSvcHandle, uint16_t *start_handle +) { + *start_handle = next_start_handle; + int end_handle = *start_handle + group_size - 1; + next_start_handle += group_size; + + *pSvcHandle = AttsDynCreateGroup(*start_handle, end_handle); + dyn_groups[next_dyn_group++] = *pSvcHandle; + + //AttsDynRegister(pSvcHandle, DynAttsReadCback, DynAttsWriteCback); + AttsDynRegister(*pSvcHandle, NULL, DynAttsWriteCback); + + // TODO: validate parameters and pointer and current service count + return 0; +} + +int epic_atts_dyn_send_service_changed_ind(void) +{ + // TODO: This is copied from an upstream stack version + // TODO: Handling of CCCDs in pairings is still broken + //GattSendServiceChangedInd(1, 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_start_handle = ATTS_DYN_START_HANDLE; + return 0; +} + +void ble_epic_att_api_init(void) +{ + att_queue = xQueueCreateStatic( + ATT_QUEUE_SIZE, + sizeof(attEvt_t), + att_queue_buffer, + &att_queue_data + ); +} + +int epic_ble_atts_handle_value_ntf( + uint8_t connId, uint16_t handle, uint16_t valueLen, uint8_t *pValue +) { + if (!DmConnInUse(connId)) { + return -EIO; + } + + // TODO: There is a race condition here. Ideally AttsHandleValueNtf would return an error or + // raise a callback + AttsHandleValueNtf(connId, handle, valueLen, pValue); + return 0; +} diff --git a/epicardium/ble/hid.c b/epicardium/ble/hid.c index 4386f5b1d..56f511421 100644 --- a/epicardium/ble/hid.c +++ b/epicardium/ble/hid.c @@ -208,17 +208,9 @@ static void hidAppReportInit(void) void hid_work_init(void); void hid_init(void) { -#ifdef HID_ATT_DYNAMIC - /* Initialize the dynamic service system */ - AttsDynInit(); - /* Add the HID service dynamically */ - pSHdl = SvcHidAddGroupDyn(); - AttsDynRegister(pSHdl, NULL, HidAttsWriteCback); -#else - /* Add the HID service statically */ SvcHidAddGroup(); SvcHidRegister(HidAttsWriteCback, NULL); -#endif /* HID_ATT_DYNAMIC */ + /* Initialize the HID profile */ HidInit(&hidAppHidConfig); diff --git a/epicardium/ble/meson.build b/epicardium/ble/meson.build index 341ad2257..66a4566ad 100644 --- a/epicardium/ble/meson.build +++ b/epicardium/ble/meson.build @@ -1,5 +1,6 @@ ble_sources = files( 'ble.c', + 'epic_att_api.c', 'stack.c', 'ble_main.c', 'ble_attc.c', diff --git a/epicardium/ble/stack.c b/epicardium/ble/stack.c index d5e092179..4bf4696fd 100644 --- a/epicardium/ble/stack.c +++ b/epicardium/ble/stack.c @@ -184,6 +184,7 @@ void StackInit(void) AttHandlerInit(handlerId); AttsInit(); AttsIndInit(); + AttsDynInit(); AttcInit(); handlerId = WsfOsSetNextHandler(SmpHandler); diff --git a/epicardium/epicardium.h b/epicardium/epicardium.h index 39cf79daa..f56b6c002 100644 --- a/epicardium/epicardium.h +++ b/epicardium/epicardium.h @@ -162,13 +162,18 @@ typedef _Bool bool; #define API_BLE_HID_SEND_REPORT 0x150 -#define API_BLE_ATTS_DYN_CREATE_GROUP 0x160 -#define API_BLE_ATTS_DYN_DELETE_GROUP 0x161 -#define API_BLE_ATTS_DYN_ADD_ATTR_DYN 0x163 -#define API_BLE_ATTS_DYN_ADD_ATTR 0x164 -#define API_BLE_ATTS_SET_ATTR 0x165 -#define API_BLE_ATTS_HANDLE_VALUE_NTF 0x166 -#define API_BLE_ATTS_GET_ATTR 0x167 +#define API_BLE_ATTS_DYN_CREATE_GROUP 0x160 +//#define API_BLE_ATTS_DYN_DELETE_GROUP 0x160 +#define API_BLE_ATTS_DYN_REGISTER 0x162 +#define API_BLE_ATTS_DYN_ADD_ATTR_DYN 0x163 +#define API_BLE_ATTS_DYN_ADD_ATTR 0x164 +#define API_BLE_ATTS_SET_ATTR 0x165 +#define API_BLE_ATTS_HANDLE_VALUE_NTF 0x166 +#define API_BLE_ATTS_GET_ATTR 0x167 +#define API_BLE_ATT_GET_EVENT 0x168 +#define API_BLE_ATTS_SEND_SERVICE_CHANGED_IND 0x169 +#define API_BLE_ATTS_DYN_DELETE_GROUPS 0x16A + /* clang-format on */ typedef uint32_t api_int_id_t; @@ -2337,6 +2342,7 @@ enum ble_event_type { BLE_EVENT_PAIRING_COMPLETE = 3, /** New scan data is available */ BLE_EVENT_SCAN_REPORT = 4, + BLE_EVENT_ATT_EVENT = 5, }; @@ -2506,12 +2512,40 @@ API(API_BLE_GET_SCAN_REPORT, int epic_ble_get_scan_report(struct epic_scan_repor */ API(API_BLE_HID_SEND_REPORT, int epic_ble_hid_send_report(uint8_t report_id, uint8_t *data, uint8_t len)); -API(API_BLE_ATTS_DYN_CREATE_GROUP, void *AttsDynCreateGroup(uint16_t startHandle, uint16_t endHandle)); -API(API_BLE_ATTS_DYN_DELETE_GROUP, void AttsDynDeleteGroup(void *pSvcHandle)); +API(API_BLE_ATTS_DYN_CREATE_GROUP, int epic_atts_dyn_create_group(uint16_t group_size, void **pSvcHandle, uint16_t *start_handle)); +//API(API_BLE_ATTS_DYN_DELETE_GROUP, void AttsDynDeleteGroup(void *pSvcHandle)); +API(API_BLE_ATTS_DYN_DELETE_GROUPS, int epic_ble_atts_dyn_delete_groups(void)); + API(API_BLE_ATTS_DYN_ADD_ATTR_DYN, void AttsDynAddAttrDyn(void *pSvcHandle, const uint8_t *pUuid, uint8_t uuidLen, const uint8_t *pValue, uint16_t len, uint16_t maxLen, uint8_t settings, uint8_t permissions)); API(API_BLE_ATTS_DYN_ADD_ATTR, void AttsDynAddAttr(void *pSvcHandle, const uint8_t *pUuid, const uint8_t *pValue, uint16_t len, uint16_t maxLen, uint8_t settings, uint8_t permissions)); + +API(API_BLE_ATTS_SEND_SERVICE_CHANGED_IND, int epic_atts_dyn_send_service_changed_ind(void)); + API(API_BLE_ATTS_SET_ATTR, uint8_t AttsSetAttr(uint16_t handle, uint16_t valueLen, uint8_t *pValue)); -API(API_BLE_ATTS_HANDLE_VALUE_NTF, void AttsHandleValueNtf(uint8_t connId, uint16_t handle, uint16_t valueLen, uint8_t *pValue)); API(API_BLE_ATTS_GET_ATTR, uint8_t AttsGetAttr(uint16_t handle, uint16_t *pLen, uint8_t **pValue)); +API(API_BLE_ATTS_HANDLE_VALUE_NTF, int epic_ble_atts_handle_value_ntf(uint8_t connId, uint16_t handle, uint16_t valueLen, uint8_t *pValue)); + + +struct epic_wsf_header +{ + uint16_t param; /*!< \brief General purpose parameter passed to event handler */ + uint8_t event; /*!< \brief General purpose event value passed to event handler */ + uint8_t status; /*!< \brief General purpose status value passed to event handler */ +}; + +struct epic_att_event +{ + struct epic_wsf_header hdr; /*!< \brief Header structure */ + uint8_t *pValue; /*!< \brief Value */ + uint16_t valueLen; /*!< \brief Value length */ + uint16_t handle; /*!< \brief Attribute handle */ + uint8_t continuing; /*!< \brief TRUE if more response packets expected */ + uint16_t mtu; /*!< \brief Negotiated MTU value */ +}; + + +API(API_BLE_ATT_GET_EVENT, int epic_ble_get_att_event(struct epic_att_event *e)); + + #endif /* _EPICARDIUM_H */ diff --git a/lib/micropython/meson.build b/lib/micropython/meson.build index bf021db1a..f85af62c9 100644 --- a/lib/micropython/meson.build +++ b/lib/micropython/meson.build @@ -157,6 +157,7 @@ micropython_sources = files( 'micropython/py/pystack.c', 'micropython/py/qstr.c', 'micropython/py/reader.c', + 'micropython/py/ringbuf.c', 'micropython/py/repl.c', 'micropython/py/runtime.c', 'micropython/py/runtime_utils.c', diff --git a/pycardium/modules/modbluetooth_card10.c b/pycardium/modules/modbluetooth_card10.c index b59b2f192..b86bcf4c9 100644 --- a/pycardium/modules/modbluetooth_card10.c +++ b/pycardium/modules/modbluetooth_card10.c @@ -1,34 +1,252 @@ #include "extmod/modbluetooth.h" #include "py/runtime.h" #include <stdint.h> +#include <stdio.h> #include <string.h> +#define ATTS_SET_WRITE_CBACK \ + 0x02 /*!< \brief Set if the group callback is executed when + this attribute is written by a client device */ +#define ATTS_SET_READ_CBACK \ + 0x04 /*!< \brief Set if the group callback is executed when + this attribute is read by a client device */ +#define ATTS_SET_VARIABLE_LEN \ + 0x08 /*!< \brief Set if the attribute has a variable length */ +#define ATTS_SET_ALLOW_OFFSET \ + 0x10 /*!< \brief Set if writes are allowed with an offset */ + +#define ATTS_PERMIT_READ 0x01 /*!< \brief Set if attribute can be read */ +#define ATTS_PERMIT_READ_AUTH \ + 0x02 /*!< \brief Set if attribute read requires authentication */ +#define ATTS_PERMIT_READ_AUTHORIZ \ + 0x04 /*!< \brief Set if attribute read requires authorization */ +#define ATTS_PERMIT_READ_ENC \ + 0x08 /*!< \brief Set if attribute read requires encryption */ +#define ATTS_PERMIT_WRITE 0x10 /*!< \brief Set if attribute can be written */ +#define ATTS_PERMIT_WRITE_AUTH \ + 0x20 /*!< \brief Set if attribute write requires authentication */ +#define ATTS_PERMIT_WRITE_AUTHORIZ \ + 0x40 /*!< \brief Set if attribute write requires authorization */ +#define ATTS_PERMIT_WRITE_ENC \ + 0x80 /*!< \brief Set if attribute write requires encryption */ + +#define UINT16_TO_BYTES(n) ((uint8_t)(n)), ((uint8_t)((n) >> 8)) +#define ATT_UUID_PRIMARY_SERVICE 0x2800 +#define ATT_UUID_CHARACTERISTIC 0x2803 +#define ATT_UUID_CLIENT_CHAR_CONFIG 0x2902 + +const uint8_t attPrimSvcUuid[] = { UINT16_TO_BYTES(ATT_UUID_PRIMARY_SERVICE) }; +const uint8_t attChUuid[] = { UINT16_TO_BYTES(ATT_UUID_CHARACTERISTIC) }; +const uint8_t attCliChCfgUuid[] = { UINT16_TO_BYTES( + ATT_UUID_CLIENT_CHAR_CONFIG) }; + +#define ATT_SUCCESS 0x00 /*!< \brief Operation successful */ +#define ATT_ERR_HANDLE 0x01 /*!< \brief Invalid handle */ +#define ATT_ERR_READ 0x02 /*!< \brief Read not permitted */ +#define ATT_ERR_WRITE 0x03 /*!< \brief Write not permitted */ +#define ATT_ERR_INVALID_PDU 0x04 /*!< \brief Invalid pdu */ +#define ATT_ERR_AUTH 0x05 /*!< \brief Insufficient authentication */ +#define ATT_ERR_NOT_SUP 0x06 /*!< \brief Request not supported */ +#define ATT_ERR_OFFSET 0x07 /*!< \brief Invalid offset */ +#define ATT_ERR_AUTHOR 0x08 /*!< \brief Insufficient authorization */ +#define ATT_ERR_QUEUE_FULL 0x09 /*!< \brief Prepare queue full */ +#define ATT_ERR_NOT_FOUND 0x0A /*!< \brief Attribute not found */ +#define ATT_ERR_NOT_LONG 0x0B /*!< \brief Attribute not long */ +#define ATT_ERR_KEY_SIZE 0x0C /*!< \brief Insufficient encryption key size */ +#define ATT_ERR_LENGTH 0x0D /*!< \brief Invalid attribute value length */ +#define ATT_ERR_UNLIKELY 0x0E /*!< \brief Other unlikely error */ +#define ATT_ERR_ENC 0x0F /*!< \brief Insufficient encryption */ +#define ATT_ERR_GROUP_TYPE 0x10 /*!< \brief Unsupported group type */ +#define ATT_ERR_RESOURCES 0x11 /*!< \brief Insufficient resources */ +#define ATT_ERR_DATABASE_OUT_OF_SYNC \ + 0x12 /*!< \brief Client out of synch with database */ +#define ATT_ERR_VALUE_NOT_ALLOWED 0x13 /*!< \brief Value not allowed */ +#define ATT_ERR_WRITE_REJ 0xFC /*!< \brief Write request rejected */ +#define ATT_ERR_CCCD 0xFD /*!< \brief CCCD improperly configured */ +#define ATT_ERR_IN_PROGRESS 0xFE /*!< \brief Procedure already in progress */ +#define ATT_ERR_RANGE 0xFF /*!< \brief Value out of range */ +/**@}*/ + +/** \name Proprietary Internal Error Codes + * These codes may be sent to application but are not present in any ATT PDU. + */ +/**@{*/ +#define ATT_ERR_MEMORY 0x70 /*!< \brief Out of memory */ +#define ATT_ERR_TIMEOUT 0x71 /*!< \brief Transaction timeout */ +#define ATT_ERR_OVERFLOW 0x72 /*!< \brief Transaction overflow */ +#define ATT_ERR_INVALID_RSP 0x73 /*!< \brief Invalid response PDU */ +#define ATT_ERR_CANCELLED 0x74 /*!< \brief Request cancelled */ +#define ATT_ERR_UNDEFINED 0x75 /*!< \brief Other undefined error */ +#define ATT_ERR_REQ_NOT_FOUND \ + 0x76 /*!< \brief Required characteristic not found */ +#define ATT_ERR_MTU_EXCEEDED \ + 0x77 /*!< \brief Attribute PDU length exceeded MTU size */ +#define ATT_CONTINUING 0x78 /*!< \brief Procedure continuing */ +#define ATT_RSP_PENDING \ + 0x79 /*!< \brief Responsed delayed pending higher layer */ + +// card10 att interface specific events +#define ATT_WRITE_CALLBACK 0x80 +#define ATT_CONNECTION_OPENED 0x81 +#define ATT_CONNECTION_CLOSED 0x82 + +#define ATTS_HANDLE_VALUE_CNF 15 + +enum notification_status { + NOTIFICATION_STATUS_UNKNOWN, + NOTIFICATION_STATUS_PENDING, + NOTIFICATION_STATUS_SUCCESS, + NOTIFICATION_STATUS_OVERFLOW +}; + const char *const not_implemented_message = "Not (yet) implemented on card10. See https://git.card10.badge.events.ccc.de/card10/firmware/-/issues/8"; +typedef struct _mp_bluetooth_card10_root_pointers_t { + // Characteristic (and descriptor) value storage. + mp_gatts_db_t gatts_db; +} mp_bluetooth_card10_root_pointers_t; + +#define GATTS_DB (MP_STATE_PORT(bluetooth_card10_root_pointers)->gatts_db) + +typedef struct { + enum notification_status notification_status; +} gatts_db_entry_t; + +static void gatts_db_create_entry(mp_gatts_db_t db, uint16_t handle) +{ + mp_map_elem_t *elem = mp_map_lookup( + db, + MP_OBJ_NEW_SMALL_INT(handle), + MP_MAP_LOOKUP_ADD_IF_NOT_FOUND + ); + gatts_db_entry_t *entry = m_new(gatts_db_entry_t, 1); + entry->notification_status = NOTIFICATION_STATUS_UNKNOWN; + elem->value = MP_OBJ_FROM_PTR(entry); +} + +static gatts_db_entry_t *gatts_db_lookup(mp_gatts_db_t db, uint16_t handle) +{ + mp_map_elem_t *elem = + mp_map_lookup(db, MP_OBJ_NEW_SMALL_INT(handle), MP_MAP_LOOKUP); + if (!elem) { + mp_raise_OSError(-EACCES); + } + return MP_OBJ_TO_PTR(elem->value); +} + static void raise(void) { mp_raise_NotImplementedError(not_implemented_message); } +static void clear_events(void) +{ + struct epic_att_event att_event; + epic_ble_get_event(); + while (epic_ble_get_att_event(&att_event) >= 0) + ; +} + +static mp_obj_t mp_ble_poll_events(mp_obj_t interrupt_id) +{ + struct epic_att_event att_event; + enum ble_event_type event = epic_ble_get_event(); + + if (event == BLE_EVENT_ATT_EVENT) { + int ret; + do { + ret = epic_ble_get_att_event(&att_event); + + if (ret >= 0) { + printf("MP got att event %d,%d,%d\n", + att_event.hdr.event, + att_event.hdr.status, + att_event.handle); + if (att_event.hdr.event == + ATTS_HANDLE_VALUE_CNF) { + gatts_db_entry_t *e = gatts_db_lookup( + GATTS_DB, att_event.handle + ); + if (att_event.hdr.status == + ATT_SUCCESS) { + e->notification_status = + NOTIFICATION_STATUS_SUCCESS; + } else if ( + att_event.hdr.status == + ATT_ERR_OVERFLOW) { + e->notification_status = + NOTIFICATION_STATUS_OVERFLOW; + } + } else if ( + att_event.hdr.event == + ATT_WRITE_CALLBACK) { + mp_bluetooth_gatts_on_write( + att_event.hdr.param, + att_event.handle + ); + } else if ( + att_event.hdr.event == + ATT_CONNECTION_OPENED) { + uint8_t event = + MP_BLUETOOTH_IRQ_CENTRAL_CONNECT; + uint16_t conn_handle = + att_event.hdr.param; + uint8_t addr_type = 0; // TODO + uint8_t addr[8] = {}; // TODO + mp_bluetooth_gap_on_connected_disconnected( + event, + conn_handle, + addr_type, + addr + ); + } else if ( + att_event.hdr.event == + ATT_CONNECTION_CLOSED) { + uint8_t event = + MP_BLUETOOTH_IRQ_CENTRAL_DISCONNECT; + uint16_t conn_handle = + att_event.hdr.param; + uint8_t addr_type = 0; // TODO + uint8_t addr[8] = {}; // TODO + mp_bluetooth_gap_on_connected_disconnected( + event, + conn_handle, + addr_type, + addr + ); + } + } + } while (ret >= 0); + } + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_1(ble_event_obj, mp_ble_poll_events); + +mp_obj_t mp_interrupt_set_callback(mp_obj_t id_in, mp_obj_t callback_obj); + // Enables the Bluetooth stack. int mp_bluetooth_init(void) { + MP_STATE_PORT(bluetooth_card10_root_pointers) = + m_new0(mp_bluetooth_card10_root_pointers_t, 1); + mp_bluetooth_gatts_db_create(&GATTS_DB); + mp_interrupt_set_callback( + MP_ROM_INT(EPIC_INT_BLE), (mp_obj_t *)&ble_event_obj + ); + clear_events(); + epic_interrupt_enable(EPIC_INT_BLE); return 0; } // Disables the Bluetooth stack. Is a no-op when not enabled. void mp_bluetooth_deinit(void) { - raise(); + //raise(); + epic_interrupt_disable(EPIC_INT_BLE); } // Returns true when the Bluetooth stack is enabled. -bool mp_bluetooth_is_enabled(void) -{ - return true; -} - bool mp_bluetooth_is_active(void) { return true; @@ -72,50 +290,12 @@ void mp_bluetooth_gap_advertise_stop(void) // Start adding services. Must be called before mp_bluetooth_register_service. int mp_bluetooth_gatts_register_service_begin(bool append) { + if (!append) { + epic_ble_atts_dyn_delete_groups(); + } return 0; } -#define ATTS_SET_UUID_128 0x01 /*!< \brief Set if the UUID is 128 bits in length */ -#define ATTS_SET_WRITE_CBACK 0x02 /*!< \brief Set if the group callback is executed when - this attribute is written by a client device */ -#define ATTS_SET_READ_CBACK 0x04 /*!< \brief Set if the group callback is executed when - this attribute is read by a client device */ -#define ATTS_SET_VARIABLE_LEN 0x08 /*!< \brief Set if the attribute has a variable length */ -#define ATTS_SET_ALLOW_OFFSET 0x10 /*!< \brief Set if writes are allowed with an offset */ -#define ATTS_SET_CCC 0x20 /*!< \brief Set if the attribute is a client characteristic - configuration descriptor */ -#define ATTS_SET_ALLOW_SIGNED 0x40 /*!< \brief Set if signed writes are allowed */ -#define ATTS_SET_REQ_SIGNED 0x80 /*!< \brief Set if signed writes are required if link - is not encrypted */ - -#define ATTS_PERMIT_READ 0x01 /*!< \brief Set if attribute can be read */ -#define ATTS_PERMIT_READ_AUTH 0x02 /*!< \brief Set if attribute read requires authentication */ -#define ATTS_PERMIT_READ_AUTHORIZ 0x04 /*!< \brief Set if attribute read requires authorization */ -#define ATTS_PERMIT_READ_ENC 0x08 /*!< \brief Set if attribute read requires encryption */ -#define ATTS_PERMIT_WRITE 0x10 /*!< \brief Set if attribute can be written */ -#define ATTS_PERMIT_WRITE_AUTH 0x20 /*!< \brief Set if attribute write requires authentication */ -#define ATTS_PERMIT_WRITE_AUTHORIZ 0x40 /*!< \brief Set if attribute write requires authorization */ -#define ATTS_PERMIT_WRITE_ENC 0x80 /*!< \brief Set if attribute write requires encryption */ - -#define ATT_PROP_BROADCAST 0x01 /*!< \brief Permit broadcasts */ -#define ATT_PROP_READ 0x02 /*!< \brief Permit reads */ -#define ATT_PROP_WRITE_NO_RSP 0x04 /*!< \brief Permit writes without response */ -#define ATT_PROP_WRITE 0x08 /*!< \brief Permit writes with response */ -#define ATT_PROP_NOTIFY 0x10 /*!< \brief Permit notifications */ -#define ATT_PROP_INDICATE 0x20 /*!< \brief Permit indications */ -#define ATT_PROP_AUTHENTICATED 0x40 /*!< \brief Permit signed writes */ -#define ATT_PROP_EXTENDED 0x80 /*!< \brief More properties defined in extended properties */ - -#define ATT_16_UUID_LEN 2 -#define UINT16_TO_BYTES(n) ((uint8_t) (n)), ((uint8_t)((n) >> 8)) -#define ATT_UUID_PRIMARY_SERVICE 0x2800 /*!< \brief Primary Service */ -#define ATT_UUID_CHARACTERISTIC 0x2803 /*!< \brief Characteristic */ -#define ATT_UUID_CLIENT_CHAR_CONFIG 0x2902 /*!< \brief Client Characteristic Configuration */ - -const uint8_t attPrimSvcUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_PRIMARY_SERVICE)}; -const uint8_t attChUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_CHARACTERISTIC)}; -const uint8_t attCliChCfgUuid[ATT_16_UUID_LEN] = {UINT16_TO_BYTES(ATT_UUID_CLIENT_CHAR_CONFIG)}; - // // Add a service with the given list of characteristics to the queue to be registered. // The value_handles won't be valid until after mp_bluetooth_register_service_end is called. int mp_bluetooth_gatts_register_service( @@ -128,70 +308,120 @@ int mp_bluetooth_gatts_register_service( uint16_t *handles, size_t num_characteristics ) { - void *pSHdl; + void *pSvcHandle; size_t num_descriptors_total = 0; - size_t num_ccds = 0; - for(size_t i=0; i<num_characteristics; i++) { + size_t num_ccds = 0; + for (size_t i = 0; i < num_characteristics; i++) { + if (num_descriptors[i]) { + // TODO: more specific error + raise(); + } num_descriptors_total += num_descriptors[i]; - if(characteristic_flags[i] & ATT_PROP_NOTIFY) { - // TODO: Also handle indications + if ((characteristic_flags[i] & + MP_BLUETOOTH_CHARACTERISTIC_FLAG_NOTIFY) || + (characteristic_flags[i] & + MP_BLUETOOTH_CHARACTERISTIC_FLAG_INDICATE)) { num_ccds++; } } // One handle for the service, one per characteristic, one per value, one per (CCC) descriptor - // TODO: Need to account for CCCDs - const uint16_t numHandles = 1 + num_characteristics * 2 + num_ccds + num_descriptors_total; - - const uint16_t startHandle = 0x200; - const uint16_t endHandle = startHandle + numHandles - 1; + const uint16_t numHandles = + 1 + num_characteristics * 2 + num_ccds + num_descriptors_total; - pSHdl = AttsDynCreateGroup(startHandle, endHandle); - // TODO NULL check + uint16_t startHandle; + // TODO: handle error + epic_atts_dyn_create_group(numHandles, &pSvcHandle, &startHandle); /* Primary service */ - uint16_t uuid_len = service_uuid->type == MP_BLUETOOTH_UUID_TYPE_16 ? 2 : 16; + uint16_t uuid_len = + service_uuid->type == MP_BLUETOOTH_UUID_TYPE_16 ? 2 : 16; uint16_t currentHandle = startHandle; - AttsDynAddAttr(pSHdl, attPrimSvcUuid, service_uuid->data, uuid_len, uuid_len, 0, ATTS_PERMIT_READ); + AttsDynAddAttr( + pSvcHandle, + attPrimSvcUuid, + service_uuid->data, + uuid_len, + uuid_len, + 0, + ATTS_PERMIT_READ + ); size_t handle_index = 0; for (size_t i = 0; i < num_characteristics; ++i) { - uint8_t flags = characteristic_flags[i]; + uint8_t flags = characteristic_flags[i]; mp_obj_bluetooth_uuid_t *uuid = characteristic_uuids[i]; - uint8_t uuid_len = uuid->type == MP_BLUETOOTH_UUID_TYPE_16 ? 2 : 16; - + uint8_t uuid_len = + uuid->type == MP_BLUETOOTH_UUID_TYPE_16 ? 2 : 16; /* Characteristic */ currentHandle++; - uint8_t characteristic[1 + 2 + 16] = {flags, UINT16_TO_BYTES(currentHandle + 1)}; + uint8_t characteristic[1 + 2 + 16] = { + flags, UINT16_TO_BYTES(currentHandle + 1) + }; memcpy(characteristic + 3, uuid->data, uuid_len); uint8_t characteristic_len = 1 + 2 + uuid_len; - AttsDynAddAttr(pSHdl, attChUuid, characteristic, characteristic_len, characteristic_len, 0, ATTS_PERMIT_READ); + AttsDynAddAttr( + pSvcHandle, + attChUuid, + characteristic, + characteristic_len, + characteristic_len, + 0, + ATTS_PERMIT_READ + ); /* Value */ currentHandle++; uint8_t permissions = 0; - uint8_t settings = ATTS_SET_VARIABLE_LEN; - if(flags & ATT_PROP_READ) { + uint8_t settings = ATTS_SET_VARIABLE_LEN; + if (flags & MP_BLUETOOTH_CHARACTERISTIC_FLAG_READ) { permissions |= ATTS_PERMIT_READ; } - if(flags & ATT_PROP_WRITE) { + if (flags & MP_BLUETOOTH_CHARACTERISTIC_FLAG_WRITE) { permissions |= ATTS_PERMIT_WRITE; + settings |= ATTS_SET_WRITE_CBACK; } - AttsDynAddAttrDyn(pSHdl, uuid->data, uuid_len, NULL, 0, MP_BLUETOOTH_DEFAULT_ATTR_LEN, - settings, permissions); + AttsDynAddAttrDyn( + pSvcHandle, + uuid->data, + uuid_len, + NULL, + 0, + MP_BLUETOOTH_DEFAULT_ATTR_LEN + 10, + settings, + permissions + ); handles[handle_index] = currentHandle; + //mp_bluetooth_gatts_db_create_entry(GATTS_DB, currentHandle, MP_BLUETOOTH_DEFAULT_ATTR_LEN); + gatts_db_create_entry(GATTS_DB, currentHandle); - if(flags & ATT_PROP_NOTIFY) { + if ((flags & MP_BLUETOOTH_CHARACTERISTIC_FLAG_NOTIFY) || + flags & (MP_BLUETOOTH_CHARACTERISTIC_FLAG_INDICATE)) { /* CCCD */ currentHandle++; //uint8_t initCcc[] = {UINT16_TO_BYTES(0x0000)}; - uint8_t initCcc[] = {UINT16_TO_BYTES(0x0001)}; - AttsDynAddAttr(pSHdl, attCliChCfgUuid, initCcc, sizeof(initCcc), sizeof(initCcc), - //ATTS_SET_READ_CBACK | ATTS_SET_WRITE_CBACK, ATTS_PERMIT_READ | ATTS_PERMIT_WRITE); - 0, ATTS_PERMIT_READ | ATTS_PERMIT_WRITE); + uint8_t initCcc[] = { UINT16_TO_BYTES(0x0001) }; + // TODO: Handle CCC data + // Settings is 0 on purpose. If set to ATTS_SET_CCC the stack starts to try and + // manage the CCC data itself. + settings = 0; + permissions = ATTS_PERMIT_READ | ATTS_PERMIT_WRITE; + AttsDynAddAttr( + pSvcHandle, + attCliChCfgUuid, + initCcc, + sizeof(initCcc), + sizeof(initCcc), + settings, + permissions + ); + + //mp_bluetooth_gatts_db_create_entry(GATTS_DB, handles[handle_index] + 1, MP_BLUETOOTH_CCCB_LEN); + //gatts_db_create_entry(GATTS_DB, handles[handle_index] + 1); + //int ret = mp_bluetooth_gatts_db_write(GATTS_DB, handles[handle_index] + 1, cccb_buf, sizeof(cccb_buf)); } handle_index++; @@ -202,6 +432,7 @@ int mp_bluetooth_gatts_register_service( // Register any queued services. int mp_bluetooth_gatts_register_service_end() { + epic_atts_dyn_send_service_changed_ind(); return 0; } @@ -219,14 +450,20 @@ int mp_bluetooth_gatts_read( int mp_bluetooth_gatts_write( uint16_t value_handle, const uint8_t *value, size_t value_len ) { + //return mp_bluetooth_gatts_db_write(MP_STATE_PORT(bluetooth_btstack_root_pointers)->gatts_db, value_handle, value, value_len); uint8_t ret = AttsSetAttr(value_handle, value_len, (uint8_t *)value); return ret; } // Notify the central that it should do a read. int mp_bluetooth_gatts_notify(uint16_t conn_handle, uint16_t value_handle) { - raise(); - return 0; + // Note: cordio doesn't appear to support sending a notification without a value, so include the stored value. + uint8_t *data = NULL; + size_t len = 0; + mp_bluetooth_gatts_read(value_handle, &data, &len); + return mp_bluetooth_gatts_notify_send( + conn_handle, value_handle, data, len + ); } // Notify the central, including a data payload. (Note: does not set the gatts db value). int mp_bluetooth_gatts_notify_send( @@ -235,7 +472,24 @@ int mp_bluetooth_gatts_notify_send( const uint8_t *value, size_t value_len ) { - AttsHandleValueNtf(conn_handle, value_handle, value_len, (uint8_t *)value); + gatts_db_entry_t *e = gatts_db_lookup(GATTS_DB, value_handle); + e->notification_status = NOTIFICATION_STATUS_PENDING; + int ret = epic_ble_atts_handle_value_ntf( + conn_handle, value_handle, value_len, (uint8_t *)value + ); + + if (ret < 0) { + return ret; + } + + while (e->notification_status == NOTIFICATION_STATUS_PENDING) { + mp_ble_poll_events(0); + } + + if (e->notification_status != NOTIFICATION_STATUS_SUCCESS) { + // TODO: better error mapping + return -EIO; + } return 0; } // Indicate the central. diff --git a/pycardium/mpconfigport.h b/pycardium/mpconfigport.h index 8749da694..1bcb79a18 100644 --- a/pycardium/mpconfigport.h +++ b/pycardium/mpconfigport.h @@ -77,6 +77,7 @@ int mp_hal_csprng_read_int(void); #define MODULE_CONFIG_ENABLED (1) #define MODULE_BLE_ENABLED (1) +#define MICROPY_BLUETOOTH_CARD10 (1) /* * This port is intended to be 32-bit, but unfortunately, int32_t for * different targets may be defined in different ways - either as int @@ -112,9 +113,18 @@ typedef long mp_off_t; #define EPIC_INT_NUM 1 #endif +#if MICROPY_BLUETOOTH_CARD10 +struct _mp_bluetooth_card10_root_pointers_t; +#define MICROPY_PORT_ROOT_POINTER_BLUETOOTH_CARD10 struct _mp_bluetooth_card10_root_pointers_t *bluetooth_card10_root_pointers; +#else +#define MICROPY_PORT_ROOT_POINTER_BLUETOOTH_CARD10 +#endif + + /* For some reason, we need to define readline history manually */ #define MICROPY_PORT_ROOT_POINTERS \ const char *readline_hist[16]; \ mp_obj_t interrupt_callbacks[EPIC_INT_NUM]; \ void *spo2_memory; \ + MICROPY_PORT_ROOT_POINTER_BLUETOOTH_CARD10 \ -- GitLab