diff options
author | Jim Mussared <jim.mussared@gmail.com> | 2020-10-16 00:15:16 +1100 |
---|---|---|
committer | Damien George <damien@micropython.org> | 2020-11-24 01:07:17 +1100 |
commit | 0e8af2b3708ab3e499491836d481b10e030408f8 (patch) | |
tree | ea916e958ac0099f197ce080d65c8e2f453266e9 /extmod/modbluetooth.c | |
parent | 64180f0742a1926162b784c68bafa49b9b58596c (diff) |
extmod/modbluetooth: Add API for L2CAP channels.
Also known as L2CAP "connection oriented channels". This provides a
socket-like data transfer mechanism for BLE.
Currently only implemented for NimBLE on STM32 / Unix.
Signed-off-by: Jim Mussared <jim.mussared@gmail.com>
Diffstat (limited to 'extmod/modbluetooth.c')
-rw-r--r-- | extmod/modbluetooth.c | 129 |
1 files changed, 111 insertions, 18 deletions
diff --git a/extmod/modbluetooth.c b/extmod/modbluetooth.c index d8068df59..2cff386f1 100644 --- a/extmod/modbluetooth.c +++ b/extmod/modbluetooth.c @@ -43,6 +43,10 @@ #error modbluetooth requires MICROPY_ENABLE_SCHEDULER #endif +#if MICROPY_PY_BLUETOOTH_ENABLE_L2CAP_CHANNELS && !MICROPY_PY_BLUETOOTH_USE_SYNC_EVENTS +#error l2cap channels require synchronous modbluetooth events +#endif + #define MP_BLUETOOTH_CONNECT_DEFAULT_SCAN_DURATION_MS 2000 #define MICROPY_PY_BLUETOOTH_MAX_EVENT_DATA_TUPLE_LEN 5 @@ -785,6 +789,58 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_2(bluetooth_ble_gattc_exchange_mtu_obj, bluetooth #endif // MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE +#if MICROPY_PY_BLUETOOTH_ENABLE_L2CAP_CHANNELS + +STATIC mp_obj_t bluetooth_ble_l2cap_listen(mp_obj_t self_in, mp_obj_t psm_in, mp_obj_t mtu_in) { + (void)self_in; + mp_int_t psm = mp_obj_get_int(psm_in); + mp_int_t mtu = mp_obj_get_int(mtu_in); + return bluetooth_handle_errno(mp_bluetooth_l2cap_listen(psm, mtu)); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_3(bluetooth_ble_l2cap_listen_obj, bluetooth_ble_l2cap_listen); + +STATIC mp_obj_t bluetooth_ble_l2cap_connect(size_t n_args, const mp_obj_t *args) { + mp_int_t conn_handle = mp_obj_get_int(args[1]); + mp_int_t psm = mp_obj_get_int(args[2]); + mp_int_t mtu = mp_obj_get_int(args[3]); + return bluetooth_handle_errno(mp_bluetooth_l2cap_connect(conn_handle, psm, mtu)); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(bluetooth_ble_l2cap_connect_obj, 4, 4, bluetooth_ble_l2cap_connect); + +STATIC mp_obj_t bluetooth_ble_l2cap_disconnect(mp_obj_t self_in, mp_obj_t conn_handle_in, mp_obj_t cid_in) { + (void)self_in; + mp_int_t conn_handle = mp_obj_get_int(conn_handle_in); + mp_int_t cid = mp_obj_get_int(cid_in); + return bluetooth_handle_errno(mp_bluetooth_l2cap_disconnect(conn_handle, cid)); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_3(bluetooth_ble_l2cap_disconnect_obj, bluetooth_ble_l2cap_disconnect); + +STATIC mp_obj_t bluetooth_ble_l2cap_send(size_t n_args, const mp_obj_t *args) { + mp_int_t conn_handle = mp_obj_get_int(args[1]); + mp_int_t cid = mp_obj_get_int(args[2]); + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(args[3], &bufinfo, MP_BUFFER_READ); + bool stalled = false; + bluetooth_handle_errno(mp_bluetooth_l2cap_send(conn_handle, cid, bufinfo.buf, bufinfo.len, &stalled)); + // Return True if the channel is still ready to send. + return mp_obj_new_bool(!stalled); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(bluetooth_ble_l2cap_send_obj, 4, 4, bluetooth_ble_l2cap_send); + +STATIC mp_obj_t bluetooth_ble_l2cap_recvinto(size_t n_args, const mp_obj_t *args) { + mp_int_t conn_handle = mp_obj_get_int(args[1]); + mp_int_t cid = mp_obj_get_int(args[2]); + mp_buffer_info_t bufinfo = {0}; + if (args[3] != mp_const_none) { + mp_get_buffer_raise(args[3], &bufinfo, MP_BUFFER_WRITE); + } + bluetooth_handle_errno(mp_bluetooth_l2cap_recvinto(conn_handle, cid, bufinfo.buf, &bufinfo.len)); + return MP_OBJ_NEW_SMALL_INT(bufinfo.len); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(bluetooth_ble_l2cap_recvinto_obj, 4, 4, bluetooth_ble_l2cap_recvinto); + +#endif // MICROPY_PY_BLUETOOTH_ENABLE_L2CAP_CHANNELS + // ---------------------------------------------------------------------------- // Bluetooth object: Definition // ---------------------------------------------------------------------------- @@ -817,6 +873,13 @@ STATIC const mp_rom_map_elem_t bluetooth_ble_locals_dict_table[] = { { 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 + #if MICROPY_PY_BLUETOOTH_ENABLE_L2CAP_CHANNELS + { MP_ROM_QSTR(MP_QSTR_l2cap_listen), MP_ROM_PTR(&bluetooth_ble_l2cap_listen_obj) }, + { MP_ROM_QSTR(MP_QSTR_l2cap_connect), MP_ROM_PTR(&bluetooth_ble_l2cap_connect_obj) }, + { MP_ROM_QSTR(MP_QSTR_l2cap_disconnect), MP_ROM_PTR(&bluetooth_ble_l2cap_disconnect_obj) }, + { MP_ROM_QSTR(MP_QSTR_l2cap_send), MP_ROM_PTR(&bluetooth_ble_l2cap_send_obj) }, + { MP_ROM_QSTR(MP_QSTR_l2cap_recvinto), MP_ROM_PTR(&bluetooth_ble_l2cap_recvinto_obj) }, + #endif }; STATIC MP_DEFINE_CONST_DICT(bluetooth_ble_locals_dict, bluetooth_ble_locals_dict_table); @@ -1051,6 +1114,37 @@ void mp_bluetooth_gatts_on_mtu_exchanged(uint16_t conn_handle, uint16_t value) { invoke_irq_handler(MP_BLUETOOTH_IRQ_MTU_EXCHANGED, args, 2, NULL_U8, 0, NULL_ADDR, NULL_I8, 0, NULL_UUID, NULL_DATA, 0); } +#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) { + uint16_t args[] = {conn_handle, cid, psm, our_mtu, peer_mtu}; + mp_obj_t result = invoke_irq_handler(MP_BLUETOOTH_IRQ_L2CAP_ACCEPT, args, 5, NULL_U8, 0, NULL_ADDR, NULL_I8, 0, NULL_UUID, NULL_DATA, 0); + // Return non-zero from IRQ handler to fail the accept. + mp_int_t ret = 0; + mp_obj_get_int_maybe(result, &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) { + uint16_t args[] = {conn_handle, cid, psm, our_mtu, peer_mtu}; + invoke_irq_handler(MP_BLUETOOTH_IRQ_L2CAP_CONNECT, args, 5, NULL_U8, 0, NULL_ADDR, NULL_I8, 0, NULL_UUID, NULL_DATA, 0); +} + +void mp_bluetooth_gattc_on_l2cap_disconnect(uint16_t conn_handle, uint16_t cid, uint16_t psm, uint16_t status) { + uint16_t args[] = {conn_handle, cid, psm, status}; + invoke_irq_handler(MP_BLUETOOTH_IRQ_L2CAP_DISCONNECT, args, 4, NULL_U8, 0, NULL_ADDR, NULL_I8, 0, NULL_UUID, NULL_DATA, 0); +} + +void mp_bluetooth_gattc_on_l2cap_send_ready(uint16_t conn_handle, uint16_t cid, uint8_t status) { + uint16_t args[] = {conn_handle, cid}; + invoke_irq_handler(MP_BLUETOOTH_IRQ_L2CAP_SEND_READY, args, 2, &status, 1, NULL_ADDR, NULL_I8, 0, NULL_UUID, NULL_DATA, 0); +} + +void mp_bluetooth_gattc_on_l2cap_recv(uint16_t conn_handle, uint16_t cid) { + uint16_t args[] = {conn_handle, cid}; + invoke_irq_handler(MP_BLUETOOTH_IRQ_L2CAP_RECV, args, 2, NULL_U8, 0, NULL_ADDR, NULL_I8, 0, NULL_UUID, NULL_DATA, 0); +} +#endif // MICROPY_PY_BLUETOOTH_ENABLE_L2CAP_CHANNELS + #if MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE void mp_bluetooth_gap_on_scan_complete(void) { invoke_irq_handler(MP_BLUETOOTH_IRQ_SCAN_DONE, NULL_U16, 0, NULL_U8, 0, NULL_ADDR, NULL_I8, 0, NULL_UUID, NULL_DATA, 0); @@ -1203,6 +1297,23 @@ void mp_bluetooth_gatts_on_indicate_complete(uint16_t conn_handle, uint16_t valu schedule_ringbuf(atomic_state); } +bool mp_bluetooth_gatts_on_read_request(uint16_t conn_handle, uint16_t value_handle) { + (void)conn_handle; + (void)value_handle; + // This must be handled synchronously and therefore cannot implemented with the ringbuffer. + return true; +} + +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); +} + #if MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE void mp_bluetooth_gap_on_scan_complete(void) { MICROPY_PY_BLUETOOTH_ENTER @@ -1322,26 +1433,8 @@ void mp_bluetooth_gattc_on_read_write_status(uint8_t event, uint16_t conn_handle } schedule_ringbuf(atomic_state); } - #endif // MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE -bool mp_bluetooth_gatts_on_read_request(uint16_t conn_handle, uint16_t value_handle) { - (void)conn_handle; - (void)value_handle; - // This must be handled synchronously and therefore cannot implemented with the ringbuffer. - return true; -} - -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); -} - #endif // MICROPY_PY_BLUETOOTH_USE_SYNC_EVENTS // ---------------------------------------------------------------------------- |