diff --git a/locale/circuitpython.pot b/locale/circuitpython.pot index 0fc96f8b6e..e421eef8ea 100644 --- a/locale/circuitpython.pot +++ b/locale/circuitpython.pot @@ -147,11 +147,6 @@ msgstr "" msgid "%q must of type %q" msgstr "" -#: shared-bindings/keypad/KeyMatrix.c shared-bindings/keypad/Keys.c -#: shared-bindings/keypad/ShiftRegisterKeys.c -msgid "%q must store bytes" -msgstr "" - #: ports/raspberrypi/common-hal/rp2pio/StateMachine.c #: shared-bindings/canio/Match.c msgid "%q out of range" diff --git a/main.c b/main.c index dc2eaebb50..395b8c3057 100755 --- a/main.c +++ b/main.c @@ -71,7 +71,7 @@ #if CIRCUITPY_BLEIO #include "shared-bindings/_bleio/__init__.h" -#include "supervisor/shared/bluetooth.h" +#include "supervisor/shared/bluetooth/bluetooth.h" #endif #if CIRCUITPY_BOARD diff --git a/ports/nrf/background.c b/ports/nrf/background.c index 269bf6737f..f4d6b0f79f 100644 --- a/ports/nrf/background.c +++ b/ports/nrf/background.c @@ -41,11 +41,6 @@ #include "common-hal/audiopwmio/PWMAudioOut.h" #endif -#if CIRCUITPY_BLEIO -#include "supervisor/shared/bluetooth.h" -#include "common-hal/_bleio/bonding.h" -#endif - void port_start_background_task(void) { } void port_finish_background_task(void) { diff --git a/ports/nrf/bluetooth/ble_drv.c b/ports/nrf/bluetooth/ble_drv.c index 044acbebfc..29b2844736 100644 --- a/ports/nrf/bluetooth/ble_drv.c +++ b/ports/nrf/bluetooth/ble_drv.c @@ -38,7 +38,7 @@ #include "py/misc.h" #include "py/mpstate.h" -#include "supervisor/shared/bluetooth.h" +// #include "supervisor/shared/bluetooth.h" nrf_nvic_state_t nrf_nvic_state = { 0 }; diff --git a/ports/nrf/bluetooth/ble_uart.c b/ports/nrf/bluetooth/ble_uart.c deleted file mode 100644 index 6b1fed1ad5..0000000000 --- a/ports/nrf/bluetooth/ble_uart.c +++ /dev/null @@ -1,191 +0,0 @@ -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) 2019 Dan Halbert for Adafruit Industries - * Copyright (c) 2018 Artur Pacholec - * Copyright (c) 2017 Glenn Ruben Bakke - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include - -#include "ble.h" -#include "ble_uart.h" -#include "ringbuffer.h" -#include "py/mphal.h" -#include "py/runtime.h" -#include "lib/utils/interrupt_char.h" -#include "shared-bindings/_bleio/Adapter.h" -#include "shared-bindings/_bleio/Characteristic.h" -#include "shared-bindings/_bleio/Device.h" -#include "shared-bindings/_bleio/Service.h" -#include "shared-bindings/_bleio/UUID.h" - -#if CIRCUITPY_CONSOLE_BLE - -static const char default_name[] = "CP-REPL"; // max 8 chars or uuid won't fit in adv data -static const char NUS_UUID[] = "6e400001-b5a3-f393-e0a9-e50e24dcca9e"; - -#define NUS_RX_UUID 0x0002 -#define NUS_TX_UUID 0x0003 -#define BUFFER_SIZE 128 - -ringBuffer_typedef(uint8_t, ringbuffer_t); - -static bleio_device_obj_t m_device; -static bleio_service_obj_t *m_nus; -static bleio_characteristic_obj_t *m_tx_chara; -static bleio_characteristic_obj_t *m_rx_chara; - -static volatile bool m_cccd_enabled; - -static uint8_t m_rx_ring_buffer_data[BUFFER_SIZE]; -static ringbuffer_t m_rx_ring_buffer = { - .size = sizeof(m_rx_ring_buffer_data) + 1, - .elems = m_rx_ring_buffer_data, -}; - -STATIC void on_ble_evt(ble_evt_t *ble_evt, void *param) { - switch (ble_evt->header.evt_id) { - case BLE_GAP_EVT_DISCONNECTED: { - mp_obj_t device_obj = MP_OBJ_FROM_PTR(&m_device); - mp_call_function_0(mp_load_attr(device_obj, qstr_from_str("start_advertising"))); - break; - } - - case BLE_GATTS_EVT_WRITE: { - ble_gatts_evt_write_t *write = &ble_evt->evt.gatts_evt.params.write; - - if (write->handle == m_tx_chara->cccd_handle) { - m_cccd_enabled = true; - } else if (write->handle == m_rx_chara->handle) { - for (size_t i = 0; i < write->len; ++i) { - #if MICROPY_KBD_EXCEPTION - if (write->data[i] == mp_interrupt_char) { - mp_sched_keyboard_interrupt(); - } else - #endif - { - bufferWrite(&m_rx_ring_buffer, write->data[i]); - } - } - } - } - } -} - -void ble_uart_init(void) { - mp_obj_t device_obj = MP_OBJ_FROM_PTR(&m_device); - m_device.base.type = &bleio_device_type; - m_device.service_list = mp_obj_new_list(0, NULL); - m_device.notif_handler = mp_const_none; - m_device.conn_handler = mp_const_none; - m_device.conn_handle = 0xFFFF; - m_device.is_peripheral = true; - m_device.name = mp_obj_new_str(default_name, strlen(default_name), false); - common_hal_bleio_adapter_get_address(&m_device.address); - - mp_obj_t nus_uuid_str = mp_obj_new_str(NUS_UUID, strlen(NUS_UUID), false); - mp_obj_t nus_uuid_obj = bleio_uuid_type.make_new(&bleio_uuid_type, 1, 0, &nus_uuid_str); - mp_obj_t nus_obj = bleio_service_type.make_new(&bleio_service_type, 1, 0, &nus_uuid_obj); - m_nus = MP_OBJ_TO_PTR(nus_obj); - mp_call_function_1(mp_load_attr(device_obj, qstr_from_str("add_service")), nus_obj); - - mp_obj_t tx_uuid_int = mp_obj_new_int(NUS_TX_UUID); - mp_obj_t tx_uuid_obj = bleio_uuid_type.make_new(&bleio_uuid_type, 1, 0, &tx_uuid_int); - mp_obj_t tx_obj = bleio_characteristic_type.make_new(&bleio_characteristic_type, 1, 0, &tx_uuid_obj); - m_tx_chara = MP_OBJ_TO_PTR(tx_obj); - m_tx_chara->uuid->type = UUID_TYPE_128BIT; - m_tx_chara->uuid->uuid_vs_idx = m_nus->uuid->uuid_vs_idx; - m_tx_chara->props.notify = true; - mp_call_function_1(mp_load_attr(nus_obj, qstr_from_str("add_characteristic")), tx_obj); - - mp_obj_t rx_uuid_int = mp_obj_new_int(NUS_RX_UUID); - mp_obj_t rx_uuid_obj = bleio_uuid_type.make_new(&bleio_uuid_type, 1, 0, &rx_uuid_int); - mp_obj_t rx_obj = bleio_characteristic_type.make_new(&bleio_characteristic_type, 1, 0, &rx_uuid_obj); - m_rx_chara = MP_OBJ_TO_PTR(rx_obj); - m_rx_chara->uuid->type = UUID_TYPE_128BIT; - m_rx_chara->uuid->uuid_vs_idx = m_nus->uuid->uuid_vs_idx; - m_rx_chara->props.write = true; - m_rx_chara->props.write_wo_resp = true; - mp_call_function_1(mp_load_attr(nus_obj, qstr_from_str("add_characteristic")), rx_obj); - - mp_call_function_0(mp_load_attr(device_obj, qstr_from_str("start_advertising"))); - - ble_drv_add_event_handler(on_ble_evt, &m_device); - - m_cccd_enabled = false; - - while (!m_cccd_enabled) { - RUN_BACKGROUND_TASKS; - } -} - -bool ble_uart_connected(void) { - return m_device.conn_handle != BLE_CONN_HANDLE_INVALID; -} - -char ble_uart_rx_chr(void) { - while (isBufferEmpty(&m_rx_ring_buffer)) { - RUN_BACKGROUND_TASKS; - } - - uint8_t byte; - bufferRead(&m_rx_ring_buffer, byte); - return (int)byte; -} - -bool ble_uart_stdin_any(void) { - return !isBufferEmpty(&m_rx_ring_buffer); -} - -void ble_uart_stdout_tx_str(const char *text) { - mp_hal_stdout_tx_strn(text, strlen(text)); -} - -int mp_hal_stdin_rx_chr(void) { - return ble_uart_rx_chr(); -} - -void mp_hal_stdout_tx_strn(const char *str, size_t len) { - size_t send_len; - - while (len > 0) { - if (len >= BLE_GATT_ATT_MTU_DEFAULT - 3) { - send_len = (BLE_GATT_ATT_MTU_DEFAULT - 3); - } else { - send_len = len; - } - - mp_buffer_info_t bufinfo = { - .buf = (uint8_t *)str, - .len = send_len, - }; - - common_hal_bleio_characteristic_write_value(m_tx_chara, &bufinfo); - - len -= send_len; - str += send_len; - } -} - -#endif // CIRCUITPY_CONSOLE_BLE diff --git a/ports/nrf/bluetooth/ringbuffer.h b/ports/nrf/bluetooth/ringbuffer.h deleted file mode 100644 index baf2732931..0000000000 --- a/ports/nrf/bluetooth/ringbuffer.h +++ /dev/null @@ -1,99 +0,0 @@ -/* The MIT License (MIT) - * - * Copyright (c) 2013 Philip Thrasher - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of - * this software and associated documentation files (the "Software"), to deal in - * the Software without restriction, including without limitation the rights to - * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of - * the Software, and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS - * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR - * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * Philip Thrasher's Crazy Awesome Ring Buffer Macros! - * - * Below you will find some naughty macros for easy owning and manipulating - * generic ring buffers. Yes, they are slightly evil in readability, but they - * are really fast, and they work great. - * - * Example usage: - * - * #include - * - * // So we can use this in any method, this gives us a typedef - * // named 'intBuffer'. - * ringBuffer_typedef(int, intBuffer); - * - * int main() { - * // Declare vars. - * intBuffer myBuffer; - * - * bufferInit(myBuffer,1024,int); - * - * // We must have the pointer. All of the macros deal with the pointer. - * // (except for init.) - * intBuffer* myBuffer_ptr; - * myBuffer_ptr = &myBuffer; - * - * // Write two values. - * bufferWrite(myBuffer_ptr,37); - * bufferWrite(myBuffer_ptr,72); - * - * // Read a value into a local variable. - * int first; - * bufferRead(myBuffer_ptr,first); - * assert(first == 37); // true - * - * int second; - * bufferRead(myBuffer_ptr,second); - * assert(second == 72); // true - * - * return 0; - * } - * - */ - -#ifndef _ringbuffer_h -#define _ringbuffer_h - -#define ringBuffer_typedef(T, NAME) \ - typedef struct { \ - int size; \ - volatile int start; \ - volatile int end; \ - T *elems; \ - } NAME - -#define bufferInit(BUF, S, T) \ - BUF.size = S + 1; \ - BUF.start = 0; \ - BUF.end = 0; \ - BUF.elems = (T *)calloc(BUF.size, sizeof(T)) - - -#define bufferDestroy(BUF) free((BUF)->elems) -#define nextStartIndex(BUF) (((BUF)->start + 1) % (BUF)->size) -#define nextEndIndex(BUF) (((BUF)->end + 1) % (BUF)->size) -#define isBufferEmpty(BUF) ((BUF)->end == (BUF)->start) -#define isBufferFull(BUF) (nextEndIndex(BUF) == (BUF)->start) - -#define bufferWrite(BUF, ELEM) \ - (BUF)->elems[(BUF)->end] = ELEM; \ - (BUF)->end = ((BUF)->end + 1) % (BUF)->size; \ - if (isBufferEmpty(BUF)) { \ - (BUF)->start = nextStartIndex(BUF); \ - } - -#define bufferRead(BUF, ELEM) \ - ELEM = (BUF)->elems[(BUF)->start]; \ - (BUF)->start = nextStartIndex(BUF); - -#endif diff --git a/ports/nrf/common-hal/_bleio/Adapter.c b/ports/nrf/common-hal/_bleio/Adapter.c index 3f8fbc3a97..bf92c7b3ba 100644 --- a/ports/nrf/common-hal/_bleio/Adapter.c +++ b/ports/nrf/common-hal/_bleio/Adapter.c @@ -40,7 +40,7 @@ #include "py/gc.h" #include "py/objstr.h" #include "py/runtime.h" -#include "supervisor/shared/bluetooth.h" +#include "supervisor/shared/bluetooth/bluetooth.h" #include "supervisor/shared/safe_mode.h" #include "supervisor/shared/tick.h" #include "supervisor/usb.h" diff --git a/ports/nrf/common-hal/_bleio/Characteristic.c b/ports/nrf/common-hal/_bleio/Characteristic.c index 621d400794..df86654c70 100644 --- a/ports/nrf/common-hal/_bleio/Characteristic.c +++ b/ports/nrf/common-hal/_bleio/Characteristic.c @@ -86,7 +86,8 @@ STATIC void characteristic_gatts_notify_indicate(uint16_t handle, uint16_t conn_ void common_hal_bleio_characteristic_construct(bleio_characteristic_obj_t *self, bleio_service_obj_t *service, uint16_t handle, bleio_uuid_obj_t *uuid, bleio_characteristic_properties_t props, bleio_attribute_security_mode_t read_perm, bleio_attribute_security_mode_t write_perm, - mp_int_t max_length, bool fixed_length, mp_buffer_info_t *initial_value_bufinfo) { + mp_int_t max_length, bool fixed_length, mp_buffer_info_t *initial_value_bufinfo, + const char *user_description) { self->service = service; self->uuid = uuid; self->handle = BLE_GATT_HANDLE_INVALID; @@ -125,7 +126,7 @@ void common_hal_bleio_characteristic_construct(bleio_characteristic_obj_t *self, if (service->is_remote) { self->handle = handle; } else { - common_hal_bleio_service_add_characteristic(self->service, self, initial_value_bufinfo); + common_hal_bleio_service_add_characteristic(self->service, self, initial_value_bufinfo, user_description); } } diff --git a/ports/nrf/common-hal/_bleio/CharacteristicBuffer.c b/ports/nrf/common-hal/_bleio/CharacteristicBuffer.c index b17327da47..ca37f41c1a 100644 --- a/ports/nrf/common-hal/_bleio/CharacteristicBuffer.c +++ b/ports/nrf/common-hal/_bleio/CharacteristicBuffer.c @@ -80,20 +80,34 @@ STATIC bool characteristic_buffer_on_ble_evt(ble_evt_t *ble_evt, void *param) { return true; } +void _common_hal_bleio_characteristic_buffer_construct(bleio_characteristic_buffer_obj_t *self, + bleio_characteristic_obj_t *characteristic, + mp_float_t timeout, + uint8_t *buffer, size_t buffer_size, + void *static_handler_entry) { + + self->characteristic = characteristic; + self->timeout_ms = timeout * 1000; + + self->ringbuf.buf = (uint8_t *)buffer; + self->ringbuf.size = buffer_size; + self->ringbuf.iget = 0; + self->ringbuf.iput = 0; + + if (static_handler_entry != NULL) { + ble_drv_add_event_handler_entry((ble_drv_evt_handler_entry_t *)static_handler_entry, characteristic_buffer_on_ble_evt, self); + } else { + ble_drv_add_event_handler(characteristic_buffer_on_ble_evt, self); + } +} + // Assumes that timeout and buffer_size have been validated before call. void common_hal_bleio_characteristic_buffer_construct(bleio_characteristic_buffer_obj_t *self, bleio_characteristic_obj_t *characteristic, mp_float_t timeout, size_t buffer_size) { - - self->characteristic = characteristic; - self->timeout_ms = timeout * 1000; - // This is a macro. - // true means long-lived, so it won't be moved. - ringbuf_alloc(&self->ringbuf, buffer_size, true); - - ble_drv_add_event_handler(characteristic_buffer_on_ble_evt, self); - + uint8_t *buffer = m_malloc(buffer_size, true); + _common_hal_bleio_characteristic_buffer_construct(self, characteristic, timeout, buffer, buffer_size, NULL); } uint32_t common_hal_bleio_characteristic_buffer_read(bleio_characteristic_buffer_obj_t *self, uint8_t *data, size_t len, int *errcode) { diff --git a/ports/nrf/common-hal/_bleio/Connection.c b/ports/nrf/common-hal/_bleio/Connection.c index 335f56c30c..0e7c6a3c54 100644 --- a/ports/nrf/common-hal/_bleio/Connection.c +++ b/ports/nrf/common-hal/_bleio/Connection.c @@ -311,6 +311,7 @@ bool connection_on_ble_evt(ble_evt_t *ble_evt, void *self_in) { // Did an sd_ble_gatts_sys_attr_set() with the stored sys_attr values. // Indicate ATTR table change because we may have reloaded since the peer last // connected. + mp_printf(&mp_plat_print, "restore sys attrs\n"); sd_ble_gatts_service_changed(self->conn_handle, 0xC, 0xFFFF); } else { // No matching bonding found, so use fresh system attributes. @@ -533,7 +534,8 @@ STATIC void on_char_discovery_rsp(ble_gattc_evt_char_disc_rsp_t *response, bleio characteristic, m_char_discovery_service, gattc_char->handle_value, uuid, props, SECURITY_MODE_OPEN, SECURITY_MODE_OPEN, GATT_MAX_DATA_LENGTH, false, // max_length, fixed_length: values don't matter for gattc - mp_const_empty_bytes); + mp_const_empty_bytes, + NULL); mp_obj_list_append(MP_OBJ_FROM_PTR(m_char_discovery_service->characteristic_list), MP_OBJ_FROM_PTR(characteristic)); diff --git a/ports/nrf/common-hal/_bleio/PacketBuffer.c b/ports/nrf/common-hal/_bleio/PacketBuffer.c index e721cfb53b..4a123a7c00 100644 --- a/ports/nrf/common-hal/_bleio/PacketBuffer.c +++ b/ports/nrf/common-hal/_bleio/PacketBuffer.c @@ -173,6 +173,23 @@ STATIC bool packet_buffer_on_ble_server_evt(ble_evt_t *ble_evt, void *param) { } break; } + case BLE_GAP_EVT_CONN_SEC_UPDATE: { // 0x1a + if (self->conn_handle != BLE_CONN_HANDLE_INVALID) { + break; + } + uint16_t conn_handle = ble_evt->evt.gatts_evt.conn_handle; + // Check to see if the bond restored the HVX state. + uint16_t cccd; + ble_gatts_value_t value; + value.len = sizeof(uint16_t); + value.offset = 0; + value.p_value = (uint8_t *)&cccd; + sd_ble_gatts_value_get(conn_handle, self->characteristic->cccd_handle, &value); + if (cccd & BLE_GATT_HVX_NOTIFICATION) { + self->conn_handle = conn_handle; + } + break; + } case BLE_GAP_EVT_CONNECTED: break; case BLE_GAP_EVT_DISCONNECTED: diff --git a/ports/nrf/common-hal/_bleio/Service.c b/ports/nrf/common-hal/_bleio/Service.c index 5302f25946..0c4ded2d1b 100644 --- a/ports/nrf/common-hal/_bleio/Service.c +++ b/ports/nrf/common-hal/_bleio/Service.c @@ -112,7 +112,8 @@ STATIC void _expand_range(uint16_t new_value, uint16_t *start, uint16_t *end) { void common_hal_bleio_service_add_characteristic(bleio_service_obj_t *self, bleio_characteristic_obj_t *characteristic, - mp_buffer_info_t *initial_value_bufinfo) { + mp_buffer_info_t *initial_value_bufinfo, + const char *user_description) { ble_gatts_char_md_t char_md = { .char_props.broadcast = (characteristic->props & CHAR_PROP_BROADCAST) ? 1 : 0, .char_props.read = (characteristic->props & CHAR_PROP_READ) ? 1 : 0, @@ -142,6 +143,19 @@ void common_hal_bleio_service_add_characteristic(bleio_service_obj_t *self, char_md.p_cccd_md = &cccd_md; } + ble_gatts_attr_md_t user_desc_md; + if (user_description != NULL && strlen(user_description) > 0) { + BLE_GAP_CONN_SEC_MODE_SET_OPEN(&user_desc_md.read_perm); + // If the description is on the Python heap, then have the SD copy it. If not, assume it's + // static and will live for longer than the SD. + user_desc_md.vloc = gc_nbytes(user_description) > 0 ? BLE_GATTS_VLOC_STACK : BLE_GATTS_VLOC_USER; + char_md.p_user_desc_md = &user_desc_md; + char_md.p_char_user_desc = (const uint8_t *)user_description; + char_md.char_user_desc_max_size = strlen(user_description); + char_md.char_user_desc_size = strlen(user_description); + mp_printf(&mp_plat_print, "user description %s\n", user_description); + } + bleio_attribute_gatts_set_security_mode(&char_attr_md.read_perm, characteristic->read_perm); bleio_attribute_gatts_set_security_mode(&char_attr_md.write_perm, characteristic->write_perm); #if CIRCUITPY_VERBOSE_BLE diff --git a/ports/nrf/common-hal/_bleio/__init__.c b/ports/nrf/common-hal/_bleio/__init__.c index 3c919c9ffe..de1641eb9d 100644 --- a/ports/nrf/common-hal/_bleio/__init__.c +++ b/ports/nrf/common-hal/_bleio/__init__.c @@ -36,7 +36,7 @@ #include "shared-bindings/_bleio/Descriptor.h" #include "shared-bindings/_bleio/Service.h" #include "shared-bindings/_bleio/UUID.h" -#include "supervisor/shared/bluetooth.h" +#include "supervisor/shared/bluetooth/bluetooth.h" #include "common-hal/_bleio/__init__.h" #include "common-hal/_bleio/bonding.h" diff --git a/ports/nrf/mpconfigport.mk b/ports/nrf/mpconfigport.mk index 6bd63bcd72..87d79cb0e5 100644 --- a/ports/nrf/mpconfigport.mk +++ b/ports/nrf/mpconfigport.mk @@ -50,6 +50,9 @@ CIRCUITPY_ALARM ?= 1 # Turn on the BLE file service CIRCUITPY_BLE_FILE_SERVICE ?= 1 +# Turn on the BLE serial service +CIRCUITPY_SERIAL_BLE ?= 1 + CIRCUITPY_COMPUTED_GOTO_SAVE_SPACE ?= 1 # nRF52840-specific diff --git a/py/circuitpy_mpconfig.mk b/py/circuitpy_mpconfig.mk index f4075f0d5a..0e2c577c69 100644 --- a/py/circuitpy_mpconfig.mk +++ b/py/circuitpy_mpconfig.mk @@ -136,9 +136,6 @@ CFLAGS += -DCIRCUITPY_DIGITALIO=$(CIRCUITPY_DIGITALIO) CIRCUITPY_COMPUTED_GOTO_SAVE_SPACE ?= 0 CFLAGS += -DCIRCUITPY_COMPUTED_GOTO_SAVE_SPACE=$(CIRCUITPY_COMPUTED_GOTO_SAVE_SPACE) -CIRCUITPY_CONSOLE_BLE ?= 0 -CFLAGS += -DCIRCUITPY_CONSOLE_BLE=$(CIRCUITPY_CONSOLE_BLE) - CIRCUITPY_CONSOLE_UART ?= 0 CFLAGS += -DCIRCUITPY_CONSOLE_UART=$(CIRCUITPY_CONSOLE_UART) @@ -255,15 +252,6 @@ CFLAGS += -DCIRCUITPY_RANDOM=$(CIRCUITPY_RANDOM) CIRCUITPY_RE ?= $(CIRCUITPY_FULL_BUILD) CFLAGS += -DCIRCUITPY_RE=$(CIRCUITPY_RE) -CIRCUITPY_REPL_BLE ?= 0 -CFLAGS += -DCIRCUITPY_REPL_BLE=$(CIRCUITPY_REPL_BLE) - -CIRCUITPY_REPL_UART ?= 0 -CFLAGS += -DCIRCUITPY_REPL_UART=$(CIRCUITPY_REPL_UART) - -CIRCUITPY_REPL_USB ?= 1 -CFLAGS += -DCIRCUITPY_REPL_USB=$(CIRCUITPY_REPL_USB) - # Should busio.I2C() check for pullups? # Some boards in combination with certain peripherals may not want this. CIRCUITPY_REQUIRE_I2C_PULLUPS ?= 1 @@ -299,6 +287,9 @@ CFLAGS += -DCIRCUITPY_SDCARDIO=$(CIRCUITPY_SDCARDIO) CIRCUITPY_SDIOIO ?= 0 CFLAGS += -DCIRCUITPY_SDIOIO=$(CIRCUITPY_SDIOIO) +CIRCUITPY_SERIAL_BLE ?= 0 +CFLAGS += -DCIRCUITPY_SERIAL_BLE=$(CIRCUITPY_SERIAL_BLE) + CIRCUITPY_SHARPDISPLAY ?= $(CIRCUITPY_FRAMEBUFFERIO) CFLAGS += -DCIRCUITPY_SHARPDISPLAY=$(CIRCUITPY_SHARPDISPLAY) diff --git a/shared-bindings/_bleio/Characteristic.c b/shared-bindings/_bleio/Characteristic.c index 3c9e189d48..b6b6ee69d4 100644 --- a/shared-bindings/_bleio/Characteristic.c +++ b/shared-bindings/_bleio/Characteristic.c @@ -45,7 +45,11 @@ //| ... //| -//| def add_to_service(self, service: Service, uuid: UUID, *, properties: int = 0, read_perm: int = Attribute.OPEN, write_perm: int = Attribute.OPEN, max_length: int = 20, fixed_length: bool = False, initial_value: Optional[ReadableBuffer] = None) -> Characteristic: +//| def add_to_service(self, service: Service, uuid: UUID, *, properties: int = 0, +//| read_perm: int = Attribute.OPEN, write_perm: int = Attribute.OPEN, +//| max_length: int = 20, fixed_length: bool = False, +//| initial_value: Optional[ReadableBuffer] = None, +//| user_description: Optional[str] = None) -> Characteristic: //| """Create a new Characteristic object, and add it to this Service. //| //| :param Service service: The service that will provide this characteristic @@ -65,6 +69,7 @@ //| :param bool fixed_length: True if the characteristic value is of fixed length. //| :param ~_typing.ReadableBuffer initial_value: The initial value for this characteristic. If not given, will be //| filled with zeros. +//| :param str user_description: User friendly description of the characteristic //| //| :return: the new Characteristic.""" //| ... @@ -73,7 +78,7 @@ STATIC mp_obj_t bleio_characteristic_add_to_service(size_t n_args, const mp_obj_ // class is arg[0], which we can ignore. enum { ARG_service, ARG_uuid, ARG_properties, ARG_read_perm, ARG_write_perm, - ARG_max_length, ARG_fixed_length, ARG_initial_value }; + ARG_max_length, ARG_fixed_length, ARG_initial_value, ARG_user_description }; static const mp_arg_t allowed_args[] = { { MP_QSTR_service, MP_ARG_REQUIRED | MP_ARG_OBJ }, { MP_QSTR_uuid, MP_ARG_REQUIRED | MP_ARG_OBJ }, @@ -83,6 +88,7 @@ STATIC mp_obj_t bleio_characteristic_add_to_service(size_t n_args, const mp_obj_ { MP_QSTR_max_length, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 20} }, { MP_QSTR_fixed_length, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false} }, { MP_QSTR_initial_value, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, + { MP_QSTR_user_description, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, }; mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; @@ -132,6 +138,11 @@ STATIC mp_obj_t bleio_characteristic_add_to_service(size_t n_args, const mp_obj_ mp_raise_ValueError(translate("initial_value length is wrong")); } + const char *user_description = NULL; + if (args[ARG_user_description].u_obj != mp_const_none) { + user_description = mp_obj_str_get_str(args[ARG_user_description].u_obj); + } + bleio_characteristic_obj_t *characteristic = m_new_obj(bleio_characteristic_obj_t); characteristic->base.type = &bleio_characteristic_type; @@ -140,7 +151,8 @@ STATIC mp_obj_t bleio_characteristic_add_to_service(size_t n_args, const mp_obj_ common_hal_bleio_characteristic_construct( characteristic, MP_OBJ_TO_PTR(service_obj), 0, MP_OBJ_TO_PTR(uuid_obj), properties, read_perm, write_perm, - max_length, fixed_length, &initial_value_bufinfo); + max_length, fixed_length, &initial_value_bufinfo, + user_description); return MP_OBJ_FROM_PTR(characteristic); } diff --git a/shared-bindings/_bleio/Characteristic.h b/shared-bindings/_bleio/Characteristic.h index 5807abf418..349dd5fdcf 100644 --- a/shared-bindings/_bleio/Characteristic.h +++ b/shared-bindings/_bleio/Characteristic.h @@ -44,7 +44,7 @@ extern bleio_uuid_obj_t *common_hal_bleio_characteristic_get_uuid(bleio_characte extern size_t common_hal_bleio_characteristic_get_max_length(bleio_characteristic_obj_t *self); extern size_t common_hal_bleio_characteristic_get_value(bleio_characteristic_obj_t *self, uint8_t *buf, size_t len); extern void common_hal_bleio_characteristic_add_descriptor(bleio_characteristic_obj_t *self, bleio_descriptor_obj_t *descriptor); -extern void common_hal_bleio_characteristic_construct(bleio_characteristic_obj_t *self, bleio_service_obj_t *service, uint16_t handle, bleio_uuid_obj_t *uuid, bleio_characteristic_properties_t props, bleio_attribute_security_mode_t read_perm, bleio_attribute_security_mode_t write_perm, mp_int_t max_length, bool fixed_length, mp_buffer_info_t *initial_value_bufinfo); +extern void common_hal_bleio_characteristic_construct(bleio_characteristic_obj_t *self, bleio_service_obj_t *service, uint16_t handle, bleio_uuid_obj_t *uuid, bleio_characteristic_properties_t props, bleio_attribute_security_mode_t read_perm, bleio_attribute_security_mode_t write_perm, mp_int_t max_length, bool fixed_length, mp_buffer_info_t *initial_value_bufinfo, const char *user_description); extern void common_hal_bleio_characteristic_set_cccd(bleio_characteristic_obj_t *self, bool notify, bool indicate); extern void common_hal_bleio_characteristic_set_value(bleio_characteristic_obj_t *self, mp_buffer_info_t *bufinfo); diff --git a/shared-bindings/_bleio/CharacteristicBuffer.h b/shared-bindings/_bleio/CharacteristicBuffer.h index e82e96ca96..1fd9dd838c 100644 --- a/shared-bindings/_bleio/CharacteristicBuffer.h +++ b/shared-bindings/_bleio/CharacteristicBuffer.h @@ -31,7 +31,15 @@ extern const mp_obj_type_t bleio_characteristic_buffer_type; -extern void common_hal_bleio_characteristic_buffer_construct(bleio_characteristic_buffer_obj_t *self, bleio_characteristic_obj_t *characteristic, mp_float_t timeout, size_t buffer_size); +void _common_hal_bleio_characteristic_buffer_construct(bleio_characteristic_buffer_obj_t *self, + bleio_characteristic_obj_t *characteristic, + mp_float_t timeout, + uint8_t *buffer, size_t buffer_size, + void *static_handler_entry); +void common_hal_bleio_characteristic_buffer_construct(bleio_characteristic_buffer_obj_t *self, + bleio_characteristic_obj_t *characteristic, + mp_float_t timeout, + size_t buffer_size); uint32_t common_hal_bleio_characteristic_buffer_read(bleio_characteristic_buffer_obj_t *self, uint8_t *data, size_t len, int *errcode); uint32_t common_hal_bleio_characteristic_buffer_rx_characters_available(bleio_characteristic_buffer_obj_t *self); void common_hal_bleio_characteristic_buffer_clear_rx_buffer(bleio_characteristic_buffer_obj_t *self); diff --git a/shared-bindings/_bleio/Service.h b/shared-bindings/_bleio/Service.h index b2a35793fb..eb1d29b57e 100644 --- a/shared-bindings/_bleio/Service.h +++ b/shared-bindings/_bleio/Service.h @@ -44,6 +44,6 @@ extern bleio_uuid_obj_t *common_hal_bleio_service_get_uuid(bleio_service_obj_t * extern mp_obj_tuple_t *common_hal_bleio_service_get_characteristics(bleio_service_obj_t *self); extern bool common_hal_bleio_service_get_is_remote(bleio_service_obj_t *self); extern bool common_hal_bleio_service_get_is_secondary(bleio_service_obj_t *self); -extern void common_hal_bleio_service_add_characteristic(bleio_service_obj_t *self, bleio_characteristic_obj_t *characteristic, mp_buffer_info_t *initial_value_bufinfo); +extern void common_hal_bleio_service_add_characteristic(bleio_service_obj_t *self, bleio_characteristic_obj_t *characteristic, mp_buffer_info_t *initial_value_bufinfo, const char *user_description); #endif // MICROPY_INCLUDED_SHARED_BINDINGS_BLEIO_SERVICE_H diff --git a/supervisor/shared/bluetooth/bluetooth.c b/supervisor/shared/bluetooth/bluetooth.c new file mode 100644 index 0000000000..abfbecd8c8 --- /dev/null +++ b/supervisor/shared/bluetooth/bluetooth.c @@ -0,0 +1,232 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Scott Shawcroft for Adafruit Industries + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include + +#include "supervisor/shared/bluetooth/bluetooth.h" + +#include "shared-bindings/_bleio/__init__.h" +#include "shared-bindings/_bleio/Adapter.h" +#if defined(CIRCUITPY_BOOT_BUTTON) +#include "shared-bindings/digitalio/DigitalInOut.h" +#endif +#include "shared-bindings/microcontroller/Processor.h" +#include "shared-bindings/microcontroller/ResetReason.h" +#include "shared-module/storage/__init__.h" + +#include "bluetooth/ble_drv.h" + +#include "common-hal/_bleio/__init__.h" + +#include "supervisor/shared/status_leds.h" +#include "supervisor/shared/tick.h" + +#include "py/mpstate.h" + +#if CIRCUITPY_BLE_FILE_SERVICE +#include "supervisor/shared/bluetooth/file_transfer.h" +#endif + +#if CIRCUITPY_SERIAL_BLE +#include "supervisor/shared/bluetooth/serial.h" +#endif + +// This standard advertisement advertises the CircuitPython editing service and a CIRCUITPY short name. +const uint8_t public_advertising_data[] = { 0x02, 0x01, 0x06, // 0-2 Flags + 0x02, 0x0a, 0xd8, // 3-5 TX power level -40 + #if CIRCUITPY_BLE_FILE_SERVICE + 0x03, 0x02, 0xbb, 0xfe, // 6 - 9 Incomplete service list (File Transfer service) + #endif + 0x0e, 0xff, 0x22, 0x08, // 10 - 13 Adafruit Manufacturer Data + 0x0a, 0x04, 0x00, // 14 - 16 Creator ID / Creation ID + CIRCUITPY_CREATOR_ID & 0xff, // 17 - 20 Creator ID + (CIRCUITPY_CREATOR_ID >> 8) & 0xff, + (CIRCUITPY_CREATOR_ID >> 16) & 0xff, + (CIRCUITPY_CREATOR_ID >> 24) & 0xff, + CIRCUITPY_CREATION_ID & 0xff, // 21 - 24 Creation ID + (CIRCUITPY_CREATION_ID >> 8) & 0xff, + (CIRCUITPY_CREATION_ID >> 16) & 0xff, + (CIRCUITPY_CREATION_ID >> 24) & 0xff, + 0x05, 0x08, 0x43, 0x49, 0x52, 0x43 // 25 - 31 - Short name +}; +const uint8_t private_advertising_data[] = { 0x02, 0x01, 0x06, // 0-2 Flags + 0x02, 0x0a, 0x00 // 3-5 TX power level 0 +}; +// This scan response advertises the full CIRCUITPYXXXX device name. +uint8_t circuitpython_scan_response_data[] = { + 0x0e, 0x09, 0x43, 0x49, 0x52, 0x43, 0x55, 0x49, 0x54, 0x50, 0x59, 0x00, 0x00, 0x00, 0x00, + #if CIRCUITPY_SERIAL_BLE + 0x06, 0x10, 0x9e, 0xca, 0xdc, 0x24, 0x0e, 0xe5, 0xa9, 0xe0, 0x93, 0xf3, 0xa3, 0xb5, 0x00, 0x00, 0x40, 0x6e, + #endif +}; + +bool boot_in_discovery_mode = false; +bool advertising = false; + +STATIC void supervisor_bluetooth_start_advertising(void) { + bool is_connected = common_hal_bleio_adapter_get_connected(&common_hal_bleio_adapter_obj); + if (is_connected) { + return; + } + bool bonded = common_hal_bleio_adapter_is_bonded_to_central(&common_hal_bleio_adapter_obj); + #if CIRCUITPY_USB + // Don't advertise when we have USB instead of BLE. + if (!bonded && !boot_in_discovery_mode) { + // mp_printf(&mp_plat_print, "skipping advertising\n"); + return; + } + #endif + uint32_t timeout = 0; + float interval = 0.1f; + int tx_power = 0; + const uint8_t *adv = private_advertising_data; + size_t adv_len = sizeof(private_advertising_data); + const uint8_t *scan_response = NULL; + size_t scan_response_len = 0; + // Advertise with less power when doing so publicly to reduce who can hear us. This will make it + // harder for someone with bad intentions to pair from a distance. + if (!bonded) { + tx_power = -40; + adv = public_advertising_data; + adv_len = sizeof(public_advertising_data); + scan_response = circuitpython_scan_response_data; + scan_response_len = sizeof(circuitpython_scan_response_data); + } + uint32_t status = _common_hal_bleio_adapter_start_advertising(&common_hal_bleio_adapter_obj, + true, + bonded, // Advertise anonymously if we are bonded + timeout, + interval, + adv, + adv_len, + scan_response, + scan_response_len, + tx_power, + NULL); + // This may fail if we are already advertising. + advertising = status == NRF_SUCCESS; +} + +#define BLE_DISCOVERY_DATA_GUARD 0xbb0000bb +#define BLE_DISCOVERY_DATA_GUARD_MASK 0xff0000ff + +void supervisor_bluetooth_init(void) { + uint32_t reset_state = port_get_saved_word(); + uint32_t ble_mode = 0; + if ((reset_state & BLE_DISCOVERY_DATA_GUARD_MASK) == BLE_DISCOVERY_DATA_GUARD) { + ble_mode = (reset_state & ~BLE_DISCOVERY_DATA_GUARD_MASK) >> 8; + } + const mcu_reset_reason_t reset_reason = common_hal_mcu_processor_get_reset_reason(); + boot_in_discovery_mode = false; + if (reset_reason != RESET_REASON_POWER_ON && + reset_reason != RESET_REASON_RESET_PIN && + reset_reason != RESET_REASON_UNKNOWN && + reset_reason != RESET_REASON_SOFTWARE) { + return; + } + + if (ble_mode == 0) { + port_set_saved_word(BLE_DISCOVERY_DATA_GUARD | (0x01 << 8)); + } + // Wait for a while to allow for reset. + + #ifdef CIRCUITPY_BOOT_BUTTON + digitalio_digitalinout_obj_t boot_button; + common_hal_digitalio_digitalinout_construct(&boot_button, CIRCUITPY_BOOT_BUTTON); + common_hal_digitalio_digitalinout_switch_to_input(&boot_button, PULL_UP); + #endif + uint64_t start_ticks = supervisor_ticks_ms64(); + uint64_t diff = 0; + if (ble_mode != 0) { + #ifdef CIRCUITPY_STATUS_LED + new_status_color(0x0000ff); + #endif + common_hal_bleio_adapter_erase_bonding(&common_hal_bleio_adapter_obj); + boot_in_discovery_mode = true; + reset_state = 0x0; + } + while (diff < 1000) { + #ifdef CIRCUITPY_STATUS_LED + // Blink on for 100, off for 100, on for 100, off for 100 and on for 200 + bool led_on = ble_mode != 0 || (diff % 150) <= 75; + if (led_on) { + new_status_color(0x0000ff); + } else { + new_status_color(BLACK); + } + #endif + #ifdef CIRCUITPY_BOOT_BUTTON + if (!common_hal_digitalio_digitalinout_get_value(&boot_button)) { + boot_in_discovery_mode = true; + break; + } + #endif + diff = supervisor_ticks_ms64() - start_ticks; + } + #if CIRCUITPY_STATUS_LED + new_status_color(BLACK); + status_led_deinit(); + #endif + port_set_saved_word(reset_state); +} + +STATIC bool was_connected; +void supervisor_bluetooth_background(void) { + bool is_connected = common_hal_bleio_adapter_get_connected(&common_hal_bleio_adapter_obj); + if (was_connected && !is_connected) { + #if CIRCUITPY_BLE_FILE_SERVICE + supervisor_bluetooth_file_transfer_disconnected(); + #endif + } + was_connected = is_connected; + if (!is_connected) { + supervisor_bluetooth_start_advertising(); + return; + } + + #if CIRCUITPY_BLE_FILE_SERVICE + supervisor_bluetooth_file_transfer_background(); + #endif +} + +void supervisor_start_bluetooth(void) { + #if !CIRCUITPY_BLE_FILE_SERVICE && !CIRCUITPY_SERIAL_BLE + return; + #endif + + common_hal_bleio_adapter_set_enabled(&common_hal_bleio_adapter_obj, true); + + #if CIRCUITPY_BLE_FILE_SERVICE + supervisor_start_bluetooth_file_transfer(); + #endif + + #if CIRCUITPY_SERIAL_BLE + supervisor_start_bluetooth_serial(); + #endif + + // Kick off advertisments + supervisor_bluetooth_background(); +} diff --git a/supervisor/shared/bluetooth.h b/supervisor/shared/bluetooth/bluetooth.h similarity index 92% rename from supervisor/shared/bluetooth.h rename to supervisor/shared/bluetooth/bluetooth.h index 3784463af3..a8f3e8b159 100644 --- a/supervisor/shared/bluetooth.h +++ b/supervisor/shared/bluetooth/bluetooth.h @@ -29,12 +29,8 @@ #include -#include "shared-bindings/_bleio/Characteristic.h" - void supervisor_bluetooth_background(void); void supervisor_bluetooth_init(void); void supervisor_start_bluetooth(void); -extern bleio_characteristic_obj_t supervisor_ble_transfer_characteristic; - #endif // MICROPY_INCLUDED_SUPERVISOR_SHARED_BLUETOOTH_H diff --git a/supervisor/shared/bluetooth.c b/supervisor/shared/bluetooth/file_transfer.c similarity index 66% rename from supervisor/shared/bluetooth.c rename to supervisor/shared/bluetooth/file_transfer.c index f89d8e3334..dcd3d36e78 100644 --- a/supervisor/shared/bluetooth.c +++ b/supervisor/shared/bluetooth/file_transfer.c @@ -24,23 +24,8 @@ * THE SOFTWARE. */ -#if !CIRCUITPY_BLE_FILE_SERVICE - -void supervisor_bluetooth_init(void) { -} - -void supervisor_start_bluetooth(void) { -} - -void supervisor_bluetooth_background(void) { -} - -#else - #include -#include "supervisor/shared/bluetooth.h" - #include "extmod/vfs.h" #include "extmod/vfs_fat.h" @@ -50,11 +35,6 @@ void supervisor_bluetooth_background(void) { #include "shared-bindings/_bleio/PacketBuffer.h" #include "shared-bindings/_bleio/Service.h" #include "shared-bindings/_bleio/UUID.h" -#if defined(CIRCUITPY_BOOT_BUTTON) -#include "shared-bindings/digitalio/DigitalInOut.h" -#endif -#include "shared-bindings/microcontroller/Processor.h" -#include "shared-bindings/microcontroller/ResetReason.h" #include "shared-module/storage/__init__.h" #include "bluetooth/ble_drv.h" @@ -62,169 +42,34 @@ void supervisor_bluetooth_background(void) { #include "common-hal/_bleio/__init__.h" #include "supervisor/shared/autoreload.h" -#include "supervisor/shared/status_leds.h" +#include "supervisor/shared/bluetooth/file_transfer_protocol.h" #include "supervisor/shared/tick.h" #include "supervisor/usb.h" #include "py/mpstate.h" -bleio_service_obj_t supervisor_ble_service; -bleio_uuid_obj_t supervisor_ble_service_uuid; -bleio_characteristic_obj_t supervisor_ble_version_characteristic; -bleio_uuid_obj_t supervisor_ble_version_uuid; -bleio_characteristic_obj_t supervisor_ble_transfer_characteristic; -bleio_uuid_obj_t supervisor_ble_transfer_uuid; +STATIC bleio_service_obj_t supervisor_ble_service; +STATIC bleio_uuid_obj_t supervisor_ble_service_uuid; +STATIC bleio_characteristic_obj_t supervisor_ble_version_characteristic; +STATIC bleio_uuid_obj_t supervisor_ble_version_uuid; +STATIC bleio_characteristic_obj_t supervisor_ble_transfer_characteristic; +STATIC bleio_uuid_obj_t supervisor_ble_transfer_uuid; // This is the base UUID for the file transfer service. const uint8_t file_transfer_base_uuid[16] = {0x72, 0x65, 0x66, 0x73, 0x6e, 0x61, 0x72, 0x54, 0x65, 0x6c, 0x69, 0x46, 0x00, 0x00, 0xaf, 0xad }; -// This standard advertisement advertises the CircuitPython editing service and a CIRCUITPY short name. -const uint8_t public_advertising_data[] = { 0x02, 0x01, 0x06, // 0-2 Flags - 0x02, 0x0a, 0xd8, // 3-5 TX power level -40 - 0x03, 0x02, 0xbb, 0xfe, // 6 - 9 Incomplete service list (File Transfer service) - 0x0e, 0xff, 0x22, 0x08, // 10 - 13 Adafruit Manufacturer Data - 0x0a, 0x04, 0x00, // 14 - 16 Creator ID / Creation ID - CIRCUITPY_CREATOR_ID & 0xff, // 17 - 20 Creator ID - (CIRCUITPY_CREATOR_ID >> 8) & 0xff, - (CIRCUITPY_CREATOR_ID >> 16) & 0xff, - (CIRCUITPY_CREATOR_ID >> 24) & 0xff, - CIRCUITPY_CREATION_ID & 0xff, // 21 - 24 Creation ID - (CIRCUITPY_CREATION_ID >> 8) & 0xff, - (CIRCUITPY_CREATION_ID >> 16) & 0xff, - (CIRCUITPY_CREATION_ID >> 24) & 0xff, - 0x05, 0x08, 0x43, 0x49, 0x52, 0x43 // 25 - 31 - Short name -}; -const uint8_t private_advertising_data[] = { 0x02, 0x01, 0x06, // 0-2 Flags - 0x02, 0x0a, 0x00 // 3-5 TX power level 0 -}; -// This scan response advertises the full CIRCUITPYXXXX device name. -uint8_t circuitpython_scan_response_data[15] = {0x0e, 0x09, 0x43, 0x49, 0x52, 0x43, 0x55, 0x49, 0x54, 0x50, 0x59, 0x00, 0x00, 0x00, 0x00}; -mp_obj_list_t service_list; -mp_obj_t service_list_items[1]; -mp_obj_list_t characteristic_list; -mp_obj_t characteristic_list_items[2]; + +STATIC mp_obj_list_t characteristic_list; +STATIC mp_obj_t characteristic_list_items[2]; // 2 * 10 ringbuf packets, 512 for a disk sector and 12 for the file transfer write header. #define PACKET_BUFFER_SIZE (2 * 10 + 512 + 12) // uint32_t so its aligned -uint32_t _buffer[PACKET_BUFFER_SIZE / 4 + 1]; -uint32_t _outgoing1[BLE_GATTS_VAR_ATTR_LEN_MAX / 4]; -uint32_t _outgoing2[BLE_GATTS_VAR_ATTR_LEN_MAX / 4]; -ble_drv_evt_handler_entry_t static_handler_entry; -bleio_packet_buffer_obj_t _transfer_packet_buffer; -bool boot_in_discovery_mode = false; -bool advertising = false; - -STATIC void supervisor_bluetooth_start_advertising(void) { - bool is_connected = common_hal_bleio_adapter_get_connected(&common_hal_bleio_adapter_obj); - if (is_connected) { - return; - } - bool bonded = common_hal_bleio_adapter_is_bonded_to_central(&common_hal_bleio_adapter_obj); - #if CIRCUITPY_USB - // Don't advertise when we have USB instead of BLE. - if (!bonded && !boot_in_discovery_mode) { - // mp_printf(&mp_plat_print, "skipping advertising\n"); - return; - } - #endif - uint32_t timeout = 0; - float interval = 0.1f; - int tx_power = 0; - const uint8_t *adv = private_advertising_data; - size_t adv_len = sizeof(private_advertising_data); - const uint8_t *scan_response = NULL; - size_t scan_response_len = 0; - // Advertise with less power when doing so publicly to reduce who can hear us. This will make it - // harder for someone with bad intentions to pair from a distance. - if (!bonded) { - tx_power = -40; - adv = public_advertising_data; - adv_len = sizeof(public_advertising_data); - scan_response = circuitpython_scan_response_data; - scan_response_len = sizeof(circuitpython_scan_response_data); - } - uint32_t status = _common_hal_bleio_adapter_start_advertising(&common_hal_bleio_adapter_obj, - true, - bonded, // Advertise anonymously if we are bonded - timeout, - interval, - adv, - adv_len, - scan_response, - scan_response_len, - tx_power, - NULL); - // This may fail if we are already advertising. - advertising = status == NRF_SUCCESS; -} - -#define BLE_DISCOVERY_DATA_GUARD 0xbb0000bb -#define BLE_DISCOVERY_DATA_GUARD_MASK 0xff0000ff - -void supervisor_bluetooth_init(void) { - uint32_t reset_state = port_get_saved_word(); - uint32_t ble_mode = 0; - if ((reset_state & BLE_DISCOVERY_DATA_GUARD_MASK) == BLE_DISCOVERY_DATA_GUARD) { - ble_mode = (reset_state & ~BLE_DISCOVERY_DATA_GUARD_MASK) >> 8; - } - const mcu_reset_reason_t reset_reason = common_hal_mcu_processor_get_reset_reason(); - boot_in_discovery_mode = false; - if (reset_reason != RESET_REASON_POWER_ON && - reset_reason != RESET_REASON_RESET_PIN && - reset_reason != RESET_REASON_UNKNOWN && - reset_reason != RESET_REASON_SOFTWARE) { - return; - } - - // ble_mode = 1; - - if (ble_mode == 0) { - port_set_saved_word(BLE_DISCOVERY_DATA_GUARD | (0x01 << 8)); - } - // Wait for a while to allow for reset. - - #ifdef CIRCUITPY_BOOT_BUTTON - digitalio_digitalinout_obj_t boot_button; - common_hal_digitalio_digitalinout_construct(&boot_button, CIRCUITPY_BOOT_BUTTON); - common_hal_digitalio_digitalinout_switch_to_input(&boot_button, PULL_UP); - #endif - uint64_t start_ticks = supervisor_ticks_ms64(); - uint64_t diff = 0; - if (ble_mode != 0) { - #ifdef CIRCUITPY_STATUS_LED - new_status_color(0x0000ff); - #endif - common_hal_bleio_adapter_erase_bonding(&common_hal_bleio_adapter_obj); - boot_in_discovery_mode = true; - reset_state = 0x0; - } - while (diff < 1000) { - #ifdef CIRCUITPY_STATUS_LED - // Blink on for 100, off for 100, on for 100, off for 100 and on for 200 - bool led_on = ble_mode != 0 || (diff % 150) <= 75; - if (led_on) { - new_status_color(0x0000ff); - } else { - new_status_color(BLACK); - } - #endif - #ifdef CIRCUITPY_BOOT_BUTTON - if (!common_hal_digitalio_digitalinout_get_value(&boot_button)) { - boot_in_discovery_mode = true; - break; - } - #endif - diff = supervisor_ticks_ms64() - start_ticks; - } - #if CIRCUITPY_STATUS_LED - new_status_color(BLACK); - status_led_deinit(); - #endif - port_set_saved_word(reset_state); -} - -void supervisor_start_bluetooth(void) { - common_hal_bleio_adapter_set_enabled(&common_hal_bleio_adapter_obj, true); +STATIC uint32_t _buffer[PACKET_BUFFER_SIZE / 4 + 1]; +STATIC uint32_t _outgoing1[BLE_GATTS_VAR_ATTR_LEN_MAX / 4]; +STATIC uint32_t _outgoing2[BLE_GATTS_VAR_ATTR_LEN_MAX / 4]; +STATIC ble_drv_evt_handler_entry_t static_handler_entry; +STATIC bleio_packet_buffer_obj_t _transfer_packet_buffer; +void supervisor_start_bluetooth_file_transfer(void) { supervisor_ble_service_uuid.base.type = &bleio_uuid_type; common_hal_bleio_uuid_construct(&supervisor_ble_service_uuid, 0xfebb, NULL); @@ -249,7 +94,8 @@ void supervisor_start_bluetooth(void) { SECURITY_MODE_NO_ACCESS, 4, // max length true, // fixed length - NULL); // no initial value + NULL, // no initial value + NULL); // no description uint32_t version = 1; mp_buffer_info_t bufinfo; @@ -269,71 +115,24 @@ void supervisor_start_bluetooth(void) { SECURITY_MODE_ENC_NO_MITM, BLE_GATTS_VAR_ATTR_LEN_MAX, // max length false, // fixed length - NULL); // no initial value + NULL, // no initial valuen + NULL); _common_hal_bleio_packet_buffer_construct( &_transfer_packet_buffer, &supervisor_ble_transfer_characteristic, _buffer, PACKET_BUFFER_SIZE, _outgoing1, _outgoing2, BLE_GATTS_VAR_ATTR_LEN_MAX, &static_handler_entry); - - // Kick off advertisments - supervisor_bluetooth_background(); } #define COMMAND_SIZE 1024 #define ANY_COMMAND 0x00 #define THIS_COMMAND 0x01 -#define READ 0x10 -#define READ_DATA 0x11 -#define READ_PACING 0x12 -#define WRITE 0x20 -#define WRITE_PACING 0x21 -#define WRITE_DATA 0x22 -#define DELETE 0x30 -#define DELETE_STATUS 0x31 -#define MKDIR 0x40 -#define MKDIR_STATUS 0x41 -#define LISTDIR 0x50 -#define LISTDIR_ENTRY 0x51 - -#define STATUS_OK 0x01 -#define STATUS_ERROR 0x02 -#define STATUS_ERROR_NO_FILE 0x03 -#define STATUS_ERROR_PROTOCOL 0x04 // Used by read and write. -FIL active_file; - -struct read_command { - uint8_t command; - uint8_t reserved; - uint16_t path_length; - uint32_t chunk_offset; - uint32_t chunk_size; - uint8_t path[]; -}; - -struct read_data { - uint8_t command; - uint8_t status; - uint16_t reserved; - uint32_t chunk_offset; - uint32_t total_length; - uint32_t data_size; - uint8_t data[]; -}; - -struct read_pacing { - uint8_t command; - uint8_t status; - uint16_t reserved; - uint32_t chunk_offset; - uint32_t chunk_size; -}; - -uint8_t _process_read(const uint8_t *raw_buf, size_t command_len) { +STATIC FIL active_file; +STATIC uint8_t _process_read(const uint8_t *raw_buf, size_t command_len) { struct read_command *command = (struct read_command *)raw_buf; size_t header_size = 12; size_t response_size = 16; @@ -390,7 +189,7 @@ uint8_t _process_read(const uint8_t *raw_buf, size_t command_len) { return READ_PACING; } -uint8_t _process_read_pacing(const uint8_t *command, size_t command_len) { +STATIC uint8_t _process_read_pacing(const uint8_t *command, size_t command_len) { size_t response_size = 4 * sizeof(uint32_t); uint32_t response[response_size / sizeof(uint32_t)]; uint8_t *response_bytes = (uint8_t *)response; @@ -429,35 +228,9 @@ uint8_t _process_read_pacing(const uint8_t *command, size_t command_len) { } // Used by write and write data to know when the write is complete. -size_t total_write_length; +STATIC size_t total_write_length; -struct write_command { - uint8_t command; - uint8_t reserved; - uint16_t path_length; - uint32_t offset; - uint32_t total_length; - uint8_t path[]; -}; - -struct write_data { - uint8_t command; - uint8_t status; - uint16_t reserved; - uint32_t offset; - uint32_t data_size; - uint8_t data[]; -}; - -struct write_pacing { - uint8_t command; - uint8_t status; - uint16_t reserved; - uint32_t offset; - uint32_t free_space; -}; - -uint8_t _process_write(const uint8_t *raw_buf, size_t command_len) { +STATIC uint8_t _process_write(const uint8_t *raw_buf, size_t command_len) { struct write_command *command = (struct write_command *)raw_buf; size_t header_size = 12; struct write_pacing response; @@ -508,7 +281,7 @@ uint8_t _process_write(const uint8_t *raw_buf, size_t command_len) { return WRITE_DATA; } -uint8_t _process_write_data(const uint8_t *raw_buf, size_t command_len) { +STATIC uint8_t _process_write_data(const uint8_t *raw_buf, size_t command_len) { struct write_data *command = (struct write_data *)raw_buf; size_t header_size = 12; struct write_pacing response; @@ -561,28 +334,16 @@ uint8_t _process_write_data(const uint8_t *raw_buf, size_t command_len) { return WRITE_DATA; } -struct delete_command { - uint8_t command; - uint8_t reserved; - uint16_t path_length; - uint8_t path[]; -}; - -struct delete_response { - uint8_t command; - uint8_t status; -}; - -uint8_t _process_delete(const uint8_t *raw_buf, size_t command_len) { +STATIC uint8_t _process_delete(const uint8_t *raw_buf, size_t command_len) { const struct delete_command *command = (struct delete_command *)raw_buf; size_t header_size = 4; - struct delete_response response; + struct delete_status response; response.command = DELETE_STATUS; response.status = STATUS_OK; if (command->path_length > (COMMAND_SIZE - header_size - 1)) { // -1 for the null we'll write // TODO: throw away any more packets of path. response.status = STATUS_ERROR; - common_hal_bleio_packet_buffer_write(&_transfer_packet_buffer, (const uint8_t *)&response, sizeof(struct delete_response), NULL, 0); + common_hal_bleio_packet_buffer_write(&_transfer_packet_buffer, (const uint8_t *)&response, sizeof(struct delete_status), NULL, 0); return ANY_COMMAND; } // We need to receive another packet to have the full path. @@ -596,32 +357,20 @@ uint8_t _process_delete(const uint8_t *raw_buf, size_t command_len) { if (result != FR_OK) { response.status = STATUS_ERROR; } - common_hal_bleio_packet_buffer_write(&_transfer_packet_buffer, (const uint8_t *)&response, sizeof(struct delete_response), NULL, 0); + common_hal_bleio_packet_buffer_write(&_transfer_packet_buffer, (const uint8_t *)&response, sizeof(struct delete_status), NULL, 0); return ANY_COMMAND; } -struct mkdir_command { - uint8_t command; - uint8_t reserved; - uint16_t path_length; - uint8_t path[]; -}; - -struct mkdir_response { - uint8_t command; - uint8_t status; -}; - -uint8_t _process_mkdir(const uint8_t *raw_buf, size_t command_len) { +STATIC uint8_t _process_mkdir(const uint8_t *raw_buf, size_t command_len) { const struct mkdir_command *command = (struct mkdir_command *)raw_buf; size_t header_size = 4; - struct mkdir_response response; + struct mkdir_status response; response.command = MKDIR_STATUS; response.status = STATUS_OK; if (command->path_length > (COMMAND_SIZE - header_size - 1)) { // -1 for the null we'll write // TODO: throw away any more packets of path. response.status = STATUS_ERROR; - common_hal_bleio_packet_buffer_write(&_transfer_packet_buffer, (const uint8_t *)&response, sizeof(struct mkdir_response), NULL, 0); + common_hal_bleio_packet_buffer_write(&_transfer_packet_buffer, (const uint8_t *)&response, sizeof(struct mkdir_status), NULL, 0); return ANY_COMMAND; } // We need to receive another packet to have the full path. @@ -636,29 +385,11 @@ uint8_t _process_mkdir(const uint8_t *raw_buf, size_t command_len) { if (result != FR_OK) { response.status = STATUS_ERROR; } - common_hal_bleio_packet_buffer_write(&_transfer_packet_buffer, (const uint8_t *)&response, sizeof(struct mkdir_response), NULL, 0); + common_hal_bleio_packet_buffer_write(&_transfer_packet_buffer, (const uint8_t *)&response, sizeof(struct mkdir_status), NULL, 0); return ANY_COMMAND; } -struct listdir_command { - uint8_t command; - uint8_t reserved; - uint16_t path_length; - uint8_t path[]; -}; - -struct listdir_entry { - uint8_t command; - uint8_t status; - uint16_t path_length; - uint32_t entry_number; - uint32_t entry_count; - uint32_t flags; - uint32_t file_size; - uint8_t path[]; -}; - -uint8_t _process_listdir(uint8_t *raw_buf, size_t command_len) { +STATIC uint8_t _process_listdir(uint8_t *raw_buf, size_t command_len) { const struct listdir_command *command = (struct listdir_command *)raw_buf; struct listdir_entry *entry = (struct listdir_entry *)raw_buf; size_t header_size = 4; @@ -740,22 +471,10 @@ uint8_t _process_listdir(uint8_t *raw_buf, size_t command_len) { // Background state that must live across background calls. After the _process // helpers to force them to not use them. -uint8_t current_command[COMMAND_SIZE] __attribute__ ((aligned(4))); -volatile size_t current_offset; -uint8_t next_command; -bool was_connected; -void supervisor_bluetooth_background(void) { - bool is_connected = common_hal_bleio_adapter_get_connected(&common_hal_bleio_adapter_obj); - if (was_connected && !is_connected) { - f_close(&active_file); - } - was_connected = is_connected; - if (!is_connected) { - next_command = 0; - supervisor_bluetooth_start_advertising(); - return; - } - +STATIC uint8_t current_command[COMMAND_SIZE] __attribute__ ((aligned(4))); +STATIC volatile size_t current_offset; +STATIC uint8_t next_command; +void supervisor_bluetooth_file_transfer_background(void) { mp_int_t size = 1; while (size > 0) { size = common_hal_bleio_packet_buffer_readinto(&_transfer_packet_buffer, current_command + current_offset, COMMAND_SIZE - current_offset); @@ -810,4 +529,7 @@ void supervisor_bluetooth_background(void) { } } -#endif // #else +void supervisor_bluetooth_file_transfer_disconnected(void) { + next_command = ANY_COMMAND; + f_close(&active_file); +} diff --git a/ports/nrf/bluetooth/ble_uart.h b/supervisor/shared/bluetooth/file_transfer.h similarity index 73% rename from ports/nrf/bluetooth/ble_uart.h rename to supervisor/shared/bluetooth/file_transfer.h index d86e6293ae..e27924e7fa 100644 --- a/ports/nrf/bluetooth/ble_uart.h +++ b/supervisor/shared/bluetooth/file_transfer.h @@ -3,7 +3,7 @@ * * The MIT License (MIT) * - * Copyright (c) 2017 Glenn Ruben Bakke + * Copyright (c) 2021 Scott Shawcroft for Adafruit Industries * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -24,17 +24,13 @@ * THE SOFTWARE. */ -#ifndef MICROPY_INCLUDED_NRF_BLUETOOTH_BLE_UART_H -#define MICROPY_INCLUDED_NRF_BLUETOOTH_BLE_UART_H +#ifndef MICROPY_INCLUDED_SUPERVISOR_SHARED_BLUETOOTH_FILE_TRANSFER_H +#define MICROPY_INCLUDED_SUPERVISOR_SHARED_BLUETOOTH_FILE_TRANSFER_H #include -#include "ble_drv.h" +void supervisor_bluetooth_file_transfer_background(void); +void supervisor_start_bluetooth_file_transfer(void); +void supervisor_bluetooth_file_transfer_disconnected(void); -void ble_uart_init(void); -bool ble_uart_connected(void); -char ble_uart_rx_chr(void); -bool ble_uart_stdin_any(void); -void ble_uart_stdout_tx_str(const char *text); - -#endif // MICROPY_INCLUDED_NRF_BLUETOOTH_BLE_UART_H +#endif // MICROPY_INCLUDED_SUPERVISOR_SHARED_BLUETOOTH_FILE_TRANSFER_H diff --git a/supervisor/shared/bluetooth/file_transfer_protocol.h b/supervisor/shared/bluetooth/file_transfer_protocol.h new file mode 100644 index 0000000000..2c8fe1e5e7 --- /dev/null +++ b/supervisor/shared/bluetooth/file_transfer_protocol.h @@ -0,0 +1,149 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Scott Shawcroft for Adafruit Industries + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef MICROPY_INCLUDED_SUPERVISOR_SHARED_BLUETOOTH_FILE_TRANSFER_PROTOCOL_H +#define MICROPY_INCLUDED_SUPERVISOR_SHARED_BLUETOOTH_FILE_TRANSFER_PROTOCOL_H + +#include + +// See https://github.com/adafruit/Adafruit_CircuitPython_BLE_File_Transfer +// for full protocol documentation and a Python client API. + +// 0x00 - 0x0f are never used by the protocol as a command +#define READ 0x10 +struct read_command { + uint8_t command; + uint8_t reserved; + uint16_t path_length; + uint32_t chunk_offset; + uint32_t chunk_size; + uint8_t path[]; +}; + +#define READ_DATA 0x11 +struct read_data { + uint8_t command; + uint8_t status; + uint16_t reserved; + uint32_t chunk_offset; + uint32_t total_length; + uint32_t data_size; + uint8_t data[]; +}; + +#define READ_PACING 0x12 +struct read_pacing { + uint8_t command; + uint8_t status; + uint16_t reserved; + uint32_t chunk_offset; + uint32_t chunk_size; +}; + +#define WRITE 0x20 +struct write_command { + uint8_t command; + uint8_t reserved; + uint16_t path_length; + uint32_t offset; + uint32_t total_length; + uint8_t path[]; +}; + +#define WRITE_PACING 0x21 +struct write_pacing { + uint8_t command; + uint8_t status; + uint16_t reserved; + uint32_t offset; + uint32_t free_space; +}; + +#define WRITE_DATA 0x22 +struct write_data { + uint8_t command; + uint8_t status; + uint16_t reserved; + uint32_t offset; + uint32_t data_size; + uint8_t data[]; +}; + +#define DELETE 0x30 +struct delete_command { + uint8_t command; + uint8_t reserved; + uint16_t path_length; + uint8_t path[]; +}; + +#define DELETE_STATUS 0x31 +struct delete_status { + uint8_t command; + uint8_t status; +}; + +#define MKDIR 0x40 +struct mkdir_command { + uint8_t command; + uint8_t reserved; + uint16_t path_length; + uint8_t path[]; +}; + +#define MKDIR_STATUS 0x41 +struct mkdir_status { + uint8_t command; + uint8_t status; +}; + +#define LISTDIR 0x50 +struct listdir_command { + uint8_t command; + uint8_t reserved; + uint16_t path_length; + uint8_t path[]; +}; + +#define LISTDIR_ENTRY 0x51 +struct listdir_entry { + uint8_t command; + uint8_t status; + uint16_t path_length; + uint32_t entry_number; + uint32_t entry_count; + uint32_t flags; + uint32_t file_size; + uint8_t path[]; +}; + +#define STATUS_OK 0x01 +#define STATUS_ERROR 0x02 +#define STATUS_ERROR_NO_FILE 0x03 +#define STATUS_ERROR_PROTOCOL 0x04 + + +#endif // MICROPY_INCLUDED_SUPERVISOR_SHARED_BLUETOOTH_FILE_TRANSFER_PROTOCOL_H diff --git a/supervisor/shared/bluetooth/serial.c b/supervisor/shared/bluetooth/serial.c new file mode 100644 index 0000000000..e1d26b89ce --- /dev/null +++ b/supervisor/shared/bluetooth/serial.c @@ -0,0 +1,153 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2021 Scott Shawcroft for Adafruit Industries + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include + +#include "shared-bindings/_bleio/__init__.h" +#include "shared-bindings/_bleio/Adapter.h" +#include "shared-bindings/_bleio/Characteristic.h" +#include "shared-bindings/_bleio/CharacteristicBuffer.h" +#include "shared-bindings/_bleio/PacketBuffer.h" +#include "shared-bindings/_bleio/Service.h" +#include "shared-bindings/_bleio/UUID.h" +#include "shared-module/storage/__init__.h" + +#include "common-hal/_bleio/__init__.h" + +#include "py/mpstate.h" + +STATIC bleio_service_obj_t supervisor_ble_serial_service; +STATIC bleio_uuid_obj_t supervisor_ble_serial_service_uuid; +STATIC bleio_characteristic_obj_t supervisor_ble_rx_characteristic; +STATIC bleio_uuid_obj_t supervisor_ble_rx_uuid; +STATIC bleio_characteristic_obj_t supervisor_ble_tx_characteristic; +STATIC bleio_uuid_obj_t supervisor_ble_tx_uuid; + +// This is the base UUID for the nordic uart service. +const uint8_t nordic_uart_base_uuid[16] = {0x9e, 0xca, 0xdc, 0x24, 0x0e, 0xe5, 0xa9, 0xe0, 0x93, 0xf3, 0xa3, 0xb5, 0x00, 0x00, 0x40, 0x6e }; + +STATIC mp_obj_list_t characteristic_list; +STATIC mp_obj_t characteristic_list_items[2]; + +STATIC uint32_t _outgoing1[BLE_GATTS_VAR_ATTR_LEN_MAX / 4]; +STATIC uint32_t _outgoing2[BLE_GATTS_VAR_ATTR_LEN_MAX / 4]; +STATIC ble_drv_evt_handler_entry_t rx_static_handler_entry; +STATIC ble_drv_evt_handler_entry_t tx_static_handler_entry; +STATIC bleio_packet_buffer_obj_t _tx_packet_buffer; +STATIC uint32_t _incoming[64]; +STATIC bleio_characteristic_buffer_obj_t _rx_buffer; + +void supervisor_start_bluetooth_serial(void) { + supervisor_ble_serial_service_uuid.base.type = &bleio_uuid_type; + common_hal_bleio_uuid_construct(&supervisor_ble_serial_service_uuid, 0x0001, nordic_uart_base_uuid); + + // We know we'll only be N characteristics so we can statically allocate it. + characteristic_list.base.type = &mp_type_list; + characteristic_list.alloc = sizeof(characteristic_list_items) / sizeof(characteristic_list_items[0]); + characteristic_list.len = 0; + characteristic_list.items = characteristic_list_items; + mp_seq_clear(characteristic_list.items, 0, characteristic_list.alloc, sizeof(*characteristic_list.items)); + + _common_hal_bleio_service_construct(&supervisor_ble_serial_service, &supervisor_ble_serial_service_uuid, false /* is secondary */, &characteristic_list); + + // RX + supervisor_ble_rx_uuid.base.type = &bleio_uuid_type; + common_hal_bleio_uuid_construct(&supervisor_ble_rx_uuid, 0x0002, nordic_uart_base_uuid); + common_hal_bleio_characteristic_construct(&supervisor_ble_rx_characteristic, + &supervisor_ble_serial_service, + 0, // handle (for remote only) + &supervisor_ble_rx_uuid, + CHAR_PROP_WRITE | CHAR_PROP_WRITE_NO_RESPONSE, + SECURITY_MODE_NO_ACCESS, + SECURITY_MODE_ENC_NO_MITM, + BLE_GATTS_VAR_ATTR_LEN_MAX, // max length + false, // fixed length + NULL, // no initial value + "CircuitPython Serial"); + + // TX + supervisor_ble_tx_uuid.base.type = &bleio_uuid_type; + common_hal_bleio_uuid_construct(&supervisor_ble_tx_uuid, 0x0003, nordic_uart_base_uuid); + common_hal_bleio_characteristic_construct(&supervisor_ble_tx_characteristic, + &supervisor_ble_serial_service, + 0, // handle (for remote only) + &supervisor_ble_tx_uuid, + CHAR_PROP_NOTIFY, + SECURITY_MODE_ENC_NO_MITM, + SECURITY_MODE_NO_ACCESS, + BLE_GATTS_VAR_ATTR_LEN_MAX, // max length + false, // fixed length + NULL, // no initial value + "CircuitPython Serial"); + + // Use a PacketBuffer to transmit so that we glom characters to transmit + // together and save BLE overhead. + _common_hal_bleio_packet_buffer_construct( + &_tx_packet_buffer, &supervisor_ble_tx_characteristic, + NULL, 0, + _outgoing1, _outgoing2, BLE_GATTS_VAR_ATTR_LEN_MAX, + &tx_static_handler_entry); + + // Use a CharacteristicBuffer for rx so we can read a single character at a time. + _common_hal_bleio_characteristic_buffer_construct(&_rx_buffer, + &supervisor_ble_rx_characteristic, + 0.1f, + (uint8_t *)_incoming, sizeof(_incoming) * sizeof(uint32_t), + &rx_static_handler_entry); +} + +bool ble_serial_connected(void) { + return _tx_packet_buffer.conn_handle != BLE_CONN_HANDLE_INVALID; +} + +bool ble_serial_available(void) { + return !common_hal_bleio_characteristic_buffer_deinited(&_rx_buffer) && common_hal_bleio_characteristic_buffer_rx_characters_available(&_rx_buffer); +} + +char ble_serial_read_char(void) { + if (common_hal_bleio_characteristic_buffer_deinited(&_rx_buffer)) { + return -1; + } + uint8_t c; + common_hal_bleio_characteristic_buffer_read(&_rx_buffer, &c, 1, NULL); + return c; +} + +void ble_serial_write(const char *text, size_t len) { + if (common_hal_bleio_packet_buffer_deinited(&_tx_packet_buffer)) { + return; + } + size_t sent = 0; + while (sent < len) { + uint16_t packet_size = MIN(len, (size_t)common_hal_bleio_packet_buffer_get_outgoing_packet_length(&_tx_packet_buffer)); + mp_int_t written = common_hal_bleio_packet_buffer_write(&_tx_packet_buffer, (const uint8_t *)text + sent, packet_size, NULL, 0); + // Error, so we drop characters to transmit. + if (written < 0) { + break; + } + sent += written; + } +} diff --git a/supervisor/shared/bluetooth/serial.h b/supervisor/shared/bluetooth/serial.h new file mode 100644 index 0000000000..ac96a67f40 --- /dev/null +++ b/supervisor/shared/bluetooth/serial.h @@ -0,0 +1,39 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2019 Scott Shawcroft for Adafruit Industries + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef MICROPY_INCLUDED_SUPERVISOR_SHARED_BLUETOOTH_SERIAL_H +#define MICROPY_INCLUDED_SUPERVISOR_SHARED_BLUETOOTH_SERIAL_H + +#include + +void supervisor_start_bluetooth_serial(void); + +bool ble_serial_connected(void); +bool ble_serial_available(void); +char ble_serial_read_char(void); +void ble_serial_write(const char *text, size_t len); + +#endif // MICROPY_INCLUDED_SUPERVISOR_SHARED_BLUETOOTH_SERIAL_H diff --git a/supervisor/shared/serial.c b/supervisor/shared/serial.c index f3a0d5a976..35feacb527 100644 --- a/supervisor/shared/serial.c +++ b/supervisor/shared/serial.c @@ -36,6 +36,10 @@ #include "shared-bindings/microcontroller/Pin.h" #include "shared-module/usb_cdc/__init__.h" +#if CIRCUITPY_SERIAL_BLE +#include "supervisor/shared/bluetooth/serial.h" +#endif + #include "tusb.h" #ifdef NRF_DEBUG_PRINT @@ -93,11 +97,24 @@ bool serial_connected(void) { #if defined(DEBUG_UART_TX) && defined(DEBUG_UART_RX) return true; - #elif CIRCUITPY_USB_CDC - return usb_cdc_console_enabled() && tud_cdc_connected(); - #else - return tud_cdc_connected(); #endif + + #if CIRCUITPY_SERIAL_BLE + if (ble_serial_connected()) { + return true; + } + #endif + + #if CIRCUITPY_USB_CDC + if (usb_cdc_console_enabled() && tud_cdc_connected()) { + return true; + } + #else + if (tud_cdc_connected()) { + return true; + } + #endif + return false; } char serial_read(void) { @@ -117,7 +134,15 @@ char serial_read(void) { char text; common_hal_busio_uart_read(&debug_uart, (uint8_t *)&text, 1, &uart_errcode); return text; - #elif CIRCUITPY_USB_CDC + #endif + + #if CIRCUITPY_SERIAL_BLE + if (ble_serial_available() > 0) { + return ble_serial_read_char(); + } + #endif + + #if CIRCUITPY_USB_CDC if (!usb_cdc_console_enabled()) { return -1; } @@ -133,14 +158,29 @@ bool serial_bytes_available(void) { #endif #if defined(DEBUG_UART_TX) && defined(DEBUG_UART_RX) - return common_hal_busio_uart_rx_characters_available(&debug_uart) || (tud_cdc_available() > 0); - #elif CIRCUITPY_USB_CDC - if (!usb_cdc_console_enabled()) { - return 0; + if (common_hal_busio_uart_rx_characters_available(&debug_uart)) { + return true; } #endif - return tud_cdc_available() > 0; + + #if CIRCUITPY_SERIAL_BLE + if (ble_serial_available()) { + return true; + } + #endif + + #if CIRCUITPY_USB_CDC + if (usb_cdc_console_enabled() && tud_cdc_available() > 0) { + return true; + } + #else + if (tud_cdc_available() > 0) { + return true; + } + #endif + return false; } + void serial_write_substring(const char *text, uint32_t length) { if (length == 0) { return; @@ -156,6 +196,20 @@ void serial_write_substring(const char *text, uint32_t length) { } #endif + #if defined(DEBUG_UART_TX) && defined(DEBUG_UART_RX) + int uart_errcode; + + common_hal_busio_uart_write(&debug_uart, (const uint8_t *)text, length, &uart_errcode); + #endif + + #ifdef NRF_DEBUG_PRINT + _debug_print_substr(text, length); + #endif + + #if CIRCUITPY_SERIAL_BLE + ble_serial_write(text, length); + #endif + #if CIRCUITPY_USB_CDC if (!usb_cdc_console_enabled()) { return; @@ -171,17 +225,6 @@ void serial_write_substring(const char *text, uint32_t length) { } usb_background(); } - - #if defined(DEBUG_UART_TX) && defined(DEBUG_UART_RX) - int uart_errcode; - - common_hal_busio_uart_write(&debug_uart, (const uint8_t *)text, length, &uart_errcode); - #endif - - #ifdef NRF_DEBUG_PRINT - _debug_print_substr(text, length); - #endif - } void serial_write(const char *text) { diff --git a/supervisor/shared/tick.c b/supervisor/shared/tick.c index 6a9a896467..05b728826e 100644 --- a/supervisor/shared/tick.c +++ b/supervisor/shared/tick.c @@ -36,11 +36,6 @@ #include "supervisor/shared/autoreload.h" #include "supervisor/shared/stack.h" -#if CIRCUITPY_BLEIO -#include "supervisor/shared/bluetooth.h" -#include "common-hal/_bleio/__init__.h" -#endif - #if CIRCUITPY_DISPLAYIO #include "shared-module/displayio/__init__.h" #endif diff --git a/supervisor/supervisor.mk b/supervisor/supervisor.mk index d1790d008a..0f53e1ebeb 100644 --- a/supervisor/supervisor.mk +++ b/supervisor/supervisor.mk @@ -28,12 +28,16 @@ SPI_FLASH_FILESYSTEM ?= 0 CFLAGS += -DSPI_FLASH_FILESYSTEM=$(SPI_FLASH_FILESYSTEM) ifeq ($(CIRCUITPY_BLEIO),1) - SRC_SUPERVISOR += supervisor/shared/bluetooth.c + SRC_SUPERVISOR += supervisor/shared/bluetooth/bluetooth.c + CIRCUITPY_CREATOR_ID ?= $(USB_VID) + CIRCUITPY_CREATION_ID ?= $(USB_PID) + CFLAGS += -DCIRCUITPY_CREATOR_ID=$(CIRCUITPY_CREATOR_ID) + CFLAGS += -DCIRCUITPY_CREATION_ID=$(CIRCUITPY_CREATION_ID) ifeq ($(CIRCUITPY_BLE_FILE_SERVICE),1) - CIRCUITPY_CREATOR_ID ?= $(USB_VID) - CIRCUITPY_CREATION_ID ?= $(USB_PID) - CFLAGS += -DCIRCUITPY_CREATOR_ID=$(CIRCUITPY_CREATOR_ID) - CFLAGS += -DCIRCUITPY_CREATION_ID=$(CIRCUITPY_CREATION_ID) + SRC_SUPERVISOR += supervisor/shared/bluetooth/file_transfer.c + endif + ifeq ($(CIRCUITPY_SERIAL_BLE),1) + SRC_SUPERVISOR += supervisor/shared/bluetooth/serial.c endif endif