extmod/modbluetooth: Implement configuration of address modes.

Changes `BLE.config('mac')` to return a tuple (addr_mode, addr).

Adds `BLE.config(addr_mode=...)` to set the addressing mode.
This commit is contained in:
Jim Mussared 2020-08-14 15:47:21 +10:00 committed by Damien George
parent 1b1b22905e
commit c4af714d58
5 changed files with 222 additions and 62 deletions

View File

@ -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) { 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); DEBUG_printf("btstack_packet_handler(packet_type=%u, packet=%p)\n", packet_type, packet);
if (packet_type != HCI_EVENT_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"); DEBUG_printf(" --> hci transport packet sent\n");
} else if (event_type == HCI_EVENT_COMMAND_COMPLETE) { } else if (event_type == HCI_EVENT_COMMAND_COMPLETE) {
DEBUG_printf(" --> hci command complete\n"); 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) { } else if (event_type == HCI_EVENT_COMMAND_STATUS) {
DEBUG_printf(" --> hci command status\n"); DEBUG_printf(" --> hci command status\n");
} else if (event_type == HCI_EVENT_NUMBER_OF_COMPLETED_PACKETS) { } 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; 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) { int mp_bluetooth_init(void) {
DEBUG_printf("mp_bluetooth_init\n"); DEBUG_printf("mp_bluetooth_init\n");
@ -505,6 +574,10 @@ int mp_bluetooth_init(void) {
btstack_memory_init(); 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_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); 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. // Clean up.
MP_STATE_PORT(bluetooth_btstack_root_pointers) = NULL; MP_STATE_PORT(bluetooth_btstack_root_pointers) = NULL;
return MP_ETIMEDOUT; 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 #if MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE
// Enable GATT_EVENT_NOTIFICATION/GATT_EVENT_INDICATION for all connections and handles. // 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); 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; return mp_bluetooth_btstack_state == MP_BLUETOOTH_BTSTACK_STATE_ACTIVE;
} }
void mp_bluetooth_get_device_addr(uint8_t *addr) { void mp_bluetooth_get_current_address(uint8_t *addr_type, uint8_t *addr) {
mp_hal_get_mac(MP_HAL_MAC_BDADDR, 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) { size_t mp_bluetooth_gap_get_device_name(const uint8_t **buf) {

View File

@ -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); return mp_obj_new_bytes(buf, len);
} }
case MP_QSTR_mac: { case MP_QSTR_mac: {
uint8_t addr_type;
uint8_t addr[6]; uint8_t addr[6];
mp_bluetooth_get_device_addr(addr); mp_bluetooth_get_current_address(&addr_type, addr);
return mp_obj_new_bytes(addr, MP_ARRAY_SIZE(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: case MP_QSTR_rxbuf:
return mp_obj_new_int(self->ringbuf.size); 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); m_del(uint8_t, old_irq_data_buf, old_irq_data_alloc);
break; 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: default:
mp_raise_ValueError(MP_ERROR_TEXT("unknown config param")); mp_raise_ValueError(MP_ERROR_TEXT("unknown config param"));
} }

View File

@ -79,15 +79,6 @@
#define MP_BLUETOOTH_UUID_TYPE_32 (4) #define MP_BLUETOOTH_UUID_TYPE_32 (4)
#define MP_BLUETOOTH_UUID_TYPE_128 (16) #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. // Event codes for the IRQ handler.
#define MP_BLUETOOTH_IRQ_CENTRAL_CONNECT (1) #define MP_BLUETOOTH_IRQ_CENTRAL_CONNECT (1)
#define MP_BLUETOOTH_IRQ_CENTRAL_DISCONNECT (2) #define MP_BLUETOOTH_IRQ_CENTRAL_DISCONNECT (2)
@ -110,6 +101,11 @@
#define MP_BLUETOOTH_IRQ_GATTC_INDICATE (19) #define MP_BLUETOOTH_IRQ_GATTC_INDICATE (19)
#define MP_BLUETOOTH_IRQ_GATTS_INDICATE_DONE (20) #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 These aren't included in the module for space reasons, but can be used
in your Python code if necessary. in your Python code if necessary.
@ -173,8 +169,11 @@ void mp_bluetooth_deinit(void);
// Returns true when the Bluetooth stack is active. // Returns true when the Bluetooth stack is active.
bool mp_bluetooth_is_active(void); bool mp_bluetooth_is_active(void);
// Gets the MAC addr of this device in big-endian format. // Gets the current address of this device in big-endian format.
void mp_bluetooth_get_device_addr(uint8_t *addr); 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. // 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); size_t mp_bluetooth_gap_get_device_name(const uint8_t **buf);

View File

@ -50,6 +50,8 @@
#define ERRNO_BLUETOOTH_NOT_ACTIVE MP_ENODEV #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. // Any BLE_HS_xxx code not in this table will default to MP_EIO.
STATIC int8_t ble_hs_err_to_errno_table[] = { STATIC int8_t ble_hs_err_to_errno_table[] = {
[BLE_HS_EAGAIN] = MP_EAGAIN, [BLE_HS_EAGAIN] = MP_EAGAIN,
@ -148,9 +150,26 @@ STATIC void reset_cb(int reason) {
(void)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) { STATIC void sync_cb(void) {
int rc; int rc;
ble_addr_t addr; (void)rc;
DEBUG_printf("sync_cb: state=%d\n", mp_bluetooth_nimble_ble_state); DEBUG_printf("sync_cb: state=%d\n", mp_bluetooth_nimble_ble_state);
@ -158,25 +177,11 @@ STATIC void sync_cb(void) {
return; return;
} }
rc = ble_hs_util_ensure_addr(0); // prefer public address if (has_public_address()) {
if (rc != 0) { nimble_address_mode = BLE_OWN_ADDR_PUBLIC;
// https://mynewt.apache.org/latest/tutorials/ble/eddystone.html#configure-the-nimble-stack-with-an-address } else {
#if MICROPY_PY_BLUETOOTH_RANDOM_ADDR nimble_address_mode = BLE_OWN_ADDR_RANDOM;
rc = ble_hs_id_gen_rnd(1, &addr); set_random_address(false);
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 (MP_BLUETOOTH_DEFAULT_ATTR_LEN > 20) { 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; return mp_bluetooth_nimble_ble_state == MP_BLUETOOTH_NIMBLE_BLE_STATE_ACTIVE;
} }
void mp_bluetooth_get_device_addr(uint8_t *addr) { void mp_bluetooth_get_current_address(uint8_t *addr_type, uint8_t *addr) {
#if MICROPY_PY_BLUETOOTH_RANDOM_ADDR if (!mp_bluetooth_is_active()) {
mp_raise_OSError(ERRNO_BLUETOOTH_NOT_ACTIVE);
}
uint8_t addr_le[6]; 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) { if (rc != 0) {
// Even with MICROPY_PY_BLUETOOTH_RANDOM_ADDR enabled the public address may mp_raise_OSError(MP_EINVAL);
// be used instead, in which case there is no random address.
ble_hs_id_copy_addr(BLE_ADDR_PUBLIC, addr_le, NULL);
} }
reverse_addr_byte_order(addr, addr_le); 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) { 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. .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); ret = ble_gap_adv_start(nimble_address_mode, 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);
if (ret == 0) { if (ret == 0) {
return 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, .passive = active_scan ? 0 : 1,
.filter_duplicates = 0, .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); 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); ble_addr_t addr_nimble = create_nimble_addr(addr_type, addr);
int err = ble_gap_connect(BLE_OWN_ADDR_PUBLIC, &addr_nimble, duration_ms, &params, &peripheral_gap_event_cb, NULL); int err = ble_gap_connect(nimble_address_mode, &addr_nimble, duration_ms, &params, &peripheral_gap_event_cb, NULL);
return ble_hs_err_to_errno(err); return ble_hs_err_to_errno(err);
} }

View File

@ -93,7 +93,4 @@ void mp_bluetooth_btstack_port_init(void) {
#endif #endif
} }
void mp_hal_get_mac(int idx, uint8_t buf[6]) {
}
#endif // MICROPY_PY_BLUETOOTH && MICROPY_BLUETOOTH_BTSTACK #endif // MICROPY_PY_BLUETOOTH && MICROPY_BLUETOOTH_BTSTACK