summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
authorJim Mussared <jim.mussared@gmail.com>2020-09-11 18:12:36 +1000
committerJim Mussared <jim.mussared@gmail.com>2020-09-18 12:51:21 +1000
commit857e2c8fd54bc1d5ec3d3c0a38e4ac989e91413a (patch)
tree6aec87ed86a6bcbfcac79c99ad1994b981b49096
parentf271b96b5c8d6a4abedc81459a1c5710eb187abb (diff)
downloadmicropython-857e2c8fd54bc1d5ec3d3c0a38e4ac989e91413a.tar.gz
micropython-857e2c8fd54bc1d5ec3d3c0a38e4ac989e91413a.zip
extmod/modbluetooth: Implement MTU.
-rw-r--r--extmod/btstack/modbluetooth_btstack.c36
-rw-r--r--extmod/modbluetooth.c28
-rw-r--r--extmod/modbluetooth.h12
-rw-r--r--extmod/nimble/modbluetooth_nimble.c44
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