extmod/modbluetooth: Merge gatts_notify/indicate implementation.

Makes gatts_notify and gatts_indicate work in the same way: by default they
send the DB value, but you can manually override the payload.

In other words, makes gatts_indicate work the same as gatts_notify.

Note: This removes support for queuing notifications and indications on
btstack when the ACL buffer is full. This functionality will be
reimplemented in a future commit.

Signed-off-by: Jim Mussared <jim.mussared@gmail.com>
This commit is contained in:
Jim Mussared 2023-03-02 15:40:59 +11:00
parent 9e6885ad82
commit bc9ec1cf71
6 changed files with 88 additions and 173 deletions

View File

@ -514,19 +514,24 @@ writes from a client to a given characteristic, use
Sends a notification request to a connected client. Sends a notification request to a connected client.
If *data* is not ``None``, then that value is sent to the client as part of If *data* is ``None`` (the default), then the current local value (as set
the notification. The local value will not be modified. with :meth:`gatts_write <BLE.gatts_write>`) will be sent.
Otherwise, if *data* is ``None``, then the current local value (as Otherwise, if *data* is not ``None``, then that value is sent to the client
set with :meth:`gatts_write <BLE.gatts_write>`) will be sent. as part of the notification. The local value will not be modified.
**Note:** The notification will be sent regardless of the subscription **Note:** The notification will be sent regardless of the subscription
status of the client to this characteristic. status of the client to this characteristic.
.. method:: BLE.gatts_indicate(conn_handle, value_handle, /) .. method:: BLE.gatts_indicate(conn_handle, value_handle, data=None, /)
Sends an indication request containing the characteristic's current value to Sends a indication request to a connected client.
a connected client.
If *data* is ``None`` (the default), then the current local value (as set
with :meth:`gatts_write <BLE.gatts_write>`) will be sent.
Otherwise, if *data* is not ``None``, then that value is sent to the client
as part of the indication. The local value will not be modified.
On acknowledgment (or failure, e.g. timeout), the On acknowledgment (or failure, e.g. timeout), the
``_IRQ_GATTS_INDICATE_DONE`` event will be raised. ``_IRQ_GATTS_INDICATE_DONE`` event will be raised.

View File

