Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 75 additions & 10 deletions supervisor/shared/bluetooth/bluetooth.c
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,20 @@
#include "supervisor/shared/bluetooth/serial.h"
#endif

// BLE workflow exists if either or both of these are turned on.
#define BLE_WORKFLOW (CIRCUITPY_BLE_FILE_SERVICE || CIRCUITPY_BLE_SERIAL_SERVICE)

#if BLE_WORKFLOW
#include "shared-bindings/_bleio/Characteristic.h"
#include "shared-bindings/_bleio/Service.h"
#include "shared-bindings/_bleio/UUID.h"
#endif

#if CIRCUITPY_STATUS_BAR
#include "supervisor/shared/status_bar.h"
#endif

#if (CIRCUITPY_BLE_FILE_SERVICE || CIRCUITPY_BLE_SERIAL_SERVICE || (CIRCUITPY_WEB_WORKFLOW && CIRCUITPY_WIFI)) && CIRCUITPY_SETTINGS_TOML
#if (BLE_WORKFLOW || (CIRCUITPY_WEB_WORKFLOW && CIRCUITPY_WIFI)) && CIRCUITPY_SETTINGS_TOML
#include "supervisor/shared/settings.h"
#endif

Expand Down Expand Up @@ -67,7 +76,7 @@ const uint8_t private_advertising_data[] = {
// This scan response advertises the full device name (if it fits.)
uint8_t circuitpython_scan_response_data[31];

#if CIRCUITPY_BLE_FILE_SERVICE || CIRCUITPY_BLE_SERIAL_SERVICE
#if BLE_WORKFLOW
static bool boot_in_discovery_mode = false;
static bool advertising = false;
static bool _private_advertising = false;
Expand Down Expand Up @@ -180,13 +189,13 @@ static void supervisor_bluetooth_start_advertising(void) {
advertising = status == 0;
}

#endif // CIRCUITPY_BLE_FILE_SERVICE || CIRCUITPY_BLE_SERIAL_SERVICE
#endif // BLE_WORKFLOW

#define BLE_DISCOVERY_DATA_GUARD 0xbb0000bb
#define BLE_DISCOVERY_DATA_GUARD_MASK 0xff0000ff

void supervisor_bluetooth_init(void) {
#if (CIRCUITPY_BLE_FILE_SERVICE || CIRCUITPY_BLE_SERIAL_SERVICE)
#if BLE_WORKFLOW

#if CIRCUITPY_SETTINGS_TOML
// Check if the user disabled BLE workflow in settings.toml. The default is that it's enabled.
Expand Down Expand Up @@ -297,7 +306,7 @@ void supervisor_bluetooth_init(void) {
}

void supervisor_bluetooth_background(void) {
#if CIRCUITPY_BLE_FILE_SERVICE || CIRCUITPY_BLE_SERIAL_SERVICE
#if BLE_WORKFLOW
if (!ble_started) {
return;
}
Expand Down Expand Up @@ -328,8 +337,21 @@ void supervisor_bluetooth_background(void) {
#endif
}

#if BLE_WORKFLOW
// adafXXXX-4369-7263-7569-74507974686e, stored little-endian. The XXXX bytes are
// filled in by common_hal_bleio_uuid_construct().
const uint8_t circuitpython_base_uuid[16] = {0x6e, 0x68, 0x74, 0x79, 0x50, 0x74, 0x69, 0x75, 0x63, 0x72, 0x69, 0x43, 0x00, 0x00, 0xaf, 0xad };

static bleio_service_obj_t guard_service;
static bleio_uuid_obj_t guard_service_uuid;
static bleio_characteristic_obj_t guard_characteristic;
static bleio_uuid_obj_t guard_characteristic_uuid;
static mp_obj_list_t guard_characteristic_list;
static mp_obj_t guard_characteristic_list_items[1];
#endif

void supervisor_start_bluetooth(void) {
#if CIRCUITPY_BLE_FILE_SERVICE || CIRCUITPY_BLE_SERIAL_SERVICE
#if BLE_WORKFLOW

if (workflow_state != WORKFLOW_ENABLED || ble_started) {
return;
Expand All @@ -345,6 +367,45 @@ void supervisor_start_bluetooth(void) {
supervisor_start_bluetooth_serial();
#endif

// Create an unused service that will be in the GATT table immediately after
// the BLE workflow services. This protects the workflow services from being
// invalidated if user code creates services.
//
// A peripheral reports its last primary service with End Group Handle 0xffff,
// and hosts cache that literally. So a service that user code adds later lands
// inside the cached range of whichever service was last, and hosts treat the
// addition as a change to that service: Chrome invalidates the page's object
// for it (and has no event to report that to JavaScript), and BlueZ drops it
// from its cache entirely. This guard is created last so that it,
// not a workflow service, is the one affected, and file transfer and serial
// keep working. Costs three attribute handles.
guard_service_uuid.base.type = &bleio_uuid_type;
common_hal_bleio_uuid_construct(&guard_service_uuid, 0x0004, circuitpython_base_uuid);
guard_characteristic_list.base.type = &mp_type_list;
guard_characteristic_list.alloc = 1;
guard_characteristic_list.len = 0;
guard_characteristic_list.items = guard_characteristic_list_items;
guard_characteristic_list_items[0] = MP_OBJ_NULL;
guard_service.base.type = &bleio_service_type;
_common_hal_bleio_service_construct(&guard_service, &guard_service_uuid, false /* is secondary */, &guard_characteristic_list);

// One read-only characteristic: we need at least one because on espressif
// a service is not registered with NimBLE until its first characteristic is added.
guard_characteristic_uuid.base.type = &bleio_uuid_type;
common_hal_bleio_uuid_construct(&guard_characteristic_uuid, 0x0005, circuitpython_base_uuid);
guard_characteristic.base.type = &bleio_characteristic_type;
common_hal_bleio_characteristic_construct(&guard_characteristic,
&guard_service,
0, // handle (for remote only)
&guard_characteristic_uuid,
CHAR_PROP_READ,
SECURITY_MODE_ENC_NO_MITM,
SECURITY_MODE_NO_ACCESS,
1, // max length
true, // fixed length
NULL, // no initial value
NULL);

// Mark as started so that the background call does something.
ble_started = true;

Expand All @@ -359,7 +420,7 @@ void supervisor_start_bluetooth(void) {
}

void supervisor_stop_bluetooth(void) {
#if CIRCUITPY_BLE_FILE_SERVICE || CIRCUITPY_BLE_SERIAL_SERVICE
#if BLE_WORKFLOW

if (!ble_started && workflow_state != WORKFLOW_ENABLED) {
return;
Expand All @@ -375,11 +436,15 @@ void supervisor_stop_bluetooth(void) {
supervisor_stop_bluetooth_serial();
#endif

// Tear down the guard service too.
common_hal_bleio_characteristic_deinit(&guard_characteristic);
common_hal_bleio_service_deinit(&guard_service);

#endif
}

void supervisor_bluetooth_enable_workflow(void) {
#if CIRCUITPY_BLE_FILE_SERVICE || CIRCUITPY_BLE_SERIAL_SERVICE
#if BLE_WORKFLOW
if (!ble_workflow_setting || workflow_state == WORKFLOW_DISABLED) {
return;
}
Expand All @@ -388,13 +453,13 @@ void supervisor_bluetooth_enable_workflow(void) {
}

void supervisor_bluetooth_disable_workflow(void) {
#if CIRCUITPY_BLE_FILE_SERVICE || CIRCUITPY_BLE_SERIAL_SERVICE
#if BLE_WORKFLOW
workflow_state = WORKFLOW_DISABLED;
#endif
}

bool supervisor_bluetooth_workflow_is_enabled(void) {
#if CIRCUITPY_BLE_FILE_SERVICE || CIRCUITPY_BLE_SERIAL_SERVICE
#if BLE_WORKFLOW
if (workflow_state == WORKFLOW_ENABLED) {
return true;
}
Expand Down
3 changes: 3 additions & 0 deletions supervisor/shared/bluetooth/bluetooth.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@
#pragma once

#include <stdbool.h>
#include <stdint.h>

extern const uint8_t circuitpython_base_uuid[16];

void supervisor_bluetooth_background(void);
void supervisor_bluetooth_init(void);
Expand Down
4 changes: 1 addition & 3 deletions supervisor/shared/bluetooth/serial.c
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#include "shared-bindings/_bleio/Service.h"
#include "shared-bindings/_bleio/UUID.h"
#include "shared-module/storage/__init__.h"
#include "supervisor/shared/bluetooth/bluetooth.h"
#include "supervisor/shared/bluetooth/serial.h"

#include "common-hal/_bleio/__init__.h"
Expand All @@ -30,9 +31,6 @@ static bleio_uuid_obj_t supervisor_ble_circuitpython_tx_uuid;
static bleio_characteristic_obj_t supervisor_ble_circuitpython_version_characteristic;
static bleio_uuid_obj_t supervisor_ble_circuitpython_version_uuid;

// This is the base UUID for the CircuitPython service.
const uint8_t circuitpython_base_uuid[16] = {0x6e, 0x68, 0x74, 0x79, 0x50, 0x74, 0x69, 0x75, 0x63, 0x72, 0x69, 0x43, 0x00, 0x00, 0xaf, 0xad };

static mp_obj_list_t characteristic_list;
static mp_obj_t characteristic_list_items[3];

Expand Down
Loading