diff --git a/extmod/btstack/modbluetooth_btstack.c b/extmod/btstack/modbluetooth_btstack.c index 03ec12b734..b028a5d1ac 100644 --- a/extmod/btstack/modbluetooth_btstack.c +++ b/extmod/btstack/modbluetooth_btstack.c @@ -271,6 +271,14 @@ STATIC void btstack_packet_handler_att_server(uint8_t packet_type, uint16_t chan } } +#if MICROPY_BLUETOOTH_BTSTACK_ZEPHYR_STATIC_ADDRESS +// During startup, the controller (e.g. Zephyr) might give us a static address that we can use. +STATIC uint8_t controller_static_addr[6] = {0}; +STATIC bool controller_static_addr_available = false; + +STATIC const uint8_t read_static_address_command_complete_prefix[] = { 0x0e, 0x1b, 0x01, 0x09, 0xfc }; +#endif + STATIC void btstack_packet_handler(uint8_t packet_type, uint8_t *packet, uint8_t irq) { DEBUG_printf("btstack_packet_handler(packet_type=%u, packet=%p)\n", packet_type, packet); if (packet_type != HCI_EVENT_PACKET) { @@ -313,6 +321,13 @@ STATIC void btstack_packet_handler(uint8_t packet_type, uint8_t *packet, uint8_t DEBUG_printf(" --> hci transport packet sent\n"); } else if (event_type == HCI_EVENT_COMMAND_COMPLETE) { DEBUG_printf(" --> hci command complete\n"); + #if MICROPY_BLUETOOTH_BTSTACK_ZEPHYR_STATIC_ADDRESS + if (memcmp(packet, read_static_address_command_complete_prefix, sizeof(read_static_address_command_complete_prefix)) == 0) { + DEBUG_printf(" --> static address available\n"); + reverse_48(&packet[7], controller_static_addr); + controller_static_addr_available = true; + } + #endif // MICROPY_BLUETOOTH_BTSTACK_ZEPHYR_STATIC_ADDRESS } else if (event_type == HCI_EVENT_COMMAND_STATUS) { DEBUG_printf(" --> hci command status\n"); } else if (event_type == HCI_EVENT_NUMBER_OF_COMPLETED_PACKETS) { @@ -493,6 +508,60 @@ STATIC void btstack_init_deinit_timeout_handler(btstack_timer_source_t *ds) { mp_bluetooth_btstack_state = MP_BLUETOOTH_BTSTACK_STATE_TIMEOUT; } +STATIC void btstack_static_address_ready(void *arg) { + DEBUG_printf("btstack_static_address_ready.\n"); + *(volatile bool *)arg = true; +} + +STATIC bool set_public_address(void) { + bd_addr_t local_addr; + gap_local_bd_addr(local_addr); + bd_addr_t null_addr = {0}; + if (memcmp(local_addr, null_addr, 6) == 0) { + DEBUG_printf("set_public_address: No public address available.\n"); + return false; + } + DEBUG_printf("set_public_address: Using controller's public address.\n"); + gap_random_address_set_mode(GAP_RANDOM_ADDRESS_TYPE_OFF); + return true; +} + +STATIC void set_random_address(void) { + #if MICROPY_BLUETOOTH_BTSTACK_ZEPHYR_STATIC_ADDRESS + if (controller_static_addr_available) { + DEBUG_printf("set_random_address: Using static address supplied by controller.\n"); + gap_random_address_set(controller_static_addr); + } else + #endif // MICROPY_BLUETOOTH_BTSTACK_ZEPHYR_STATIC_ADDRESS + { + DEBUG_printf("set_random_address: Generating random static address.\n"); + btstack_crypto_random_t sm_crypto_random_request; + bd_addr_t static_addr; + volatile bool ready = false; + btstack_crypto_random_generate(&sm_crypto_random_request, static_addr, 6, &btstack_static_address_ready, (void *)&ready); + while (!ready) { + MICROPY_EVENT_POLL_HOOK + } + DEBUG_printf("set_random_address: Address generated.\n"); + gap_random_address_set(static_addr); + } + + // Wait for the controller to accept this address. + while (true) { + uint8_t addr_type; + bd_addr_t addr; + gap_le_get_own_address(&addr_type, addr); + + bd_addr_t null_addr = {0}; + if (memcmp(addr, null_addr, 6) != 0) { + break; + } + + MICROPY_EVENT_POLL_HOOK + } + DEBUG_printf("set_random_address: Address loaded by controller\n"); +} + int mp_bluetooth_init(void) { DEBUG_printf("mp_bluetooth_init\n"); @@ -505,6 +574,10 @@ int mp_bluetooth_init(void) { btstack_memory_init(); + #if MICROPY_BLUETOOTH_BTSTACK_ZEPHYR_STATIC_ADDRESS + controller_static_addr_available = false; + #endif + MP_STATE_PORT(bluetooth_btstack_root_pointers) = m_new0(mp_bluetooth_btstack_root_pointers_t, 1); mp_bluetooth_gatts_db_create(&MP_STATE_PORT(bluetooth_btstack_root_pointers)->gatts_db); @@ -563,9 +636,23 @@ int mp_bluetooth_init(void) { // Clean up. MP_STATE_PORT(bluetooth_btstack_root_pointers) = NULL; + return MP_ETIMEDOUT; } + DEBUG_printf("mp_bluetooth_init: stack startup complete\n"); + + // At this point if the controller has its own public address, btstack will know this. + // However, if this is not available, then attempt to get a static address: + // - For a Zephyr controller on nRF, a static address will be available during startup. + // - Otherwise we ask the controller to generate a static address for us. + // In either case, calling gap_random_address_set will set the mode to STATIC, and then + // immediately set the address on the controller. We then wait until this address becomes available. + + if (!set_public_address()) { + set_random_address(); + } + #if MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE // Enable GATT_EVENT_NOTIFICATION/GATT_EVENT_INDICATION for all connections and handles. gatt_client_listen_for_characteristic_value_updates(&MP_STATE_PORT(bluetooth_btstack_root_pointers)->notification, &btstack_packet_handler_generic, GATT_CLIENT_ANY_CONNECTION, NULL); @@ -612,8 +699,39 @@ bool mp_bluetooth_is_active(void) { return mp_bluetooth_btstack_state == MP_BLUETOOTH_BTSTACK_STATE_ACTIVE; } -void mp_bluetooth_get_device_addr(uint8_t *addr) { - mp_hal_get_mac(MP_HAL_MAC_BDADDR, addr); +void mp_bluetooth_get_current_address(uint8_t *addr_type, uint8_t *addr) { + if (!mp_bluetooth_is_active()) { + mp_raise_OSError(ERRNO_BLUETOOTH_NOT_ACTIVE); + } + + DEBUG_printf("mp_bluetooth_get_current_address\n"); + gap_le_get_own_address(addr_type, addr); +} + +void mp_bluetooth_set_address_mode(uint8_t addr_mode) { + if (!mp_bluetooth_is_active()) { + mp_raise_OSError(ERRNO_BLUETOOTH_NOT_ACTIVE); + } + + switch (addr_mode) { + case MP_BLUETOOTH_ADDRESS_MODE_PUBLIC: { + DEBUG_printf("mp_bluetooth_set_address_mode: public\n"); + if (!set_public_address()) { + // No public address available. + mp_raise_OSError(MP_EINVAL); + } + break; + } + case MP_BLUETOOTH_ADDRESS_MODE_RANDOM: { + DEBUG_printf("mp_bluetooth_set_address_mode: random\n"); + set_random_address(); + break; + } + case MP_BLUETOOTH_ADDRESS_MODE_RPA: + case MP_BLUETOOTH_ADDRESS_MODE_NRPA: + // Not yet supported. + mp_raise_OSError(MP_EINVAL); + } } size_t mp_bluetooth_gap_get_device_name(const uint8_t **buf) { diff --git a/extmod/modbluetooth.c b/extmod/modbluetooth.c index 82efe49385..a8a762ce32 100644 --- a/extmod/modbluetooth.c +++ b/extmod/modbluetooth.c @@ -308,9 +308,11 @@ STATIC mp_obj_t bluetooth_ble_config(size_t n_args, const mp_obj_t *args, mp_map return mp_obj_new_bytes(buf, len); } case MP_QSTR_mac: { + uint8_t addr_type; uint8_t addr[6]; - mp_bluetooth_get_device_addr(addr); - return mp_obj_new_bytes(addr, MP_ARRAY_SIZE(addr)); + mp_bluetooth_get_current_address(&addr_type, addr); + mp_obj_t items[] = { MP_OBJ_NEW_SMALL_INT(addr_type), mp_obj_new_bytes(addr, MP_ARRAY_SIZE(addr)) }; + return mp_obj_new_tuple(2, items); } case MP_QSTR_rxbuf: return mp_obj_new_int(self->ringbuf.size); @@ -366,6 +368,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_addr_mode: { + mp_int_t addr_mode = mp_obj_get_int(e->value); + mp_bluetooth_set_address_mode(addr_mode); + break; + } default: mp_raise_ValueError(MP_ERROR_TEXT("unknown config param")); } diff --git a/extmod/modbluetooth.h b/extmod/modbluetooth.h index 2c07683124..23ff97bc61 100644 --- a/extmod/modbluetooth.h +++ b/extmod/modbluetooth.h @@ -79,15 +79,6 @@ #define MP_BLUETOOTH_UUID_TYPE_32 (4) #define MP_BLUETOOTH_UUID_TYPE_128 (16) -// Address types (for the addr_type params). -// Ports will need to map these to their own values. -#define MP_BLUETOOTH_ADDR_PUBLIC (0x00) // Public (identity) address. (Same as NimBLE and NRF SD) -#define MP_BLUETOOTH_ADDR_RANDOM_STATIC (0x01) // Random static (identity) address. (Same as NimBLE and NRF SD) -#define MP_BLUETOOTH_ADDR_PUBLIC_ID (0x02) // (Same as NimBLE) -#define MP_BLUETOOTH_ADDR_RANDOM_ID (0x03) // (Same as NimBLE) -#define MP_BLUETOOTH_ADDR_RANDOM_PRIVATE_RESOLVABLE (0x12) // Random private resolvable address. (NRF SD 0x02) -#define MP_BLUETOOTH_ADDR_RANDOM_PRIVATE_NON_RESOLVABLE (0x13) // Random private non-resolvable address. (NRF SD 0x03) - // Event codes for the IRQ handler. #define MP_BLUETOOTH_IRQ_CENTRAL_CONNECT (1) #define MP_BLUETOOTH_IRQ_CENTRAL_DISCONNECT (2) @@ -110,6 +101,11 @@ #define MP_BLUETOOTH_IRQ_GATTC_INDICATE (19) #define MP_BLUETOOTH_IRQ_GATTS_INDICATE_DONE (20) +#define MP_BLUETOOTH_ADDRESS_MODE_PUBLIC (0) +#define MP_BLUETOOTH_ADDRESS_MODE_RANDOM (1) +#define MP_BLUETOOTH_ADDRESS_MODE_RPA (2) +#define MP_BLUETOOTH_ADDRESS_MODE_NRPA (3) + /* These aren't included in the module for space reasons, but can be used in your Python code if necessary. @@ -173,8 +169,11 @@ void mp_bluetooth_deinit(void); // Returns true when the Bluetooth stack is active. bool mp_bluetooth_is_active(void); -// Gets the MAC addr of this device in big-endian format. -void mp_bluetooth_get_device_addr(uint8_t *addr); +// Gets the current address of this device in big-endian format. +void mp_bluetooth_get_current_address(uint8_t *addr_type, uint8_t *addr); + +// Sets the addressing mode to use. +void mp_bluetooth_set_address_mode(uint8_t addr_mode); // Get or set the GAP device name that will be used by service 0x1800, characteristic 0x2a00. size_t mp_bluetooth_gap_get_device_name(const uint8_t **buf); diff --git a/extmod/nimble/modbluetooth_nimble.c b/extmod/nimble/modbluetooth_nimble.c index 61ba3a3aba..1c782943ab 100644 --- a/extmod/nimble/modbluetooth_nimble.c +++ b/extmod/nimble/modbluetooth_nimble.c @@ -50,6 +50,8 @@ #define ERRNO_BLUETOOTH_NOT_ACTIVE MP_ENODEV +STATIC uint8_t nimble_address_mode = BLE_OWN_ADDR_RANDOM; + // Any BLE_HS_xxx code not in this table will default to MP_EIO. STATIC int8_t ble_hs_err_to_errno_table[] = { [BLE_HS_EAGAIN] = MP_EAGAIN, @@ -148,9 +150,26 @@ STATIC void reset_cb(int reason) { (void)reason; } +STATIC bool has_public_address(void) { + return ble_hs_id_copy_addr(BLE_ADDR_PUBLIC, NULL, NULL) == 0; +} + +STATIC void set_random_address(bool nrpa) { + int rc; + (void)rc; + DEBUG_printf("sync_cb: Generating random static address\n"); + ble_addr_t addr; + rc = ble_hs_id_gen_rnd(nrpa ? 1 : 0, &addr); + assert(rc == 0); + rc = ble_hs_id_set_rnd(addr.val); + assert(rc == 0); + rc = ble_hs_util_ensure_addr(1); + assert(rc == 0); +} + STATIC void sync_cb(void) { int rc; - ble_addr_t addr; + (void)rc; DEBUG_printf("sync_cb: state=%d\n", mp_bluetooth_nimble_ble_state); @@ -158,25 +177,11 @@ STATIC void sync_cb(void) { return; } - rc = ble_hs_util_ensure_addr(0); // prefer public address - if (rc != 0) { - // https://mynewt.apache.org/latest/tutorials/ble/eddystone.html#configure-the-nimble-stack-with-an-address - #if MICROPY_PY_BLUETOOTH_RANDOM_ADDR - rc = ble_hs_id_gen_rnd(1, &addr); - assert(rc == 0); - rc = ble_hs_id_set_rnd(addr.val); - assert(rc == 0); - #else - uint8_t addr_be[6]; - mp_hal_get_mac(MP_HAL_MAC_BDADDR, addr_be); - reverse_addr_byte_order(addr.val, addr_be); - // ble_hs_id_set_pub(addr.val); - rc = ble_hs_id_set_rnd(addr.val); - assert(rc == 0); - #endif - - rc = ble_hs_util_ensure_addr(0); // prefer public address - assert(rc == 0); + if (has_public_address()) { + nimble_address_mode = BLE_OWN_ADDR_PUBLIC; + } else { + nimble_address_mode = BLE_OWN_ADDR_RANDOM; + set_random_address(false); } if (MP_BLUETOOTH_DEFAULT_ATTR_LEN > 20) { @@ -412,19 +417,65 @@ bool mp_bluetooth_is_active(void) { return mp_bluetooth_nimble_ble_state == MP_BLUETOOTH_NIMBLE_BLE_STATE_ACTIVE; } -void mp_bluetooth_get_device_addr(uint8_t *addr) { - #if MICROPY_PY_BLUETOOTH_RANDOM_ADDR +void mp_bluetooth_get_current_address(uint8_t *addr_type, uint8_t *addr) { + if (!mp_bluetooth_is_active()) { + mp_raise_OSError(ERRNO_BLUETOOTH_NOT_ACTIVE); + } + uint8_t addr_le[6]; - int rc = ble_hs_id_copy_addr(BLE_ADDR_RANDOM, addr_le, NULL); + + switch (nimble_address_mode) { + case BLE_OWN_ADDR_PUBLIC: + *addr_type = BLE_ADDR_PUBLIC; + break; + case BLE_OWN_ADDR_RANDOM: + *addr_type = BLE_ADDR_RANDOM; + break; + case BLE_OWN_ADDR_RPA_PUBLIC_DEFAULT: + case BLE_OWN_ADDR_RPA_RANDOM_DEFAULT: + default: + // TODO: If RPA/NRPA in use, get the current value. + // Is this even possible in NimBLE? + mp_raise_OSError(MP_EINVAL); + } + + int rc = ble_hs_id_copy_addr(*addr_type, addr_le, NULL); if (rc != 0) { - // Even with MICROPY_PY_BLUETOOTH_RANDOM_ADDR enabled the public address may - // be used instead, in which case there is no random address. - ble_hs_id_copy_addr(BLE_ADDR_PUBLIC, addr_le, NULL); + mp_raise_OSError(MP_EINVAL); } reverse_addr_byte_order(addr, addr_le); - #else - mp_hal_get_mac(MP_HAL_MAC_BDADDR, addr); - #endif +} + +void mp_bluetooth_set_address_mode(uint8_t addr_mode) { + switch (addr_mode) { + case MP_BLUETOOTH_ADDRESS_MODE_PUBLIC: + if (!has_public_address()) { + // No public address available. + mp_raise_OSError(MP_EINVAL); + } + nimble_address_mode = BLE_OWN_ADDR_PUBLIC; + break; + case MP_BLUETOOTH_ADDRESS_MODE_RANDOM: + // Generate an static random address. + set_random_address(false); + nimble_address_mode = BLE_OWN_ADDR_RANDOM; + break; + case MP_BLUETOOTH_ADDRESS_MODE_RPA: + if (has_public_address()) { + nimble_address_mode = BLE_OWN_ADDR_RPA_PUBLIC_DEFAULT; + } else { + // Generate an static random address to use as the identity address. + set_random_address(false); + nimble_address_mode = BLE_OWN_ADDR_RPA_RANDOM_DEFAULT; + } + break; + case MP_BLUETOOTH_ADDRESS_MODE_NRPA: + // Generate an NRPA. + set_random_address(true); + // In NimBLE, NRPA is treated like a static random address that happens to be an NRPA. + nimble_address_mode = BLE_OWN_ADDR_RANDOM; + break; + } } size_t mp_bluetooth_gap_get_device_name(const uint8_t **buf) { @@ -473,19 +524,7 @@ int mp_bluetooth_gap_advertise_start(bool connectable, int32_t interval_us, cons .channel_map = 7, // all 3 channels. }; - ret = ble_gap_adv_start(BLE_OWN_ADDR_PUBLIC, NULL, BLE_HS_FOREVER, &adv_params, gap_event_cb, NULL); - if (ret == 0) { - return 0; - } - ret = ble_gap_adv_start(BLE_OWN_ADDR_RPA_PUBLIC_DEFAULT, NULL, BLE_HS_FOREVER, &adv_params, gap_event_cb, NULL); - if (ret == 0) { - return 0; - } - ret = ble_gap_adv_start(BLE_OWN_ADDR_RPA_RANDOM_DEFAULT, NULL, BLE_HS_FOREVER, &adv_params, gap_event_cb, NULL); - if (ret == 0) { - return 0; - } - ret = ble_gap_adv_start(BLE_OWN_ADDR_RANDOM, NULL, BLE_HS_FOREVER, &adv_params, gap_event_cb, NULL); + ret = ble_gap_adv_start(nimble_address_mode, NULL, BLE_HS_FOREVER, &adv_params, gap_event_cb, NULL); if (ret == 0) { return 0; } @@ -756,7 +795,7 @@ int mp_bluetooth_gap_scan_start(int32_t duration_ms, int32_t interval_us, int32_ .passive = active_scan ? 0 : 1, .filter_duplicates = 0, }; - int err = ble_gap_disc(BLE_OWN_ADDR_PUBLIC, duration_ms, &discover_params, gap_scan_cb, NULL); + int err = ble_gap_disc(nimble_address_mode, duration_ms, &discover_params, gap_scan_cb, NULL); return ble_hs_err_to_errno(err); } @@ -847,7 +886,7 @@ int mp_bluetooth_gap_peripheral_connect(uint8_t addr_type, const uint8_t *addr, }; ble_addr_t addr_nimble = create_nimble_addr(addr_type, addr); - int err = ble_gap_connect(BLE_OWN_ADDR_PUBLIC, &addr_nimble, duration_ms, ¶ms, &peripheral_gap_event_cb, NULL); + int err = ble_gap_connect(nimble_address_mode, &addr_nimble, duration_ms, ¶ms, &peripheral_gap_event_cb, NULL); return ble_hs_err_to_errno(err); } diff --git a/ports/unix/mpbtstackport_common.c b/ports/unix/mpbtstackport_common.c index eb1b1c9c80..621e661f9e 100644 --- a/ports/unix/mpbtstackport_common.c +++ b/ports/unix/mpbtstackport_common.c @@ -93,7 +93,4 @@ void mp_bluetooth_btstack_port_init(void) { #endif } -void mp_hal_get_mac(int idx, uint8_t buf[6]) { -} - #endif // MICROPY_PY_BLUETOOTH && MICROPY_BLUETOOTH_BTSTACK