modbluetooth_card10.c 23.55 KiB
#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>
enum notification_status {
NOTIFICATION_STATUS_UNKNOWN,
NOTIFICATION_STATUS_PENDING,
NOTIFICATION_STATUS_SUCCESS,
NOTIFICATION_STATUS_OVERFLOW
};
typedef struct _pendig_gattc_operation_obj_t {
mp_obj_base_t base;
uint16_t conn_handle;
uint16_t value_handle;
unsigned int mode;
uint16_t value_len;
uint8_t value[];
} pending_gattc_operation_obj_t;
STATIC const mp_obj_type_t pending_gattc_operation_type;
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_obj_t attc_pending;
} 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)
#define ATTC_PENDING \
(MP_STATE_PORT(bluetooth_card10_root_pointers)->attc_pending)
typedef struct {
enum notification_status notification_status;
} gatts_status_entry_t;
static bool active = false;
static mp_obj_bluetooth_uuid_t uuid_filter;
const char *const not_implemented_message =
"Not (yet) implemented on card10. See https://git.card10.badge.events.ccc.de/card10/firmware/-/issues/8";
static void raise(void)
{
mp_raise_NotImplementedError(not_implemented_message);
}
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 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;
}
}
#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;
service_uuid.base.type =
&mp_type_bluetooth_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;
characteristic_uuid.base.type =
&mp_type_bluetooth_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;
descriptor_uuid.base.type =
&mp_type_bluetooth_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_bluetooth_gattc_on_data_available(
MP_BLUETOOTH_IRQ_GATTC_READ_RESULT,
1,
att_event->handle,
(const uint8_t **)&att_event->pValue,
&att_event->valueLen,
1
);
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_bluetooth_gattc_on_data_available(
ev,
1,
att_event->handle,
(const uint8_t **)&att_event->pValue,
&att_event->valueLen,
1
);
}
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
);
}
bool send_attc = false;
size_t len;
mp_obj_t *items;
if (att_event->hdr.event == ATTC_WRITE_CMD_RSP) {
mp_obj_list_get(ATTC_PENDING, &len, &items);
for (size_t i = 0; i < len; i++) {
pending_gattc_operation_obj_t *op =
MP_OBJ_TO_PTR(items[i]);
if (op->mode == MP_BLUETOOTH_WRITE_MODE_NO_RESPONSE &&
op->conn_handle == 1 &&
op->value_handle == att_event->handle) {
mp_obj_subscr(
ATTC_PENDING,
MP_OBJ_NEW_SMALL_INT(i),
MP_OBJ_NULL
);
send_attc = true;
break;
}
}
}
if (att_event->hdr.event == ATTC_WRITE_RSP) {
mp_obj_list_get(ATTC_PENDING, &len, &items);
for (size_t i = 0; i < len; i++) {
pending_gattc_operation_obj_t *op =
MP_OBJ_TO_PTR(items[i]);
if (op->mode == MP_BLUETOOTH_WRITE_MODE_WITH_RESPONSE &&
op->conn_handle == 1 &&
op->value_handle == att_event->handle) {
mp_obj_subscr(
ATTC_PENDING,
MP_OBJ_NEW_SMALL_INT(i),
MP_OBJ_NULL
);
send_attc = true;
break;
}
}
}
if (send_attc) {
mp_obj_list_get(ATTC_PENDING, &len, &items);
if (len > 0) {
pending_gattc_operation_obj_t *op =
MP_OBJ_TO_PTR(items[0]);
if (op->mode == MP_BLUETOOTH_WRITE_MODE_NO_RESPONSE) {
epic_ble_attc_write_no_rsp(
op->conn_handle,
op->value_handle,
op->value,
op->value_len
);
} else if (
op->mode ==
MP_BLUETOOTH_WRITE_MODE_WITH_RESPONSE) {
epic_ble_attc_write(
op->conn_handle,
op->value_handle,
op->value,
op->value_len
);
}
}
}
#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;
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)
{
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);
ATTC_PENDING = mp_obj_new_list(0, NULL);
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)
{
epic_interrupt_disable(EPIC_INT_BLE);
active = false;
}
// Returns true when the Bluetooth stack is enabled.
bool mp_bluetooth_is_active(void)
{
return active;
}
// Gets the current address of this device in big-endian format.
void mp_bluetooth_get_current_address(uint8_t *addr_type, uint8_t *addr)
{
*addr_type = 0; // Public address
epic_ble_get_address(addr);
}
// Sets the addressing mode to use.
void mp_bluetooth_set_address_mode(uint8_t addr_mode)
{
raise();
}
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.
// 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
) {
// Dropping any current connection starts advertising on the card10
int connection = epic_ble_is_connection_open();
if (connection > 0) {
epic_ble_close_connection(connection);
}
return epic_ble_advertise(
interval_us,
adv_data,
adv_data_len,
sr_data,
sr_data_len,
connectable
);
}
// Stop advertisement. No-op when already stopped.
void mp_bluetooth_gap_advertise_stop(void)
{
epic_ble_advertise_stop();
}
// 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,
uint16_t *characteristic_flags,
mp_obj_bluetooth_uuid_t **descriptor_uuids,
uint16_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++) {
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()
{
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
);
}
// 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);
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)
{
// 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
) {
// TODO: We could make use of a list similar to GATTC operations to
// avoid polling for the return value in this function
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)
{
// TODO: We could make use of a list similar to GATTC operations to
// avoid polling for the return value in this function
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)
{
epic_ble_close_connection(conn_handle);
return 0;
}
// Set/get the MTU that we will respond to a MTU exchange with.
int mp_bluetooth_get_preferred_mtu(void)
{
raise();
return 0;
}
int mp_bluetooth_set_preferred_mtu(uint16_t mtu)
{
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,
bool active_scan
) {
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, 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,
const mp_obj_bluetooth_uuid_t *uuid
) {
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
) {
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)
{
// TODO: Make use of ATTC_PENDING list
return epic_ble_attc_read(conn_handle, value_handle);
}
// 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
) {
int err = 0;
if (mode != MP_BLUETOOTH_WRITE_MODE_WITH_RESPONSE &&
mode != MP_BLUETOOTH_WRITE_MODE_NO_RESPONSE) {
return MP_EINVAL;
}
pending_gattc_operation_obj_t *op = m_new_obj_var(
pending_gattc_operation_obj_t, uint8_t, *value_len
);
op->base.type = &pending_gattc_operation_type;
op->conn_handle = conn_handle;
op->value_handle = value_handle;
op->mode = mode;
op->value_len = *value_len;
memcpy(op->value, value, *value_len);
mp_obj_list_append(ATTC_PENDING, MP_OBJ_FROM_PTR(op));
size_t len;
mp_obj_t *items;
mp_obj_list_get(ATTC_PENDING, &len, &items);
if (len == 1) {
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
);
}
}
return err;
}
// Initiate MTU exchange for a specific connection using the preferred MTU.
int mp_bluetooth_gattc_exchange_mtu(uint16_t conn_handle)
{
raise();
return 0;
}
#endif