Select Git revision
settings.py
Forked from
card10 / firmware
Source project has a limited visibility.
modbluetooth_card10.c 22.67 KiB
#include "extmod/modbluetooth.h"
#include "py/runtime.h"
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#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
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 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)
{
printf("MP got att event %d,%d,%d\n",
att_event->hdr.event,
att_event->hdr.status,
att_event->handle);
if (att_event->hdr.event == ATTS_HANDLE_VALUE_CNF) {
gatts_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;
printf("MP got dm event %d,%d\n", hdr->event, hdr->status);
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)
{
printf("MP got att write %d,%d\n",
att_write->handle,
att_write->valueLen);
mp_bluetooth_gatts_db_entry_t *entry =
mp_bluetooth_gatts_db_lookup(GATTS_DB, att_write->handle);
if (!entry) {
printf("ble: epic_att_write handle not found\n");
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)
{
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);
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);
}
// Returns true when the Bluetooth stack is enabled.
bool mp_bluetooth_is_active(void)
{
return true;
}
// Gets the MAC addr of this device in big-endian format.
void mp_bluetooth_get_device_addr(uint8_t *addr)
{
}
size_t mp_bluetooth_gap_get_device_name(const uint8_t **buf)
{
return 0;
}
int mp_bluetooth_gap_set_device_name(const uint8_t *buf, size_t len)
{
return 0;
}
// Start advertisement. Will re-start advertisement when already enabled.
// Returns errno on failure.
int mp_bluetooth_gap_advertise_start(
bool connectable,
int32_t interval_us,
const uint8_t *adv_data,
size_t adv_data_len,
const uint8_t *sr_data,
size_t sr_data_len
) {
// Temporarily going to scanner drops the connection
// TODO: modify the advertising data
epic_ble_set_mode(false, true);
epic_ble_set_mode(false, false);
return 0;
}
// Stop advertisement. No-op when already stopped.
void mp_bluetooth_gap_advertise_stop(void)
{
raise();
}
// Start adding services. Must be called before mp_bluetooth_register_service.
int mp_bluetooth_gatts_register_service_begin(bool append)
{
if (!append) {
epic_ble_atts_dyn_delete_groups();
}
return 0;
}
// // 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(
mp_obj_bluetooth_uuid_t *service_uuid,
mp_obj_bluetooth_uuid_t **characteristic_uuids,
uint8_t *characteristic_flags,
mp_obj_bluetooth_uuid_t **descriptor_uuids,
uint8_t *descriptor_flags,
uint8_t *num_descriptors,
uint16_t *handles,
size_t num_characteristics
) {
void *pSvcHandle;
size_t num_descriptors_total = 0;
size_t num_ccds = 0;
for (size_t i = 0; i < num_characteristics; i++) {
if (num_descriptors[i]) {
// TODO: more specific error
raise();
}
num_descriptors_total += num_descriptors[i];
if ((characteristic_flags[i] &
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;
uint16_t startHandle;
uint16_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,
&startHandle
);
uint16_t currentHandle = startHandle;
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];
uint8_t uuid_len =
uuid->type == MP_BLUETOOTH_UUID_TYPE_16 ? 2 : 16;
currentHandle++;
epic_atts_dyn_add_characteristic(
pSvcHandle,
uuid->data,
uuid_len,
flags,
MP_BLUETOOTH_DEFAULT_ATTR_LEN,
currentHandle
);
currentHandle++;
;
handles[handle_index] = currentHandle;
mp_bluetooth_gatts_db_create_entry(
GATTS_DB, currentHandle, MP_BLUETOOTH_DEFAULT_ATTR_LEN
);
gatts_status_create_entry(GATTS_STATUS, currentHandle);
if ((flags & MP_BLUETOOTH_CHARACTERISTIC_FLAG_NOTIFY) ||
flags & (MP_BLUETOOTH_CHARACTERISTIC_FLAG_INDICATE)) {
/* CCCD */
currentHandle++;
uint8_t initCcc[2] = {};
// TODO: activate notification/indications by default
// Not according to spec
if (flags & MP_BLUETOOTH_CHARACTERISTIC_FLAG_NOTIFY) {
initCcc[0] |= 0x01;
}
if (flags & MP_BLUETOOTH_CHARACTERISTIC_FLAG_INDICATE) {
initCcc[0] |= 0x02;
}
// TODO: Handle CCC data
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,
initCcc,
sizeof(initCcc),
sizeof(initCcc)
);
epic_ble_atts_set_attr(
currentHandle, initCcc, sizeof(initCcc)
);
//mp_bluetooth_gatts_db_create_entry(GATTS_DB, handles[handle_index] + 1, MP_BLUETOOTH_CCCB_LEN);
//gatts_status_create_entry(GATTS_STATUS, handles[handle_index] + 1);
//int ret = mp_bluetooth_gatts_db_write(GATTS_DB, handles[handle_index] + 1, cccb_buf, sizeof(cccb_buf));
}
handle_index++;
}
return 0;
}
// Register any queued services.
int mp_bluetooth_gatts_register_service_end()
{
epic_atts_dyn_send_service_changed_ind();
return 0;
}
// Read the value from the local gatts db (likely this has been written by a central).
int mp_bluetooth_gatts_read(
uint16_t value_handle, uint8_t **value, size_t *value_len
) {
return mp_bluetooth_gatts_db_read(
GATTS_DB, value_handle, value, value_len
);
#if 0
uint16_t pLen;
uint8_t ret = AttsGetAttr(value_handle, &pLen, value);
*value_len = pLen;
return ret;
#endif
}
// 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
) {
// TODO: which return value to choose?
mp_bluetooth_gatts_db_write(GATTS_DB, value_handle, value, value_len);
uint8_t ret = AttsSetAttr(value_handle, value_len, (uint8_t *)value);
return ret;
}
// Notify the central that it should do a read.
int mp_bluetooth_gatts_notify(uint16_t conn_handle, uint16_t value_handle)
{
// 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
) {
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)
{
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;
}
// Resize and enable/disable append-mode on a value.
// 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)
{
// 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();
return 0;
}
#if MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE
// 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
) {
raise();
return 0;
}
// Stop discovery (if currently active).
int mp_bluetooth_gap_scan_stop(void)
{
raise();
return 0;
}
// Connect to a found peripheral.
int mp_bluetooth_gap_peripheral_connect(
uint8_t addr_type, const uint8_t *addr, int32_t duration_ms
) {
raise();
return 0;
}
// Find all primary services on the connected peripheral.
int mp_bluetooth_gattc_discover_primary_services(uint16_t conn_handle)
{
raise();
return 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
) {
raise();
return 0;
}
// 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;
}
// 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;
}
// Write the value to the remote peripheral.
int mp_bluetooth_gattc_write(
uint16_t conn_handle,
uint16_t value_handle,
const uint8_t *value,
size_t *value_len,
unsigned int mode
) {
raise();
return 0;
}
#endif