Browse Source

extmod/modbluetooth: Separate enabling of "client" from "central".

Previously, the MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE macro
controlled enabling both the central mode and the GATT client
functionality (because usually the two go together).

This commits adds a new MICROPY_PY_BLUETOOTH_ENABLE_GATT_CLIENT
macro that separately enables the GATT client functionality.
This defaults to MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE.

This also fixes a bug in the NimBLE bindings where a notification
or indication would not be received by a peripheral (acting as client)
as gap_event_cb wasn't handling it. Now both central_gap_event_cb
and peripheral_gap_event_cb share the same common handler for these
events.

Signed-off-by: Jim Mussared <jim.mussared@gmail.com>
pull/6931/head
Jim Mussared 4 years ago
parent
commit
a76604afba
  1. 30
      extmod/btstack/modbluetooth_btstack.c
  2. 32
      extmod/modbluetooth.c
  3. 24
      extmod/modbluetooth.h
  4. 336
      extmod/nimble/modbluetooth_nimble.c

30
extmod/btstack/modbluetooth_btstack.c

@ -91,7 +91,7 @@ STATIC mp_obj_bluetooth_uuid_t create_mp_uuid(uint16_t uuid16, const uint8_t *uu
} }
return result; return result;
} }
#endif #endif // MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE
// Notes on supporting background ops (e.g. an attempt to gatts_notify while // Notes on supporting background ops (e.g. an attempt to gatts_notify while
// an existing notification is in progress): // an existing notification is in progress):
@ -218,7 +218,7 @@ STATIC mp_btstack_pending_op_t *btstack_enqueue_pending_operation(uint16_t op_ty
return pending_op; return pending_op;
} }
#if MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE #if MICROPY_PY_BLUETOOTH_ENABLE_GATT_CLIENT
// Cleans up a pending op of the specified type for this conn_handle (and if specified, value_handle). // Cleans up a pending op of the specified type for this conn_handle (and if specified, value_handle).
// Used by MP_BLUETOOTH_BTSTACK_PENDING_WRITE and MP_BLUETOOTH_BTSTACK_PENDING_WRITE_NO_RESPONSE. // Used by MP_BLUETOOTH_BTSTACK_PENDING_WRITE and MP_BLUETOOTH_BTSTACK_PENDING_WRITE_NO_RESPONSE.
@ -418,6 +418,8 @@ STATIC void btstack_packet_handler(uint8_t packet_type, uint8_t *packet, uint8_t
uint8_t length = gap_event_advertising_report_get_data_length(packet); uint8_t length = gap_event_advertising_report_get_data_length(packet);
const uint8_t *data = gap_event_advertising_report_get_data(packet); const uint8_t *data = gap_event_advertising_report_get_data(packet);
mp_bluetooth_gap_on_scan_result(address_type, address, adv_event_type, rssi, data, length); mp_bluetooth_gap_on_scan_result(address_type, address, adv_event_type, rssi, data, length);
#endif // MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE
#if MICROPY_PY_BLUETOOTH_ENABLE_GATT_CLIENT
} else if (event_type == GATT_EVENT_QUERY_COMPLETE) { } else if (event_type == GATT_EVENT_QUERY_COMPLETE) {
uint16_t conn_handle = gatt_event_query_complete_get_handle(packet); uint16_t conn_handle = gatt_event_query_complete_get_handle(packet);
uint16_t status = gatt_event_query_complete_get_att_status(packet); uint16_t status = gatt_event_query_complete_get_att_status(packet);
@ -487,7 +489,7 @@ STATIC void btstack_packet_handler(uint8_t packet_type, uint8_t *packet, uint8_t
// Note: Can't "del" the pending_op from IRQ context. Leave it for the GC. // Note: Can't "del" the pending_op from IRQ context. Leave it for the GC.
} }
#endif // MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE #endif // MICROPY_PY_BLUETOOTH_ENABLE_GATT_CLIENT
} else { } else {
DEBUG_printf(" --> hci event type: unknown (0x%02x)\n", event_type); DEBUG_printf(" --> hci event type: unknown (0x%02x)\n", event_type);
} }
@ -506,7 +508,7 @@ STATIC btstack_packet_callback_registration_t hci_event_callback_registration =
.callback = &btstack_packet_handler_generic .callback = &btstack_packet_handler_generic
}; };
#if MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE #if MICROPY_PY_BLUETOOTH_ENABLE_GATT_CLIENT
// For when the handler is being used for service discovery. // For when the handler is being used for service discovery.
STATIC void btstack_packet_handler_discover_services(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) { STATIC void btstack_packet_handler_discover_services(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) {
(void)channel; (void)channel;
@ -541,7 +543,7 @@ STATIC void btstack_packet_handler_write_with_response(uint8_t packet_type, uint
(void)size; (void)size;
btstack_packet_handler(packet_type, packet, MP_BLUETOOTH_IRQ_GATTC_WRITE_DONE); btstack_packet_handler(packet_type, packet, MP_BLUETOOTH_IRQ_GATTC_WRITE_DONE);
} }
#endif // MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE #endif // MICROPY_PY_BLUETOOTH_ENABLE_GATT_CLIENT
STATIC btstack_timer_source_t btstack_init_deinit_timeout; STATIC btstack_timer_source_t btstack_init_deinit_timeout;
@ -662,12 +664,12 @@ int mp_bluetooth_init(void) {
sm_set_er(dummy_key); sm_set_er(dummy_key);
sm_set_ir(dummy_key); sm_set_ir(dummy_key);
#if MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE #if MICROPY_PY_BLUETOOTH_ENABLE_GATT_CLIENT
gatt_client_init(); gatt_client_init();
// We always require explicitly exchanging MTU with ble.gattc_exchange_mtu(). // We always require explicitly exchanging MTU with ble.gattc_exchange_mtu().
gatt_client_mtu_enable_auto_negotiation(false); gatt_client_mtu_enable_auto_negotiation(false);
#endif #endif // MICROPY_PY_BLUETOOTH_ENABLE_GATT_CLIENT
// Register for HCI events. // Register for HCI events.
hci_add_event_handler(&hci_event_callback_registration); hci_add_event_handler(&hci_event_callback_registration);
@ -719,10 +721,10 @@ int mp_bluetooth_init(void) {
set_random_address(); set_random_address();
} }
#if MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE #if MICROPY_PY_BLUETOOTH_ENABLE_GATT_CLIENT
// Enable GATT_EVENT_NOTIFICATION/GATT_EVENT_INDICATION for all connections and handles. // Enable GATT_EVENT_NOTIFICATION/GATT_EVENT_INDICATION for all connections and handles.
gatt_client_listen_for_characteristic_value_updates(&MP_STATE_PORT(bluetooth_btstack_root_pointers)->notification, &btstack_packet_handler_generic, GATT_CLIENT_ANY_CONNECTION, NULL); gatt_client_listen_for_characteristic_value_updates(&MP_STATE_PORT(bluetooth_btstack_root_pointers)->notification, &btstack_packet_handler_generic, GATT_CLIENT_ANY_CONNECTION, NULL);
#endif #endif // MICROPY_PY_BLUETOOTH_ENABLE_GATT_CLIENT
return 0; return 0;
} }
@ -737,10 +739,10 @@ void mp_bluetooth_deinit(void) {
mp_bluetooth_gap_advertise_stop(); mp_bluetooth_gap_advertise_stop();
#if MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE #if MICROPY_PY_BLUETOOTH_ENABLE_GATT_CLIENT
// Remove our registration for notify/indicate. // Remove our registration for notify/indicate.
gatt_client_stop_listening_for_characteristic_value_updates(&MP_STATE_PORT(bluetooth_btstack_root_pointers)->notification); gatt_client_stop_listening_for_characteristic_value_updates(&MP_STATE_PORT(bluetooth_btstack_root_pointers)->notification);
#endif #endif // MICROPY_PY_BLUETOOTH_ENABLE_GATT_CLIENT
// Set a timer that will forcibly set the state to TIMEOUT, which will stop the loop below. // Set a timer that will forcibly set the state to TIMEOUT, which will stop the loop below.
btstack_run_loop_set_timer(&btstack_init_deinit_timeout, BTSTACK_INIT_DEINIT_TIMEOUT_MS); btstack_run_loop_set_timer(&btstack_init_deinit_timeout, BTSTACK_INIT_DEINIT_TIMEOUT_MS);
@ -1232,6 +1234,10 @@ int mp_bluetooth_gap_peripheral_connect(uint8_t addr_type, const uint8_t *addr,
return btstack_error_to_errno(gap_connect(btstack_addr, addr_type)); return btstack_error_to_errno(gap_connect(btstack_addr, addr_type));
} }
#endif // MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE
#if MICROPY_PY_BLUETOOTH_ENABLE_GATT_CLIENT
int mp_bluetooth_gattc_discover_primary_services(uint16_t conn_handle, const mp_obj_bluetooth_uuid_t *uuid) { int mp_bluetooth_gattc_discover_primary_services(uint16_t conn_handle, const mp_obj_bluetooth_uuid_t *uuid) {
DEBUG_printf("mp_bluetooth_gattc_discover_primary_services\n"); DEBUG_printf("mp_bluetooth_gattc_discover_primary_services\n");
uint8_t err; uint8_t err;
@ -1346,7 +1352,7 @@ int mp_bluetooth_gattc_exchange_mtu(uint16_t conn_handle) {
return 0; return 0;
} }
#endif // MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE #endif // MICROPY_PY_BLUETOOTH_ENABLE_GATT_CLIENT
#if MICROPY_PY_BLUETOOTH_ENABLE_L2CAP_CHANNELS #if MICROPY_PY_BLUETOOTH_ENABLE_L2CAP_CHANNELS

32
extmod/modbluetooth.c

@ -761,7 +761,7 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(bluetooth_ble_gatts_set_buffer_obj, 3
// Bluetooth object: GATTC (Central/Scanner role) // Bluetooth object: GATTC (Central/Scanner role)
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
#if MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE #if MICROPY_PY_BLUETOOTH_ENABLE_GATT_CLIENT
STATIC mp_obj_t bluetooth_ble_gattc_discover_services(size_t n_args, const mp_obj_t *args) { STATIC mp_obj_t bluetooth_ble_gattc_discover_services(size_t n_args, const mp_obj_t *args) {
mp_int_t conn_handle = mp_obj_get_int(args[1]); mp_int_t conn_handle = mp_obj_get_int(args[1]);
@ -830,7 +830,7 @@ STATIC mp_obj_t bluetooth_ble_gattc_exchange_mtu(mp_obj_t self_in, mp_obj_t conn
} }
STATIC MP_DEFINE_CONST_FUN_OBJ_2(bluetooth_ble_gattc_exchange_mtu_obj, bluetooth_ble_gattc_exchange_mtu); STATIC MP_DEFINE_CONST_FUN_OBJ_2(bluetooth_ble_gattc_exchange_mtu_obj, bluetooth_ble_gattc_exchange_mtu);
#endif // MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE #endif // MICROPY_PY_BLUETOOTH_ENABLE_GATT_CLIENT
#if MICROPY_PY_BLUETOOTH_ENABLE_L2CAP_CHANNELS #if MICROPY_PY_BLUETOOTH_ENABLE_L2CAP_CHANNELS
@ -921,15 +921,15 @@ STATIC const mp_rom_map_elem_t bluetooth_ble_locals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR_gap_pair), MP_ROM_PTR(&bluetooth_ble_gap_pair_obj) }, { MP_ROM_QSTR(MP_QSTR_gap_pair), MP_ROM_PTR(&bluetooth_ble_gap_pair_obj) },
{ MP_ROM_QSTR(MP_QSTR_gap_passkey), MP_ROM_PTR(&bluetooth_ble_gap_passkey_obj) }, { MP_ROM_QSTR(MP_QSTR_gap_passkey), MP_ROM_PTR(&bluetooth_ble_gap_passkey_obj) },
#endif #endif
// GATT Server (i.e. peripheral/advertiser role) // GATT Server
{ MP_ROM_QSTR(MP_QSTR_gatts_register_services), MP_ROM_PTR(&bluetooth_ble_gatts_register_services_obj) }, { MP_ROM_QSTR(MP_QSTR_gatts_register_services), MP_ROM_PTR(&bluetooth_ble_gatts_register_services_obj) },
{ MP_ROM_QSTR(MP_QSTR_gatts_read), MP_ROM_PTR(&bluetooth_ble_gatts_read_obj) }, { MP_ROM_QSTR(MP_QSTR_gatts_read), MP_ROM_PTR(&bluetooth_ble_gatts_read_obj) },
{ MP_ROM_QSTR(MP_QSTR_gatts_write), MP_ROM_PTR(&bluetooth_ble_gatts_write_obj) }, { MP_ROM_QSTR(MP_QSTR_gatts_write), MP_ROM_PTR(&bluetooth_ble_gatts_write_obj) },
{ MP_ROM_QSTR(MP_QSTR_gatts_notify), MP_ROM_PTR(&bluetooth_ble_gatts_notify_obj) }, { MP_ROM_QSTR(MP_QSTR_gatts_notify), MP_ROM_PTR(&bluetooth_ble_gatts_notify_obj) },
{ MP_ROM_QSTR(MP_QSTR_gatts_indicate), MP_ROM_PTR(&bluetooth_ble_gatts_indicate_obj) }, { MP_ROM_QSTR(MP_QSTR_gatts_indicate), MP_ROM_PTR(&bluetooth_ble_gatts_indicate_obj) },
{ MP_ROM_QSTR(MP_QSTR_gatts_set_buffer), MP_ROM_PTR(&bluetooth_ble_gatts_set_buffer_obj) }, { MP_ROM_QSTR(MP_QSTR_gatts_set_buffer), MP_ROM_PTR(&bluetooth_ble_gatts_set_buffer_obj) },
#if MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE #if MICROPY_PY_BLUETOOTH_ENABLE_GATT_CLIENT
// GATT Client (i.e. central/scanner role) // GATT Client
{ MP_ROM_QSTR(MP_QSTR_gattc_discover_services), MP_ROM_PTR(&bluetooth_ble_gattc_discover_services_obj) }, { MP_ROM_QSTR(MP_QSTR_gattc_discover_services), MP_ROM_PTR(&bluetooth_ble_gattc_discover_services_obj) },
{ MP_ROM_QSTR(MP_QSTR_gattc_discover_characteristics), MP_ROM_PTR(&bluetooth_ble_gattc_discover_characteristics_obj) }, { MP_ROM_QSTR(MP_QSTR_gattc_discover_characteristics), MP_ROM_PTR(&bluetooth_ble_gattc_discover_characteristics_obj) },
{ MP_ROM_QSTR(MP_QSTR_gattc_discover_descriptors), MP_ROM_PTR(&bluetooth_ble_gattc_discover_descriptors_obj) }, { MP_ROM_QSTR(MP_QSTR_gattc_discover_descriptors), MP_ROM_PTR(&bluetooth_ble_gattc_discover_descriptors_obj) },
@ -1067,6 +1067,8 @@ STATIC mp_obj_t bluetooth_ble_invoke_irq(mp_obj_t none_in) {
} else if (event == MP_BLUETOOTH_IRQ_SCAN_DONE) { } else if (event == MP_BLUETOOTH_IRQ_SCAN_DONE) {
// No params required. // No params required.
data_tuple->len = 0; data_tuple->len = 0;
#endif
#if MICROPY_PY_BLUETOOTH_ENABLE_GATT_CLIENT
} else if (event == MP_BLUETOOTH_IRQ_GATTC_SERVICE_RESULT) { } else if (event == MP_BLUETOOTH_IRQ_GATTC_SERVICE_RESULT) {
// conn_handle, start_handle, end_handle, uuid // conn_handle, start_handle, end_handle, uuid
ringbuf_extract(&o->ringbuf, data_tuple, 3, 0, NULL, 0, &o->irq_data_uuid, NULL); ringbuf_extract(&o->ringbuf, data_tuple, 3, 0, NULL, 0, &o->irq_data_uuid, NULL);
@ -1085,7 +1087,7 @@ STATIC mp_obj_t bluetooth_ble_invoke_irq(mp_obj_t none_in) {
} else if (event == MP_BLUETOOTH_IRQ_GATTC_READ_DONE || event == MP_BLUETOOTH_IRQ_GATTC_WRITE_DONE) { } else if (event == MP_BLUETOOTH_IRQ_GATTC_READ_DONE || event == MP_BLUETOOTH_IRQ_GATTC_WRITE_DONE) {
// conn_handle, value_handle, status // conn_handle, value_handle, status
ringbuf_extract(&o->ringbuf, data_tuple, 3, 0, NULL, 0, NULL, NULL); ringbuf_extract(&o->ringbuf, data_tuple, 3, 0, NULL, 0, NULL, NULL);
#endif // MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE #endif // MICROPY_PY_BLUETOOTH_ENABLE_GATT_CLIENT
} }
MICROPY_PY_BLUETOOTH_EXIT MICROPY_PY_BLUETOOTH_EXIT
@ -1228,7 +1230,7 @@ void mp_bluetooth_gatts_on_mtu_exchanged(uint16_t conn_handle, uint16_t value) {
} }
#if MICROPY_PY_BLUETOOTH_ENABLE_L2CAP_CHANNELS #if MICROPY_PY_BLUETOOTH_ENABLE_L2CAP_CHANNELS
mp_int_t mp_bluetooth_gattc_on_l2cap_accept(uint16_t conn_handle, uint16_t cid, uint16_t psm, uint16_t our_mtu, uint16_t peer_mtu) { mp_int_t mp_bluetooth_on_l2cap_accept(uint16_t conn_handle, uint16_t cid, uint16_t psm, uint16_t our_mtu, uint16_t peer_mtu) {
mp_int_t args[] = {conn_handle, cid, psm, our_mtu, peer_mtu}; mp_int_t args[] = {conn_handle, cid, psm, our_mtu, peer_mtu};
mp_obj_t result = invoke_irq_handler(MP_BLUETOOTH_IRQ_L2CAP_ACCEPT, args, 5, 0, NULL_ADDR, NULL_UUID, NULL_DATA, NULL_DATA_LEN, 0); mp_obj_t result = invoke_irq_handler(MP_BLUETOOTH_IRQ_L2CAP_ACCEPT, args, 5, 0, NULL_ADDR, NULL_UUID, NULL_DATA, NULL_DATA_LEN, 0);
// Return non-zero from IRQ handler to fail the accept. // Return non-zero from IRQ handler to fail the accept.
@ -1237,22 +1239,22 @@ mp_int_t mp_bluetooth_gattc_on_l2cap_accept(uint16_t conn_handle, uint16_t cid,
return ret; return ret;
} }
void mp_bluetooth_gattc_on_l2cap_connect(uint16_t conn_handle, uint16_t cid, uint16_t psm, uint16_t our_mtu, uint16_t peer_mtu) { void mp_bluetooth_on_l2cap_connect(uint16_t conn_handle, uint16_t cid, uint16_t psm, uint16_t our_mtu, uint16_t peer_mtu) {
mp_int_t args[] = {conn_handle, cid, psm, our_mtu, peer_mtu}; mp_int_t args[] = {conn_handle, cid, psm, our_mtu, peer_mtu};
invoke_irq_handler(MP_BLUETOOTH_IRQ_L2CAP_CONNECT, args, 5, 0, NULL_ADDR, NULL_UUID, NULL_DATA, NULL_DATA_LEN, 0); invoke_irq_handler(MP_BLUETOOTH_IRQ_L2CAP_CONNECT, args, 5, 0, NULL_ADDR, NULL_UUID, NULL_DATA, NULL_DATA_LEN, 0);
} }
void mp_bluetooth_gattc_on_l2cap_disconnect(uint16_t conn_handle, uint16_t cid, uint16_t psm, uint16_t status) { void mp_bluetooth_on_l2cap_disconnect(uint16_t conn_handle, uint16_t cid, uint16_t psm, uint16_t status) {
mp_int_t args[] = {conn_handle, cid, psm, status}; mp_int_t args[] = {conn_handle, cid, psm, status};
invoke_irq_handler(MP_BLUETOOTH_IRQ_L2CAP_DISCONNECT, args, 4, 0, NULL_ADDR, NULL_UUID, NULL_DATA, NULL_DATA_LEN, 0); invoke_irq_handler(MP_BLUETOOTH_IRQ_L2CAP_DISCONNECT, args, 4, 0, NULL_ADDR, NULL_UUID, NULL_DATA, NULL_DATA_LEN, 0);
} }
void mp_bluetooth_gattc_on_l2cap_send_ready(uint16_t conn_handle, uint16_t cid, uint8_t status) { void mp_bluetooth_on_l2cap_send_ready(uint16_t conn_handle, uint16_t cid, uint8_t status) {
mp_int_t args[] = {conn_handle, cid, status}; mp_int_t args[] = {conn_handle, cid, status};
invoke_irq_handler(MP_BLUETOOTH_IRQ_L2CAP_SEND_READY, args, 3, 0, NULL_ADDR, NULL_UUID, NULL_DATA, NULL_DATA_LEN, 0); invoke_irq_handler(MP_BLUETOOTH_IRQ_L2CAP_SEND_READY, args, 3, 0, NULL_ADDR, NULL_UUID, NULL_DATA, NULL_DATA_LEN, 0);
} }
void mp_bluetooth_gattc_on_l2cap_recv(uint16_t conn_handle, uint16_t cid) { void mp_bluetooth_on_l2cap_recv(uint16_t conn_handle, uint16_t cid) {
mp_int_t args[] = {conn_handle, cid}; mp_int_t args[] = {conn_handle, cid};
invoke_irq_handler(MP_BLUETOOTH_IRQ_L2CAP_RECV, args, 2, 0, NULL_ADDR, NULL_UUID, NULL_DATA, NULL_DATA_LEN, 0); invoke_irq_handler(MP_BLUETOOTH_IRQ_L2CAP_RECV, args, 2, 0, NULL_ADDR, NULL_UUID, NULL_DATA, NULL_DATA_LEN, 0);
} }
@ -1267,7 +1269,9 @@ void mp_bluetooth_gap_on_scan_result(uint8_t addr_type, const uint8_t *addr, uin
mp_int_t args[] = {addr_type, adv_type, rssi}; mp_int_t args[] = {addr_type, adv_type, rssi};
invoke_irq_handler(MP_BLUETOOTH_IRQ_SCAN_RESULT, args, 1, 2, addr, NULL_UUID, &data, &data_len, 1); invoke_irq_handler(MP_BLUETOOTH_IRQ_SCAN_RESULT, args, 1, 2, addr, NULL_UUID, &data, &data_len, 1);
} }
#endif // MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE
#if MICROPY_PY_BLUETOOTH_ENABLE_GATT_CLIENT
void mp_bluetooth_gattc_on_primary_service_result(uint16_t conn_handle, uint16_t start_handle, uint16_t end_handle, mp_obj_bluetooth_uuid_t *service_uuid) { void mp_bluetooth_gattc_on_primary_service_result(uint16_t conn_handle, uint16_t start_handle, uint16_t end_handle, mp_obj_bluetooth_uuid_t *service_uuid) {
mp_int_t args[] = {conn_handle, start_handle, end_handle}; mp_int_t args[] = {conn_handle, start_handle, end_handle};
invoke_irq_handler(MP_BLUETOOTH_IRQ_GATTC_SERVICE_RESULT, args, 3, 0, NULL_ADDR, service_uuid, NULL_DATA, NULL_DATA_LEN, 0); invoke_irq_handler(MP_BLUETOOTH_IRQ_GATTC_SERVICE_RESULT, args, 3, 0, NULL_ADDR, service_uuid, NULL_DATA, NULL_DATA_LEN, 0);
@ -1325,7 +1329,7 @@ void mp_bluetooth_gattc_on_read_write_status(uint8_t event, uint16_t conn_handle
invoke_irq_handler(event, args, 3, 0, NULL_ADDR, NULL_UUID, NULL_DATA, NULL_DATA_LEN, 0); invoke_irq_handler(event, args, 3, 0, NULL_ADDR, NULL_UUID, NULL_DATA, NULL_DATA_LEN, 0);
} }
#endif // MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE #endif // MICROPY_PY_BLUETOOTH_ENABLE_GATT_CLIENT
#else // !MICROPY_PY_BLUETOOTH_USE_SYNC_EVENTS #else // !MICROPY_PY_BLUETOOTH_USE_SYNC_EVENTS
// Callbacks are called in interrupt context (i.e. can't allocate), so we need to push the data // Callbacks are called in interrupt context (i.e. can't allocate), so we need to push the data
@ -1471,7 +1475,9 @@ void mp_bluetooth_gap_on_scan_result(uint8_t addr_type, const uint8_t *addr, uin
} }
schedule_ringbuf(atomic_state); schedule_ringbuf(atomic_state);
} }
#endif // MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE
#if MICROPY_PY_BLUETOOTH_ENABLE_GATT_CLIENT
void mp_bluetooth_gattc_on_primary_service_result(uint16_t conn_handle, uint16_t start_handle, uint16_t end_handle, mp_obj_bluetooth_uuid_t *service_uuid) { void mp_bluetooth_gattc_on_primary_service_result(uint16_t conn_handle, uint16_t start_handle, uint16_t end_handle, mp_obj_bluetooth_uuid_t *service_uuid) {
MICROPY_PY_BLUETOOTH_ENTER MICROPY_PY_BLUETOOTH_ENTER
mp_obj_bluetooth_ble_t *o = MP_OBJ_TO_PTR(MP_STATE_VM(bluetooth)); mp_obj_bluetooth_ble_t *o = MP_OBJ_TO_PTR(MP_STATE_VM(bluetooth));
@ -1559,7 +1565,7 @@ void mp_bluetooth_gattc_on_read_write_status(uint8_t event, uint16_t conn_handle
} }
schedule_ringbuf(atomic_state); schedule_ringbuf(atomic_state);
} }
#endif // MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE #endif // MICROPY_PY_BLUETOOTH_ENABLE_GATT_CLIENT
#endif // MICROPY_PY_BLUETOOTH_USE_SYNC_EVENTS #endif // MICROPY_PY_BLUETOOTH_USE_SYNC_EVENTS

