diff --git a/epicardium/ble/ble.c b/epicardium/ble/ble.c index bfb33db010e1b66505a8fdabbfc78f1df269e9fe..0b066379664dd565edf62cf4b16b2b93796a744d 100644 --- a/epicardium/ble/ble.c +++ b/epicardium/ble/ble.c @@ -30,6 +30,9 @@ struct log_packet_header { uint32_t timestamp_us_h; uint32_t timestamp_us_l; }; + +static uint8_t bdAddr[6] = { 0xCA, 0x4D, 0x10, 0x00, 0x00, 0x00 }; + static const uint8_t log_header[] = { 'b', 't', 's', 'n', 'o', 'o', 'p', 0, 0, 0, 0, 1, 0, 0, 0x03, 0xea }; @@ -178,7 +181,6 @@ static void WsfInit(void) /* TODO: We need a source of MACs */ static void setAddress(void) { - uint8_t bdAddr[6] = { 0xCA, 0x4D, 0x10, 0x00, 0x00, 0x00 }; char buf[32]; int result = epic_config_get_string("ble_mac", buf, sizeof(buf)); @@ -222,6 +224,11 @@ static void setAddress(void) HciVsSetBdAddr(bdAddr); } /*************************************************************************************************/ +void epic_ble_get_address(uint8_t *addr) +{ + memcpy(addr, bdAddr, sizeof(bdAddr)); +} +/*************************************************************************************************/ static void vTimerCallback(xTimerHandle pxTimer) { //printf("wake\n"); @@ -428,7 +435,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 93261a58d1b8ac12dbd0091d70ea007d21af933f..38c60f731a27ada94022ad2dcbd11f467824f0d6 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,24 @@ 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_epic_att_api_init(void); +void ble_epic_att_api_event(attEvt_t *att_event); +void ble_epic_att_api_free_att_write_data(struct epic_att_write *w); + +void ble_epic_ble_api_trigger_event(enum epic_ble_event_type type, void *data); +void ble_epic_ble_api_init(void); +void ble_epic_dm_api_event(dmEvt_t *dm_event); + +/************************************************************************************************** + Data Types +**************************************************************************************************/ + +/*! Application message type */ +typedef union +{ + wsfMsgHdr_t hdr; + dmEvt_t dm; + attsCccEvt_t ccc; + attEvt_t att; +} bleMsg_t; diff --git a/epicardium/ble/ble_main.c b/epicardium/ble/ble_main.c index 05e1da53cbef9cd782f6821eab686b429ab418df..7e96fc7cc5049b576c8794267634ff465a5f3c1d 100644 --- a/epicardium/ble/ble_main.c +++ b/epicardium/ble/ble_main.c @@ -17,36 +17,28 @@ #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 "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_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" #include "hid.h" +#include "uart.h" #include "ble_api.h" #include "epicardium.h" -#include "api/interrupt-sender.h" #include "modules/log.h" #include "modules/config.h" @@ -55,24 +47,10 @@ static bool active; static uint8_t advertising_mode = APP_MODE_NONE; static uint8_t advertising_mode_target = APP_MODE_NONE; -static enum ble_event_type ble_event; static struct epic_scan_report scan_reports[SCAN_REPORTS_NUM]; static int scan_reports_head; static int scan_reports_tail; -/************************************************************************************************** - Data Types -**************************************************************************************************/ - -/*! Application message type */ -typedef union -{ - wsfMsgHdr_t hdr; - dmEvt_t dm; - attsCccEvt_t ccc; - attEvt_t att; -} bleMsg_t; - /************************************************************************************************** Configurable Parameters **************************************************************************************************/ @@ -243,6 +221,7 @@ static const attsCccSet_t bleCccSet[BLE_NUM_CCC_IDX] = {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 */ }; /************************************************************************************************** @@ -435,8 +414,6 @@ static void bleCccCback(attsCccEvt_t *pEvt) } } - - /*************************************************************************************************/ /*! * \brief Process CCC state change. @@ -642,27 +619,6 @@ void epic_ble_compare_response(bool confirmed) /* error condition */ } } -static void trigger_event(enum ble_event_type event) -{ - bool enabled; - epic_interrupt_is_enabled(EPIC_INT_BLE, &enabled); - - /* Print a warning if the app is missing events. Missing scan results - * is considered OK though, as they are queued and periodic. */ - if(ble_event && enabled && ble_event != BLE_EVENT_SCAN_REPORT) { - LOG_WARN("ble", "Application missed event %u", ble_event); - } - - ble_event = event; - api_interrupt_trigger(EPIC_INT_BLE); -} - -enum ble_event_type epic_ble_get_event(void) -{ - enum ble_event_type event = ble_event; - ble_event = 0; - return event; -} static void bleHandleNumericComparison(dmSecCnfIndEvt_t *pCnfInd) { @@ -673,7 +629,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_epic_ble_api_trigger_event(BLE_EVENT_HANDLE_NUMERIC_COMPARISON, NULL); } int epic_ble_get_scan_report(struct epic_scan_report *rpt) @@ -694,7 +650,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_epic_ble_api_trigger_event(BLE_EVENT_SCAN_REPORT, NULL); return; } scan_reports_head = next_head; @@ -710,7 +666,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_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"); @@ -739,6 +695,7 @@ static void bleProcMsg(bleMsg_t *pMsg) case ATTS_HANDLE_VALUE_CNF: HidProcMsg(&pMsg->hdr); + UartProcMsg(pMsg); break; case ATTS_CCC_STATE_IND: @@ -773,6 +730,7 @@ static void bleProcMsg(bleMsg_t *pMsg) connOpen->peerAddr[1], connOpen->peerAddr[0]); bleESS_ccc_update(); HidProcMsg(&pMsg->hdr); + UartProcMsg(pMsg); break; case DM_CONN_CLOSE_IND: @@ -808,7 +766,6 @@ static void bleProcMsg(bleMsg_t *pMsg) */ advertising_mode = APP_MODE_NONE; AppAdvStop(); - bleClose(pMsg); break; @@ -821,7 +778,7 @@ static void bleProcMsg(bleMsg_t *pMsg) AppDbNvmStoreBond(last_pairing); pair_connId = DM_CONN_ID_NONE; - trigger_event(BLE_EVENT_PAIRING_COMPLETE); + 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); @@ -846,7 +803,7 @@ static void bleProcMsg(bleMsg_t *pMsg) DmSecGenerateEccKeyReq(); pair_connId = DM_CONN_ID_NONE; - trigger_event(BLE_EVENT_PAIRING_FAILED); + ble_epic_ble_api_trigger_event(BLE_EVENT_PAIRING_FAILED, NULL); break; case DM_SEC_ENCRYPT_IND: @@ -942,12 +899,18 @@ static void BleHandler(wsfEventMask_t event, wsfMsgHdr_t *pMsg) /* process discovery-related messages */ AppDiscProcDmMsg((dmEvt_t *) pMsg); + + ble_epic_dm_api_event((dmEvt_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]); + /* 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) { @@ -972,7 +935,6 @@ static void BleHandler(wsfEventMask_t event, wsfMsgHdr_t *pMsg) /*************************************************************************************************/ void BleStart(void) { - BleHandlerInit(); /* Register for stack callbacks */ @@ -992,7 +954,14 @@ void BleStart(void) 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 */ diff --git a/epicardium/ble/cccd.h b/epicardium/ble/cccd.h index 5c3edf3cb64361af9e5cb841731a80318f102d90..0e49f311871c7c3ee6a872ce42fecf7f48853097 100644 --- a/epicardium/ble/cccd.h +++ b/epicardium/ble/cccd.h @@ -13,6 +13,7 @@ enum 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 }; diff --git a/epicardium/ble/epic_att_api.c b/epicardium/ble/epic_att_api.c new file mode 100644 index 0000000000000000000000000000000000000000..74faa187d50ec813083fdc84d17ef1ac75f220bf --- /dev/null +++ b/epicardium/ble/epic_att_api.c @@ -0,0 +1,280 @@ +#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 "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->handle >= ATTS_DYN_START_HANDLE && + att_event->handle < next_handle) { + attEvt_t *e = WsfBufAlloc(sizeof(*e)); + + if (e) { + memcpy(e, att_event, sizeof(*e)); + ble_epic_ble_api_trigger_event(BLE_EVENT_ATT_EVENT, e); + } else { + LOG_WARN("ble", "could not allocate att event"); + } + } +} + +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; +} diff --git a/epicardium/ble/epic_ble_api.c b/epicardium/ble/epic_ble_api.c new file mode 100644 index 0000000000000000000000000000000000000000..726d26a949ece4dbefbebc4d7e9f0b8b8c9e818e --- /dev/null +++ b/epicardium/ble/epic_ble_api.c @@ -0,0 +1,115 @@ +#include "ble_api.h" + +#include "epicardium.h" +#include "modules/log.h" +#include "api/interrupt-sender.h" + +#include "wsf_buf.h" +#include "app_api.h" +#include "svc_core.h" + +#include "FreeRTOS.h" +#include "queue.h" + +#include <stdint.h> +#include <string.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; + +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 (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); + } + + api_interrupt_trigger(EPIC_INT_BLE); + } else { + epic_ble_free_event(&e); + } +} + +int epic_ble_get_event(struct epic_ble_event *e) +{ + 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(); +} + +void ble_epic_dm_api_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 epic_ble_close_connection(uint8_t connId) +{ + AppConnClose(connId); +} + +int epic_ble_is_connection_open(void) +{ + return AppConnIsOpen(); +} + +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; +} diff --git a/epicardium/ble/hid.c b/epicardium/ble/hid.c index 4386f5b1dd0cbbce6d1e59b33d4fb55a14315033..56f5114218af7ad7db5c04b583c08ecbaeab4a0b 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 341ad22571c4d5bc94c9bcf997c320682659142e..2372440b4e5416aeb6fa57c52154afce12608b7d 100644 --- a/epicardium/ble/meson.build +++ b/epicardium/ble/meson.build @@ -1,5 +1,7 @@ ble_sources = files( 'ble.c', + 'epic_ble_api.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 d5e092179ef76827ccbea05ae3a10059e9ffac1c..4bf4696fd8639c1bf9b3101f3b58101e4220962b 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/ble/uart.c b/epicardium/ble/uart.c index 5fdfb74ac9885975e125f51216bd2953986eca9c..55bf3ee8d6ebeb4df026303d5a51a6a40728155f 100644 --- a/epicardium/ble/uart.c +++ b/epicardium/ble/uart.c @@ -1,8 +1,13 @@ +#include "uart.h" +#include "cccd.h" + #include "modules/modules.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,24 +16,10 @@ #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); @@ -41,7 +32,10 @@ static const uint8_t uartTxCh[] = {ATT_PROP_READ | ATT_PROP_NOTIFY, UINT16_TO_BY static const uint16_t uartTxCh_len = sizeof(uartTxCh); static const uint8_t attUartTxChUuid[] = {0x9E,0xCA,0xDC,0x24,0x0E,0xE5, 0xA9,0xE0,0x93,0xF3,0xA3,0xB5,0x03,0x00,0x40,0x6E}; -static uint8_t ble_uart_tx_buf[128]; +static uint8_t uartValTxChCcc[] = {UINT16_TO_BYTES(0x0000)}; +static const uint16_t uartLenTxChCcc = sizeof(uartValTxChCcc); + +static uint8_t ble_uart_tx_buf[20]; static uint16_t ble_uart_buf_tx_fill = 0; /* clang-format on */ @@ -96,18 +90,17 @@ static const attsAttr_t uartAttrCfgList[] = { }, /* UART tx CCC descriptor */ { - .pUuid = attCliChCfgUuid, - .pValue = NULL, - .pLen = NULL, - .maxLen = 0, - .settings = ATTS_SET_CCC, - .permissions = ATTS_PERMIT_WRITE | ATTS_PERMIT_WRITE_ENC | - ATTS_PERMIT_WRITE_AUTH | ATTS_PERMIT_READ | - ATTS_PERMIT_READ_ENC | ATTS_PERMIT_READ_AUTH, + .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? }, -}; -dmConnId_t active_connection = 0; +}; static uint8_t UARTWriteCback( dmConnId_t connId, @@ -118,62 +111,80 @@ 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; } -static 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) { - for (int 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++; - } + if (ble_uart_buf_tx_fill == 0) { + return; + } - if (ble_uart_buf_tx_fill == 128 || pValue[i] == '\r' || - pValue[i] == '\n') { - if (ble_uart_buf_tx_fill > 0) { - if (active_connection) { - int x = xTaskGetTickCount() - - ble_uart_lasttick; - if (x < 100) { - /* - * TODO: Ugly hack if we already - * send something recently. - * Figure out how fast we - * can send or use indications. - */ - vTaskDelay(100 - x); - } + 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 = { @@ -188,3 +199,19 @@ void bleuart_init(void) /* Add the UART service */ AttsAddGroup(&uartCfgGroup); } + +void UartProcMsg(bleMsg_t *pMsg) +{ + if (pMsg->hdr.event == ATTS_HANDLE_VALUE_CNF) { + if (pMsg->att.handle == UART_TX_HDL) { + if (pMsg->hdr.status == ATT_SUCCESS) { + done = true; + } else if (pMsg->hdr.status == ATT_ERR_OVERFLOW) { + again = true; + } + } + } + if (pMsg->hdr.event == DM_CONN_OPEN_IND) { + ble_uart_buf_tx_fill = 0; + } +} diff --git a/epicardium/ble/uart.h b/epicardium/ble/uart.h new file mode 100644 index 0000000000000000000000000000000000000000..4d3079b5e4bb167e93498e6e87e77704a11c226d --- /dev/null +++ b/epicardium/ble/uart.h @@ -0,0 +1,19 @@ +#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); + diff --git a/epicardium/epicardium.h b/epicardium/epicardium.h index 7715568ca50e77289ae51aacd54bcc40d1e4deb2..3b93ea5bd1cdf3718867bc52986e34728f5e38c2 100644 --- a/epicardium/epicardium.h +++ b/epicardium/epicardium.h @@ -159,9 +159,28 @@ typedef _Bool bool; #define API_BLE_GET_SCAN_REPORT 0x144 #define API_BLE_GET_LAST_PAIRING_NAME 0x145 #define API_BLE_GET_PEER_DEVICE_NAME 0x146 +#define API_BLE_FREE_EVENT 0x147 #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_DELETE_GROUPS 0x169 +#define API_BLE_ATTS_DYN_ADD_CHARACTERISTIC 0x16B +#define API_BLE_ATTS_DYN_ADD_DESCRIPTOR 0x163 +#define API_BLE_ATTS_SET_BUFFER 0x16A +#define API_BLE_ATTS_SEND_SERVICE_CHANGED_IND 0x168 + +#define API_BLE_ATTS_SET_ATTR 0x170 +#define API_BLE_ATTS_HANDLE_VALUE_NTF 0x171 +#define API_BLE_ATTS_HANDLE_VALUE_IND 0x172 + +#define API_BLE_CLOSE_CONNECTION 0x180 +#define API_BLE_IS_CONNECTION_OPEN 0x181 +#define API_BLE_SET_DEVICE_NAME 0x182 +#define API_BLE_GET_DEVICE_NAME 0x183 +#define API_BLE_GET_ADDRESS 0x184 + /* clang-format on */ typedef uint32_t api_int_id_t; @@ -2319,7 +2338,7 @@ API(API_CONFIG_SET_STRING, int epic_config_set_string(const char *key, const cha /** * BLE event type */ -enum ble_event_type { +enum epic_ble_event_type { /** No event pending */ BLE_EVENT_NONE = 0, /** Numeric comparison requested */ @@ -2330,25 +2349,143 @@ enum ble_event_type { BLE_EVENT_PAIRING_COMPLETE = 3, /** New scan data is available */ BLE_EVENT_SCAN_REPORT = 4, + BLE_EVENT_ATT_EVENT = 5, + BLE_EVENT_ATT_WRITE = 6, + BLE_EVENT_DM_EVENT = 7, +}; + +/** + * MicroPython Bluetooth support data types. Please + * do not use them until they are stabilized. + */ +typedef uint8_t bdAddr_t[6]; + +struct epic_wsf_header +{ + /** General purpose parameter passed to event handler */ + uint16_t param; + /** General purpose event value passed to event handler */ + uint8_t event; + /** General purpose status value passed to event handler */ + uint8_t status; }; +struct epic_att_event +{ + /** Header structure */ + struct epic_wsf_header hdr; + /** Value */ + uint8_t *pValue; + /** Value length */ + uint16_t valueLen; + /** Attribute handle */ + uint16_t handle; + /** TRUE if more response packets expected */ + uint8_t continuing; + /** Negotiated MTU value */ + uint16_t mtu; +}; + +struct epic_hciLeConnCmpl_event +{ /** Event header */ + struct epic_wsf_header hdr; + /** Status. */ + uint8_t status; + /** Connection handle. */ + uint16_t handle; + /** Local connection role. */ + uint8_t role; + /** Peer address type. */ + uint8_t addrType; + /** Peer address. */ + bdAddr_t peerAddr; + /** Connection interval */ + uint16_t connInterval; + /** Connection latency. */ + uint16_t connLatency; + /** Supervision timeout. */ + uint16_t supTimeout; + /** Clock accuracy. */ + uint8_t clockAccuracy; + + /** enhanced fields */ + /** Local RPA. */ + bdAddr_t localRpa; + /** Peer RPA. */ + bdAddr_t peerRpa; +}; + +/*! \brief Disconnect complete event */ +struct epic_hciDisconnectCmpl_event +{ + /** Event header */ + struct epic_wsf_header hdr; + /** Disconnect complete status. */ + uint8_t status; + /** Connect handle. */ + uint16_t handle; + /** Reason. */ + uint8_t reason; +}; + +struct epic_dm_event +{ + union { + /** LE connection complete. */ + struct epic_hciLeConnCmpl_event leConnCmpl; + /** Disconnect complete. */ + struct epic_hciDisconnectCmpl_event disconnectCmpl; + }; +}; + +struct epic_att_write +{ + /** Header structure */ + struct epic_wsf_header hdr; + /** Value length */ + uint16_t valueLen; + /** Attribute handle */ + uint16_t handle; + + uint8_t operation; + uint16_t offset; + void *buffer; +}; + +struct epic_ble_event { + enum epic_ble_event_type type; + union { + void *data; + struct epic_att_event *att_event; + struct epic_dm_event *dm_event; + struct epic_att_write *att_write; + }; +}; /** - * Scan report data. Bases on ``hciLeAdvReportEvt_t`` from BLE stack. + * Scan report data. Based on ``hciLeAdvReportEvt_t`` from BLE stack. * * TODO: 64 bytes for data is an arbitrary number ATM */ struct epic_scan_report { - uint8_t data[64]; /*!< \brief advertising or scan response data. */ - uint8_t len; /*!< \brief length of advertising or scan response data. */ - int8_t rssi; /*!< \brief RSSI. */ - uint8_t eventType; /*!< \brief Advertising event type. */ - uint8_t addrType; /*!< \brief Address type. */ - uint8_t addr[6]; /*!< \brief Device address. */ - - /* \brief direct fields */ - uint8_t directAddrType; /*!< \brief Direct advertising address type. */ - uint8_t directAddr[6]; /*!< \brief Direct advertising address. */ + /** advertising or scan response data. */ + uint8_t data[64]; + /** length of advertising or scan response data. */ + uint8_t len; + /** RSSI. */ + int8_t rssi; + /** Advertising event type. */ + uint8_t eventType; + /** Address type. */ + uint8_t addrType; + /** Device address. */ + uint8_t addr[6]; + + /** direct fields */ + /** Direct advertising address type. */ + uint8_t directAddrType; + /** Direct advertising address. */ + uint8_t directAddr[6]; }; /** @@ -2379,15 +2516,10 @@ API_ISR(EPIC_INT_BLE, epic_isr_ble); /** * Retreive the event which triggered :c:func:`epic_isr_ble` * - * The handling code needs to ensure to handle interrupts in a timely - * manner as new events will overwrite each other. Reading the event - * automatically resets it to :c:data:`BLE_EVENT_NONE`. - * - * :return: Event which triggered the interrupt. - * * .. versionadded:: 1.16 + * .. versionchanged:: 1.17 */ -API(API_BLE_GET_EVENT, enum ble_event_type epic_ble_get_event(void)); +API(API_BLE_GET_EVENT, int epic_ble_get_event(struct epic_ble_event *e)); /** * Retrieve the compare value of an ongoing pairing procedure. @@ -2499,5 +2631,35 @@ 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)); + +/** + * MicroPython BLE support API + * + * Please do not use this API outside the MicroPython Bluetooth module. The API designed to work + * specifically with that module. + * + * The MicroPython Bluetooth module is still in flux so this API will continue to change as well. + */ +API(API_BLE_ATTS_DYN_CREATE_GROUP, int epic_atts_dyn_create_service(const uint8_t *uuid, uint8_t uuid_len, uint16_t group_size, void **pSvcHandle)); +//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_CHARACTERISTIC, 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)); +API(API_BLE_ATTS_DYN_ADD_DESCRIPTOR, 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)); + +API(API_BLE_ATTS_SEND_SERVICE_CHANGED_IND, int epic_atts_dyn_send_service_changed_ind(void)); + +API(API_BLE_ATTS_SET_ATTR, int epic_ble_atts_set_attr(uint16_t handle, const uint8_t *value, uint16_t value_len)); +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)); +API(API_BLE_ATTS_HANDLE_VALUE_IND, int epic_ble_atts_handle_value_ind(uint8_t connId, uint16_t handle, uint16_t valueLen, uint8_t *pValue)); +API(API_BLE_ATTS_SET_BUFFER, int epic_ble_atts_set_buffer(uint16_t value_handle, size_t len, bool append)); + +API(API_BLE_FREE_EVENT, int epic_ble_free_event(struct epic_ble_event *e)); +API(API_BLE_CLOSE_CONNECTION, void epic_ble_close_connection(uint8_t connId)); +API(API_BLE_IS_CONNECTION_OPEN, int epic_ble_is_connection_open(void)); +API(API_BLE_SET_DEVICE_NAME, int epic_ble_set_device_name(const uint8_t *buf, uint16_t len)); +API(API_BLE_GET_DEVICE_NAME, int epic_ble_get_device_name(uint8_t **buf, uint16_t *len)); +API(API_BLE_GET_ADDRESS, void epic_ble_get_address(uint8_t *addr)); + #endif /* _EPICARDIUM_H */ diff --git a/epicardium/modules/serial.c b/epicardium/modules/serial.c index f2ec9e9344c9566005fc3e896f70ece2bdbc798e..6be231d6538ecd436708ce024b74b6ce8ea2188f 100644 --- a/epicardium/modules/serial.c +++ b/epicardium/modules/serial.c @@ -50,10 +50,7 @@ void serial_return_to_synchronous() write_stream_buffer = NULL; } -/* - * API-call to write a string. Output goes to both CDCACM and UART - */ -void epic_uart_write_str(const char *str, size_t length) +static void write_str_(const char *str, size_t length) { if (length == 0) { return; @@ -129,6 +126,22 @@ void epic_uart_write_str(const char *str, size_t length) } } +/* + * API-call to write a string. Output goes to CDCACM, UART and BLE + * + * This is user data from core 1. + */ +void epic_uart_write_str(const char *str, size_t length) +{ + /* Make sure that we are not in an interrupt when talking to BLE. + * Should not be the case if this is called from core 1 + * anyways. */ + if (!xPortIsInsideInterrupt()) { + ble_uart_write((uint8_t *)str, length); + } + write_str_(str, length); +} + static void serial_flush_from_isr(void) { uint8_t rx_data[32]; @@ -196,7 +209,6 @@ static void serial_flush_from_thread(void) taskEXIT_CRITICAL(); cdcacm_write((uint8_t *)&rx_data, received_bytes); - ble_uart_write((uint8_t *)&rx_data, received_bytes); } while (received_bytes > 0); } @@ -243,6 +255,11 @@ int epic_uart_read_str(char *buf, size_t cnt) return i; } +/* + * Write a string from epicardium. Output goes to CDCACM and UART + * + * This mainly log data from epicardium, not user date from core 1. + */ long _write_epicardium(int fd, const char *buf, size_t cnt) { /* @@ -252,12 +269,12 @@ long _write_epicardium(int fd, const char *buf, size_t cnt) size_t i, last = 0; for (i = 0; i < cnt; i++) { if (buf[i] == '\n') { - epic_uart_write_str(&buf[last], i - last); - epic_uart_write_str("\r", 1); + write_str_(&buf[last], i - last); + write_str_("\r", 1); last = i; } } - epic_uart_write_str(&buf[last], cnt - last); + write_str_(&buf[last], cnt - last); return cnt; } diff --git a/lib/micropython/gen-qstr.sh b/lib/micropython/gen-qstr.sh index 747ae3f614104dadf98e0d579ccc3c0ae22a1c6f..e339e7d000d60d74051ce05478381616d190ed8d 100755 --- a/lib/micropython/gen-qstr.sh +++ b/lib/micropython/gen-qstr.sh @@ -18,12 +18,12 @@ gcc -E -DNO_QSTR -I"$SOURCE_DIR/micropython" -I"$PROJECT_SRC" -I"$OUTPUT_DIR" "$ rm -rf "$OUTPUT_DIR/qstr" # Generate qstr.split -"$PYTHON" "$SOURCE_DIR/micropython/py/makeqstrdefs.py" split \ - "$OUTPUT_DIR/qstr.i.last" "$OUTPUT_DIR/qstr" "$OUTPUT_DIR/qstrdefs.collected.h" >/dev/null +"$PYTHON" "$SOURCE_DIR/micropython/py/makeqstrdefs.py" split qstr\ + "$OUTPUT_DIR/qstr.i.last" "$OUTPUT_DIR/qstr" "$OUTPUT_DIR/qstrdefs.collected.h" # Generate qstr.collected.h -"$PYTHON" "$SOURCE_DIR/micropython/py/makeqstrdefs.py" cat \ - "$OUTPUT_DIR/qstr.i.last" "$OUTPUT_DIR/qstr" "$OUTPUT_DIR/qstrdefs.collected.h" >/dev/null +"$PYTHON" "$SOURCE_DIR/micropython/py/makeqstrdefs.py" cat qstr\ + "$OUTPUT_DIR/qstr.i.last" "$OUTPUT_DIR/qstr" "$OUTPUT_DIR/qstrdefs.collected.h" # Preprocess Header ... I did not come up with this, this is code copied from # the official make file. Seriously. diff --git a/lib/micropython/meson.build b/lib/micropython/meson.build index bf021db1a6a636e6c46a2896019ac5b57802a5c3..f85af62c96aa1c3a14c32f6c99c3ec9bb6690a4e 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/lib/micropython/micropython b/lib/micropython/micropython index 1f371947309c5ea6023b6d9065415697cbc75578..b0932fcf2e2f9a81abf7737ed4b2573bd9ad4a49 160000 --- a/lib/micropython/micropython +++ b/lib/micropython/micropython @@ -1 +1 @@ -Subproject commit 1f371947309c5ea6023b6d9065415697cbc75578 +Subproject commit b0932fcf2e2f9a81abf7737ed4b2573bd9ad4a49 diff --git a/lib/sdk/Libraries/BTLE/stack/ble-host/include/att_api.h b/lib/sdk/Libraries/BTLE/stack/ble-host/include/att_api.h index 0138ea019d14c696078fc62a213537e9be38c893..713f6525c5007dadf425d4228620f6f544324dd1 100644 --- a/lib/sdk/Libraries/BTLE/stack/ble-host/include/att_api.h +++ b/lib/sdk/Libraries/BTLE/stack/ble-host/include/att_api.h @@ -981,6 +981,15 @@ void AttsDynAddAttr(void *pSvcHandle, const uint8_t *pUuid, const uint8_t *pValu /*************************************************************************************************/ void AttsDynAddAttrConst(void *pSvcHandle, const uint8_t *pUuid, const uint8_t *pValue, const uint16_t len, uint8_t settings, uint8_t permissions); + +void AttsDynAddAttrDyn(void *pSvcHandle, const uint8_t *pUuid, uint8_t uuidLen, const uint8_t *pValue, + uint16_t len, const uint16_t maxLen, uint8_t settings, uint8_t permissions); + +void AttsDynAddAttrDynConst(void *pSvcHandle, const uint8_t *pUuid, uint8_t uuidLen, const uint8_t *pValue, + uint16_t len, const uint16_t maxLen, uint8_t settings, uint8_t permissions); + +uint8_t AttsDynResize(uint16_t handle, uint16_t maxLen); + /**@}*/ /** \name ATT Server Testing diff --git a/lib/sdk/Libraries/BTLE/stack/ble-host/sources/stack/att/atts_dyn.c b/lib/sdk/Libraries/BTLE/stack/ble-host/sources/stack/att/atts_dyn.c index 30dc989ff0afe0e83a65dcc4f703521c0af4d5d8..f523cf687575f9a68c22fd2e092dfda239c7b0e6 100644 --- a/lib/sdk/Libraries/BTLE/stack/ble-host/sources/stack/att/atts_dyn.c +++ b/lib/sdk/Libraries/BTLE/stack/ble-host/sources/stack/att/atts_dyn.c @@ -351,3 +351,162 @@ void AttsDynAddAttrConst(void *pSvcHandle, const uint8_t *pUuid, const uint8_t * AttsAddGroup(&pGroup->group); } } + +void AttsDynAddAttrDyn(void *pSvcHandle, const uint8_t *pUuid, uint8_t uuidLen, const uint8_t *pValue, + uint16_t len, const uint16_t maxLen, uint8_t settings, uint8_t permissions) +{ + attsAttr_t *pAttr; + attsDynGroupCb_t *pGroup = pSvcHandle; + uint16_t handle = pGroup->group.startHandle + pGroup->currentAttr++; + + /* Verify inputs */ + WSF_ASSERT(handle <= pGroup->group.endHandle); + WSF_ASSERT(pUuid); + WSF_ASSERT(len <= maxLen); + + pAttr = pGroup->group.pAttr + (handle - pGroup->group.startHandle); + + + /* Allocate a buffer for the UUID of the attribute */ + pAttr->pUuid = attsDynAlloc(uuidLen); + WSF_ASSERT(pAttr->pUuid); + + if (pAttr->pUuid == NULL) + { + return; + } + + /* Allocate a buffer for the length of the attribute */ + pAttr->pLen = attsDynAlloc(sizeof(uint16_t)); + WSF_ASSERT(pAttr->pLen); + + if (pAttr->pLen == NULL) + { + return; + } + + /* Allocate a buffer for the value of the attribute */ + pAttr->pValue = attsDynAlloc(maxLen); + WSF_ASSERT(pAttr->pValue); + + if (pAttr->pValue == NULL) + { + return; + } + + /* Set the attribute values */ + memcpy(pAttr->pUuid, pUuid, uuidLen); + pAttr->maxLen = maxLen; + pAttr->settings = settings; + pAttr->permissions = permissions; + + + + if (pValue) + { + /* Copy the initial value */ + memcpy(pAttr->pValue, pValue, len); + *pAttr->pLen = len; + } + else + { + /* No initial value, zero value and length */ + memset(pAttr->pValue, 0, maxLen); + *pAttr->pLen = 0; + } + + /* Add the service when the last attribute has been added */ + if (handle == pGroup->group.endHandle) + { + AttsAddGroup(&pGroup->group); + } +} + +uint8_t AttsDynResize(uint16_t handle, uint16_t maxLen) +{ + attsAttr_t *pAttr; + attsGroup_t *pGroup; + void *pValue; + uint8_t err = ATT_SUCCESS; + + /* find attribute */ + if ((pAttr = attsFindByHandle(handle, &pGroup)) != NULL) + { + /* Only alllocate a value if there is a change and if + * it was allocated before. */ + if(pAttr->pValue && pAttr->maxLen != maxLen ) + { + /* Allocate new buffer for the value of the attribute */ + pValue = attsDynAlloc(maxLen); + WSF_ASSERT(pValue); + + if (pValue == NULL) + { + return ATT_ERR_MEMORY; + } + + pAttr->pValue = pValue; + memset(pValue, 0, maxLen); + } + + pAttr->maxLen = maxLen; + } + /* else attribute not found */ + else + { + err = ATT_ERR_NOT_FOUND; + } + + return err; + +} + +void AttsDynAddAttrDynConst(void *pSvcHandle, const uint8_t *pUuid, uint8_t uuidLen, const uint8_t *pValue, + uint16_t len, const uint16_t maxLen, uint8_t settings, uint8_t permissions) +{ + attsAttr_t *pAttr; + attsDynGroupCb_t *pGroup = pSvcHandle; + uint16_t handle = pGroup->group.startHandle + pGroup->currentAttr++; + + /* Verify inputs */ + WSF_ASSERT(handle <= pGroup->group.endHandle); + WSF_ASSERT(pUuid); + WSF_ASSERT(len <= maxLen); + + pAttr = pGroup->group.pAttr + (handle - pGroup->group.startHandle); + + + /* Allocate a buffer for the UUID of the attribute */ + pAttr->pUuid = attsDynAlloc(uuidLen); + WSF_ASSERT(pAttr->pUuid); + + if (pAttr->pUuid == NULL) + { + return; + } + + /* Allocate a buffer for the length of the attribute */ + pAttr->pLen = attsDynAlloc(sizeof(uint16_t)); + WSF_ASSERT(pAttr->pLen); + + if (pAttr->pLen == NULL) + { + return; + } + + pAttr->pValue = pValue; + *pAttr->pLen = len; + + /* Set the attribute values */ + memcpy(pAttr->pUuid, pUuid, uuidLen); + pAttr->maxLen = maxLen; + pAttr->settings = settings; + pAttr->permissions = permissions; + + /* Add the service when the last attribute has been added */ + if (handle == pGroup->group.endHandle) + { + AttsAddGroup(&pGroup->group); + } +} + diff --git a/lib/sdk/Libraries/BTLE/stack/ble-profiles/sources/profiles/gatt/gatt_api.h b/lib/sdk/Libraries/BTLE/stack/ble-profiles/sources/profiles/gatt/gatt_api.h index 028016242eea1d038513f5ca619edb6f61401635..8dd5fcb013715abb9e2a6061349f157b28a150fd 100644 --- a/lib/sdk/Libraries/BTLE/stack/ble-profiles/sources/profiles/gatt/gatt_api.h +++ b/lib/sdk/Libraries/BTLE/stack/ble-profiles/sources/profiles/gatt/gatt_api.h @@ -74,6 +74,31 @@ void GattDiscover(dmConnId_t connId, uint16_t *pHdlList); /*************************************************************************************************/ uint8_t GattValueUpdate(uint16_t *pHdlList, attEvt_t *pMsg); +/*************************************************************************************************/ +/*! + * \brief Set Index of the Service Changed CCCD in the ATT Server. + * + * \param idx Index of the Service Changed CCCD in the ATT Server. + * + * \return None. + */ +/*************************************************************************************************/ +void GattSetSvcChangedIdx(uint8_t idx); + +/*************************************************************************************************/ +/*! + * \brief Send Service Change Indications to the specified connections if they are configured to + * do so. + * + * \param connId DM Connection identifier or \ref DM_CONN_ID_NONE to send to all connections. + * \param start start handle for service changed value. + * \param end end handle for service changed value. + * + * \return None. + */ +/*************************************************************************************************/ +void GattSendServiceChangedInd(dmConnId_t connId, uint16_t start, uint16_t end); + /*! \} */ /* GATT_PROFILE */ #ifdef __cplusplus diff --git a/lib/sdk/Libraries/BTLE/stack/ble-profiles/sources/profiles/gatt/gatt_main.c b/lib/sdk/Libraries/BTLE/stack/ble-profiles/sources/profiles/gatt/gatt_main.c index d7aeb9af7b81add8fa9f09aebd5afbf1e0330915..ad52cc678af7d1119f246b8e43a05dbef892cb2e 100644 --- a/lib/sdk/Libraries/BTLE/stack/ble-profiles/sources/profiles/gatt/gatt_main.c +++ b/lib/sdk/Libraries/BTLE/stack/ble-profiles/sources/profiles/gatt/gatt_main.c @@ -19,13 +19,30 @@ #include "wsf_types.h" #include "wsf_assert.h" +#include "util/bstream.h" #include "app_api.h" #include "gatt/gatt_api.h" +#include "svc_core.h" +#include "att_api.h" + +/************************************************************************************************** +Data Types +**************************************************************************************************/ + +/* Control block. */ +typedef struct +{ + bool_t svcChangedCccdIdxSet; /* Check if Service Changed CCCD index has been initialized. */ + uint8_t svcChangedCccdIdx; /* Stored index of Service Changed CCCD. */ +} gattServCb_t; /************************************************************************************************** Local Variables **************************************************************************************************/ +/* Control block. */ +gattServCb_t gattServCb; + /*! GATT service characteristics for discovery */ /*! Service changed */ @@ -46,7 +63,7 @@ static const attcDiscChar_t gattScCcc = static const attcDiscChar_t *gattDiscCharList[] = { &gattSc, /* Service changed */ - &gattScCcc /* Service changed client characteristic configuration descriptor */ + &gattScCcc, /* Service changed client characteristic configuration descriptor */ }; /* sanity check: make sure handle list length matches characteristic list length */ @@ -101,3 +118,68 @@ uint8_t GattValueUpdate(uint16_t *pHdlList, attEvt_t *pMsg) return status; } + +/*************************************************************************************************/ +/*! + * \brief Set Index of the Service Changed CCCD in the ATT Server. + * + * \param idx Index of the Service Changed CCCD in the ATT Server. + * + * \return None. + */ +/*************************************************************************************************/ +void GattSetSvcChangedIdx(uint8_t idx) +{ + gattServCb.svcChangedCccdIdxSet = TRUE; + gattServCb.svcChangedCccdIdx = idx; +} + +/*************************************************************************************************/ +/*! + * \brief Send Service Change Indications to the specified connections if they are configured to + * do so. + * + * \param connId DM Connection identifier or \ref DM_CONN_ID_NONE to send to all connections. + * \param start start handle for service changed value. + * \param end end handle for service changed value. + * + * \return None. + */ +/*************************************************************************************************/ +void GattSendServiceChangedInd(dmConnId_t connId, uint16_t start, uint16_t end) +{ + uint8_t svcChangedValues[4]; + uint8_t *p; + + if (!gattServCb.svcChangedCccdIdxSet) + { + return; + } + + p = svcChangedValues; + UINT16_TO_BSTREAM(p, start); + UINT16_TO_BSTREAM(p, end); + + /* If connection is not specified */ + if (connId == DM_CONN_ID_NONE) + { + /* Send to all. */ + for (connId = 1; connId <= DM_CONN_MAX; connId++) + { + if (AttsCccEnabled(connId, gattServCb.svcChangedCccdIdx)) + { + AttsHandleValueInd(connId, GATT_SC_HDL, sizeof(svcChangedValues), svcChangedValues); + } + } + } + else + { + /* Send to only this one. */ + if (AttsCccEnabled(connId, gattServCb.svcChangedCccdIdx)) + { + AttsHandleValueInd(connId, GATT_SC_HDL, sizeof(svcChangedValues), svcChangedValues); + } + } +} + + diff --git a/preload/apps/ble/__init__.py b/preload/apps/ble/__init__.py index b50a69808d7c1ac081771ff13d841ba5efc9547e..9eff8891c0343d6fc6283b191a7378dd7a44e3bd 100644 --- a/preload/apps/ble/__init__.py +++ b/preload/apps/ble/__init__.py @@ -6,7 +6,7 @@ import sys_ble import interrupt import config -ble_event = None +ble_events = [] is_active = False STATE_IDLE = 1 @@ -16,8 +16,13 @@ STATE_FAIL = 4 def ble_callback(_): - global ble_event - ble_event = sys_ble.get_event() + global ble_events + + while True: + event = sys_ble.get_event() + if event == sys_ble.EVENT_NONE: + break + ble_events.append(event) def init(): @@ -95,6 +100,11 @@ while True: v = ~v_old & v_new v_old = v_new + ble_event = None + if len(ble_events) > 0: + ble_event = ble_events[0] + ble_events = ble_events[1:] + if state == STATE_IDLE: # print config screen disp.clear() @@ -104,7 +114,6 @@ while True: # wait for button press or ble_event if ble_event == sys_ble.EVENT_HANDLE_NUMERIC_COMPARISON: - ble_event = None state = STATE_NUMERIC_COMPARISON if v & buttons.TOP_RIGHT: toggle() @@ -128,7 +137,6 @@ while True: # wait for button press or ble_event if ble_event == sys_ble.EVENT_PAIRING_FAILED: - ble_event = None state = STATE_FAIL if v & buttons.BOTTOM_LEFT: sys_ble.confirm_compare_value(True) @@ -144,10 +152,8 @@ while True: elif state == STATE_WAIT_FOR_COMPLETION: # Wait for pairing to complete if ble_event == sys_ble.EVENT_PAIRING_FAILED: - ble_event = None state = STATE_FAIL elif ble_event == sys_ble.EVENT_PAIRING_COMPLETE: - ble_event = None pairing_name = sys_ble.get_last_pairing_name().split("/")[-1].split(".")[0] disp.clear() disp.print("BLE Pairing", posy=0, fg=[0, 0, 255]) diff --git a/preload/apps/exnostat/__init__.py b/preload/apps/exnostat/__init__.py index 57c69fc19a1377123c47c1bf8c2b576753526888..60cf16d3bb07f33325053e8ff81b0462e505b0dc 100644 --- a/preload/apps/exnostat/__init__.py +++ b/preload/apps/exnostat/__init__.py @@ -100,14 +100,17 @@ def process_scan_report(scan_report): def ble_callback(_): - event = sys_ble.get_event() - if event == sys_ble.EVENT_SCAN_REPORT: - while True: - scan_report = sys_ble.get_scan_report() - if scan_report == None: - break - process_scan_report(scan_report) - prune() + while True: + event = sys_ble.get_event() + if event == sys_ble.EVENT_NONE: + break + if event == sys_ble.EVENT_SCAN_REPORT: + while True: + scan_report = sys_ble.get_scan_report() + if scan_report == None: + break + process_scan_report(scan_report) + prune() def show_stats(): diff --git a/pycardium/modules/fat_file.c b/pycardium/modules/fat_file.c index ce89e32e8061d81fae9cb6159552dc434a6bf3b7..ed4f1669c61462b78f5a5a3dc279476989511031 100644 --- a/pycardium/modules/fat_file.c +++ b/pycardium/modules/fat_file.c @@ -111,11 +111,11 @@ file_obj_ioctl(mp_obj_t o_in, mp_uint_t request, uintptr_t arg, int *errcode) STATIC const mp_arg_t file_open_args[] = { { MP_QSTR_file, MP_ARG_OBJ | MP_ARG_REQUIRED, - { .u_rom_obj = MP_ROM_PTR(&mp_const_none_obj) } }, + { .u_rom_obj = mp_const_none } }, { MP_QSTR_mode, MP_ARG_OBJ, { .u_obj = MP_OBJ_NEW_QSTR(MP_QSTR_r) } }, { MP_QSTR_encoding, MP_ARG_OBJ | MP_ARG_KW_ONLY, - { .u_rom_obj = MP_ROM_PTR(&mp_const_none_obj) } }, + { .u_rom_obj = mp_const_none } }, }; #define FILE_OPEN_NUM_ARGS MP_ARRAY_SIZE(file_open_args) diff --git a/pycardium/modules/modbluetooth_card10.c b/pycardium/modules/modbluetooth_card10.c index 0bff819a6aec832559e69a8fee90676218212a40..a13d3f7934cc10080b041995807cf3d04a3264ac 100644 --- a/pycardium/modules/modbluetooth_card10.c +++ b/pycardium/modules/modbluetooth_card10.c @@ -1,38 +1,212 @@ +#include "modbluetooth_card10.h" #include "extmod/modbluetooth.h" #include "py/runtime.h" #include <stdint.h> +#include <stdio.h> +#include <string.h> + +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_gatts_db_t gatts_status; +} mp_bluetooth_card10_root_pointers_t; + +#define GATTS_DB (MP_STATE_PORT(bluetooth_card10_root_pointers)->gatts_db) +#define GATTS_STATUS \ + (MP_STATE_PORT(bluetooth_card10_root_pointers)->gatts_status) + +typedef struct { + enum notification_status notification_status; +} gatts_status_entry_t; + +static bool active = false; + +static void gatts_status_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_status_entry_t *entry = m_new(gatts_status_entry_t, 1); + entry->notification_status = NOTIFICATION_STATUS_UNKNOWN; + elem->value = MP_OBJ_FROM_PTR(entry); +} + +static gatts_status_entry_t * +gatts_status_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_ble_event ble_event; + int ret; + + do { + ret = epic_ble_get_event(&ble_event); + if (ret >= 0) { + epic_ble_free_event(&ble_event); + } + } while (ret >= 0); +} + +static void handle_att_event(struct epic_att_event *att_event) +{ + if (att_event->hdr.event == ATTS_HANDLE_VALUE_CNF) { + gatts_status_entry_t *e = + gatts_status_lookup(GATTS_STATUS, 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; + } + } +} + +static void handle_dm_event(struct epic_dm_event *dm_event) +{ + struct epic_wsf_header *hdr = (struct epic_wsf_header *)dm_event; + + if (hdr->event == DM_CONN_OPEN_IND) { + struct epic_hciLeConnCmpl_event *e = + (struct epic_hciLeConnCmpl_event *)dm_event; + mp_bluetooth_gap_on_connected_disconnected( + MP_BLUETOOTH_IRQ_CENTRAL_CONNECT, + e->hdr.param, + e->addrType, + e->peerAddr + ); + } else if (hdr->event == DM_CONN_CLOSE_IND) { + struct epic_hciDisconnectCmpl_event *e = + (struct epic_hciDisconnectCmpl_event *)dm_event; + uint8_t addr[6] = {}; + mp_bluetooth_gap_on_connected_disconnected( + MP_BLUETOOTH_IRQ_CENTRAL_DISCONNECT, + e->hdr.param, + 0xFF, + addr + ); + } +} + +static void handle_att_write(struct epic_att_write *att_write) +{ + mp_bluetooth_gatts_db_entry_t *entry = + mp_bluetooth_gatts_db_lookup(GATTS_DB, att_write->handle); + if (!entry) { + return; + } + + // TODO: Use `offset` arg. + size_t append_offset = 0; + if (entry->append) { + append_offset = entry->data_len; + } + entry->data_len = + MIN(entry->data_alloc, att_write->valueLen + append_offset); + memcpy(entry->data + append_offset, + att_write->buffer, + entry->data_len - append_offset); + + mp_bluetooth_gatts_on_write(att_write->hdr.param, att_write->handle); +} + +static mp_obj_t mp_ble_poll_events(mp_obj_t interrupt_id) +{ + struct epic_ble_event ble_event; + int ret; + + do { + ret = epic_ble_get_event(&ble_event); + if (ret >= 0) { + if (ble_event.type == BLE_EVENT_ATT_EVENT) { + handle_att_event(ble_event.att_event); + } + if (ble_event.type == BLE_EVENT_DM_EVENT) { + handle_dm_event(ble_event.dm_event); + } + if (ble_event.type == BLE_EVENT_ATT_WRITE) { + handle_att_write(ble_event.att_write); + } + epic_ble_free_event(&ble_event); + } + } 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) { - raise(); + MP_STATE_PORT(bluetooth_card10_root_pointers) = + m_new0(mp_bluetooth_card10_root_pointers_t, 1); + + mp_bluetooth_gatts_db_create(&GATTS_DB); + mp_bluetooth_gatts_db_create(&GATTS_STATUS); + + mp_interrupt_set_callback( + MP_ROM_INT(EPIC_INT_BLE), (mp_obj_t *)&ble_event_obj + ); + clear_events(); + epic_interrupt_enable(EPIC_INT_BLE); + active = true; return 0; } // Disables the Bluetooth stack. Is a no-op when not enabled. void mp_bluetooth_deinit(void) { - raise(); + epic_interrupt_disable(EPIC_INT_BLE); + active = false; } // Returns true when the Bluetooth stack is enabled. -bool mp_bluetooth_is_enabled(void) +bool mp_bluetooth_is_active(void) { - raise(); - return false; + return active; } // Gets the MAC addr of this device in big-endian format. void mp_bluetooth_get_device_addr(uint8_t *addr) { + epic_ble_get_address(addr); +} + +size_t mp_bluetooth_gap_get_device_name(const uint8_t **buf) +{ + uint16_t len; + epic_ble_get_device_name((uint8_t **)buf, &len); + return len; +} + +int mp_bluetooth_gap_set_device_name(const uint8_t *buf, size_t len) +{ + return epic_ble_set_device_name(buf, len); } // Start advertisement. Will re-start advertisement when already enabled. @@ -45,7 +219,12 @@ int mp_bluetooth_gap_advertise_start( const uint8_t *sr_data, size_t sr_data_len ) { - raise(); + // Dropping any current connection starts advertising on the card10 + // TODO: modify the advertising data + int connection = epic_ble_is_connection_open(); + if (connection > 0) { + epic_ble_close_connection(connection); + } return 0; } @@ -58,9 +237,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) { - raise(); + if (!append) { + epic_ble_atts_dyn_delete_groups(); + } return 0; } + // // 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( @@ -73,13 +255,138 @@ int mp_bluetooth_gatts_register_service( uint16_t *handles, size_t num_characteristics ) { - raise(); + void *pSvcHandle; + + size_t num_descriptors_total = 0; + size_t num_ccds = 0; + for (size_t i = 0; i < num_characteristics; i++) { + num_descriptors_total += num_descriptors[i]; + 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 + const uint16_t numHandles = + 1 + num_characteristics * 2 + num_ccds + num_descriptors_total; + + uint8_t uuid_len = + service_uuid->type == MP_BLUETOOTH_UUID_TYPE_16 ? 2 : 16; + + // TODO: handle error + epic_atts_dyn_create_service( + service_uuid->data, uuid_len, numHandles, &pSvcHandle + ); + + size_t descriptor_index = 0; + size_t handle_index = 0; + + for (size_t i = 0; i < num_characteristics; i++) { + uint8_t flags = characteristic_flags[i]; + mp_obj_bluetooth_uuid_t *uuid = characteristic_uuids[i]; + uuid_len = uuid->type == MP_BLUETOOTH_UUID_TYPE_16 ? 2 : 16; + + uint16_t value_handle; + epic_atts_dyn_add_characteristic( + pSvcHandle, + uuid->data, + uuid_len, + flags, + MP_BLUETOOTH_DEFAULT_ATTR_LEN, + &value_handle + ); + + ; + handles[handle_index] = value_handle; + mp_bluetooth_gatts_db_create_entry( + GATTS_DB, value_handle, MP_BLUETOOTH_DEFAULT_ATTR_LEN + ); + gatts_status_create_entry(GATTS_STATUS, value_handle); + + if ((flags & MP_BLUETOOTH_CHARACTERISTIC_FLAG_NOTIFY) || + flags & (MP_BLUETOOTH_CHARACTERISTIC_FLAG_INDICATE)) { + /* CCCD */ + uint8_t cccd_buf[2] = {}; + // TODO: Handle CCC data. + // Until then: activate notification/indications by default. + if (flags & MP_BLUETOOTH_CHARACTERISTIC_FLAG_NOTIFY) { + cccd_buf[0] |= 0x01; + } + if (flags & MP_BLUETOOTH_CHARACTERISTIC_FLAG_INDICATE) { + cccd_buf[0] |= 0x02; + } + + uint16_t cccd_handle; + uint8_t ccc_uuid[] = { 0x02, 0x29 }; + uint8_t flags = MP_BLUETOOTH_CHARACTERISTIC_FLAG_READ | + MP_BLUETOOTH_CHARACTERISTIC_FLAG_WRITE; + epic_ble_atts_dyn_add_descriptor( + pSvcHandle, + ccc_uuid, + sizeof(ccc_uuid), + flags, + cccd_buf, + sizeof(cccd_buf), + sizeof(cccd_buf), + &cccd_handle + ); + + mp_bluetooth_gatts_db_create_entry( + GATTS_DB, cccd_handle, sizeof(cccd_buf) + ); + int ret = mp_bluetooth_gatts_db_write( + GATTS_DB, + cccd_handle, + cccd_buf, + sizeof(cccd_buf) + ); + if (ret) { + return ret; + } + } + + handle_index++; + + for (size_t j = 0; j < num_descriptors[i]; j++) { + flags = descriptor_flags[descriptor_index]; + mp_obj_bluetooth_uuid_t *uuid = + descriptor_uuids[descriptor_index]; + uuid_len = uuid->type == MP_BLUETOOTH_UUID_TYPE_16 ? 2 : + 16; + + uint16_t descriptor_handle; + epic_ble_atts_dyn_add_descriptor( + pSvcHandle, + uuid->data, + uuid_len, + flags, + NULL, + 0, + MP_BLUETOOTH_DEFAULT_ATTR_LEN, + &descriptor_handle + ); + + handles[handle_index] = descriptor_handle; + mp_bluetooth_gatts_db_create_entry( + GATTS_DB, + descriptor_handle, + MP_BLUETOOTH_DEFAULT_ATTR_LEN + ); + + descriptor_index++; + handle_index++; + } + } + return 0; } // Register any queued services. int mp_bluetooth_gatts_register_service_end() { - raise(); + epic_atts_dyn_send_service_changed_ind(); return 0; } @@ -87,36 +394,91 @@ int mp_bluetooth_gatts_register_service_end() int mp_bluetooth_gatts_read( uint16_t value_handle, uint8_t **value, size_t *value_len ) { - raise(); - return 0; + return mp_bluetooth_gatts_db_read( + GATTS_DB, value_handle, value, value_len + ); } // Write a value to the local gatts db (ready to be queried by a central). int mp_bluetooth_gatts_write( uint16_t value_handle, const uint8_t *value, size_t value_len ) { - raise(); - return 0; + // TODO: which return value to choose? + mp_bluetooth_gatts_db_write(GATTS_DB, value_handle, value, value_len); + int ret = epic_ble_atts_set_attr(value_handle, value, value_len); + 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); + mp_bluetooth_gatts_db_read(GATTS_DB, 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( uint16_t conn_handle, uint16_t value_handle, const uint8_t *value, - size_t *value_len + size_t value_len ) { - raise(); + gatts_status_entry_t *e = + gatts_status_lookup(GATTS_STATUS, 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. int mp_bluetooth_gatts_indicate(uint16_t conn_handle, uint16_t value_handle) { - raise(); + gatts_status_entry_t *e = + gatts_status_lookup(GATTS_STATUS, value_handle); + e->notification_status = NOTIFICATION_STATUS_PENDING; + + // Note: cordio doesn't appear to support sending a notification without a value, so include the stored value. + uint8_t *value = NULL; + size_t value_len = 0; + mp_bluetooth_gatts_read(value_handle, &value, &value_len); + + int ret = epic_ble_atts_handle_value_ind( + 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; + } + + // TODO: How does the cordio stack signal that the indication was acked? + // Need to call mp_bluetooth_gatts_on_indicate_complete afterwards return 0; } @@ -124,14 +486,15 @@ int mp_bluetooth_gatts_indicate(uint16_t conn_handle, uint16_t value_handle) // Append-mode means that remote writes will append and local reads will clear after reading. int mp_bluetooth_gatts_set_buffer(uint16_t value_handle, size_t len, bool append) { - raise(); - return 0; + // TODO; which return value to use? + mp_bluetooth_gatts_db_resize(GATTS_DB, value_handle, len, append); + return -epic_ble_atts_set_buffer(value_handle, len, append); } // Disconnect from a central or peripheral. int mp_bluetooth_gap_disconnect(uint16_t conn_handle) { - raise(); + epic_ble_close_connection(conn_handle); return 0; } diff --git a/pycardium/modules/modbluetooth_card10.h b/pycardium/modules/modbluetooth_card10.h new file mode 100644 index 0000000000000000000000000000000000000000..0e2296a7e99fcfa862e2721f5c947dd139afb923 --- /dev/null +++ b/pycardium/modules/modbluetooth_card10.h @@ -0,0 +1,131 @@ +#pragma once + +/* + * card10 related BLE defines. They are taken from the cordio (not card10!) + * BLE stack which is used by the card10. + */ + +#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 */ + +#define DM_CBACK_START 0x20 /*!< \brief DM callback event starting value */ + +/*! \brief DM callback events */ +enum { DM_RESET_CMPL_IND = DM_CBACK_START, /*!< \brief Reset complete */ + DM_ADV_START_IND, /*!< \brief Advertising started */ + DM_ADV_STOP_IND, /*!< \brief Advertising stopped */ + DM_ADV_NEW_ADDR_IND, /*!< \brief New resolvable address has been generated */ + DM_SCAN_START_IND, /*!< \brief Scanning started */ + DM_SCAN_STOP_IND, /*!< \brief Scanning stopped */ + DM_SCAN_REPORT_IND, /*!< \brief Scan data received from peer device */ + DM_CONN_OPEN_IND, /*!< \brief Connection opened */ + DM_CONN_CLOSE_IND, /*!< \brief Connection closed */ + DM_CONN_UPDATE_IND, /*!< \brief Connection update complete */ + DM_SEC_PAIR_CMPL_IND, /*!< \brief Pairing completed successfully */ + DM_SEC_PAIR_FAIL_IND, /*!< \brief Pairing failed or other security failure */ + DM_SEC_ENCRYPT_IND, /*!< \brief Connection encrypted */ + DM_SEC_ENCRYPT_FAIL_IND, /*!< \brief Encryption failed */ + DM_SEC_AUTH_REQ_IND, /*!< \brief PIN or OOB data requested for pairing */ + DM_SEC_KEY_IND, /*!< \brief Security key indication */ + DM_SEC_LTK_REQ_IND, /*!< \brief LTK requested for encyption */ + DM_SEC_PAIR_IND, /*!< \brief Incoming pairing request from master */ + DM_SEC_SLAVE_REQ_IND, /*!< \brief Incoming security request from slave */ + DM_SEC_CALC_OOB_IND, /*!< \brief Result of OOB Confirm Calculation Generation */ + DM_SEC_ECC_KEY_IND, /*!< \brief Result of ECC Key Generation */ + DM_SEC_COMPARE_IND, /*!< \brief Result of Just Works/Numeric Comparison Compare Value Calculation */ + DM_SEC_KEYPRESS_IND, /*!< \brief Keypress indication from peer in passkey security */ + DM_PRIV_RESOLVED_ADDR_IND, /*!< \brief Private address resolved */ + DM_PRIV_GENERATE_ADDR_IND, /*!< \brief Private resolvable address generated */ + DM_CONN_READ_RSSI_IND, /*!< \brief Connection RSSI read */ + DM_PRIV_ADD_DEV_TO_RES_LIST_IND, /*!< \brief Device added to resolving list */ + DM_PRIV_REM_DEV_FROM_RES_LIST_IND, /*!< \brief Device removed from resolving list */ + DM_PRIV_CLEAR_RES_LIST_IND, /*!< \brief Resolving list cleared */ + DM_PRIV_READ_PEER_RES_ADDR_IND, /*!< \brief Peer resolving address read */ + DM_PRIV_READ_LOCAL_RES_ADDR_IND, /*!< \brief Local resolving address read */ + DM_PRIV_SET_ADDR_RES_ENABLE_IND, /*!< \brief Address resolving enable set */ + DM_REM_CONN_PARAM_REQ_IND, /*!< \brief Remote connection parameter requested */ + DM_CONN_DATA_LEN_CHANGE_IND, /*!< \brief Data length changed */ + DM_CONN_WRITE_AUTH_TO_IND, /*!< \brief Write authenticated payload complete */ + DM_CONN_AUTH_TO_EXPIRED_IND, /*!< \brief Authenticated payload timeout expired */ + DM_PHY_READ_IND, /*!< \brief Read PHY */ + DM_PHY_SET_DEF_IND, /*!< \brief Set default PHY */ + DM_PHY_UPDATE_IND, /*!< \brief PHY update */ + DM_ADV_SET_START_IND, /*!< \brief Advertising set(s) started */ + DM_ADV_SET_STOP_IND, /*!< \brief Advertising set(s) stopped */ + DM_SCAN_REQ_RCVD_IND, /*!< \brief Scan request received */ + DM_EXT_SCAN_START_IND, /*!< \brief Extended scanning started */ + DM_EXT_SCAN_STOP_IND, /*!< \brief Extended scanning stopped */ + DM_EXT_SCAN_REPORT_IND, /*!< \brief Extended scan data received from peer device */ + DM_PER_ADV_SET_START_IND, /*!< \brief Periodic advertising set started */ + DM_PER_ADV_SET_STOP_IND, /*!< \brief Periodic advertising set stopped */ + DM_PER_ADV_SYNC_EST_IND, /*!< \brief Periodic advertising sync established */ + DM_PER_ADV_SYNC_EST_FAIL_IND, /*!< \brief Periodic advertising sync establishment failed */ + DM_PER_ADV_SYNC_LOST_IND, /*!< \brief Periodic advertising sync lost */ + DM_PER_ADV_SYNC_TRSF_EST_IND, /*!< \brief Periodic advertising sync transfer established */ + DM_PER_ADV_SYNC_TRSF_EST_FAIL_IND, /*!< \brief Periodic advertising sync transfer establishment failed */ + DM_PER_ADV_SYNC_TRSF_IND, /*!< \brief Periodic advertising sync info transferred */ + DM_PER_ADV_SET_INFO_TRSF_IND, /*!< \brief Periodic advertising set sync info transferred */ + DM_PER_ADV_REPORT_IND, /*!< \brief Periodic advertising data received from peer device */ + DM_REMOTE_FEATURES_IND, /*!< \brief Remote features from peer device */ + DM_READ_REMOTE_VER_INFO_IND, /*!< \brief Remote LL version information read */ + DM_CONN_IQ_REPORT_IND, /*!< \brief IQ samples from CTE of received packet from peer device */ + DM_CTE_REQ_FAIL_IND, /*!< \brief CTE request failed */ + DM_CONN_CTE_RX_SAMPLE_START_IND, /*!< \brief Sampling received CTE started */ + DM_CONN_CTE_RX_SAMPLE_STOP_IND, /*!< \brief Sampling received CTE stopped */ + DM_CONN_CTE_TX_CFG_IND, /*!< \brief Connection CTE transmit parameters configured */ + DM_CONN_CTE_REQ_START_IND, /*!< \brief Initiating connection CTE request started */ + DM_CONN_CTE_REQ_STOP_IND, /*!< \brief Initiating connection CTE request stopped */ + DM_CONN_CTE_RSP_START_IND, /*!< \brief Responding to connection CTE request started */ + DM_CONN_CTE_RSP_STOP_IND, /*!< \brief Responding to connection CTE request stopped */ + DM_READ_ANTENNA_INFO_IND, /*!< \brief Antenna information read */ + DM_L2C_CMD_REJ_IND, /*!< \brief L2CAP Command Reject */ + DM_ERROR_IND, /*!< \brief General error */ + DM_HW_ERROR_IND, /*!< \brief Hardware error */ + DM_VENDOR_SPEC_IND /*!< \brief Vendor specific event */ +}; + +#define ATTS_HANDLE_VALUE_CNF 15 + + diff --git a/pycardium/modules/sys_ble.c b/pycardium/modules/sys_ble.c index 7b95d8d4d9081a72ed687eeb0c40fd750589e09e..b491f9e148d51d1df6d579fcd636f527e3c2df79 100644 --- a/pycardium/modules/sys_ble.c +++ b/pycardium/modules/sys_ble.c @@ -84,7 +84,12 @@ static MP_DEFINE_CONST_FUN_OBJ_0( static mp_obj_t mp_ble_get_event(void) { - return mp_obj_new_int(epic_ble_get_event()); + struct epic_ble_event e; + if (epic_ble_get_event(&e) >= 0) { + epic_ble_free_event(&e); + return mp_obj_new_int(e.type); + } + return mp_obj_new_int(BLE_EVENT_NONE); } static MP_DEFINE_CONST_FUN_OBJ_0(ble_get_event_obj, mp_ble_get_event); diff --git a/pycardium/mpconfigport.h b/pycardium/mpconfigport.h index 8749da69403d4eeadd7e799f612c94662bc1d5fd..1bcb79a1804deb525996192b5ec822d2df42a034 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 \ diff --git a/pycardium/mphalport.c b/pycardium/mphalport.c index 0c5c5b2cc84135584aa74df1253dc9b9f5f15c8c..49fbf9232d5c93527cdfe21f7948b7e7f9fa8419 100644 --- a/pycardium/mphalport.c +++ b/pycardium/mphalport.c @@ -232,7 +232,7 @@ static void systick_delay_sleep(uint32_t us) * One example of this happeing is the KeyboardInterrupt * (CTRL+C) which will abort the running code and exit to REPL. */ - mp_handle_pending(); + mp_handle_pending(true); } }