diff --git a/epicardium/ble/epic_att_api.c b/epicardium/ble/epic_att_api.c index 74faa187d50ec813083fdc84d17ef1ac75f220bf..bcdd420ad00337039f366ef33c5bb609782960fc 100644 --- a/epicardium/ble/epic_att_api.c +++ b/epicardium/ble/epic_att_api.c @@ -25,15 +25,29 @@ 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 (att_event->hdr.event != ATTS_HANDLE_VALUE_CNF || + (att_event->handle >= ATTS_DYN_START_HANDLE && + att_event->handle < next_handle)) { + size_t value_len = 0; + if (att_event->hdr.event == ATTC_READ_BY_GROUP_TYPE_RSP || + att_event->hdr.event == ATTC_READ_BY_TYPE_RSP || + att_event->hdr.event == ATTC_FIND_INFO_RSP || + att_event->hdr.event == ATTC_HANDLE_VALUE_NTF || + att_event->hdr.event == ATTC_HANDLE_VALUE_IND) { + value_len = att_event->valueLen; + } + attEvt_t *e = WsfBufAlloc(sizeof(*e) + value_len); if (e) { memcpy(e, att_event, sizeof(*e)); + memcpy(e + 1, att_event->pValue, value_len); ble_epic_ble_api_trigger_event(BLE_EVENT_ATT_EVENT, e); } else { - LOG_WARN("ble", "could not allocate att event"); + LOG_WARN( + "ble", + "could not allocate att event of size %d", + sizeof(*e) + att_event->valueLen + ); } } } @@ -278,3 +292,70 @@ int epic_ble_atts_set_attr( uint8_t ret = AttsSetAttr(handle, value_len, (uint8_t *)value); return ret; } + +int epic_ble_attc_discover_primary_services( + uint8_t connId, const uint8_t *uuid, uint8_t uuid_len +) { + if (uuid_len == 0 || uuid == NULL) { + AttcReadByGroupTypeReq( + connId, 1, 0xFFFF, 2, (uint8_t *)attPrimSvcUuid, TRUE + ); + } else { + AttcFindByTypeValueReq( + connId, + ATT_HANDLE_START, + ATT_HANDLE_MAX, + ATT_UUID_PRIMARY_SERVICE, + uuid_len, + (uint8_t *)uuid, + FALSE + ); + } + return 0; +} + +int epic_ble_attc_discover_characteristics( + uint8_t connId, uint16_t start_handle, uint16_t end_handle +) { + AttcReadByTypeReq( + connId, + start_handle, + end_handle, + ATT_16_UUID_LEN, + (uint8_t *)attChUuid, + TRUE + ); + return 0; +} + +int epic_ble_attc_discover_descriptors( + uint8_t connId, uint16_t start_handle, uint16_t end_handle +) { + AttcFindInfoReq(connId, start_handle, end_handle, TRUE); + return 0; +} + +int epic_ble_attc_read(uint8_t connId, uint16_t value_handle) +{ + AttcReadReq(connId, value_handle); + return 0; +} + +int epic_ble_attc_write_no_rsp( + uint8_t connId, + uint16_t value_handle, + const uint8_t *value, + uint16_t value_len +) { + AttcWriteCmd(connId, value_handle, value_len, (uint8_t *)value); + return 0; +} +int epic_ble_attc_write( + uint8_t connId, + uint16_t value_handle, + const uint8_t *value, + uint16_t value_len +) { + AttcWriteReq(connId, value_handle, value_len, (uint8_t *)value); + return 0; +} diff --git a/epicardium/epicardium.h b/epicardium/epicardium.h index 75901978e91b0c8104bd4d70a3c4c21f5669a585..2cbe812e426020c21a25afe6c1b6043444a80a95 100644 --- a/epicardium/epicardium.h +++ b/epicardium/epicardium.h @@ -182,6 +182,12 @@ typedef _Bool bool; #define API_BLE_GET_ADDRESS 0x184 #define API_BLE_ADVERTISE 0x185 #define API_BLE_ADVERTISE_STOP 0x186 +#define API_BLE_DISCOVER_PRIMARY_SERVICES 0x187 +#define API_BLE_DISCOVER_CHARACTERISTICS 0x188 +#define API_BLE_DISCOVER_DESCRIPTORS 0x189 +#define API_BLE_ATTC_READ 0x18A +#define API_BLE_ATTC_WRITE_NO_RSP 0x18B +#define API_BLE_ATTC_WRITE 0x18C /* clang-format on */ @@ -2666,5 +2672,11 @@ API(API_BLE_GET_ADDRESS, void epic_ble_get_address(uint8_t *addr)); API(API_BLE_ADVERTISE, int epic_ble_advertise(int interval_us, const uint8_t *adv_data, size_t adv_data_len, const uint8_t *sr_data, size_t sr_data_len, bool connectable)); API(API_BLE_ADVERTISE_STOP, int epic_ble_advertise_stop(void)); +API(API_BLE_DISCOVER_PRIMARY_SERVICES, int epic_ble_attc_discover_primary_services(uint8_t connId, const uint8_t *uuid, uint8_t uuid_len)); +API(API_BLE_DISCOVER_CHARACTERISTICS, int epic_ble_attc_discover_characteristics(uint8_t connId, uint16_t start_handle, uint16_t end_handle)); +API(API_BLE_DISCOVER_DESCRIPTORS, int epic_ble_attc_discover_descriptors(uint8_t connId, uint16_t start_handle, uint16_t end_handle)); +API(API_BLE_ATTC_READ, int epic_ble_attc_read(uint8_t connId, uint16_t value_handle)); +API(API_BLE_ATTC_WRITE_NO_RSP, int epic_ble_attc_write_no_rsp(uint8_t connId, uint16_t value_handle, const uint8_t *value, uint16_t value_len)); +API(API_BLE_ATTC_WRITE, int epic_ble_attc_write(uint8_t connId, uint16_t value_handle, const uint8_t *value, uint16_t value_len)); #endif /* _EPICARDIUM_H */ diff --git a/pycardium/modules/modbluetooth_card10.c b/pycardium/modules/modbluetooth_card10.c index 4a659f93d42fa430af82e18d5ed8bc2c7b08248d..91e2e399ee464b7b32f82ff7fcba7a18223c8b5c 100644 --- a/pycardium/modules/modbluetooth_card10.c +++ b/pycardium/modules/modbluetooth_card10.c @@ -1,6 +1,7 @@ #include "modbluetooth_card10.h" #include "extmod/modbluetooth.h" #include "py/runtime.h" +#include "py/mperrno.h" #include <stdint.h> #include <stdio.h> #include <string.h> @@ -27,6 +28,7 @@ typedef struct { } gatts_status_entry_t; static bool active = false; +static mp_obj_bluetooth_uuid_t uuid_filter; static void gatts_status_create_entry(mp_gatts_db_t db, uint16_t handle) { @@ -75,12 +77,195 @@ static void handle_att_event(struct epic_att_event *att_event) e->notification_status = NOTIFICATION_STATUS_OVERFLOW; } } +#if MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE + if (att_event->hdr.event == ATTC_READ_BY_GROUP_TYPE_RSP) { + // TODO: can we get the connection id from the event? + + uint8_t *v = att_event->pValue; + uint8_t entry_size = v[0]; + + if (att_event->hdr.status == ATT_SUCCESS) { + for (size_t i = 1; i < att_event->valueLen; + i += entry_size) { + uint16_t start_handle = (v[i + 1] << 8) | v[i]; + uint16_t end_handle = + (v[i + 3] << 8) | v[i + 2]; + mp_obj_bluetooth_uuid_t service_uuid; + if (entry_size == 6) { + memcpy(service_uuid.data, v + i + 4, 2); + service_uuid.type = + MP_BLUETOOTH_UUID_TYPE_16; + } else if (entry_size == 20) { + memcpy(service_uuid.data, + v + i + 4, + 16); + service_uuid.type = + MP_BLUETOOTH_UUID_TYPE_128; + } + mp_bluetooth_gattc_on_primary_service_result( + 1, + start_handle, + end_handle, + &service_uuid + ); + } + } + + if (att_event->hdr.status != ATT_SUCCESS || + att_event->continuing == 0) { + uint16_t status = 0; + mp_bluetooth_gattc_on_discover_complete( + MP_BLUETOOTH_IRQ_GATTC_SERVICE_DONE, 1, status + ); + } + } + + if (att_event->hdr.event == ATTC_READ_BY_TYPE_RSP) { + /* if read by type successful */ + if (att_event->hdr.status == ATT_SUCCESS) { + // TODO: can we get the connection id from the event? + uint8_t *v = att_event->pValue; + uint8_t entry_size = v[0]; + + for (size_t i = 1; i < att_event->valueLen; + i += entry_size) { + uint16_t def_handle = (v[i + 1] << 8) | v[i]; + uint8_t properties = v[i + 2]; + uint16_t value_handle = + (v[i + 4] << 8) | v[i + 3]; + mp_obj_bluetooth_uuid_t characteristic_uuid; + if (entry_size == 2 + 1 + 2 + 2) { + memcpy(characteristic_uuid.data, + v + i + 5, + 2); + characteristic_uuid.type = + MP_BLUETOOTH_UUID_TYPE_16; + } else if (entry_size == 2 + 1 + 2 + 16) { + memcpy(characteristic_uuid.data, + v + i + 5, + 16); + characteristic_uuid.type = + MP_BLUETOOTH_UUID_TYPE_128; + } + + // TODO: uuid_filter is set: compare against uuid + mp_bluetooth_gattc_on_characteristic_result( + 1, + def_handle, + value_handle, + properties, + &characteristic_uuid + ); + } + } + + if (att_event->hdr.status != ATT_SUCCESS || + att_event->continuing == 0) { + uint16_t status = 0; + mp_bluetooth_gattc_on_discover_complete( + MP_BLUETOOTH_IRQ_GATTC_CHARACTERISTIC_DONE, + 1, + status + ); + } + } + + if (att_event->hdr.event == ATTC_FIND_INFO_RSP) { + if (att_event->hdr.status == ATT_SUCCESS) { + // TODO: can we get the connection id from the event? + uint8_t *v = att_event->pValue; + uint8_t entry_size = v[0] == 1 ? 4 : 18; + + for (size_t i = 1; i < att_event->valueLen; + i += entry_size) { + uint16_t descriptor_handle = + (v[i + 1] << 8) | v[i]; + mp_obj_bluetooth_uuid_t descriptor_uuid; + if (entry_size == 2 + 2) { + memcpy(descriptor_uuid.data, + v + i + 2, + 2); + descriptor_uuid.type = + MP_BLUETOOTH_UUID_TYPE_16; + } else if (entry_size == 2 + 16) { + memcpy(descriptor_uuid.data, + v + i + 2, + 16); + descriptor_uuid.type = + MP_BLUETOOTH_UUID_TYPE_128; + } + + mp_bluetooth_gattc_on_descriptor_result( + 1, descriptor_handle, &descriptor_uuid + ); + } + } + + if (att_event->hdr.status != ATT_SUCCESS || + att_event->continuing == 0) { + uint16_t status = 0; + mp_bluetooth_gattc_on_discover_complete( + MP_BLUETOOTH_IRQ_GATTC_DESCRIPTOR_DONE, + 1, + status + ); + } + } + + if (att_event->hdr.event == ATTC_READ_RSP) { + mp_uint_t atomic_state; + size_t len = mp_bluetooth_gattc_on_data_available_start( + MP_BLUETOOTH_IRQ_GATTC_READ_RESULT, + 1, + att_event->handle, + att_event->valueLen, + &atomic_state + ); + mp_bluetooth_gattc_on_data_available_chunk( + att_event->pValue, len + ); + mp_bluetooth_gattc_on_data_available_end(atomic_state); + mp_bluetooth_gattc_on_read_write_status( + MP_BLUETOOTH_IRQ_GATTC_READ_DONE, + 1, + att_event->handle, + att_event->hdr.status + ); + } + + if (att_event->hdr.event == ATTC_HANDLE_VALUE_NTF || + att_event->hdr.event == ATTC_HANDLE_VALUE_IND) { + uint16_t ev = att_event->hdr.event == ATTC_HANDLE_VALUE_NTF ? + MP_BLUETOOTH_IRQ_GATTC_NOTIFY : + MP_BLUETOOTH_IRQ_GATTC_INDICATE; + mp_uint_t atomic_state; + size_t len = mp_bluetooth_gattc_on_data_available_start( + ev, + 1, + att_event->handle, + att_event->valueLen, + &atomic_state + ); + mp_bluetooth_gattc_on_data_available_chunk( + att_event->pValue, len + ); + mp_bluetooth_gattc_on_data_available_end(atomic_state); + } + + if (att_event->hdr.event == ATTC_WRITE_RSP) { + mp_bluetooth_gattc_on_read_write_status( + MP_BLUETOOTH_IRQ_GATTC_WRITE_DONE, + 1, + att_event->handle, + att_event->hdr.status + ); + } +#endif } 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; @@ -508,7 +693,10 @@ static void raise(void) // Start a discovery (scan). Set duration to zero to run continuously. int mp_bluetooth_gap_scan_start( - int32_t duration_ms, int32_t interval_us, int32_t window_us + int32_t duration_ms, + int32_t interval_us, + int32_t window_us, + bool active_scan ) { raise(); return 0; @@ -530,33 +718,52 @@ int mp_bluetooth_gap_peripheral_connect( } // Find all primary services on the connected peripheral. -int mp_bluetooth_gattc_discover_primary_services(uint16_t conn_handle) -{ - raise(); - return 0; +int mp_bluetooth_gattc_discover_primary_services( + uint16_t conn_handle, const mp_obj_bluetooth_uuid_t *uuid +) { + if (uuid) { + uint8_t uuid_len = + uuid->type == MP_BLUETOOTH_UUID_TYPE_16 ? 2 : 16; + return epic_ble_attc_discover_primary_services( + conn_handle, uuid->data, uuid_len + ); + } else { + return epic_ble_attc_discover_primary_services( + conn_handle, NULL, 0 + ); + } } // Find all characteristics on the specified service on a connected peripheral. int mp_bluetooth_gattc_discover_characteristics( - uint16_t conn_handle, uint16_t start_handle, uint16_t end_handle + uint16_t conn_handle, + uint16_t start_handle, + uint16_t end_handle, + const mp_obj_bluetooth_uuid_t *uuid ) { - raise(); - return 0; + if (uuid) { + uuid_filter = *uuid; + } else { + uuid_filter.type = 0; + } + return epic_ble_attc_discover_characteristics( + conn_handle, start_handle, end_handle + ); } // Find all descriptors on the specified characteristic on a connected peripheral. int mp_bluetooth_gattc_discover_descriptors( uint16_t conn_handle, uint16_t start_handle, uint16_t end_handle ) { - raise(); - return 0; + return epic_ble_attc_discover_descriptors( + conn_handle, start_handle, end_handle + ); } // Initiate read of a value from the remote peripheral. int mp_bluetooth_gattc_read(uint16_t conn_handle, uint16_t value_handle) { - raise(); - return 0; + return epic_ble_attc_read(conn_handle, value_handle); } // Write the value to the remote peripheral. @@ -567,7 +774,18 @@ int mp_bluetooth_gattc_write( size_t *value_len, unsigned int mode ) { - raise(); - return 0; + int err; + if (mode == MP_BLUETOOTH_WRITE_MODE_NO_RESPONSE) { + err = epic_ble_attc_write_no_rsp( + conn_handle, value_handle, value, *value_len + ); + } else if (mode == MP_BLUETOOTH_WRITE_MODE_WITH_RESPONSE) { + err = epic_ble_attc_write( + conn_handle, value_handle, value, *value_len + ); + } else { + err = MP_EINVAL; + } + return err; } #endif diff --git a/pycardium/modules/modbluetooth_card10.h b/pycardium/modules/modbluetooth_card10.h index 0e2296a7e99fcfa862e2721f5c947dd139afb923..880cc37244ec4cf26d59929555daa8085cad1f26 100644 --- a/pycardium/modules/modbluetooth_card10.h +++ b/pycardium/modules/modbluetooth_card10.h @@ -126,6 +126,39 @@ enum { DM_RESET_CMPL_IND = DM_CBACK_START, /*!< \brief Reset complete */ DM_VENDOR_SPEC_IND /*!< \brief Vendor specific event */ }; -#define ATTS_HANDLE_VALUE_CNF 15 +/** \name ATT Callback Events + * Events related to ATT transactions. + */ +/**@{*/ +#define ATT_CBACK_START 0x02 /*!< \brief ATT callback event starting value */ + +/*! \brief ATT client callback events */ +enum /*!< \brief Internal note: event values match method values */ +{ + ATTC_FIND_INFO_RSP = ATT_CBACK_START, /*!< \brief Find information response */ + ATTC_FIND_BY_TYPE_VALUE_RSP, /*!< \brief Find by type value response */ + ATTC_READ_BY_TYPE_RSP, /*!< \brief Read by type value response */ + ATTC_READ_RSP, /*!< \brief Read response */ + ATTC_READ_LONG_RSP, /*!< \brief Read long response */ + ATTC_READ_MULTIPLE_RSP, /*!< \brief Read multiple response */ + ATTC_READ_BY_GROUP_TYPE_RSP, /*!< \brief Read group type response */ + ATTC_WRITE_RSP, /*!< \brief Write response */ + ATTC_WRITE_CMD_RSP, /*!< \brief Write command response */ + ATTC_PREPARE_WRITE_RSP, /*!< \brief Prepare write response */ + ATTC_EXECUTE_WRITE_RSP, /*!< \brief Execute write response */ + ATTC_HANDLE_VALUE_NTF, /*!< \brief Handle value notification */ + ATTC_HANDLE_VALUE_IND, /*!< \brief Handle value indication */ + /* ATT server callback events */ + ATTS_HANDLE_VALUE_CNF, /*!< \brief Handle value confirmation */ + ATTS_CCC_STATE_IND, /*!< \brief Client chracteristic configuration state change */ + ATTS_DB_HASH_CALC_CMPL_IND, /*!< \brief Database hash calculation complete */ + /* ATT common callback events */ + ATT_MTU_UPDATE_IND /*!< \brief Negotiated MTU value */ +}; + +/*! \brief ATT callback events */ +#define ATT_CBACK_END ATT_MTU_UPDATE_IND /*!< \brief ATT callback event ending value */ +/**@}*/ + diff --git a/pycardium/mpconfigport.h b/pycardium/mpconfigport.h index 1bcb79a1804deb525996192b5ec822d2df42a034..7e2a56ec31356d50f45d5511560a8188daa19a87 100644 --- a/pycardium/mpconfigport.h +++ b/pycardium/mpconfigport.h @@ -21,6 +21,7 @@ #define MICROPY_LONGINT_IMPL (MICROPY_LONGINT_IMPL_MPZ) #define MICROPY_ENABLE_SCHEDULER (1) +#define MICROPY_SCHEDULER_DEPTH (8) #define MICROPY_ENABLE_SOURCE_LINE (1) @@ -54,6 +55,7 @@ int mp_hal_csprng_read_int(void); #define MICROPY_PY_FRAMEBUF (1) #define MICROPY_PY_BLUETOOTH (1) #define MICROPY_PY_BUILTINS_MEMORYVIEW (1) +#define MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE (1) /* Modules */ #define MODULE_BHI160_ENABLED (1)