@ -130,8 +130,6 @@ STATIC mp_obj_bluetooth_uuid_t create_mp_uuid(uint16_t uuid16, const uint8_t *uu
// Pending operation types. // Pending operation types.
enum { enum {
// Queued for sending when possible. // Queued for sending when possible.
MP_BLUETOOTH_BTSTACK_PENDING_NOTIFY, // Waiting for context callback
MP_BLUETOOTH_BTSTACK_PENDING_INDICATE, // Waiting for context callback
MP_BLUETOOTH_BTSTACK_PENDING_WRITE_NO_RESPONSE, // Waiting for conn handle MP_BLUETOOTH_BTSTACK_PENDING_WRITE_NO_RESPONSE, // Waiting for conn handle
// Hold buffer pointer until complete. // Hold buffer pointer until complete.
MP_BLUETOOTH_BTSTACK_PENDING_WRITE, // Waiting for write done event MP_BLUETOOTH_BTSTACK_PENDING_WRITE, // Waiting for write done event
@ -150,11 +148,7 @@ struct _mp_btstack_pending_op_t {
uint16_t conn_handle; uint16_t conn_handle;
uint16_t value_handle; uint16_t value_handle;
// For notify/indicate only. // For write-without-response, this is the actual buffer to send.
// context_registration.context will point back to this struct.
btstack_context_callback_registration_t context_registration;
// For notify/indicate/write-without-response, this is the actual buffer to send.
// For write-with-response, just holding onto the buffer for GC ref. // For write-with-response, just holding onto the buffer for GC ref.
size_t len; size_t len;
uint8_t buf[]; uint8_t buf[];
@ -170,30 +164,6 @@ STATIC void btstack_remove_pending_operation(mp_btstack_pending_op_t *pending_op
} }
} }
// Called in response to a gatts_notify/indicate being unable to complete, which then calls
// att_server_request_to_send_notification.
// We now have an opportunity to re-try the operation with an empty ACL buffer.
STATIC void btstack_notify_indicate_ready_handler(void *context) {
MICROPY_PY_BLUETOOTH_ENTER
mp_btstack_pending_op_t *pending_op = (mp_btstack_pending_op_t *)context;
DEBUG_printf("btstack_notify_indicate_ready_handler op_type=%d conn_handle=%d value_handle=%d len=%zu\n", pending_op->op_type, pending_op->conn_handle, pending_op->value_handle, pending_op->len);
if (pending_op->op_type == MP_BLUETOOTH_BTSTACK_PENDING_NOTIFY) {
int err = att_server_notify(pending_op->conn_handle, pending_op->value_handle, pending_op->buf, pending_op->len);
DEBUG_printf("btstack_notify_indicate_ready_handler: sending notification err=%d\n", err);
assert(err == ERROR_CODE_SUCCESS);
(void)err;
} else {
assert(pending_op->op_type == MP_BLUETOOTH_BTSTACK_PENDING_INDICATE);
int err = att_server_indicate(pending_op->conn_handle, pending_op->value_handle, NULL, 0);
DEBUG_printf("btstack_notify_indicate_ready_handler: sending indication err=%d\n", err);
assert(err == ERROR_CODE_SUCCESS);
(void)err;
}
// Can't free the pending op as we're in IRQ context. Leave it for the GC.
btstack_remove_pending_operation(pending_op, false /* del */);
MICROPY_PY_BLUETOOTH_EXIT
}
// Register a pending background operation -- copies the buffer, and makes it known to the GC. // Register a pending background operation -- copies the buffer, and makes it known to the GC.
STATIC mp_btstack_pending_op_t *btstack_enqueue_pending_operation(uint16_t op_type, uint16_t conn_handle, uint16_t value_handle, const uint8_t *buf, size_t len) { STATIC mp_btstack_pending_op_t *btstack_enqueue_pending_operation(uint16_t op_type, uint16_t conn_handle, uint16_t value_handle, const uint8_t *buf, size_t len) {
DEBUG_printf("btstack_enqueue_pending_operation op_type=%d conn_handle=%d value_handle=%d len=%zu\n", op_type, conn_handle, value_handle, len); DEBUG_printf("btstack_enqueue_pending_operation op_type=%d conn_handle=%d value_handle=%d len=%zu\n", op_type, conn_handle, value_handle, len);
@ -204,11 +174,6 @@ STATIC mp_btstack_pending_op_t *btstack_enqueue_pending_operation(uint16_t op_ty
pending_op->len = len; pending_op->len = len;
memcpy(pending_op->buf, buf, len); memcpy(pending_op->buf, buf, len);
if (op_type == MP_BLUETOOTH_BTSTACK_PENDING_NOTIFY || op_type == MP_BLUETOOTH_BTSTACK_PENDING_INDICATE) {
pending_op->context_registration.callback = &btstack_notify_indicate_ready_handler;
pending_op->context_registration.context = pending_op;
}
MICROPY_PY_BLUETOOTH_ENTER MICROPY_PY_BLUETOOTH_ENTER
bool added = btstack_linked_list_add(&MP_STATE_PORT(bluetooth_btstack_root_pointers)->pending_ops, (btstack_linked_item_t *)pending_op); bool added = btstack_linked_list_add(&MP_STATE_PORT(bluetooth_btstack_root_pointers)->pending_ops, (btstack_linked_item_t *)pending_op);
assert(added); assert(added);
@ -854,7 +819,7 @@ void mp_bluetooth_set_io_capability(uint8_t capability) {
#endif // MICROPY_PY_BLUETOOTH_ENABLE_PAIRING_BONDING #endif // MICROPY_PY_BLUETOOTH_ENABLE_PAIRING_BONDING
size_t mp_bluetooth_gap_get_device_name(const uint8_t **buf) { size_t mp_bluetooth_gap_get_device_name(const uint8_t **buf) {
uint8_t *value = NULL; const uint8_t *value = NULL;
size_t value_len = 0; size_t value_len = 0;
mp_bluetooth_gatts_db_read(MP_STATE_PORT(bluetooth_btstack_root_pointers)->gatts_db, BTSTACK_GAP_DEVICE_NAME_HANDLE, &value, &value_len); mp_bluetooth_gatts_db_read(MP_STATE_PORT(bluetooth_btstack_root_pointers)->gatts_db, BTSTACK_GAP_DEVICE_NAME_HANDLE, &value, &value_len);
*buf = value; *buf = value;
@ -1095,7 +1060,7 @@ int mp_bluetooth_gatts_register_service_end(void) {
return 0; return 0;
} }
int mp_bluetooth_gatts_read(uint16_t value_handle, uint8_t **value, size_t *value_len) { int mp_bluetooth_gatts_read(uint16_t value_handle, const uint8_t **value, size_t *value_len) {
DEBUG_printf("mp_bluetooth_gatts_read\n"); DEBUG_printf("mp_bluetooth_gatts_read\n");
if (!mp_bluetooth_is_active()) { if (!mp_bluetooth_is_active()) {
return ERRNO_BLUETOOTH_NOT_ACTIVE; return ERRNO_BLUETOOTH_NOT_ACTIVE;
@ -1114,85 +1079,41 @@ int mp_bluetooth_gatts_write(uint16_t value_handle, const uint8_t *value, size_t
return mp_bluetooth_gatts_db_write(MP_STATE_PORT(bluetooth_btstack_root_pointers)->gatts_db, value_handle, value, value_len); return mp_bluetooth_gatts_db_write(MP_STATE_PORT(bluetooth_btstack_root_pointers)->gatts_db, value_handle, value, value_len);
} }
int mp_bluetooth_gatts_notify(uint16_t conn_handle, uint16_t value_handle) { int mp_bluetooth_gatts_notify_indicate(uint16_t conn_handle, uint16_t value_handle, int gatts_op, const uint8_t *value, size_t value_len) {
DEBUG_printf("mp_bluetooth_gatts_notify\n"); DEBUG_printf("mp_bluetooth_gatts_notify_indicate\n");
if (!mp_bluetooth_is_active()) { if (!mp_bluetooth_is_active()) {
return ERRNO_BLUETOOTH_NOT_ACTIVE; return ERRNO_BLUETOOTH_NOT_ACTIVE;
} }
// Note: btstack doesn't appear to support sending a notification without a value, so include the stored value. if (!value) {
uint8_t *data = NULL; // NULL value means "use DB value".
size_t len = 0; mp_bluetooth_gatts_db_read(MP_STATE_PORT(bluetooth_btstack_root_pointers)->gatts_db, value_handle, &value, &value_len);
mp_bluetooth_gatts_db_read(MP_STATE_PORT(bluetooth_btstack_root_pointers)->gatts_db, value_handle, &data, &len);
return mp_bluetooth_gatts_notify_send(conn_handle, value_handle, data, len);
}
int mp_bluetooth_gatts_notify_send(uint16_t conn_handle, uint16_t value_handle, const uint8_t *value, size_t value_len) {
DEBUG_printf("mp_bluetooth_gatts_notify_send\n");
if (!mp_bluetooth_is_active()) {
return ERRNO_BLUETOOTH_NOT_ACTIVE;
} }
int err = ERROR_CODE_UNKNOWN_HCI_COMMAND;
// Attempt to send immediately. If it succeeds, btstack will copy the buffer. // Attempt to send immediately. If it succeeds, btstack will copy the buffer.
MICROPY_PY_BLUETOOTH_ENTER MICROPY_PY_BLUETOOTH_ENTER
int err = att_server_notify(conn_handle, value_handle, value, value_len); switch (gatts_op) {
case MP_BLUETOOTH_GATTS_OP_NOTIFY:
err = att_server_notify(conn_handle, value_handle, value, value_len);
break;
case MP_BLUETOOTH_GATTS_OP_INDICATE:
// Indicate will raise ATT_EVENT_HANDLE_VALUE_INDICATION_COMPLETE when
// acknowledged (or timeout/error).
err = att_server_indicate(conn_handle, value_handle, value, value_len);
break;
}
MICROPY_PY_BLUETOOTH_EXIT MICROPY_PY_BLUETOOTH_EXIT
if (err == BTSTACK_ACL_BUFFERS_FULL) { if (err == BTSTACK_ACL_BUFFERS_FULL) {
DEBUG_printf("mp_bluetooth_gatts_notify_send: ACL buffer full, scheduling callback\n"); DEBUG_printf("mp_bluetooth_gatts_notify_indicate: ACL buffer full, scheduling callback\n");
// Schedule callback, making a copy of the buffer.
mp_btstack_pending_op_t *pending_op = btstack_enqueue_pending_operation(MP_BLUETOOTH_BTSTACK_PENDING_NOTIFY, conn_handle, value_handle, value, value_len);
err = att_server_request_to_send_notification(&pending_op->context_registration, conn_handle); // TODO: re-implement the handling for this.
if (err != ERROR_CODE_SUCCESS) {
// Failure. Unref and free the pending operation.
btstack_remove_pending_operation(pending_op, true /* del */);
}
return 0;
} else {
return btstack_error_to_errno(err);
}
}
int mp_bluetooth_gatts_indicate(uint16_t conn_handle, uint16_t value_handle) {
DEBUG_printf("mp_bluetooth_gatts_indicate\n");
if (!mp_bluetooth_is_active()) {
return ERRNO_BLUETOOTH_NOT_ACTIVE;
} }
uint8_t *data = NULL; return btstack_error_to_errno(err);
size_t len = 0;
mp_bluetooth_gatts_db_read(MP_STATE_PORT(bluetooth_btstack_root_pointers)->gatts_db, value_handle, &data, &len);
// Indicate will raise ATT_EVENT_HANDLE_VALUE_INDICATION_COMPLETE when
// acknowledged (or timeout/error).
// Attempt to send immediately, will copy buffer.
MICROPY_PY_BLUETOOTH_ENTER
int err = att_server_indicate(conn_handle, value_handle, data, len);
MICROPY_PY_BLUETOOTH_EXIT
if (err == BTSTACK_ACL_BUFFERS_FULL) {
DEBUG_printf("mp_bluetooth_gatts_indicate: ACL buffer full, scheduling callback\n");
// Schedule callback, making a copy of the buffer.
mp_btstack_pending_op_t *pending_op = btstack_enqueue_pending_operation(MP_BLUETOOTH_BTSTACK_PENDING_INDICATE, conn_handle, value_handle, data, len);
err = att_server_request_to_send_indication(&pending_op->context_registration, conn_handle);
if (err != ERROR_CODE_SUCCESS) {
// Failure. Unref and free the pending operation.
btstack_remove_pending_operation(pending_op, true /* del */);
}
return 0;
} else {
return btstack_error_to_errno(err);
}
} }
int mp_bluetooth_gatts_set_buffer(uint16_t value_handle, size_t len, bool append) { int mp_bluetooth_gatts_set_buffer(uint16_t value_handle, size_t len, bool append) {

View File

@ -733,7 +733,7 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(bluetooth_ble_gap_passkey_obj, 4, 4,
STATIC mp_obj_t bluetooth_ble_gatts_read(mp_obj_t self_in, mp_obj_t value_handle_in) { STATIC mp_obj_t bluetooth_ble_gatts_read(mp_obj_t self_in, mp_obj_t value_handle_in) {
(void)self_in; (void)self_in;
size_t len = 0; size_t len = 0;
uint8_t *buf; const uint8_t *buf;
mp_bluetooth_gatts_read(mp_obj_get_int(value_handle_in), &buf, &len); mp_bluetooth_gatts_read(mp_obj_get_int(value_handle_in), &buf, &len);
return mp_obj_new_bytes(buf, len); return mp_obj_new_bytes(buf, len);
} }
@ -751,32 +751,30 @@ STATIC mp_obj_t bluetooth_ble_gatts_write(size_t n_args, const mp_obj_t *args) {
} }
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(bluetooth_ble_gatts_write_obj, 3, 4, bluetooth_ble_gatts_write); STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(bluetooth_ble_gatts_write_obj, 3, 4, bluetooth_ble_gatts_write);
STATIC mp_obj_t bluetooth_ble_gatts_notify(size_t n_args, const mp_obj_t *args) { STATIC mp_obj_t bluetooth_ble_gatts_notify_indicate(size_t n_args, const mp_obj_t *args, int gatts_op) {
mp_int_t conn_handle = mp_obj_get_int(args[1]); mp_int_t conn_handle = mp_obj_get_int(args[1]);
mp_int_t value_handle = mp_obj_get_int(args[2]); mp_int_t value_handle = mp_obj_get_int(args[2]);
const uint8_t *value = NULL;
size_t value_len = 0;
if (n_args == 4 && args[3] != mp_const_none) { if (n_args == 4 && args[3] != mp_const_none) {
mp_buffer_info_t bufinfo = {0}; mp_buffer_info_t bufinfo = {0};
mp_get_buffer_raise(args[3], &bufinfo, MP_BUFFER_READ); mp_get_buffer_raise(args[3], &bufinfo, MP_BUFFER_READ);
int err = mp_bluetooth_gatts_notify_send(conn_handle, value_handle, bufinfo.buf, bufinfo.len); value = bufinfo.buf;
bluetooth_handle_errno(err); value_len = bufinfo.len;
return mp_const_none;
} else {
int err = mp_bluetooth_gatts_notify(conn_handle, value_handle);
return bluetooth_handle_errno(err);
} }
return bluetooth_handle_errno(mp_bluetooth_gatts_notify_indicate(conn_handle, value_handle, gatts_op, value, value_len));
}
STATIC mp_obj_t bluetooth_ble_gatts_notify(size_t n_args, const mp_obj_t *args) {
return bluetooth_ble_gatts_notify_indicate(n_args, args, MP_BLUETOOTH_GATTS_OP_NOTIFY);
} }
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(bluetooth_ble_gatts_notify_obj, 3, 4, bluetooth_ble_gatts_notify); STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(bluetooth_ble_gatts_notify_obj, 3, 4, bluetooth_ble_gatts_notify);
STATIC mp_obj_t bluetooth_ble_gatts_indicate(mp_obj_t self_in, mp_obj_t conn_handle_in, mp_obj_t value_handle_in) { STATIC mp_obj_t bluetooth_ble_gatts_indicate(size_t n_args, const mp_obj_t *args) {
(void)self_in; return bluetooth_ble_gatts_notify_indicate(n_args, args, MP_BLUETOOTH_GATTS_OP_INDICATE);
mp_int_t conn_handle = mp_obj_get_int(conn_handle_in);
mp_int_t value_handle = mp_obj_get_int(value_handle_in);
int err = mp_bluetooth_gatts_indicate(conn_handle, value_handle);
return bluetooth_handle_errno(err);
} }
STATIC MP_DEFINE_CONST_FUN_OBJ_3(bluetooth_ble_gatts_indicate_obj, bluetooth_ble_gatts_indicate); STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(bluetooth_ble_gatts_indicate_obj, 3, 4, bluetooth_ble_gatts_indicate);
STATIC mp_obj_t bluetooth_ble_gatts_set_buffer(size_t n_args, const mp_obj_t *args) { STATIC mp_obj_t bluetooth_ble_gatts_set_buffer(size_t n_args, const mp_obj_t *args) {
mp_int_t value_handle = mp_obj_get_int(args[1]); mp_int_t value_handle = mp_obj_get_int(args[1]);
@ -1718,7 +1716,7 @@ mp_bluetooth_gatts_db_entry_t *mp_bluetooth_gatts_db_lookup(mp_gatts_db_t db, ui
return MP_OBJ_TO_PTR(elem->value); return MP_OBJ_TO_PTR(elem->value);
} }
int mp_bluetooth_gatts_db_read(mp_gatts_db_t db, uint16_t handle, uint8_t **value, size_t *value_len) { int mp_bluetooth_gatts_db_read(mp_gatts_db_t db, uint16_t handle, const uint8_t **value, size_t *value_len) {
MICROPY_PY_BLUETOOTH_ENTER MICROPY_PY_BLUETOOTH_ENTER
mp_bluetooth_gatts_db_entry_t *entry = mp_bluetooth_gatts_db_lookup(db, handle); mp_bluetooth_gatts_db_entry_t *entry = mp_bluetooth_gatts_db_lookup(db, handle);
if (entry) { if (entry) {

View File

@ -186,6 +186,10 @@
#define MP_BLUETOOTH_PASSKEY_ACTION_DISPLAY (3) #define MP_BLUETOOTH_PASSKEY_ACTION_DISPLAY (3)
#define MP_BLUETOOTH_PASSKEY_ACTION_NUMERIC_COMPARISON (4) #define MP_BLUETOOTH_PASSKEY_ACTION_NUMERIC_COMPARISON (4)
// These are the ops for mp_bluetooth_gatts_notify_indicate.
#define MP_BLUETOOTH_GATTS_OP_NOTIFY (1)
#define MP_BLUETOOTH_GATTS_OP_INDICATE (2)
/* /*
These aren't included in the module for space reasons, but can be used These aren't included in the module for space reasons, but can be used
in your Python code if necessary. in your Python code if necessary.
@ -333,15 +337,11 @@ int mp_bluetooth_gatts_register_service(mp_obj_bluetooth_uuid_t *service_uuid, m
int mp_bluetooth_gatts_register_service_end(void); int mp_bluetooth_gatts_register_service_end(void);
// Read the value from the local gatts db (likely this has been written by a central). // 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); int mp_bluetooth_gatts_read(uint16_t value_handle, const uint8_t **value, size_t *value_len);
// Write a value to the local gatts db (ready to be queried by a central). Optionally send notifications/indications. // Write a value to the local gatts db (ready to be queried by a central). Optionally send notifications/indications.
int mp_bluetooth_gatts_write(uint16_t value_handle, const uint8_t *value, size_t value_len, bool send_update); int mp_bluetooth_gatts_write(uint16_t value_handle, const uint8_t *value, size_t value_len, bool send_update);
// Notify the central that it should do a read. // Send a notification/indication to the central, optionally with custom payload (otherwise the DB value is used).
int mp_bluetooth_gatts_notify(uint16_t conn_handle, uint16_t value_handle); int mp_bluetooth_gatts_notify_indicate(uint16_t conn_handle, uint16_t value_handle, int gatts_op, const uint8_t *value, size_t value_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);
// Indicate the central.
int mp_bluetooth_gatts_indicate(uint16_t conn_handle, uint16_t value_handle);
// Resize and enable/disable append-mode on a value. // Resize and enable/disable append-mode on a value.
// Append-mode means that remote writes will append and local reads will clear after reading. // Append-mode means that remote writes will append and local reads will clear after reading.
@ -508,7 +508,7 @@ STATIC inline void mp_bluetooth_gatts_db_reset(mp_gatts_db_t db) {
void mp_bluetooth_gatts_db_create_entry(mp_gatts_db_t db, uint16_t handle, size_t len); void mp_bluetooth_gatts_db_create_entry(mp_gatts_db_t db, uint16_t handle, size_t len);
mp_bluetooth_gatts_db_entry_t *mp_bluetooth_gatts_db_lookup(mp_gatts_db_t db, uint16_t handle); mp_bluetooth_gatts_db_entry_t *mp_bluetooth_gatts_db_lookup(mp_gatts_db_t db, uint16_t handle);
int mp_bluetooth_gatts_db_read(mp_gatts_db_t db, uint16_t handle, uint8_t **value, size_t *value_len); int mp_bluetooth_gatts_db_read(mp_gatts_db_t db, uint16_t handle, const uint8_t **value, size_t *value_len);
int mp_bluetooth_gatts_db_write(mp_gatts_db_t db, uint16_t handle, const uint8_t *value, size_t value_len); int mp_bluetooth_gatts_db_write(mp_gatts_db_t db, uint16_t handle, const uint8_t *value, size_t value_len);
int mp_bluetooth_gatts_db_resize(mp_gatts_db_t db, uint16_t handle, size_t len, bool append); int mp_bluetooth_gatts_db_resize(mp_gatts_db_t db, uint16_t handle, size_t len, bool append);

View File

@ -1008,7 +1008,7 @@ int mp_bluetooth_gap_disconnect(uint16_t conn_handle) {
return ble_hs_err_to_errno(ble_gap_terminate(conn_handle, BLE_ERR_REM_USER_CONN_TERM)); return ble_hs_err_to_errno(ble_gap_terminate(conn_handle, BLE_ERR_REM_USER_CONN_TERM));
} }
int mp_bluetooth_gatts_read(uint16_t value_handle, uint8_t **value, size_t *value_len) { int mp_bluetooth_gatts_read(uint16_t value_handle, const uint8_t **value, size_t *value_len) {
if (!mp_bluetooth_is_active()) { if (!mp_bluetooth_is_active()) {
return ERRNO_BLUETOOTH_NOT_ACTIVE; return ERRNO_BLUETOOTH_NOT_ACTIVE;
} }
@ -1026,35 +1026,40 @@ int mp_bluetooth_gatts_write(uint16_t value_handle, const uint8_t *value, size_t
return err; return err;
} }
// TODO: Could use ble_gatts_chr_updated to send to all subscribed centrals. int mp_bluetooth_gatts_notify_indicate(uint16_t conn_handle, uint16_t value_handle, int gatts_op, const uint8_t *value, size_t value_len) {
int mp_bluetooth_gatts_notify(uint16_t conn_handle, uint16_t value_handle) {
if (!mp_bluetooth_is_active()) { if (!mp_bluetooth_is_active()) {
return ERRNO_BLUETOOTH_NOT_ACTIVE; return ERRNO_BLUETOOTH_NOT_ACTIVE;
} }
// Confusingly, notify/notify_custom/indicate are "gattc" function (even though they're used by peripherals (i.e. gatt servers)).
int err = BLE_HS_EINVAL;
// NULL om in the _custom methods means "use DB value" (NimBLE will call
// back into mp_bluetooth_gatts_read for us).
struct os_mbuf *om = NULL;
if (value) {
om = ble_hs_mbuf_from_flat(value, value_len);
if (om == NULL) {
return MP_ENOMEM;
}
}
// Note: Confusingly, Nimble's notify/notify_custom and indicate/indicate_custom
// are "gattc" functions (even though they're used by peripherals, i.e. gatt servers).
// See https://www.mail-archive.com/dev@mynewt.apache.org/msg01293.html // See https://www.mail-archive.com/dev@mynewt.apache.org/msg01293.html
return ble_hs_err_to_errno(ble_gattc_notify(conn_handle, value_handle));
}
int mp_bluetooth_gatts_notify_send(uint16_t conn_handle, uint16_t value_handle, const uint8_t *value, size_t value_len) { switch (gatts_op) {
if (!mp_bluetooth_is_active()) { case MP_BLUETOOTH_GATTS_OP_NOTIFY:
return ERRNO_BLUETOOTH_NOT_ACTIVE; err = ble_gattc_notify_custom(conn_handle, value_handle, om);
break;
case MP_BLUETOOTH_GATTS_OP_INDICATE:
// This will raise BLE_GAP_EVENT_NOTIFY_TX with a status when it is
// acknowledged (or timeout/error).
err = ble_gattc_indicate_custom(conn_handle, value_handle, om);
break;
} }
struct os_mbuf *om = ble_hs_mbuf_from_flat(value, value_len);
if (om == NULL) {
return MP_ENOMEM;
}
return ble_hs_err_to_errno(ble_gattc_notify_custom(conn_handle, value_handle, om));
}
int mp_bluetooth_gatts_indicate(uint16_t conn_handle, uint16_t value_handle) { return ble_hs_err_to_errno(err);
if (!mp_bluetooth_is_active()) {
return ERRNO_BLUETOOTH_NOT_ACTIVE;
}
// This will raise BLE_GAP_EVENT_NOTIFY_TX with a status when it is
// acknowledged (or timeout/error).
return ble_hs_err_to_errno(ble_gattc_indicate(conn_handle, value_handle));
} }
int mp_bluetooth_gatts_set_buffer(uint16_t value_handle, size_t len, bool append) { int mp_bluetooth_gatts_set_buffer(uint16_t value_handle, size_t len, bool append) {

View File

@ -301,7 +301,7 @@ int mp_bluetooth_gap_disconnect(uint16_t conn_handle) {
return MP_EOPNOTSUPP; return MP_EOPNOTSUPP;
} }
int mp_bluetooth_gatts_read(uint16_t value_handle, uint8_t **value, size_t *value_len) { int mp_bluetooth_gatts_read(uint16_t value_handle, const uint8_t **value, size_t *value_len) {
if (!mp_bluetooth_is_active()) { if (!mp_bluetooth_is_active()) {
return ERRNO_BLUETOOTH_NOT_ACTIVE; return ERRNO_BLUETOOTH_NOT_ACTIVE;
} }
@ -318,21 +318,7 @@ int mp_bluetooth_gatts_write(uint16_t value_handle, const uint8_t *value, size_t
return mp_bluetooth_gatts_db_write(MP_STATE_PORT(bluetooth_zephyr_root_pointers)->gatts_db, value_handle, value, value_len); return mp_bluetooth_gatts_db_write(MP_STATE_PORT(bluetooth_zephyr_root_pointers)->gatts_db, value_handle, value, value_len);
} }
int mp_bluetooth_gatts_notify(uint16_t conn_handle, uint16_t value_handle) { int mp_bluetooth_gatts_notify_indicate(uint16_t conn_handle, uint16_t value_handle, int gatts_op, const uint8_t *value, size_t value_len) {
if (!mp_bluetooth_is_active()) {
return ERRNO_BLUETOOTH_NOT_ACTIVE;
}
return MP_EOPNOTSUPP;
}
int mp_bluetooth_gatts_notify_send(uint16_t conn_handle, uint16_t value_handle, const uint8_t *value, size_t value_len) {
if (!mp_bluetooth_is_active()) {
return ERRNO_BLUETOOTH_NOT_ACTIVE;
}
return MP_EOPNOTSUPP;
}
int mp_bluetooth_gatts_indicate(uint16_t conn_handle, uint16_t value_handle) {
if (!mp_bluetooth_is_active()) { if (!mp_bluetooth_is_active()) {
return ERRNO_BLUETOOTH_NOT_ACTIVE; return ERRNO_BLUETOOTH_NOT_ACTIVE;
} }