diff options
author | Jim Mussared <jim.mussared@gmail.com> | 2020-09-11 18:12:36 +1000 |
---|---|---|
committer | Jim Mussared <jim.mussared@gmail.com> | 2020-09-18 12:51:21 +1000 |
commit | 857e2c8fd54bc1d5ec3d3c0a38e4ac989e91413a (patch) | |
tree | 6aec87ed86a6bcbfcac79c99ad1994b981b49096 | |
parent | f271b96b5c8d6a4abedc81459a1c5710eb187abb (diff) | |
download | micropython-857e2c8fd54bc1d5ec3d3c0a38e4ac989e91413a.tar.gz micropython-857e2c8fd54bc1d5ec3d3c0a38e4ac989e91413a.zip |
extmod/modbluetooth: Implement MTU.
-rw-r--r-- | extmod/btstack/modbluetooth_btstack.c | 36 | ||||
-rw-r--r-- | extmod/modbluetooth.c | 28 | ||||
-rw-r--r-- | extmod/modbluetooth.h | 12 | ||||
-rw-r--r-- | extmod/nimble/modbluetooth_nimble.c | 44 |
4 files changed, 116 insertions, 4 deletions
diff --git a/extmod/btstack/modbluetooth_btstack.c b/extmod/btstack/modbluetooth_btstack.c index cde802a61f..ae96035868 100644 --- a/extmod/btstack/modbluetooth_btstack.c +++ b/extmod/btstack/modbluetooth_btstack.c @@ -264,6 +264,11 @@ STATIC void btstack_packet_handler_att_server(uint8_t packet_type, uint16_t chan uint16_t value_handle = att_event_handle_value_indication_complete_get_attribute_handle(packet); uint8_t status = att_event_handle_value_indication_complete_get_status(packet); mp_bluetooth_gatts_on_indicate_complete(conn_handle, value_handle, status); + } else if (event_type == ATT_EVENT_MTU_EXCHANGE_COMPLETE) { + // This is triggered in peripheral mode, when exchange initiated by us or remote. + uint16_t conn_handle = att_event_mtu_exchange_complete_get_handle(packet); + uint16_t mtu = att_event_mtu_exchange_complete_get_MTU(packet); + mp_bluetooth_gatts_on_mtu_exchanged(conn_handle, mtu); } else if (event_type == HCI_EVENT_LE_META || event_type == HCI_EVENT_DISCONNECTION_COMPLETE) { // Ignore, duplicated by att_server.c. } else { @@ -339,8 +344,6 @@ STATIC void btstack_packet_handler(uint8_t packet_type, uint8_t *packet, uint8_t DEBUG_printf(" --> btstack # conns changed\n"); } else if (event_type == HCI_EVENT_VENDOR_SPECIFIC) { DEBUG_printf(" --> hci vendor specific\n"); - } else if (event_type == GATT_EVENT_MTU) { - DEBUG_printf(" --> hci MTU\n"); } else if (event_type == HCI_EVENT_DISCONNECTION_COMPLETE) { DEBUG_printf(" --> hci disconnect complete\n"); uint16_t conn_handle = hci_event_disconnection_complete_get_connection_handle(packet); @@ -621,6 +624,9 @@ int mp_bluetooth_init(void) { #if MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE gatt_client_init(); + + // We always require explicitly exchanging MTU with ble.gattc_exchange_mtu(). + gatt_client_mtu_enable_auto_negotiation(false); #endif // Register for HCI events. @@ -1042,6 +1048,24 @@ int mp_bluetooth_gatts_set_buffer(uint16_t value_handle, size_t len, bool append return mp_bluetooth_gatts_db_resize(MP_STATE_PORT(bluetooth_btstack_root_pointers)->gatts_db, value_handle, len, append); } +int mp_bluetooth_get_preferred_mtu(void) { + if (!mp_bluetooth_is_active()) { + mp_raise_OSError(ERRNO_BLUETOOTH_NOT_ACTIVE); + } + return l2cap_max_le_mtu(); +} + +int mp_bluetooth_set_preferred_mtu(uint16_t mtu) { + if (!mp_bluetooth_is_active()) { + return ERRNO_BLUETOOTH_NOT_ACTIVE; + } + l2cap_set_max_le_mtu(mtu); + if (l2cap_max_le_mtu() != mtu) { + return MP_EINVAL; + } + return 0; +} + int mp_bluetooth_gap_disconnect(uint16_t conn_handle) { DEBUG_printf("mp_bluetooth_gap_disconnect\n"); gap_disconnect(conn_handle); @@ -1204,6 +1228,14 @@ int mp_bluetooth_gattc_write(uint16_t conn_handle, uint16_t value_handle, const return btstack_error_to_errno(err); } + +int mp_bluetooth_gattc_exchange_mtu(uint16_t conn_handle) { + DEBUG_printf("mp_bluetooth_exchange_mtu: conn_handle=%d mtu=%d\n", conn_handle, l2cap_max_le_mtu()); + + gatt_client_send_mtu_negotiation(&btstack_packet_handler_att_server, conn_handle); + + return 0; +} #endif // MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE #endif // MICROPY_PY_BLUETOOTH && MICROPY_BLUETOOTH_BTSTACK diff --git a/extmod/modbluetooth.c b/extmod/modbluetooth.c index f77c7eb63c..daf9cd0d19 100644 --- a/extmod/modbluetooth.c +++ b/extmod/modbluetooth.c @@ -316,6 +316,8 @@ STATIC mp_obj_t bluetooth_ble_config(size_t n_args, const mp_obj_t *args, mp_map } case MP_QSTR_rxbuf: return mp_obj_new_int(self->ringbuf.size); + case MP_QSTR_mtu: + return mp_obj_new_int(mp_bluetooth_get_preferred_mtu()); default: mp_raise_ValueError(MP_ERROR_TEXT("unknown config param")); } @@ -368,6 +370,11 @@ STATIC mp_obj_t bluetooth_ble_config(size_t n_args, const mp_obj_t *args, mp_map m_del(uint8_t, old_irq_data_buf, old_irq_data_alloc); break; } + case MP_QSTR_mtu: { + mp_int_t mtu = mp_obj_get_int(e->value); + bluetooth_handle_errno(mp_bluetooth_set_preferred_mtu(mtu)); + break; + } case MP_QSTR_addr_mode: { mp_int_t addr_mode = mp_obj_get_int(e->value); mp_bluetooth_set_address_mode(addr_mode); @@ -770,6 +777,13 @@ STATIC mp_obj_t bluetooth_ble_gattc_write(size_t n_args, const mp_obj_t *args) { } STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(bluetooth_ble_gattc_write_obj, 4, 5, bluetooth_ble_gattc_write); +STATIC mp_obj_t bluetooth_ble_gattc_exchange_mtu(mp_obj_t self_in, mp_obj_t conn_handle_in) { + (void)self_in; + uint16_t conn_handle = mp_obj_get_int(conn_handle_in); + return bluetooth_handle_errno(mp_bluetooth_gattc_exchange_mtu(conn_handle)); +} +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 // ---------------------------------------------------------------------------- @@ -802,6 +816,7 @@ STATIC const mp_rom_map_elem_t bluetooth_ble_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_gattc_discover_descriptors), MP_ROM_PTR(&bluetooth_ble_gattc_discover_descriptors_obj) }, { MP_ROM_QSTR(MP_QSTR_gattc_read), MP_ROM_PTR(&bluetooth_ble_gattc_read_obj) }, { MP_ROM_QSTR(MP_QSTR_gattc_write), MP_ROM_PTR(&bluetooth_ble_gattc_write_obj) }, + { MP_ROM_QSTR(MP_QSTR_gattc_exchange_mtu), MP_ROM_PTR(&bluetooth_ble_gattc_exchange_mtu_obj) }, #endif }; STATIC MP_DEFINE_CONST_DICT(bluetooth_ble_locals_dict, bluetooth_ble_locals_dict_table); @@ -911,6 +926,9 @@ STATIC mp_obj_t bluetooth_ble_invoke_irq(mp_obj_t none_in) { } else if (event == MP_BLUETOOTH_IRQ_GATTS_INDICATE_DONE) { // conn_handle, value_handle, status ringbuf_extract(&o->ringbuf, data_tuple, 2, 1, NULL, 0, NULL, NULL); + } else if (event == MP_BLUETOOTH_IRQ_MTU_EXCHANGED) { + // conn_handle, mtu + ringbuf_extract(&o->ringbuf, data_tuple, 2, 0, NULL, 0, NULL, NULL); #if MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE } else if (event == MP_BLUETOOTH_IRQ_SCAN_RESULT) { // addr_type, addr, adv_type, rssi, adv_data @@ -1184,6 +1202,16 @@ bool mp_bluetooth_gatts_on_read_request(uint16_t conn_handle, uint16_t value_han } #endif +void mp_bluetooth_gatts_on_mtu_exchanged(uint16_t conn_handle, uint16_t value) { + MICROPY_PY_BLUETOOTH_ENTER + mp_obj_bluetooth_ble_t *o = MP_OBJ_TO_PTR(MP_STATE_VM(bluetooth)); + if (enqueue_irq(o, 2 + 2, MP_BLUETOOTH_IRQ_MTU_EXCHANGED)) { + ringbuf_put16(&o->ringbuf, conn_handle); + ringbuf_put16(&o->ringbuf, value); + } + schedule_ringbuf(atomic_state); +} + void mp_bluetooth_gatts_db_create_entry(mp_gatts_db_t db, uint16_t handle, size_t len) { mp_map_elem_t *elem = mp_map_lookup(db, MP_OBJ_NEW_SMALL_INT(handle), MP_MAP_LOOKUP_ADD_IF_NOT_FOUND); mp_bluetooth_gatts_db_entry_t *entry = m_new(mp_bluetooth_gatts_db_entry_t, 1); diff --git a/extmod/modbluetooth.h b/extmod/modbluetooth.h index 23ff97bc61..2df4c3c25f 100644 --- a/extmod/modbluetooth.h +++ b/extmod/modbluetooth.h @@ -100,6 +100,7 @@ #define MP_BLUETOOTH_IRQ_GATTC_NOTIFY (18) #define MP_BLUETOOTH_IRQ_GATTC_INDICATE (19) #define MP_BLUETOOTH_IRQ_GATTS_INDICATE_DONE (20) +#define MP_BLUETOOTH_IRQ_MTU_EXCHANGED (21) #define MP_BLUETOOTH_ADDRESS_MODE_PUBLIC (0) #define MP_BLUETOOTH_ADDRESS_MODE_RANDOM (1) @@ -131,6 +132,7 @@ _IRQ_GATTC_WRITE_DONE = const(17) _IRQ_GATTC_NOTIFY = const(18) _IRQ_GATTC_INDICATE = const(19) _IRQ_GATTS_INDICATE_DONE = const(20) +_IRQ_MTU_EXCHANGED = const(21) */ // Common UUID type. @@ -212,6 +214,10 @@ int mp_bluetooth_gatts_set_buffer(uint16_t value_handle, size_t len, bool append // Disconnect from a central or peripheral. int mp_bluetooth_gap_disconnect(uint16_t conn_handle); +// Set/get the MTU that we will respond to a MTU exchange with. +int mp_bluetooth_get_preferred_mtu(void); +int mp_bluetooth_set_preferred_mtu(uint16_t mtu); + #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); @@ -236,6 +242,9 @@ int mp_bluetooth_gattc_read(uint16_t conn_handle, uint16_t 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); + +// Initiate MTU exchange for a specific connection using the preferred MTU. +int mp_bluetooth_gattc_exchange_mtu(uint16_t conn_handle); #endif ///////////////////////////////////////////////////////////////////////////// @@ -255,6 +264,9 @@ void mp_bluetooth_gatts_on_indicate_complete(uint16_t conn_handle, uint16_t valu bool mp_bluetooth_gatts_on_read_request(uint16_t conn_handle, uint16_t value_handle); #endif +// Call this when an MTU exchange completes. +void mp_bluetooth_gatts_on_mtu_exchanged(uint16_t conn_handle, uint16_t value); + #if MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE // Notify modbluetooth that scan has finished, either timeout, manually, or by some other action (e.g. connecting). void mp_bluetooth_gap_on_scan_complete(void); diff --git a/extmod/nimble/modbluetooth_nimble.c b/extmod/nimble/modbluetooth_nimble.c index 9ca90b6024..c0d6d64cfc 100644 --- a/extmod/nimble/modbluetooth_nimble.c +++ b/extmod/nimble/modbluetooth_nimble.c @@ -288,9 +288,17 @@ STATIC int gap_event_cb(struct ble_gap_event *event, void *arg) { // 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); } + break; } - } + 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; + } + } return 0; } @@ -637,7 +645,7 @@ int mp_bluetooth_gatts_register_service_begin(bool append) { return 0; } -int mp_bluetooth_gatts_register_service_end() { +int mp_bluetooth_gatts_register_service_end(void) { int ret = ble_gatts_start(); if (ret != 0) { return ble_hs_err_to_errno(ret); @@ -769,6 +777,23 @@ int mp_bluetooth_gatts_set_buffer(uint16_t value_handle, size_t len, bool append return mp_bluetooth_gatts_db_resize(MP_STATE_PORT(bluetooth_nimble_root_pointers)->gatts_db, value_handle, len, append); } +int mp_bluetooth_get_preferred_mtu(void) { + if (!mp_bluetooth_is_active()) { + mp_raise_OSError(ERRNO_BLUETOOTH_NOT_ACTIVE); + } + return ble_att_preferred_mtu(); +} + +int mp_bluetooth_set_preferred_mtu(uint16_t mtu) { + if (!mp_bluetooth_is_active()) { + return ERRNO_BLUETOOTH_NOT_ACTIVE; + } + if (ble_att_set_preferred_mtu(mtu)) { + return MP_EINVAL; + } + return 0; +} + #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) { @@ -884,6 +909,14 @@ STATIC int peripheral_gap_event_cb(struct ble_gap_event *event, void *arg) { // TODO 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; + } + default: break; } @@ -1042,6 +1075,13 @@ int mp_bluetooth_gattc_write(uint16_t conn_handle, uint16_t value_handle, const return ble_hs_err_to_errno(err); } +int mp_bluetooth_gattc_exchange_mtu(uint16_t conn_handle) { + DEBUG_printf("mp_bluetooth_exchange_mtu: conn_handle=%d mtu=%d\n", conn_handle, ble_att_preferred_mtu()); + + // Using NULL callback (we'll get notified in gap_event_cb instead). + 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 && MICROPY_BLUETOOTH_NIMBLE |