24
extmod/modbluetooth.h

@ -43,6 +43,12 @@
#define MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE (0) #define MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE (0)
#endif #endif
#ifndef MICROPY_PY_BLUETOOTH_ENABLE_GATT_CLIENT
// Enable the client by default if we're enabling central mode. It's possible
// to enable client without central though.
#define MICROPY_PY_BLUETOOTH_ENABLE_GATT_CLIENT (MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE)
#endif
#ifndef MICROPY_PY_BLUETOOTH_USE_SYNC_EVENTS #ifndef MICROPY_PY_BLUETOOTH_USE_SYNC_EVENTS
// This can be enabled if the BLE stack runs entirely in scheduler context // This can be enabled if the BLE stack runs entirely in scheduler context
// and therefore is able to call directly into the VM to run Python callbacks. // and therefore is able to call directly into the VM to run Python callbacks.
@ -365,7 +371,9 @@ int mp_bluetooth_gap_scan_stop(void);
// Connect to a found peripheral. // Connect to a found peripheral.
int mp_bluetooth_gap_peripheral_connect(uint8_t addr_type, const uint8_t *addr, int32_t duration_ms); int mp_bluetooth_gap_peripheral_connect(uint8_t addr_type, const uint8_t *addr, int32_t duration_ms);
#endif
#if MICROPY_PY_BLUETOOTH_ENABLE_GATT_CLIENT
// Find all primary services on the connected peripheral. // 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); int mp_bluetooth_gattc_discover_primary_services(uint16_t conn_handle, const mp_obj_bluetooth_uuid_t *uuid);
@ -383,7 +391,7 @@ int mp_bluetooth_gattc_write(uint16_t conn_handle, uint16_t value_handle, const
// Initiate MTU exchange for a specific connection using the preferred MTU. // Initiate MTU exchange for a specific connection using the preferred MTU.
int mp_bluetooth_gattc_exchange_mtu(uint16_t conn_handle); int mp_bluetooth_gattc_exchange_mtu(uint16_t conn_handle);
#endif // MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE #endif // MICROPY_PY_BLUETOOTH_ENABLE_GATT_CLIENT
#if MICROPY_PY_BLUETOOTH_ENABLE_L2CAP_CHANNELS #if MICROPY_PY_BLUETOOTH_ENABLE_L2CAP_CHANNELS
int mp_bluetooth_l2cap_listen(uint16_t psm, uint16_t mtu); int mp_bluetooth_l2cap_listen(uint16_t psm, uint16_t mtu);
@ -440,7 +448,9 @@ void mp_bluetooth_gap_on_scan_complete(void);
// Notify modbluetooth of a scan result. // Notify modbluetooth of a scan result.
void mp_bluetooth_gap_on_scan_result(uint8_t addr_type, const uint8_t *addr, uint8_t adv_type, const int8_t rssi, const uint8_t *data, size_t data_len); void mp_bluetooth_gap_on_scan_result(uint8_t addr_type, const uint8_t *addr, uint8_t adv_type, const int8_t rssi, const uint8_t *data, size_t data_len);
#endif // MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE
#if MICROPY_PY_BLUETOOTH_ENABLE_GATT_CLIENT
// Notify modbluetooth that a service was found (either by discover-all, or discover-by-uuid). // Notify modbluetooth that a service was found (either by discover-all, or discover-by-uuid).
void mp_bluetooth_gattc_on_primary_service_result(uint16_t conn_handle, uint16_t start_handle, uint16_t end_handle, mp_obj_bluetooth_uuid_t *service_uuid); void mp_bluetooth_gattc_on_primary_service_result(uint16_t conn_handle, uint16_t start_handle, uint16_t end_handle, mp_obj_bluetooth_uuid_t *service_uuid);
@ -458,14 +468,14 @@ void mp_bluetooth_gattc_on_data_available(uint8_t event, uint16_t conn_handle, u
// Notify modbluetooth that a read or write operation has completed. // Notify modbluetooth that a read or write operation has completed.
void mp_bluetooth_gattc_on_read_write_status(uint8_t event, uint16_t conn_handle, uint16_t value_handle, uint16_t status); void mp_bluetooth_gattc_on_read_write_status(uint8_t event, uint16_t conn_handle, uint16_t value_handle, uint16_t status);
#endif // MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE #endif // MICROPY_PY_BLUETOOTH_ENABLE_GATT_CLIENT
#if MICROPY_PY_BLUETOOTH_ENABLE_L2CAP_CHANNELS #if MICROPY_PY_BLUETOOTH_ENABLE_L2CAP_CHANNELS
mp_int_t mp_bluetooth_gattc_on_l2cap_accept(uint16_t conn_handle, uint16_t cid, uint16_t psm, uint16_t our_mtu, uint16_t peer_mtu); mp_int_t mp_bluetooth_on_l2cap_accept(uint16_t conn_handle, uint16_t cid, uint16_t psm, uint16_t our_mtu, uint16_t peer_mtu);
void mp_bluetooth_gattc_on_l2cap_connect(uint16_t conn_handle, uint16_t cid, uint16_t psm, uint16_t our_mtu, uint16_t peer_mtu); void mp_bluetooth_on_l2cap_connect(uint16_t conn_handle, uint16_t cid, uint16_t psm, uint16_t our_mtu, uint16_t peer_mtu);
void mp_bluetooth_gattc_on_l2cap_disconnect(uint16_t conn_handle, uint16_t cid, uint16_t psm, uint16_t status); void mp_bluetooth_on_l2cap_disconnect(uint16_t conn_handle, uint16_t cid, uint16_t psm, uint16_t status);
void mp_bluetooth_gattc_on_l2cap_send_ready(uint16_t conn_handle, uint16_t cid, uint8_t status); void mp_bluetooth_on_l2cap_send_ready(uint16_t conn_handle, uint16_t cid, uint8_t status);
void mp_bluetooth_gattc_on_l2cap_recv(uint16_t conn_handle, uint16_t cid); void mp_bluetooth_on_l2cap_recv(uint16_t conn_handle, uint16_t cid);
#endif // MICROPY_PY_BLUETOOTH_ENABLE_L2CAP_CHANNELS #endif // MICROPY_PY_BLUETOOTH_ENABLE_L2CAP_CHANNELS
// For stacks that don't manage attribute value data (currently all of them), helpers // For stacks that don't manage attribute value data (currently all of them), helpers

336
extmod/nimble/modbluetooth_nimble.c

@ -80,6 +80,69 @@ STATIC int8_t ble_hs_err_to_errno_table[] = {
[BLE_HS_EBADDATA] = MP_EINVAL, [BLE_HS_EBADDATA] = MP_EINVAL,
}; };
STATIC int ble_hs_err_to_errno(int err);
STATIC ble_uuid_t *create_nimble_uuid(const mp_obj_bluetooth_uuid_t *uuid, ble_uuid_any_t *storage);
STATIC void reverse_addr_byte_order(uint8_t *addr_out, const uint8_t *addr_in);
#if MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE
STATIC mp_obj_bluetooth_uuid_t create_mp_uuid(const ble_uuid_any_t *uuid);
STATIC ble_addr_t create_nimble_addr(uint8_t addr_type, const uint8_t *addr);
#endif
STATIC void reset_cb(int reason);
STATIC bool has_public_address(void);
STATIC void set_random_address(bool nrpa);
#if MICROPY_PY_BLUETOOTH_ENABLE_PAIRING_BONDING
STATIC int load_irk(void);
#endif
STATIC void sync_cb(void);
#if !MICROPY_BLUETOOTH_NIMBLE_BINDINGS_ONLY
STATIC void ble_hs_shutdown_stop_cb(int status, void *arg);
#endif
// Successfully registered service/char/desc handles.
STATIC void gatts_register_cb(struct ble_gatt_register_ctxt *ctxt, void *arg);
// Events about a connected central (we're in peripheral role).
STATIC int central_gap_event_cb(struct ble_gap_event *event, void *arg);
#if MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE
// Events about a connected peripheral (we're in central role).
STATIC int peripheral_gap_event_cb(struct ble_gap_event *event, void *arg);
#endif
// Used by both of the above.
STATIC int commmon_gap_event_cb(struct ble_gap_event *event, void *arg);
#if MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE
// Scan results.
STATIC int gap_scan_cb(struct ble_gap_event *event, void *arg);
#endif
#if MICROPY_PY_BLUETOOTH_ENABLE_GATT_CLIENT
// Data available (either due to notify/indicate or successful read).
STATIC void gattc_on_data_available(uint8_t event, uint16_t conn_handle, uint16_t value_handle, const struct os_mbuf *om);
// Client discovery callbacks.
STATIC int ble_gattc_service_cb(uint16_t conn_handle, const struct ble_gatt_error *error, const struct ble_gatt_svc *service, void *arg);
STATIC int ble_gattc_characteristic_cb(uint16_t conn_handle, const struct ble_gatt_error *error, const struct ble_gatt_chr *characteristic, void *arg);
STATIC int ble_gattc_descriptor_cb(uint16_t conn_handle, const struct ble_gatt_error *error, uint16_t characteristic_val_handle, const struct ble_gatt_dsc *descriptor, void *arg);
// Client read/write handlers.
STATIC int ble_gattc_attr_read_cb(uint16_t conn_handle, const struct ble_gatt_error *error, struct ble_gatt_attr *attr, void *arg);
STATIC int ble_gattc_attr_write_cb(uint16_t conn_handle, const struct ble_gatt_error *error, struct ble_gatt_attr *attr, void *arg);
#endif
#if MICROPY_PY_BLUETOOTH_ENABLE_PAIRING_BONDING
// Bonding store.
STATIC int ble_store_ram_read(int obj_type, const union ble_store_key *key, union ble_store_value *value);
STATIC int ble_store_ram_write(int obj_type, const union ble_store_value *val);
STATIC int ble_store_ram_delete(int obj_type, const union ble_store_key *key);
#endif
STATIC int ble_hs_err_to_errno(int err) { STATIC int ble_hs_err_to_errno(int err) {
DEBUG_printf("ble_hs_err_to_errno: %d\n", err); DEBUG_printf("ble_hs_err_to_errno: %d\n", err);
if (!err) { if (!err) {
@ -334,8 +397,54 @@ STATIC void gatts_register_cb(struct ble_gatt_register_ctxt *ctxt, void *arg) {
} }
} }
STATIC int gap_event_cb(struct ble_gap_event *event, void *arg) { STATIC int commmon_gap_event_cb(struct ble_gap_event *event, void *arg) {
DEBUG_printf("gap_event_cb: type=%d\n", event->type); struct ble_gap_conn_desc desc;
switch (event->type) {
#if MICROPY_PY_BLUETOOTH_ENABLE_GATT_CLIENT
case BLE_GAP_EVENT_NOTIFY_RX: {
uint16_t ev = event->notify_rx.indication == 0 ? MP_BLUETOOTH_IRQ_GATTC_NOTIFY : MP_BLUETOOTH_IRQ_GATTC_INDICATE;
gattc_on_data_available(ev, event->notify_rx.conn_handle, event->notify_rx.attr_handle, event->notify_rx.om);
return 0;
}
#endif // MICROPY_PY_BLUETOOTH_ENABLE_GATT_CLIENT
case BLE_GAP_EVENT_CONN_UPDATE: {
DEBUG_printf("commmon_gap_event_cb: connection update: status=%d\n", event->conn_update.status);
if (ble_gap_conn_find(event->conn_update.conn_handle, &desc) == 0) {
mp_bluetooth_gap_on_connection_update(event->conn_update.conn_handle, desc.conn_itvl, desc.conn_latency, desc.supervision_timeout, event->conn_update.status == 0 ? 0 : 1);
}
return 0;
}
case BLE_GAP_EVENT_MTU: {
if (event->mtu.channel_id == BLE_L2CAP_CID_ATT) {
DEBUG_printf("commmon_gap_event_cb: mtu update: conn_handle=%d cid=%d mtu=%d\n", event->mtu.conn_handle, event->mtu.channel_id, event->mtu.value);
mp_bluetooth_gatts_on_mtu_exchanged(event->mtu.conn_handle, event->mtu.value);
}
return 0;
}
case BLE_GAP_EVENT_ENC_CHANGE: {
DEBUG_printf("commmon_gap_event_cb: enc change: status=%d\n", event->enc_change.status);
#if MICROPY_PY_BLUETOOTH_ENABLE_PAIRING_BONDING
if (ble_gap_conn_find(event->enc_change.conn_handle, &desc) == 0) {
mp_bluetooth_gatts_on_encryption_update(event->conn_update.conn_handle,
desc.sec_state.encrypted, desc.sec_state.authenticated,
desc.sec_state.bonded, desc.sec_state.key_size);
}
#endif
return 0;
}
default:
DEBUG_printf("commmon_gap_event_cb: unknown type %d\n", event->type);
return 0;
}
}
STATIC int central_gap_event_cb(struct ble_gap_event *event, void *arg) {
DEBUG_printf("central_gap_event_cb: type=%d\n", event->type);
if (!mp_bluetooth_is_active()) { if (!mp_bluetooth_is_active()) {
return 0; return 0;
} }
@ -344,7 +453,7 @@ STATIC int gap_event_cb(struct ble_gap_event *event, void *arg) {
switch (event->type) { switch (event->type) {
case BLE_GAP_EVENT_CONNECT: case BLE_GAP_EVENT_CONNECT:
DEBUG_printf("gap_event_cb: connect: status=%d\n", event->connect.status); DEBUG_printf("central_gap_event_cb: connect: status=%d\n", event->connect.status);
if (event->connect.status == 0) { if (event->connect.status == 0) {
// Connection established. // Connection established.
ble_gap_conn_find(event->connect.conn_handle, &desc); ble_gap_conn_find(event->connect.conn_handle, &desc);
@ -354,60 +463,32 @@ STATIC int gap_event_cb(struct ble_gap_event *event, void *arg) {
// Connection failed. // Connection failed.
mp_bluetooth_gap_on_connected_disconnected(MP_BLUETOOTH_IRQ_CENTRAL_DISCONNECT, event->connect.conn_handle, 0xff, addr); mp_bluetooth_gap_on_connected_disconnected(MP_BLUETOOTH_IRQ_CENTRAL_DISCONNECT, event->connect.conn_handle, 0xff, addr);
} }
break; return 0;
case BLE_GAP_EVENT_DISCONNECT: case BLE_GAP_EVENT_DISCONNECT:
// Disconnect. // Disconnect.
DEBUG_printf("gap_event_cb: disconnect: reason=%d\n", event->disconnect.reason); DEBUG_printf("central_gap_event_cb: disconnect: reason=%d\n", event->disconnect.reason);
reverse_addr_byte_order(addr, event->disconnect.conn.peer_id_addr.val); reverse_addr_byte_order(addr, event->disconnect.conn.peer_id_addr.val);
mp_bluetooth_gap_on_connected_disconnected(MP_BLUETOOTH_IRQ_CENTRAL_DISCONNECT, event->disconnect.conn.conn_handle, event->disconnect.conn.peer_id_addr.type, addr); mp_bluetooth_gap_on_connected_disconnected(MP_BLUETOOTH_IRQ_CENTRAL_DISCONNECT, event->disconnect.conn.conn_handle, event->disconnect.conn.peer_id_addr.type, addr);
break; return 0;
case BLE_GAP_EVENT_NOTIFY_TX: { case BLE_GAP_EVENT_NOTIFY_TX: {
DEBUG_printf("gap_event_cb: notify_tx: %d %d\n", event->notify_tx.indication, event->notify_tx.status); DEBUG_printf("central_gap_event_cb: notify_tx: %d %d\n", event->notify_tx.indication, event->notify_tx.status);
// This event corresponds to either a sent notify/indicate (status == 0), or an indication confirmation (status != 0). // This event corresponds to either a sent notify/indicate (status == 0), or an indication confirmation (status != 0).
if (event->notify_tx.indication && event->notify_tx.status != 0) { if (event->notify_tx.indication && event->notify_tx.status != 0) {
// Map "done/ack" to 0, otherwise pass the status directly. // Map "done/ack" to 0, otherwise pass the status directly.
mp_bluetooth_gatts_on_indicate_complete(event->notify_tx.conn_handle, event->notify_tx.attr_handle, event->notify_tx.status == BLE_HS_EDONE ? 0 : event->notify_tx.status); mp_bluetooth_gatts_on_indicate_complete(event->notify_tx.conn_handle, event->notify_tx.attr_handle, event->notify_tx.status == BLE_HS_EDONE ? 0 : event->notify_tx.status);
} }
break; return 0;
}
case BLE_GAP_EVENT_MTU: {
if (event->mtu.channel_id == BLE_L2CAP_CID_ATT) {
DEBUG_printf("gap_event_cb: mtu update: conn_handle=%d cid=%d mtu=%d\n", event->mtu.conn_handle, event->mtu.channel_id, event->mtu.value);
mp_bluetooth_gatts_on_mtu_exchanged(event->mtu.conn_handle, event->mtu.value);
}
break;
} }
case BLE_GAP_EVENT_PHY_UPDATE_COMPLETE: case BLE_GAP_EVENT_PHY_UPDATE_COMPLETE:
DEBUG_printf("gap_event_cb: phy update: %d\n", event->phy_updated.tx_phy); DEBUG_printf("central_gap_event_cb: phy update: %d\n", event->phy_updated.tx_phy);
break; return 0;
case BLE_GAP_EVENT_CONN_UPDATE: {
DEBUG_printf("gap_event_cb: connection update: status=%d\n", event->conn_update.status);
if (ble_gap_conn_find(event->conn_update.conn_handle, &desc) == 0) {
mp_bluetooth_gap_on_connection_update(event->conn_update.conn_handle, desc.conn_itvl, desc.conn_latency, desc.supervision_timeout, event->conn_update.status == 0 ? 0 : 1);
}
break;
}
case BLE_GAP_EVENT_ENC_CHANGE: {
DEBUG_printf("gap_event_cb: enc change: status=%d\n", event->enc_change.status);
#if MICROPY_PY_BLUETOOTH_ENABLE_PAIRING_BONDING
if (ble_gap_conn_find(event->enc_change.conn_handle, &desc) == 0) {
mp_bluetooth_gatts_on_encryption_update(event->conn_update.conn_handle,
desc.sec_state.encrypted, desc.sec_state.authenticated,
desc.sec_state.bonded, desc.sec_state.key_size);
}
#endif
break;
}
case BLE_GAP_EVENT_REPEAT_PAIRING: { case BLE_GAP_EVENT_REPEAT_PAIRING: {
// We recognized this peer but the peer doesn't recognize us. // We recognized this peer but the peer doesn't recognize us.
DEBUG_printf("gap_event_cb: repeat pairing: conn_handle=%d\n", event->repeat_pairing.conn_handle); DEBUG_printf("central_gap_event_cb: repeat pairing: conn_handle=%d\n", event->repeat_pairing.conn_handle);
// TODO: Consider returning BLE_GAP_REPEAT_PAIRING_IGNORE (and // TODO: Consider returning BLE_GAP_REPEAT_PAIRING_IGNORE (and
// possibly an API to configure this). // possibly an API to configure this).
@ -423,7 +504,7 @@ STATIC int gap_event_cb(struct ble_gap_event *event, void *arg) {
} }
case BLE_GAP_EVENT_PASSKEY_ACTION: { case BLE_GAP_EVENT_PASSKEY_ACTION: {
DEBUG_printf("gap_event_cb: passkey action: conn_handle=%d action=%d num=" UINT_FMT "\n", event->passkey.conn_handle, event->passkey.params.action, (mp_uint_t)event->passkey.params.numcmp); DEBUG_printf("central_gap_event_cb: passkey action: conn_handle=%d action=%d num=" UINT_FMT "\n", event->passkey.conn_handle, event->passkey.params.action, (mp_uint_t)event->passkey.params.numcmp);
#if MICROPY_PY_BLUETOOTH_ENABLE_PAIRING_BONDING #if MICROPY_PY_BLUETOOTH_ENABLE_PAIRING_BONDING
mp_bluetooth_gap_on_passkey_action(event->passkey.conn_handle, event->passkey.params.action, event->passkey.params.numcmp); mp_bluetooth_gap_on_passkey_action(event->passkey.conn_handle, event->passkey.params.action, event->passkey.params.numcmp);
@ -431,12 +512,9 @@ STATIC int gap_event_cb(struct ble_gap_event *event, void *arg) {
return 0; return 0;
} }
default:
DEBUG_printf("gap_event_cb: unknown type %d\n", event->type);
break;
} }
return 0;
return commmon_gap_event_cb(event, arg);
} }
#if !MICROPY_BLUETOOTH_NIMBLE_BINDINGS_ONLY #if !MICROPY_BLUETOOTH_NIMBLE_BINDINGS_ONLY
@ -738,7 +816,7 @@ int mp_bluetooth_gap_advertise_start(bool connectable, int32_t interval_us, cons
.channel_map = 7, // all 3 channels. .channel_map = 7, // all 3 channels.
}; };
ret = ble_gap_adv_start(nimble_address_mode, NULL, BLE_HS_FOREVER, &adv_params, gap_event_cb, NULL); ret = ble_gap_adv_start(nimble_address_mode, NULL, BLE_HS_FOREVER, &adv_params, central_gap_event_cb, NULL);
if (ret == 0) { if (ret == 0) {
return 0; return 0;
} }
@ -1025,38 +1103,6 @@ int mp_bluetooth_gap_passkey(uint16_t conn_handle, uint8_t action, mp_int_t pass
#if MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE #if MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE
STATIC void gattc_on_data_available(uint8_t event, uint16_t conn_handle, uint16_t value_handle, const struct os_mbuf *om) {
// When the HCI data for an ATT payload arrives, the L2CAP channel will
// buffer it into its receive buffer. We set BLE_L2CAP_JOIN_RX_FRAGS=1 in
// syscfg.h so it should be rare that the mbuf is fragmented, but we do need
// to be able to handle it. We pass all the fragments up to modbluetooth.c
// which will create a temporary buffer on the MicroPython heap if necessary
// to re-assemble them.
// Count how many links are in the mbuf chain.
size_t n = 0;
const struct os_mbuf *elem = om;
while (elem) {
n += 1;
elem = SLIST_NEXT(elem, om_next);
}
// Grab data pointers and lengths for each of the links.
const uint8_t **data = mp_local_alloc(sizeof(uint8_t *) * n);
uint16_t *data_len = mp_local_alloc(sizeof(uint16_t) * n);
for (size_t i = 0; i < n; ++i) {
data[i] = OS_MBUF_DATA(om, const uint8_t *);
data_len[i] = om->om_len;
om = SLIST_NEXT(om, om_next);
}
// Pass all the fragments together.
mp_bluetooth_gattc_on_data_available(event, conn_handle, value_handle, data, data_len, n);
mp_local_free(data_len);
mp_local_free(data);
}
STATIC int gap_scan_cb(struct ble_gap_event *event, void *arg) { STATIC int gap_scan_cb(struct ble_gap_event *event, void *arg) {
DEBUG_printf("gap_scan_cb: event=%d type=%d\n", event->type, event->type == BLE_GAP_EVENT_DISC ? event->disc.event_type : -1); DEBUG_printf("gap_scan_cb: event=%d type=%d\n", event->type, event->type == BLE_GAP_EVENT_DISC ? event->disc.event_type : -1);
if (!mp_bluetooth_is_active()) { if (!mp_bluetooth_is_active()) {
@ -1135,56 +1181,17 @@ STATIC int peripheral_gap_event_cb(struct ble_gap_event *event, void *arg) {
// Connection failed. // Connection failed.
mp_bluetooth_gap_on_connected_disconnected(MP_BLUETOOTH_IRQ_PERIPHERAL_DISCONNECT, event->connect.conn_handle, 0xff, addr); mp_bluetooth_gap_on_connected_disconnected(MP_BLUETOOTH_IRQ_PERIPHERAL_DISCONNECT, event->connect.conn_handle, 0xff, addr);
} }
break; return 0;
case BLE_GAP_EVENT_DISCONNECT: case BLE_GAP_EVENT_DISCONNECT:
// Disconnect. // Disconnect.
DEBUG_printf("peripheral_gap_event_cb: reason=%d\n", event->disconnect.reason); DEBUG_printf("peripheral_gap_event_cb: reason=%d\n", event->disconnect.reason);
reverse_addr_byte_order(addr, event->disconnect.conn.peer_id_addr.val); reverse_addr_byte_order(addr, event->disconnect.conn.peer_id_addr.val);
mp_bluetooth_gap_on_connected_disconnected(MP_BLUETOOTH_IRQ_PERIPHERAL_DISCONNECT, event->disconnect.conn.conn_handle, event->disconnect.conn.peer_id_addr.type, addr); mp_bluetooth_gap_on_connected_disconnected(MP_BLUETOOTH_IRQ_PERIPHERAL_DISCONNECT, event->disconnect.conn.conn_handle, event->disconnect.conn.peer_id_addr.type, addr);
return 0;
break;
case BLE_GAP_EVENT_NOTIFY_RX: {
uint16_t ev = event->notify_rx.indication == 0 ? MP_BLUETOOTH_IRQ_GATTC_NOTIFY : MP_BLUETOOTH_IRQ_GATTC_INDICATE;
gattc_on_data_available(ev, event->notify_rx.conn_handle, event->notify_rx.attr_handle, event->notify_rx.om);
break;
}
case BLE_GAP_EVENT_CONN_UPDATE: {
DEBUG_printf("peripheral_gap_event_cb: connection update: status=%d\n", event->conn_update.status);
if (ble_gap_conn_find(event->conn_update.conn_handle, &desc) == 0) {
mp_bluetooth_gap_on_connection_update(event->conn_update.conn_handle, desc.conn_itvl, desc.conn_latency, desc.supervision_timeout, event->conn_update.status == 0 ? 0 : 1);
}
break;
}
case BLE_GAP_EVENT_MTU: {
if (event->mtu.channel_id == BLE_L2CAP_CID_ATT) {
DEBUG_printf("peripheral_gap_event_cb: mtu update: conn_handle=%d cid=%d mtu=%d\n", event->mtu.conn_handle, event->mtu.channel_id, event->mtu.value);
mp_bluetooth_gatts_on_mtu_exchanged(event->mtu.conn_handle, event->mtu.value);
}
break;
}
case BLE_GAP_EVENT_ENC_CHANGE: {
DEBUG_printf("peripheral_gap_event_cb: enc change: status=%d\n", event->enc_change.status);
#if MICROPY_PY_BLUETOOTH_ENABLE_PAIRING_BONDING
if (ble_gap_conn_find(event->enc_change.conn_handle, &desc) == 0) {
mp_bluetooth_gatts_on_encryption_update(event->conn_update.conn_handle,
desc.sec_state.encrypted, desc.sec_state.authenticated,
desc.sec_state.bonded, desc.sec_state.key_size);
}
#endif
break;
}
default:
DEBUG_printf("peripheral_gap_event_cb: unknown type %d\n", event->type);
break;
} }
return 0; return commmon_gap_event_cb(event, arg);
} }
int mp_bluetooth_gap_peripheral_connect(uint8_t addr_type, const uint8_t *addr, int32_t duration_ms) { int mp_bluetooth_gap_peripheral_connect(uint8_t addr_type, const uint8_t *addr, int32_t duration_ms) {
@ -1213,8 +1220,8 @@ int mp_bluetooth_gap_peripheral_connect(uint8_t addr_type, const uint8_t *addr,
return ble_hs_err_to_errno(err); return ble_hs_err_to_errno(err);
} }
STATIC int peripheral_discover_service_cb(uint16_t conn_handle, const struct ble_gatt_error *error, const struct ble_gatt_svc *service, void *arg) { STATIC int ble_gattc_service_cb(uint16_t conn_handle, const struct ble_gatt_error *error, const struct ble_gatt_svc *service, void *arg) {
DEBUG_printf("peripheral_discover_service_cb: conn_handle=%d status=%d start_handle=%d\n", conn_handle, error->status, service ? service->start_handle : -1); DEBUG_printf("ble_gattc_service_cb: conn_handle=%d status=%d start_handle=%d\n", conn_handle, error->status, service ? service->start_handle : -1);
if (!mp_bluetooth_is_active()) { if (!mp_bluetooth_is_active()) {
return 0; return 0;
} }
@ -1227,6 +1234,42 @@ STATIC int peripheral_discover_service_cb(uint16_t conn_handle, const struct ble
return 0; return 0;
} }
#endif // MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE
#if MICROPY_PY_BLUETOOTH_ENABLE_GATT_CLIENT
STATIC void gattc_on_data_available(uint8_t event, uint16_t conn_handle, uint16_t value_handle, const struct os_mbuf *om) {
// When the HCI data for an ATT payload arrives, the L2CAP channel will
// buffer it into its receive buffer. We set BLE_L2CAP_JOIN_RX_FRAGS=1 in
// syscfg.h so it should be rare that the mbuf is fragmented, but we do need
// to be able to handle it. We pass all the fragments up to modbluetooth.c
// which will create a temporary buffer on the MicroPython heap if necessary
// to re-assemble them.
// Count how many links are in the mbuf chain.
size_t n = 0;
const struct os_mbuf *elem = om;
while (elem) {
n += 1;
elem = SLIST_NEXT(elem, om_next);
}
// Grab data pointers and lengths for each of the links.
const uint8_t **data = mp_local_alloc(sizeof(uint8_t *) * n);
uint16_t *data_len = mp_local_alloc(sizeof(uint16_t) * n);
for (size_t i = 0; i < n; ++i) {
data[i] = OS_MBUF_DATA(om, const uint8_t *);
data_len[i] = om->om_len;
om = SLIST_NEXT(om, om_next);
}
// Pass all the fragments together.
mp_bluetooth_gattc_on_data_available(event, conn_handle, value_handle, data, data_len, n);
mp_local_free(data_len);
mp_local_free(data);
}
int mp_bluetooth_gattc_discover_primary_services(uint16_t conn_handle, const mp_obj_bluetooth_uuid_t *uuid) { int mp_bluetooth_gattc_discover_primary_services(uint16_t conn_handle, const mp_obj_bluetooth_uuid_t *uuid) {
if (!mp_bluetooth_is_active()) { if (!mp_bluetooth_is_active()) {
return ERRNO_BLUETOOTH_NOT_ACTIVE; return ERRNO_BLUETOOTH_NOT_ACTIVE;
@ -1235,15 +1278,15 @@ int mp_bluetooth_gattc_discover_primary_services(uint16_t conn_handle, const mp_
if (uuid) { if (uuid) {
ble_uuid_any_t nimble_uuid; ble_uuid_any_t nimble_uuid;
create_nimble_uuid(uuid, &nimble_uuid); create_nimble_uuid(uuid, &nimble_uuid);
err = ble_gattc_disc_svc_by_uuid(conn_handle, &nimble_uuid.u, &peripheral_discover_service_cb, NULL); err = ble_gattc_disc_svc_by_uuid(conn_handle, &nimble_uuid.u, &ble_gattc_service_cb, NULL);
} else { } else {
err = ble_gattc_disc_all_svcs(conn_handle, &peripheral_discover_service_cb, NULL); err = ble_gattc_disc_all_svcs(conn_handle, &ble_gattc_service_cb, NULL);
} }
return ble_hs_err_to_errno(err); return ble_hs_err_to_errno(err);
} }
STATIC int ble_gatt_characteristic_cb(uint16_t conn_handle, const struct ble_gatt_error *error, const struct ble_gatt_chr *characteristic, void *arg) { STATIC int ble_gattc_characteristic_cb(uint16_t conn_handle, const struct ble_gatt_error *error, const struct ble_gatt_chr *characteristic, void *arg) {
DEBUG_printf("ble_gatt_characteristic_cb: conn_handle=%d status=%d def_handle=%d val_handle=%d\n", conn_handle, error->status, characteristic ? characteristic->def_handle : -1, characteristic ? characteristic->val_handle : -1); DEBUG_printf("ble_gattc_characteristic_cb: conn_handle=%d status=%d def_handle=%d val_handle=%d\n", conn_handle, error->status, characteristic ? characteristic->def_handle : -1, characteristic ? characteristic->val_handle : -1);
if (!mp_bluetooth_is_active()) { if (!mp_bluetooth_is_active()) {
return 0; return 0;
} }
@ -1264,15 +1307,15 @@ int mp_bluetooth_gattc_discover_characteristics(uint16_t conn_handle, uint16_t s
if (uuid) { if (uuid) {
ble_uuid_any_t nimble_uuid; ble_uuid_any_t nimble_uuid;
create_nimble_uuid(uuid, &nimble_uuid); create_nimble_uuid(uuid, &nimble_uuid);
err = ble_gattc_disc_chrs_by_uuid(conn_handle, start_handle, end_handle, &nimble_uuid.u, &ble_gatt_characteristic_cb, NULL); err = ble_gattc_disc_chrs_by_uuid(conn_handle, start_handle, end_handle, &nimble_uuid.u, &ble_gattc_characteristic_cb, NULL);
} else { } else {
err = ble_gattc_disc_all_chrs(conn_handle, start_handle, end_handle, &ble_gatt_characteristic_cb, NULL); err = ble_gattc_disc_all_chrs(conn_handle, start_handle, end_handle, &ble_gattc_characteristic_cb, NULL);
} }
return ble_hs_err_to_errno(err); return ble_hs_err_to_errno(err);
} }
STATIC int ble_gatt_descriptor_cb(uint16_t conn_handle, const struct ble_gatt_error *error, uint16_t characteristic_val_handle, const struct ble_gatt_dsc *descriptor, void *arg) { STATIC int ble_gattc_descriptor_cb(uint16_t conn_handle, const struct ble_gatt_error *error, uint16_t characteristic_val_handle, const struct ble_gatt_dsc *descriptor, void *arg) {
DEBUG_printf("ble_gatt_descriptor_cb: conn_handle=%d status=%d chr_handle=%d dsc_handle=%d\n", conn_handle, error->status, characteristic_val_handle, descriptor ? descriptor->handle : -1); DEBUG_printf("ble_gattc_descriptor_cb: conn_handle=%d status=%d chr_handle=%d dsc_handle=%d\n", conn_handle, error->status, characteristic_val_handle, descriptor ? descriptor->handle : -1);
if (!mp_bluetooth_is_active()) { if (!mp_bluetooth_is_active()) {
return 0; return 0;
} }
@ -1289,13 +1332,13 @@ int mp_bluetooth_gattc_discover_descriptors(uint16_t conn_handle, uint16_t start
if (!mp_bluetooth_is_active()) { if (!mp_bluetooth_is_active()) {
return ERRNO_BLUETOOTH_NOT_ACTIVE; return ERRNO_BLUETOOTH_NOT_ACTIVE;
} }
int err = ble_gattc_disc_all_dscs(conn_handle, start_handle, end_handle, &ble_gatt_descriptor_cb, NULL); int err = ble_gattc_disc_all_dscs(conn_handle, start_handle, end_handle, &ble_gattc_descriptor_cb, NULL);
return ble_hs_err_to_errno(err); return ble_hs_err_to_errno(err);
} }
STATIC int ble_gatt_attr_read_cb(uint16_t conn_handle, const struct ble_gatt_error *error, struct ble_gatt_attr *attr, void *arg) { STATIC int ble_gattc_attr_read_cb(uint16_t conn_handle, const struct ble_gatt_error *error, struct ble_gatt_attr *attr, void *arg) {
uint16_t handle = attr ? attr->handle : (error ? error->att_handle : 0xffff); uint16_t handle = attr ? attr->handle : (error ? error->att_handle : 0xffff);
DEBUG_printf("ble_gatt_attr_read_cb: conn_handle=%d status=%d handle=%d\n", conn_handle, error->status, handle); DEBUG_printf("ble_gattc_attr_read_cb: conn_handle=%d status=%d handle=%d\n", conn_handle, error->status, handle);
if (!mp_bluetooth_is_active()) { if (!mp_bluetooth_is_active()) {
return 0; return 0;
} }
@ -1311,13 +1354,13 @@ int mp_bluetooth_gattc_read(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;
} }
int err = ble_gattc_read(conn_handle, value_handle, &ble_gatt_attr_read_cb, NULL); int err = ble_gattc_read(conn_handle, value_handle, &ble_gattc_attr_read_cb, NULL);
return ble_hs_err_to_errno(err); return ble_hs_err_to_errno(err);
} }
STATIC int ble_gatt_attr_write_cb(uint16_t conn_handle, const struct ble_gatt_error *error, struct ble_gatt_attr *attr, void *arg) { STATIC int ble_gattc_attr_write_cb(uint16_t conn_handle, const struct ble_gatt_error *error, struct ble_gatt_attr *attr, void *arg) {
uint16_t handle = attr ? attr->handle : (error ? error->att_handle : 0xffff); uint16_t handle = attr ? attr->handle : (error ? error->att_handle : 0xffff);
DEBUG_printf("ble_gatt_attr_write_cb: conn_handle=%d status=%d handle=%d\n", conn_handle, error->status, handle); DEBUG_printf("ble_gattc_attr_write_cb: conn_handle=%d status=%d handle=%d\n", conn_handle, error->status, handle);
if (!mp_bluetooth_is_active()) { if (!mp_bluetooth_is_active()) {
return 0; return 0;
} }
@ -1334,7 +1377,7 @@ int mp_bluetooth_gattc_write(uint16_t conn_handle, uint16_t value_handle, const
if (mode == MP_BLUETOOTH_WRITE_MODE_NO_RESPONSE) { if (mode == MP_BLUETOOTH_WRITE_MODE_NO_RESPONSE) {
err = ble_gattc_write_no_rsp_flat(conn_handle, value_handle, value, *value_len); err = ble_gattc_write_no_rsp_flat(conn_handle, value_handle, value, *value_len);
} else if (mode == MP_BLUETOOTH_WRITE_MODE_WITH_RESPONSE) { } else if (mode == MP_BLUETOOTH_WRITE_MODE_WITH_RESPONSE) {
err = ble_gattc_write_flat(conn_handle, value_handle, value, *value_len, &ble_gatt_attr_write_cb, NULL); err = ble_gattc_write_flat(conn_handle, value_handle, value, *value_len, &ble_gattc_attr_write_cb, NULL);
} else { } else {
err = BLE_HS_EINVAL; err = BLE_HS_EINVAL;
} }
@ -1348,7 +1391,7 @@ int mp_bluetooth_gattc_exchange_mtu(uint16_t conn_handle) {
return ble_hs_err_to_errno(ble_gattc_exchange_mtu(conn_handle, NULL, NULL)); return ble_hs_err_to_errno(ble_gattc_exchange_mtu(conn_handle, NULL, NULL));
} }
#endif // MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE #endif // MICROPY_PY_BLUETOOTH_ENABLE_GATT_CLIENT
#if MICROPY_PY_BLUETOOTH_ENABLE_L2CAP_CHANNELS #if MICROPY_PY_BLUETOOTH_ENABLE_L2CAP_CHANNELS
@ -1375,6 +1418,11 @@ typedef struct _mp_bluetooth_nimble_l2cap_channel_t {
os_membuf_t sdu_mem[]; os_membuf_t sdu_mem[];
} mp_bluetooth_nimble_l2cap_channel_t; } mp_bluetooth_nimble_l2cap_channel_t;
STATIC void destroy_l2cap_channel();
STATIC int l2cap_channel_event(struct ble_l2cap_event *event, void *arg);
STATIC mp_bluetooth_nimble_l2cap_channel_t *get_l2cap_channel_for_conn_cid(uint16_t conn_handle, uint16_t cid);
STATIC int create_l2cap_channel(uint16_t mtu, mp_bluetooth_nimble_l2cap_channel_t **out);
STATIC void destroy_l2cap_channel() { STATIC void destroy_l2cap_channel() {
// Only free the l2cap channel if we're the one that initiated the connection. // Only free the l2cap channel if we're the one that initiated the connection.
// Listeners continue listening on the same channel. // Listeners continue listening on the same channel.
@ -1395,9 +1443,9 @@ STATIC int l2cap_channel_event(struct ble_l2cap_event *event, void *arg) {
ble_l2cap_get_chan_info(event->connect.chan, &info); ble_l2cap_get_chan_info(event->connect.chan, &info);
if (event->connect.status == 0) { if (event->connect.status == 0) {
mp_bluetooth_gattc_on_l2cap_connect(event->connect.conn_handle, info.scid, info.psm, info.our_coc_mtu, info.peer_coc_mtu); mp_bluetooth_on_l2cap_connect(event->connect.conn_handle, info.scid, info.psm, info.our_coc_mtu, info.peer_coc_mtu);
} else { } else {
mp_bluetooth_gattc_on_l2cap_disconnect(event->connect.conn_handle, info.scid, info.psm, event->connect.status); mp_bluetooth_on_l2cap_disconnect(event->connect.conn_handle, info.scid, info.psm, event->connect.status);
destroy_l2cap_channel(); destroy_l2cap_channel();
} }
break; break;
@ -1405,7 +1453,7 @@ STATIC int l2cap_channel_event(struct ble_l2cap_event *event, void *arg) {
case BLE_L2CAP_EVENT_COC_DISCONNECTED: { case BLE_L2CAP_EVENT_COC_DISCONNECTED: {
DEBUG_printf("l2cap_channel_event: disconnect: conn_handle=%d\n", event->disconnect.conn_handle); DEBUG_printf("l2cap_channel_event: disconnect: conn_handle=%d\n", event->disconnect.conn_handle);
ble_l2cap_get_chan_info(event->disconnect.chan, &info); ble_l2cap_get_chan_info(event->disconnect.chan, &info);
mp_bluetooth_gattc_on_l2cap_disconnect(event->disconnect.conn_handle, info.scid, info.psm, 0); mp_bluetooth_on_l2cap_disconnect(event->disconnect.conn_handle, info.scid, info.psm, 0);
destroy_l2cap_channel(); destroy_l2cap_channel();
break; break;
} }
@ -1413,7 +1461,7 @@ STATIC int l2cap_channel_event(struct ble_l2cap_event *event, void *arg) {
DEBUG_printf("l2cap_channel_event: accept: conn_handle=%d peer_sdu_size=%d\n", event->accept.conn_handle, event->accept.peer_sdu_size); DEBUG_printf("l2cap_channel_event: accept: conn_handle=%d peer_sdu_size=%d\n", event->accept.conn_handle, event->accept.peer_sdu_size);
chan->chan = event->accept.chan; chan->chan = event->accept.chan;
ble_l2cap_get_chan_info(event->accept.chan, &info); ble_l2cap_get_chan_info(event->accept.chan, &info);
int ret = mp_bluetooth_gattc_on_l2cap_accept(event->accept.conn_handle, info.scid, info.psm, info.our_coc_mtu, info.peer_coc_mtu); int ret = mp_bluetooth_on_l2cap_accept(event->accept.conn_handle, info.scid, info.psm, info.our_coc_mtu, info.peer_coc_mtu);
if (ret != 0) { if (ret != 0) {
return ret; return ret;
} }
@ -1461,7 +1509,7 @@ STATIC int l2cap_channel_event(struct ble_l2cap_event *event, void *arg) {
// Don't allow granting more credits until after the IRQ is handled. // Don't allow granting more credits until after the IRQ is handled.
chan->irq_in_progress = true; chan->irq_in_progress = true;
mp_bluetooth_gattc_on_l2cap_recv(event->receive.conn_handle, info.scid); mp_bluetooth_on_l2cap_recv(event->receive.conn_handle, info.scid);
chan->irq_in_progress = false; chan->irq_in_progress = false;
// If all data has been consumed by the IRQ handler, then now allow // If all data has been consumed by the IRQ handler, then now allow
@ -1480,7 +1528,7 @@ STATIC int l2cap_channel_event(struct ble_l2cap_event *event, void *arg) {
DEBUG_printf("l2cap_channel_event: tx_unstalled: conn_handle=%d status=%d\n", event->tx_unstalled.conn_handle, event->tx_unstalled.status); DEBUG_printf("l2cap_channel_event: tx_unstalled: conn_handle=%d status=%d\n", event->tx_unstalled.conn_handle, event->tx_unstalled.status);
ble_l2cap_get_chan_info(event->receive.chan, &info); ble_l2cap_get_chan_info(event->receive.chan, &info);
// Map status to {0,1} (i.e. "sent everything", or "partial send"). // Map status to {0,1} (i.e. "sent everything", or "partial send").
mp_bluetooth_gattc_on_l2cap_send_ready(event->tx_unstalled.conn_handle, info.scid, event->tx_unstalled.status == 0 ? 0 : 1); mp_bluetooth_on_l2cap_send_ready(event->tx_unstalled.conn_handle, info.scid, event->tx_unstalled.status == 0 ? 0 : 1);
break; break;
} }
case BLE_L2CAP_EVENT_COC_RECONFIG_COMPLETED: { case BLE_L2CAP_EVENT_COC_RECONFIG_COMPLETED: {

Loading…
Cancel
Save