diff --git a/hal/inc/ble_hal.h b/hal/inc/ble_hal.h index a5e1b16ddb..694dedaa52 100644 --- a/hal/inc/ble_hal.h +++ b/hal/inc/ble_hal.h @@ -112,6 +112,14 @@ typedef enum hal_ble_uuid_type_t { BLE_UUID_TYPE_128BIT_SHORTED = 2 } hal_ble_uuid_type_t; +typedef enum hal_ble_pairing_io_caps_t { + BLE_IO_CAPS_NONE = 0, + BLE_IO_CAPS_DISPLAY_ONLY = 1, + BLE_IO_CAPS_DISPLAY_YESNO = 2, + BLE_IO_CAPS_KEYBOARD_ONLY = 3, + BLE_IO_CAPS_KEYBOARD_DISPLAY = 4 +} hal_ble_pairing_io_caps_t; + typedef enum hal_ble_evts_type_t { BLE_EVT_UNKNOWN = 0x00, BLE_EVT_ADV_STOPPED = 0x01, @@ -125,6 +133,10 @@ typedef enum hal_ble_evts_type_t { BLE_EVT_DATA_WRITTEN = 0x09, BLE_EVT_DATA_NOTIFIED = 0x0A, BLE_EVT_CHAR_CCCD_UPDATED = 0x0B, + BLE_EVT_PAIRING_REQUEST_RECEIVED = 0x0C, + BLE_EVT_PAIRING_PASSKEY_DISPLAY = 0x0D, + BLE_EVT_PAIRING_PASSKEY_INPUT = 0x0E, + BLE_EVT_PAIRING_STATUS_UPDATED = 0x0F, BLE_EVT_MAX = 0x7FFFFFFF } hal_ble_evts_type_t; @@ -273,6 +285,26 @@ typedef struct hal_ble_att_mtu_updated_evt_t { size_t att_mtu_size; } hal_ble_att_mtu_updated_evt_t; +typedef struct hal_ble_pairing_request_evt_t { + // We may expose the LESC, OOB, bond flags set by peer when necessary. + uint8_t reserved[4]; +} hal_ble_pairing_request_evt_t; + +typedef struct hal_ble_pairing_passkey_display_evt_t { + const uint8_t* passkey; +} hal_ble_pairing_passkey_display_evt_t; + +typedef struct hal_ble_pairing_passkey_input_evt_t { + uint8_t reserved[4]; +} hal_ble_pairing_passkey_input_evt_t; + +typedef struct hal_ble_pairing_status_updated_evt_t { + int status; + uint8_t bonded : 1; + uint8_t lesc : 1; + uint8_t reserved[3]; +} hal_ble_pairing_status_updated_evt_t; + typedef struct hal_ble_link_evt_t { hal_ble_evts_type_t type; union { @@ -280,6 +312,10 @@ typedef struct hal_ble_link_evt_t { hal_ble_disconnected_evt_t disconnected; hal_ble_conn_params_updated_evt_t conn_params_updated; hal_ble_att_mtu_updated_evt_t att_mtu_updated; + hal_ble_pairing_request_evt_t pairing_request; + hal_ble_pairing_passkey_display_evt_t passkey_display; + hal_ble_pairing_passkey_input_evt_t passkey_input; + hal_ble_pairing_status_updated_evt_t pairing_status; } params; hal_ble_conn_handle_t conn_handle; } hal_ble_link_evt_t; @@ -357,6 +393,13 @@ typedef struct hal_ble_cccd_config_t { ble_sig_cccd_value_t cccd_value; } hal_ble_cccd_config_t; +typedef struct hal_ble_pairing_config_t { + uint16_t version; + uint16_t size; + hal_ble_pairing_io_caps_t io_caps; + uint8_t reserved[3]; +} hal_ble_pairing_config_t; + /** * Acquires the lock for exclusive access to the BLE API. * @@ -781,6 +824,60 @@ int hal_ble_gap_get_connection_info(hal_ble_conn_handle_t conn_handle, hal_ble_c */ int hal_ble_gap_get_rssi(hal_ble_conn_handle_t conn_handle, void* reserved); +/** + * Set pairing configurations + * + * @param[in] config BLE pairing configurations, @ref hal_ble_pairing_config_t. + * + * @returns 0 on success, system_error_t on error. + */ +int hal_ble_gap_set_pairing_config(const hal_ble_pairing_config_t* config, void* reserved); + +/** + * Start pairing with the peer device. + * + * @param[in] conn_handle BLE connection handle. + * + * @returns 0 on success, system_error_t on error. + */ +int hal_ble_gap_start_pairing(hal_ble_conn_handle_t conn_handle, void* reserved); + +/** + * Reject pairing request. + * + * @param[in] conn_handle BLE connection handle. + * + * @returns 0 on success, system_error_t on error. + */ +int hal_ble_gap_reject_pairing(hal_ble_conn_handle_t conn_handle, void* reserved); + +/** + * Provide the 6-digits passkey that is observed from the peer's display. + * + * @param[in] conn_handle BLE connection handle. + * + * @returns 0 on success, system_error_t on error. + */ +int hal_ble_gap_set_pairing_passkey(hal_ble_conn_handle_t conn_handle, const uint8_t* passkey, void* reserved); + +/** + * Check if pairing with peer device is in progress + * + * @param[in] conn_handle BLE connection handle. + * + * @returns true if pairing, otherwise false. + */ +bool hal_ble_gap_is_pairing(hal_ble_conn_handle_t conn_handle, void* reserved); + +/** + * Check if it is paired with peer device + * + * @param[in] conn_handle BLE connection handle. + * + * @returns true if paired, otherwise false. + */ +bool hal_ble_gap_is_paired(hal_ble_conn_handle_t conn_handle, void* reserved); + /** * Add a BLE service. * diff --git a/hal/inc/ble_hal_defines.h b/hal/inc/ble_hal_defines.h index cea203fb0f..20dc342b3e 100644 --- a/hal/inc/ble_hal_defines.h +++ b/hal/inc/ble_hal_defines.h @@ -34,6 +34,9 @@ * @{ */ #define BLE_SIG_ADDR_LEN (6) +// BLE authentication passkey length +#define BLE_PAIRING_PASSKEY_LEN (6) + // BLE device address type typedef enum ble_sig_addr_type_t { BLE_SIG_ADDR_TYPE_PUBLIC = 0x00, /**< Public (identity) address.*/ diff --git a/hal/inc/hal_dynalib_ble.h b/hal/inc/hal_dynalib_ble.h index 9b0eac1655..a6a0d25348 100644 --- a/hal/inc/hal_dynalib_ble.h +++ b/hal/inc/hal_dynalib_ble.h @@ -97,6 +97,13 @@ DYNALIB_FN(63, hal_ble, hal_ble_cancel_callback_on_adv_events, int(hal_ble_on_ad DYNALIB_FN(64, hal_ble, hal_ble_gatt_server_notify_characteristic_value, ssize_t(hal_ble_attr_handle_t, const uint8_t*, size_t, void*)) DYNALIB_FN(65, hal_ble, hal_ble_gatt_server_indicate_characteristic_value, ssize_t(hal_ble_attr_handle_t, const uint8_t*, size_t, void*)) +DYNALIB_FN(66, hal_ble, hal_ble_gap_set_pairing_config, int(const hal_ble_pairing_config_t*, void*)) +DYNALIB_FN(67, hal_ble, hal_ble_gap_start_pairing, int(hal_ble_conn_handle_t, void*)) +DYNALIB_FN(68, hal_ble, hal_ble_gap_reject_pairing, int(hal_ble_conn_handle_t, void*)) +DYNALIB_FN(69, hal_ble, hal_ble_gap_set_pairing_passkey, int(hal_ble_conn_handle_t, const uint8_t*, void*)) +DYNALIB_FN(70, hal_ble, hal_ble_gap_is_pairing, bool(hal_ble_conn_handle_t, void*)) +DYNALIB_FN(71, hal_ble, hal_ble_gap_is_paired, bool(hal_ble_conn_handle_t, void*)) + DYNALIB_END(hal_ble) #endif /* HAL_PLATFORM_BLE */ diff --git a/hal/src/nRF52840/ble_hal.cpp b/hal/src/nRF52840/ble_hal.cpp index b0eb65d40d..847cea3c1f 100644 --- a/hal/src/nRF52840/ble_hal.cpp +++ b/hal/src/nRF52840/ble_hal.cpp @@ -76,7 +76,7 @@ StaticRecursiveMutex s_bleMutex; bool bleInLockedMode = false; -const auto BLE_CONN_CFG_TAG = 1; +constexpr auto BLE_CONN_CFG_TAG = 1; // BLE service base start handle. const hal_ble_attr_handle_t SERVICES_BASE_START_HANDLE = 0x0001; @@ -84,12 +84,15 @@ const hal_ble_attr_handle_t SERVICES_BASE_START_HANDLE = 0x0001; const hal_ble_attr_handle_t SERVICES_TOP_END_HANDLE = 0xFFFF; // Pool for BLE event data. -const size_t BLE_EVT_DATA_POOL_SIZE = 2048; +constexpr size_t BLE_EVT_DATA_POOL_SIZE = 2048; // Timeout for a BLE procedure. -const uint32_t BLE_OPERATION_TIMEOUT_MS = 30000; +constexpr uint32_t BLE_OPERATION_TIMEOUT_MS = 30000; // Delay for GATT Client to send the ATT MTU exchanging request. -const uint32_t BLE_ATT_MTU_EXCHANGE_DELAY_MS = 800; +constexpr uint32_t BLE_ATT_MTU_EXCHANGE_DELAY_MS = 800; + +constexpr uint8_t BLE_ENC_MIN_KEY_SIZE = 7; +constexpr uint8_t BLE_ENC_MAX_KEY_SIZE = 16; static const uint8_t BleAdvEvtTypeMap[] = { BLE_GAP_ADV_TYPE_CONNECTABLE_SCANNABLE_UNDIRECTED, @@ -401,7 +404,8 @@ class BleObject::ConnectionsManager { connectSemaphore_(nullptr), disconnectSemaphore_(nullptr), attMtuExchangeConnHandle_(BLE_INVALID_CONN_HANDLE), - attMtuExchangeTimer_(nullptr) { + attMtuExchangeTimer_(nullptr), + ioCaps_(BLE_IO_CAPS_NONE) { connectingAddr_ = {}; } ~ConnectionsManager() = default; @@ -420,6 +424,12 @@ class BleObject::ConnectionsManager { int disconnectAll(); int updateConnectionParams(hal_ble_conn_handle_t connHandle, const hal_ble_conn_params_t* params); int getConnectionInfo(hal_ble_conn_handle_t connHandle, hal_ble_conn_info_t* info); + int setPairingConfig(const hal_ble_pairing_config_t* config); + int startPairing(hal_ble_conn_handle_t connHandle); + int rejectPairing(hal_ble_conn_handle_t connHandle); + int setPairingPasskey(hal_ble_conn_handle_t connHandle, const uint8_t* passkey); + bool isPairing(hal_ble_conn_handle_t connHandle); + bool isPaired(hal_ble_conn_handle_t connHandle); bool valid(hal_ble_conn_handle_t connHandle); ssize_t getAttMtu(hal_ble_conn_handle_t connHandle); int setDesiredAttMtu(size_t attMtu); @@ -427,6 +437,7 @@ class BleObject::ConnectionsManager { int processDisconnectedEventFromThread(const ble_evt_t* event); int processConnParamsUpdatedEventFromThread(const ble_evt_t* event); int processAttMtuExchangeEventFromThread(const ble_evt_t* event); + int processSecurityEventFromThread(const ble_evt_t* event); private: struct BleLinkEventHandler { @@ -434,10 +445,23 @@ class BleObject::ConnectionsManager { void* context; }; + enum BlePairingState { + BLE_PAIRING_STATE_NOT_INITIATED, + BLE_PAIRING_STATE_SEC_REQ_RECEIVED, // Central + BLE_PAIRING_STATE_SEC_REQ_SENT, // Peripheral + BLE_PAIRING_STATE_PAIRING_REQ_SENT, // Central + BLE_PAIRING_STATE_PAIRING_REQ_RECEIVED, // Peripheral + BLE_PAIRING_STATE_PASSKEY_DISPLAY, + BLE_PAIRING_STATE_PASSKEY_INPUT, + BLE_PAIRING_STATE_REJECTED, + BLE_PAIRING_STATE_PAIRED + }; + struct BleConnection { hal_ble_conn_info_t info; BleLinkEventHandler handler; // It is used for central link only. bool isMtuExchanged; + BlePairingState pairState; }; int configureAttMtu(hal_ble_conn_handle_t connHandle, size_t effective); @@ -451,6 +475,8 @@ class BleObject::ConnectionsManager { static void onAttMtuExchangeTimerExpired(os_timer_t timer); static ble_gap_conn_params_t toPlatformConnParams(const hal_ble_conn_params_t* halConnParams); static hal_ble_conn_params_t toHalConnParams(const ble_gap_conn_params_t* params); + static uint8_t toPlatformIoCaps(hal_ble_pairing_io_caps_t ioCaps); + static hal_ble_pairing_io_caps_t toHalIoCaps(uint8_t ioCaps); static void onConnParamsUpdateTimerExpired(os_timer_t timer); void notifyLinkEvent(const hal_ble_link_evt_t& event); static void processConnectionEvents(const ble_evt_t* event, void* context); @@ -468,6 +494,7 @@ class BleObject::ConnectionsManager { os_semaphore_t disconnectSemaphore_; /**< Semaphore to wait until connection disconnected. */ volatile hal_ble_conn_handle_t attMtuExchangeConnHandle_; /**< Current handle of the connection to execute ATT_MTU exchange procedure. */ os_timer_t attMtuExchangeTimer_; /**< Timer used for sending the exchanging ATT_MTU request after connection established. */ + hal_ble_pairing_io_caps_t ioCaps_; // GATT Server and GATT client share the same ATT_MTU. static size_t desiredAttMtu_; Vector connections_; @@ -714,6 +741,14 @@ os_thread_return_t BleObject::BleEventDispatcher::processBleEventFromThread(void case BLE_GATTC_EVT_HVX: { BleObject::getInstance().gattc()->processDataNotifiedEventFromThread(event); } + case BLE_GAP_EVT_PASSKEY_DISPLAY: + case BLE_GAP_EVT_AUTH_KEY_REQUEST: + case BLE_GAP_EVT_SEC_REQUEST: + case BLE_GAP_EVT_SEC_PARAMS_REQUEST: + case BLE_GAP_EVT_AUTH_STATUS: + case BLE_GAP_EVT_CONN_SEC_UPDATE: { + BleObject::getInstance().connMgr()->processSecurityEventFromThread(event); + } default: { break; } @@ -874,24 +909,6 @@ void BleObject::BleGap::processBleGapEvents(const ble_evt_t* event, void* contex } break; } - case BLE_GAP_EVT_SEC_PARAMS_REQUEST: { - LOG_DEBUG(TRACE, "BLE GAP event: security parameters request."); - // Pairing is not supported - ret = sd_ble_gap_sec_params_reply(event->evt.gap_evt.conn_handle, BLE_GAP_SEC_STATUS_PAIRING_NOT_SUPP, nullptr, nullptr); - if (ret != NRF_SUCCESS) { - LOG(ERROR, "sd_ble_gap_sec_params_reply() failed: %u", (unsigned)ret); - } - break; - } - case BLE_GAP_EVT_AUTH_STATUS: { - LOG_DEBUG(TRACE, "BLE GAP event: authentication status updated."); - if (event->evt.gap_evt.params.auth_status.auth_status == BLE_GAP_SEC_STATUS_SUCCESS) { - LOG_DEBUG(TRACE, "Authentication succeeded"); - } else { - LOG_DEBUG(WARN, "Authentication failed, status: %d", (int)event->evt.gap_evt.params.auth_status.auth_status); - } - break; - } case BLE_GAP_EVT_TIMEOUT: { if (event->evt.gap_evt.params.timeout.src == BLE_GAP_TIMEOUT_SRC_AUTH_PAYLOAD) { LOG_DEBUG(ERROR, "BLE GAP event: Authenticated payload timeout"); @@ -1866,6 +1883,117 @@ int BleObject::ConnectionsManager::getConnectionInfo(hal_ble_conn_handle_t connH return SYSTEM_ERROR_NONE; } +int BleObject::ConnectionsManager::setPairingConfig(const hal_ble_pairing_config_t* config) { + CHECK_TRUE(config, SYSTEM_ERROR_INVALID_ARGUMENT); + ioCaps_ = config->io_caps; + return SYSTEM_ERROR_NONE; +} + +int BleObject::ConnectionsManager::startPairing(hal_ble_conn_handle_t connHandle) { + BleConnection* connection = fetchConnection(connHandle); + CHECK_TRUE(connection, SYSTEM_ERROR_NOT_FOUND); + CHECK_TRUE(connection->pairState == BLE_PAIRING_STATE_NOT_INITIATED || + connection->pairState == BLE_PAIRING_STATE_REJECTED || + connection->pairState == BLE_PAIRING_STATE_SEC_REQ_RECEIVED, SYSTEM_ERROR_INVALID_STATE); + ble_gap_sec_params_t secParams = {}; + if (ioCaps_ != BLE_IO_CAPS_NONE) { + secParams.mitm = true; + } + if (connection->info.role == BLE_ROLE_CENTRAL) { + secParams.io_caps = toPlatformIoCaps(ioCaps_); + secParams.oob = false; + secParams.min_key_size = BLE_ENC_MIN_KEY_SIZE; + secParams.max_key_size = BLE_ENC_MAX_KEY_SIZE; + secParams.kdist_own.enc = true; + secParams.kdist_own.id = true; + secParams.kdist_peer.enc = true; + secParams.kdist_peer.id = true; + } + LOG_DEBUG(TRACE, "Own security params: %d, %d, %d, %d, %d, %d", secParams.bond, secParams.mitm, secParams.lesc, secParams.io_caps, secParams.keypress, secParams.oob); + int ret = sd_ble_gap_authenticate(connection->info.conn_handle, &secParams); + if (ret != NRF_SUCCESS) { + connection->pairState = BLE_PAIRING_STATE_NOT_INITIATED; + hal_ble_link_evt_t linkEvent = {}; + linkEvent.type = BLE_EVT_PAIRING_STATUS_UPDATED; + linkEvent.conn_handle = connection->info.conn_handle; + linkEvent.params.pairing_status.status = nrf_system_error(ret); + notifyLinkEvent(linkEvent); + return nrf_system_error(ret); + } + if (connection->info.role == BLE_ROLE_CENTRAL) { + connection->pairState = BLE_PAIRING_STATE_PAIRING_REQ_SENT; + LOG_DEBUG(TRACE, "Secure request is sent successfully."); + } else { + connection->pairState = BLE_PAIRING_STATE_SEC_REQ_SENT; + LOG_DEBUG(TRACE, "Pairing request is sent successfully."); + } + return SYSTEM_ERROR_NONE; +} + +int BleObject::ConnectionsManager::rejectPairing(hal_ble_conn_handle_t connHandle) { + BleConnection* connection = fetchConnection(connHandle); + CHECK_TRUE(connection, SYSTEM_ERROR_NOT_FOUND); + switch (connection->pairState) { + case BLE_PAIRING_STATE_SEC_REQ_RECEIVED: { + connection->pairState = BLE_PAIRING_STATE_REJECTED; + int ret = sd_ble_gap_authenticate(connection->info.conn_handle, nullptr); + CHECK_NRF_RETURN(ret, nrf_system_error(ret)); + break; + } + case BLE_PAIRING_STATE_PAIRING_REQ_RECEIVED: { + connection->pairState = BLE_PAIRING_STATE_REJECTED; + int ret = sd_ble_gap_sec_params_reply(connHandle, BLE_GAP_SEC_STATUS_PAIRING_NOT_SUPP, nullptr, nullptr); + CHECK_NRF_RETURN(ret, nrf_system_error(ret)); + break; + } + case BLE_GAP_EVT_PASSKEY_DISPLAY: + case BLE_PAIRING_STATE_PASSKEY_INPUT: { + connection->pairState = BLE_PAIRING_STATE_REJECTED; + int ret = sd_ble_gap_auth_key_reply(connHandle, BLE_GAP_AUTH_KEY_TYPE_NONE, nullptr); + CHECK_NRF_RETURN(ret, nrf_system_error(ret)); + break; + } + default: { + return SYSTEM_ERROR_INVALID_STATE; + } + } + return SYSTEM_ERROR_NONE; +} + +int BleObject::ConnectionsManager::setPairingPasskey(hal_ble_conn_handle_t connHandle, const uint8_t* passkey) { + BleConnection* connection = fetchConnection(connHandle); + CHECK_TRUE(connection, SYSTEM_ERROR_NOT_FOUND); + CHECK_TRUE(connection->pairState == BLE_PAIRING_STATE_PASSKEY_INPUT, SYSTEM_ERROR_INVALID_STATE); + for (uint8_t i = 0; i < 6; i++) { + if (!std::isdigit(passkey[i])) { + sd_ble_gap_auth_key_reply(connHandle, BLE_GAP_AUTH_KEY_TYPE_NONE/*reject*/, nullptr); + LOG(ERROR, "Invalid digits."); + connection->pairState = BLE_PAIRING_STATE_REJECTED; + return SYSTEM_ERROR_INVALID_ARGUMENT; + } + } + int ret = sd_ble_gap_auth_key_reply(connHandle, BLE_GAP_AUTH_KEY_TYPE_PASSKEY, passkey); + if (ret != NRF_SUCCESS) { + connection->pairState = BLE_PAIRING_STATE_NOT_INITIATED; + return nrf_system_error(ret); + } + return SYSTEM_ERROR_NONE; +} + +bool BleObject::ConnectionsManager::isPairing(hal_ble_conn_handle_t connHandle) { + BleConnection* connection = fetchConnection(connHandle); + CHECK_TRUE(connection, false); + return (connection->pairState != BLE_PAIRING_STATE_NOT_INITIATED && + connection->pairState != BLE_PAIRING_STATE_PAIRED && + connection->pairState != BLE_PAIRING_STATE_REJECTED); +} + +bool BleObject::ConnectionsManager::isPaired(hal_ble_conn_handle_t connHandle) { + BleConnection* connection = fetchConnection(connHandle); + CHECK_TRUE(connection, false); + return connection->pairState == BLE_PAIRING_STATE_PAIRED; +} + bool BleObject::ConnectionsManager::valid(hal_ble_conn_handle_t connHandle) { const BleConnection* connection = fetchConnection(connHandle); return connection != nullptr; @@ -1891,6 +2019,26 @@ hal_ble_conn_params_t BleObject::ConnectionsManager::toHalConnParams(const ble_g return halConnParams; } +uint8_t BleObject::ConnectionsManager::toPlatformIoCaps(hal_ble_pairing_io_caps_t ioCaps) { + switch (ioCaps) { + case BLE_IO_CAPS_DISPLAY_ONLY: return BLE_GAP_IO_CAPS_DISPLAY_ONLY; + case BLE_IO_CAPS_DISPLAY_YESNO: return BLE_GAP_IO_CAPS_DISPLAY_YESNO; + case BLE_IO_CAPS_KEYBOARD_ONLY: return BLE_GAP_IO_CAPS_KEYBOARD_ONLY; + case BLE_IO_CAPS_KEYBOARD_DISPLAY: return BLE_GAP_IO_CAPS_KEYBOARD_DISPLAY; + default: return BLE_GAP_IO_CAPS_NONE; + } +} + +hal_ble_pairing_io_caps_t BleObject::ConnectionsManager::toHalIoCaps(uint8_t ioCaps) { + switch (ioCaps) { + case BLE_GAP_IO_CAPS_DISPLAY_ONLY: return BLE_IO_CAPS_DISPLAY_ONLY; + case BLE_GAP_IO_CAPS_DISPLAY_YESNO: return BLE_IO_CAPS_DISPLAY_YESNO; + case BLE_GAP_IO_CAPS_KEYBOARD_ONLY: return BLE_IO_CAPS_KEYBOARD_ONLY; + case BLE_GAP_IO_CAPS_KEYBOARD_DISPLAY: return BLE_IO_CAPS_KEYBOARD_DISPLAY; + default: return BLE_IO_CAPS_NONE; + } +} + ssize_t BleObject::ConnectionsManager::getAttMtu(hal_ble_conn_handle_t connHandle) { const auto connection = fetchConnection(connHandle); CHECK_TRUE(connection, SYSTEM_ERROR_NOT_FOUND); @@ -2059,6 +2207,7 @@ int BleObject::ConnectionsManager::processConnectedEventFromThread(const ble_evt connection.info.address = toHalAddress(connected.peer_addr); connection.info.att_mtu = BLE_DEFAULT_ATT_MTU_SIZE; // Use the default ATT_MTU on connected. connection.isMtuExchanged = false; + connection.pairState = BLE_PAIRING_STATE_NOT_INITIATED; int ret = addConnection(connection); if (ret != SYSTEM_ERROR_NONE) { LOG(ERROR, "Add new connection failed. Disconnects from peer."); @@ -2131,7 +2280,7 @@ int BleObject::ConnectionsManager::processDisconnectedEventFromThread(const ble_ if (disconnectingHandle_ == connection->info.conn_handle) { os_semaphore_give(disconnectSemaphore_, false); } else { - // Notify the connected event. + // Notify the disconnected event. hal_ble_link_evt_t linkEvent = {}; linkEvent.type = BLE_EVT_DISCONNECTED; linkEvent.conn_handle = connection->info.conn_handle; @@ -2142,6 +2291,111 @@ int BleObject::ConnectionsManager::processDisconnectedEventFromThread(const ble_ return SYSTEM_ERROR_NONE; } +int BleObject::ConnectionsManager::processSecurityEventFromThread(const ble_evt_t* event) { + BleConnection* connection = fetchConnection(event->evt.gap_evt.conn_handle); + CHECK_TRUE(connection, SYSTEM_ERROR_NOT_FOUND); + CHECK_TRUE(connection->pairState != BLE_PAIRING_STATE_PAIRED, SYSTEM_ERROR_INVALID_STATE); + if (event->header.evt_id == BLE_GAP_EVT_SEC_REQUEST) { + LOG_DEBUG(TRACE, "BLE event: BLE_GAP_EVT_SEC_REQUEST"); + if (connection->info.role == BLE_ROLE_CENTRAL) { + connection->pairState = BLE_PAIRING_STATE_SEC_REQ_RECEIVED; + hal_ble_link_evt_t linkEvent = {}; + linkEvent.type = BLE_EVT_PAIRING_REQUEST_RECEIVED; + linkEvent.conn_handle = connection->info.conn_handle; + notifyLinkEvent(linkEvent); + // User application may call rejectPairing() to update the pairing state in event handler + if (connection->pairState != BLE_PAIRING_STATE_REJECTED) { + return startPairing(event->evt.gap_evt.conn_handle); + } + } + } else if (event->header.evt_id == BLE_GAP_EVT_SEC_PARAMS_REQUEST) { + LOG_DEBUG(TRACE, "BLE event: BLE_GAP_EVT_SEC_PARAMS_REQUEST"); + // const ble_gap_sec_params_t& peerSecParams = event->evt.gap_evt.params.sec_params_request.peer_params; + // LOG_DEBUG(TRACE, "Peer security params: %d, %d, %d, %d, %d, %d", peerSecParams.bond, peerSecParams.mitm, peerSecParams.lesc, peerSecParams.io_caps, peerSecParams.keypress, peerSecParams.oob); + + int ret = NRF_SUCCESS; + if (connection->info.role == BLE_ROLE_CENTRAL) { + ret = sd_ble_gap_sec_params_reply(event->evt.gap_evt.conn_handle, BLE_GAP_SEC_STATUS_SUCCESS, nullptr, nullptr); + } else { + // If peripheral requests to pair, don't notify the event. + if (connection->pairState != BLE_PAIRING_STATE_SEC_REQ_SENT) { + connection->pairState = BLE_PAIRING_STATE_PAIRING_REQ_RECEIVED; + hal_ble_link_evt_t linkEvent = {}; + linkEvent.type = BLE_EVT_PAIRING_REQUEST_RECEIVED; + linkEvent.conn_handle = connection->info.conn_handle; + notifyLinkEvent(linkEvent); + } + // User application may call rejectPairing() to update the pairing state in event handler + if (connection->pairState != BLE_PAIRING_STATE_REJECTED) { + ble_gap_sec_params_t secParams = {}; + if (ioCaps_ != BLE_IO_CAPS_NONE) { + secParams.mitm = true; + } + secParams.io_caps = toPlatformIoCaps(ioCaps_); + secParams.oob = false; + secParams.min_key_size = BLE_ENC_MIN_KEY_SIZE; + secParams.max_key_size = BLE_ENC_MAX_KEY_SIZE; + secParams.kdist_own.enc = true; + secParams.kdist_own.id = true; + secParams.kdist_peer.enc = true; + secParams.kdist_peer.id = true; + ret = sd_ble_gap_sec_params_reply(event->evt.gap_evt.conn_handle, BLE_GAP_SEC_STATUS_SUCCESS, &secParams, nullptr); + } + } + if (ret != NRF_SUCCESS) { + connection->pairState = BLE_PAIRING_STATE_NOT_INITIATED; + hal_ble_link_evt_t linkEvent = {}; + linkEvent.type = BLE_EVT_PAIRING_STATUS_UPDATED; + linkEvent.conn_handle = connection->info.conn_handle; + linkEvent.params.pairing_status.status = nrf_system_error(ret); + notifyLinkEvent(linkEvent); + LOG(ERROR, "sd_ble_gap_sec_params_reply() failed: %u", (unsigned)ret); + } + } else if (event->header.evt_id == BLE_GAP_EVT_PASSKEY_DISPLAY) { + LOG_DEBUG(TRACE, "BLE event: BLE_GAP_EVT_PASSKEY_DISPLAY"); + connection->pairState = BLE_PAIRING_STATE_PASSKEY_DISPLAY; + const ble_gap_evt_passkey_display_t& passkeyDisplay = event->evt.gap_evt.params.passkey_display; + hal_ble_link_evt_t linkEvent = {}; + linkEvent.type = BLE_EVT_PAIRING_PASSKEY_DISPLAY; + linkEvent.conn_handle = connection->info.conn_handle; + linkEvent.params.passkey_display.passkey = passkeyDisplay.passkey; + notifyLinkEvent(linkEvent); + } else if (event->header.evt_id == BLE_GAP_EVT_AUTH_KEY_REQUEST) { + LOG_DEBUG(TRACE, "BLE event: BLE_GAP_EVT_AUTH_KEY_REQUEST"); + connection->pairState = BLE_PAIRING_STATE_PASSKEY_INPUT; + const ble_gap_evt_auth_key_request_t& keyRequest = event->evt.gap_evt.params.auth_key_request; + if (keyRequest.key_type == BLE_GAP_AUTH_KEY_TYPE_PASSKEY) { + hal_ble_link_evt_t linkEvent = {}; + linkEvent.type = BLE_EVT_PAIRING_PASSKEY_INPUT; + linkEvent.conn_handle = connection->info.conn_handle; + notifyLinkEvent(linkEvent); + } else { + LOG(ERROR, "OOB data not supported!"); + } + } else if (event->header.evt_id == BLE_GAP_EVT_CONN_SEC_UPDATE) { + LOG_DEBUG(TRACE, "BLE event: BLE_GAP_EVT_CONN_SEC_UPDATE"); + // const ble_gap_evt_conn_sec_update_t& secUpdate = event->evt.gap_evt.params.conn_sec_update; + // LOG_DEBUG(TRACE, "Secure mode: %d, level: %d", secUpdate.conn_sec.sec_mode.sm, secUpdate.conn_sec.sec_mode.lv); + } else if (event->header.evt_id == BLE_GAP_EVT_AUTH_STATUS) { + LOG_DEBUG(TRACE, "BLE event: BLE_GAP_EVT_AUTH_STATUS"); + const ble_gap_evt_auth_status_t& authStatus = event->evt.gap_evt.params.auth_status; + LOG_DEBUG(TRACE, "Authen status: 0x%02X, reason: %d, bond: %d, lesc: %d", authStatus.auth_status, authStatus.error_src, authStatus.bonded, authStatus.lesc); + if (authStatus.auth_status == BLE_GAP_SEC_STATUS_SUCCESS) { + connection->pairState = BLE_PAIRING_STATE_PAIRED; + } else { + connection->pairState = BLE_PAIRING_STATE_NOT_INITIATED; + } + hal_ble_link_evt_t linkEvent = {}; + linkEvent.type = BLE_EVT_PAIRING_STATUS_UPDATED; + linkEvent.conn_handle = connection->info.conn_handle; + linkEvent.params.pairing_status.status = authStatus.auth_status; + linkEvent.params.pairing_status.bonded = authStatus.bonded; + linkEvent.params.pairing_status.lesc = authStatus.lesc; + notifyLinkEvent(linkEvent); + } + return SYSTEM_ERROR_NONE; +} + int BleObject::ConnectionsManager::processConnParamsUpdatedEventFromThread(const ble_evt_t* event) { const ble_gap_evt_conn_param_update_t& connParamsUpdate = event->evt.gap_evt.params.conn_param_update; BleConnection* connection = fetchConnection(event->evt.gap_evt.conn_handle); @@ -2251,6 +2505,22 @@ void BleObject::ConnectionsManager::processConnectionEvents(const ble_evt_t* eve BleObject::getInstance().dispatcher()->enqueue(&connParamsUpdateEvent); break; } + case BLE_GAP_EVT_PASSKEY_DISPLAY: + case BLE_GAP_EVT_AUTH_KEY_REQUEST: + case BLE_GAP_EVT_SEC_REQUEST: + case BLE_GAP_EVT_SEC_PARAMS_REQUEST: + case BLE_GAP_EVT_CONN_SEC_UPDATE: + case BLE_GAP_EVT_AUTH_STATUS: { + ble_evt_t* secEvent = (ble_evt_t*)BleObject::getInstance().dispatcher()->allocEventData(sizeof(ble_evt_t)); + if (!secEvent) { + LOG(ERROR, "Allocate memory for BLE event failed."); + SPARK_ASSERT(false); + break; + } + memcpy(secEvent, event, sizeof(ble_evt_t)); + BleObject::getInstance().dispatcher()->enqueue(&secEvent); + break; + } case BLE_GAP_EVT_TIMEOUT: { if (event->evt.gap_evt.params.timeout.src == BLE_GAP_TIMEOUT_SRC_CONN) { LOG_DEBUG(ERROR, "BLE GAP event: Connection timeout"); @@ -3783,6 +4053,48 @@ int hal_ble_gap_get_rssi(hal_ble_conn_handle_t conn_handle, void* reserved) { return 0; } +int hal_ble_gap_set_pairing_config(const hal_ble_pairing_config_t* config, void* reserved) { + BleLock lk; + LOG_DEBUG(TRACE, "hal_ble_gap_set_pairing_config()."); + CHECK_TRUE(BleObject::getInstance().initialized(), SYSTEM_ERROR_INVALID_STATE); + return BleObject::getInstance().connMgr()->setPairingConfig(config); +} + +int hal_ble_gap_start_pairing(hal_ble_conn_handle_t conn_handle, void* reserved) { + BleLock lk; + LOG_DEBUG(TRACE, "hal_ble_gap_start_pairing()."); + CHECK_TRUE(BleObject::getInstance().initialized(), SYSTEM_ERROR_INVALID_STATE); + return BleObject::getInstance().connMgr()->startPairing(conn_handle); +} + +int hal_ble_gap_reject_pairing(hal_ble_conn_handle_t conn_handle, void* reserved) { + BleLock lk; + LOG_DEBUG(TRACE, "hal_ble_gap_reject_pairing()."); + CHECK_TRUE(BleObject::getInstance().initialized(), SYSTEM_ERROR_INVALID_STATE); + return BleObject::getInstance().connMgr()->rejectPairing(conn_handle); +} + +int hal_ble_gap_set_pairing_passkey(hal_ble_conn_handle_t conn_handle, const uint8_t* passkey, void* reserved) { + BleLock lk; + LOG_DEBUG(TRACE, "hal_ble_gap_set_pairing_passkey()."); + CHECK_TRUE(BleObject::getInstance().initialized(), SYSTEM_ERROR_INVALID_STATE); + return BleObject::getInstance().connMgr()->setPairingPasskey(conn_handle, passkey); +} + +bool hal_ble_gap_is_pairing(hal_ble_conn_handle_t conn_handle, void* reserved) { + BleLock lk; + LOG_DEBUG(TRACE, "hal_ble_gap_is_pairing()."); + CHECK_TRUE(BleObject::getInstance().initialized(), false); + return BleObject::getInstance().connMgr()->isPairing(conn_handle); +} + +bool hal_ble_gap_is_paired(hal_ble_conn_handle_t conn_handle, void* reserved) { + BleLock lk; + LOG_DEBUG(TRACE, "hal_ble_gap_is_paired()."); + CHECK_TRUE(BleObject::getInstance().initialized(), false); + return BleObject::getInstance().connMgr()->isPaired(conn_handle); +} + /********************************************** * BLE GATT Server APIs */ diff --git a/user/tests/wiring/api/ble.cpp b/user/tests/wiring/api/ble.cpp index 58e2f136c6..f1fc6c8051 100644 --- a/user/tests/wiring/api/ble.cpp +++ b/user/tests/wiring/api/ble.cpp @@ -18,6 +18,9 @@ class Handlers { void disconnectedHandler(const BlePeerDevice& peer) { } + + void pairingEventHandler(const BlePairingEvent& event) { + } }; Handlers bleHandlerInstance; @@ -37,6 +40,24 @@ void connectedHandlerFunc(const BlePeerDevice& peer, void* context) { void disconnectedHandlerFunc(const BlePeerDevice& peer, void* context) { } +void pairingEventHandlerFunc(const BlePairingEvent& event, void* context) { + if (event.type == BlePairingEventType::REQUEST_RECEIVED) { + BlePeerDevice peer = event.peer; + } else if (event.type == BlePairingEventType::STATUS_UPDATED) { + int status = event.payload.status.status; + bool bonded = event.payload.status.bonded; + bool lesc = event.payload.status.lesc; + (void)status; + (void)bonded; + (void)lesc; + } else if (event.type == BlePairingEventType::PASSKEY_DISPLAY) { + uint8_t key = event.payload.passkey[0]; + (void)key; + } else if (event.type == BlePairingEventType::PASSKEY_INPUT) { + + } +} + test(ble_characteristic_property) { API_COMPILE({ EnumFlags props = BleCharacteristicProperty::BROADCAST | @@ -149,6 +170,25 @@ test(ble_uuid_order) { (void)order; } +test(ble_pairing_io_caps) { + BlePairingIoCaps ioCaps; + API_COMPILE({ ioCaps = BlePairingIoCaps::NONE; }); + API_COMPILE({ ioCaps = BlePairingIoCaps::DISPLAY_ONLY; }); + API_COMPILE({ ioCaps = BlePairingIoCaps::DISPLAY_YESNO; }); + API_COMPILE({ ioCaps = BlePairingIoCaps::KEYBOARD_ONLY; }); + API_COMPILE({ ioCaps = BlePairingIoCaps::KEYBOARD_DISPLAY; }); + (void)ioCaps; +} + +test(ble_pairing_event_type) { + BlePairingEventType type; + API_COMPILE({ type = BlePairingEventType::REQUEST_RECEIVED; }); + API_COMPILE({ type = BlePairingEventType::PASSKEY_DISPLAY; }); + API_COMPILE({ type = BlePairingEventType::PASSKEY_INPUT; }); + API_COMPILE({ type = BlePairingEventType::STATUS_UPDATED; }); + (void)type; +} + test(ble_address_class) { hal_ble_addr_t halAddr; uint8_t addrArray[BLE_SIG_ADDR_LEN]; @@ -745,6 +785,18 @@ test(ble_local_device_class) { API_COMPILE({ BLE.onDisconnected([](const BlePeerDevice&) {}); }); API_COMPILE({ BLE.onDisconnected(&Handlers::disconnectedHandler, &bleHandlerInstance); }); API_COMPILE({ BLE.onDisconnected(std::bind(&Handlers::disconnectedHandler, &bleHandlerInstance, _1)); }); + + API_COMPILE({ int ret = BLE.setPairingIoCaps(BlePairingIoCaps::NONE); (void)ret; }); + API_COMPILE({ int ret = BLE.startPairing(BlePeerDevice()); (void)ret; }); + API_COMPILE({ int ret = BLE.rejectPairing(BlePeerDevice()); (void)ret; }); + API_COMPILE({ uint8_t passkey[6]; int ret = BLE.setPairingPasskey(BlePeerDevice(), passkey); (void)ret; }); + API_COMPILE({ bool ret = BLE.isPairing(BlePeerDevice()); (void)ret; }); + API_COMPILE({ bool ret = BLE.isPaired(BlePeerDevice()); (void)ret; }); + API_COMPILE({ BLE.onPairingEvent(pairingEventHandlerFunc); }); + API_COMPILE({ BLE.onPairingEvent(pairingEventHandlerFunc, nullptr); }); + API_COMPILE({ BLE.onPairingEvent([](const BlePairingEvent&) {}); }); + API_COMPILE({ BLE.onPairingEvent(&Handlers::pairingEventHandler, &bleHandlerInstance); }); + API_COMPILE({ BLE.onPairingEvent(std::bind(&Handlers::pairingEventHandler, &bleHandlerInstance, _1)); }); } #endif diff --git a/user/tests/wiring/ble_central_peripheral/ble_central/central.cpp b/user/tests/wiring/ble_central_peripheral/ble_central/central.cpp index af2d613fc0..ef06fb8fa0 100644 --- a/user/tests/wiring/ble_central_peripheral/ble_central/central.cpp +++ b/user/tests/wiring/ble_central_peripheral/ble_central/central.cpp @@ -77,6 +77,7 @@ test(BLE_01_Central_Scan_And_Connect) { assertTrue(uuids[0] == 0x1234); assertTrue(uuids[1] == 0x5678); peerAddr = results[i].address(); + Serial.println("Connecting..."); peer = BLE.connect(peerAddr); if (peer.connected()) { assertTrue(peer.getCharacteristicByDescription(peerCharRead, "read")); @@ -161,66 +162,31 @@ test(BLE_11_Central_Write_Characteristic_With_Write_Write_Wo_Rsp_Property_Nack) } test(BLE_12_Central_Received_Characteristic_With_Notify_Property_Auto) { - size_t wait = 20; //Wait for 2s to receive the data from BLE peripheral. - while (!str1Rec && wait > 0) { - delay(100); - wait--; - } - assertTrue(wait > 0); + assertTrue(waitFor([]{ return str1Rec; }, 2000)); } test(BLE_13_Central_Received_Characteristic_With_Notify_Property_Nack) { - size_t wait = 20; //Wait for 2s to receive the data from BLE peripheral. - while (!str2Rec && wait > 0) { - delay(100); - wait--; - } - assertTrue(wait > 0); + assertTrue(waitFor([]{ return str2Rec; }, 2000)); } test(BLE_14_Central_Received_Characteristic_With_Indicate_Property_Auto) { - size_t wait = 20; //Wait for 2s to receive the data from BLE peripheral. - while (!str3Rec && wait > 0) { - delay(100); - wait--; - } - assertTrue(wait > 0); + assertTrue(waitFor([]{ return str3Rec; }, 2000)); } test(BLE_15_Central_Received_Characteristic_With_Indicate_Property_Ack) { - size_t wait = 20; //Wait for 2s to receive the data from BLE peripheral. - while (!str4Rec && wait > 0) { - delay(100); - wait--; - } - assertTrue(wait > 0); + assertTrue(waitFor([]{ return str4Rec; }, 2000)); } test(BLE_16_Central_Received_Characteristic_With_Notify_Indicate_Property_Auto) { - size_t wait = 20; //Wait for 2s to receive the data from BLE peripheral. - while (!str5Rec && wait > 0) { - delay(100); - wait--; - } - assertTrue(wait > 0); + assertTrue(waitFor([]{ return str5Rec; }, 2000)); } test(BLE_17_Central_Received_Characteristic_With_Notify_Indicate_Property_Ack) { - size_t wait = 20; //Wait for 2s to receive the data from BLE peripheral. - while (!str6Rec && wait > 0) { - delay(100); - wait--; - } - assertTrue(wait > 0); + assertTrue(waitFor([]{ return str6Rec; }, 2000)); } test(BLE_18_Central_Received_Characteristic_With_Notify_Indicate_Property_Nack) { - size_t wait = 20; //Wait for 2s to receive the data from BLE peripheral. - while (!str7Rec && wait > 0) { - delay(100); - wait--; - } - assertTrue(wait > 0); + assertTrue(waitFor([]{ return str7Rec; }, 2000)); BLE.disconnect(peer); int ret = BLE.setScanTimeout(500); // Scan timeout: 5s @@ -290,6 +256,234 @@ test(BLE_21_Central_Discover_Characteristics_Of_Service) { assertTrue(characteristicsOfCustomService[4].UUID() == "6E400005-B5A3-F393-E0A9-E50E24DCCA9E"); assertTrue(characteristicsOfCustomService[5].UUID() == "6E400006-B5A3-F393-E0A9-E50E24DCCA9E"); assertTrue(characteristicsOfCustomService[6].UUID() == "6E400007-B5A3-F393-E0A9-E50E24DCCA9E"); + + BLE.disconnect(peer); +} + +test(BLE_22_Central_Pairing_Sync) { + // Indicate the peer device to start pairing tests. + peer = BLE.connect(peerAddr, false); + assertTrue(peer.connected()); + + const String str("deadbeef"); + int ret = peerCharWrite.setValue(str, BleTxRxType::ACK); + assertTrue(ret == (int)str.length()); + + BLE.disconnect(peer); + delay(2000); +} + +static bool pairingRequested = false; +static int pairingStatus = -1; + +static void pairingTestRoutine(bool request) { + peer = BLE.connect(peerAddr, false); + assertTrue(peer.connected()); + + pairingStatus = -1; + pairingRequested = false; + BLE.onPairingEvent([&](const BlePairingEvent& event) { + if (event.type == BlePairingEventType::REQUEST_RECEIVED) { + // Serial.println("Request received"); + pairingRequested = true; + } else if (event.type == BlePairingEventType::STATUS_UPDATED) { + pairingStatus = event.payload.status.status; + // Serial.println("status updateed"); + } else if (event.type == BlePairingEventType::PASSKEY_DISPLAY) { + Serial.print("Please enter the following passkey on the other side: "); + for (uint8_t i = 0; i < BLE_PAIRING_PASSKEY_LEN; i++) { + Serial.printf("%c", event.payload.passkey[i]); + } + Serial.println(""); + } else if (event.type == BlePairingEventType::PASSKEY_INPUT) { + Serial.print("Please enter 6-digits passkey displayed on the other side: "); + uint8_t i = 0; + uint8_t passkey[BLE_PAIRING_PASSKEY_LEN]; + while (i < BLE_PAIRING_PASSKEY_LEN) { + if (Serial.available()) { + passkey[i] = Serial.read(); + Serial.write(passkey[i++]); + } + } + Serial.println(""); + BLE.setPairingPasskey(event.peer, passkey); + } + }); + + if (request) { + assertEqual(BLE.startPairing(peer), (int)SYSTEM_ERROR_NONE); + } else { + assertTrue(waitFor([&]{ return pairingRequested; }, 5000)); + } + assertTrue(BLE.isPairing(peer)); + assertTrue(waitFor([&]{ return !BLE.isPairing(peer); }, 20000)); + assertTrue(BLE.isPaired(peer)); + assertEqual(pairingStatus, (int)SYSTEM_ERROR_NONE); + + delay(500); + BLE.disconnect(peer); + delay(500); +} + +test(BLE_23_Central_Initiate_Pairing_Central_Io_Caps_None_Peripheral_Io_Caps_None) { + BLE.setPairingIoCaps(BlePairingIoCaps::NONE); + pairingTestRoutine(true); +} + +test(BLE_24_Central_Pairing_Receiption_Central_Io_Caps_None_Peripheral_Io_Caps_Dispaly_Only) { + BLE.setPairingIoCaps(BlePairingIoCaps::NONE); + pairingTestRoutine(false); +} + +test(BLE_25_Central_Initiate_Pairing_Central_Io_Caps_None_Peripheral_Io_Caps_Dispaly_Yesno) { + BLE.setPairingIoCaps(BlePairingIoCaps::NONE); + pairingTestRoutine(true); +} + +test(BLE_26_Central_Pairing_Receiption_Central_Io_Caps_None_Peripheral_Io_Caps_Keyboard_Only) { + BLE.setPairingIoCaps(BlePairingIoCaps::NONE); + pairingTestRoutine(false); +} + +test(BLE_27_Central_Initiate_Pairing_Central_Io_Caps_None_Peripheral_Io_Caps_Keyboard_Dispaly) { + BLE.setPairingIoCaps(BlePairingIoCaps::NONE); + pairingTestRoutine(true); +} + +test(BLE_28_Central_Pairing_Receiption_Central_Io_Caps_Dispaly_Only_Peripheral_Io_Caps_None) { + BLE.setPairingIoCaps(BlePairingIoCaps::DISPLAY_ONLY); + pairingTestRoutine(false); +} + +test(BLE_29_Central_Initiate_Pairing_Central_Io_Caps_Dispaly_Only_Peripheral_Io_Caps_Display_Only) { + BLE.setPairingIoCaps(BlePairingIoCaps::DISPLAY_ONLY); + pairingTestRoutine(true); +} + +test(BLE_30_Central_Pairing_Receiption_Central_Io_Caps_Dispaly_Only_Peripheral_Io_Caps_Display_Yesno) { + BLE.setPairingIoCaps(BlePairingIoCaps::DISPLAY_ONLY); + pairingTestRoutine(false); +} + +test(BLE_31_Central_Initiate_Pairing_Central_Io_Caps_Dispaly_Only_Peripheral_Io_Caps_Keyboard_Only) { + BLE.setPairingIoCaps(BlePairingIoCaps::DISPLAY_ONLY); + pairingTestRoutine(true); +} + +test(BLE_32_Central_Pairing_Receiption_Central_Io_Caps_Dispaly_Only_Peripheral_Io_Caps_Keyboard_Display) { + BLE.setPairingIoCaps(BlePairingIoCaps::DISPLAY_ONLY); + pairingTestRoutine(false); +} + +test(BLE_33_Central_Initiate_Pairing_Central_Io_Caps_Dispaly_Yesno_Peripheral_Io_Caps_None) { + BLE.setPairingIoCaps(BlePairingIoCaps::DISPLAY_YESNO); + pairingTestRoutine(true); +} + +test(BLE_34_Central_Pairing_Receiption_Central_Io_Caps_Dispaly_Yesno_Peripheral_Io_Caps_Display_Only) { + BLE.setPairingIoCaps(BlePairingIoCaps::DISPLAY_YESNO); + pairingTestRoutine(false); +} + +test(BLE_35_Central_Initiate_Pairing_Central_Io_Caps_Dispaly_Yesno_Peripheral_Io_Caps_Display_Yesno) { + BLE.setPairingIoCaps(BlePairingIoCaps::DISPLAY_YESNO); + pairingTestRoutine(true); +} + +test(BLE_36_Central_Pairing_Receiption_Central_Io_Caps_Dispaly_Yesno_Peripheral_Io_Caps_Keyboard_Only) { + BLE.setPairingIoCaps(BlePairingIoCaps::DISPLAY_YESNO); + pairingTestRoutine(false); +} + +test(BLE_37_Central_Initiate_Pairing_Central_Io_Caps_Dispaly_Yesno_Peripheral_Io_Caps_Keyboard_Display) { + BLE.setPairingIoCaps(BlePairingIoCaps::DISPLAY_YESNO); + pairingTestRoutine(true); +} + +test(BLE_38_Central_Pairing_Receiption_Central_Io_Caps_Keyboard_Only_Peripheral_Io_Caps_None) { + BLE.setPairingIoCaps(BlePairingIoCaps::KEYBOARD_ONLY); + pairingTestRoutine(false); +} + +test(BLE_39_Central_Initiate_Pairing_Central_Io_Caps_Keyboard_Only_Peripheral_Io_Caps_Display_Only) { + BLE.setPairingIoCaps(BlePairingIoCaps::KEYBOARD_ONLY); + pairingTestRoutine(true); +} + +test(BLE_40_Central_Pairing_Receiption_Central_Io_Caps_Keyboard_Only_Peripheral_Io_Caps_Display_Yesno) { + BLE.setPairingIoCaps(BlePairingIoCaps::KEYBOARD_ONLY); + pairingTestRoutine(false); +} + +test(BLE_41_Central_Initiate_Pairing_Central_Io_Caps_Keyboard_Only_Peripheral_Io_Caps_Keyboard_Only) { + BLE.setPairingIoCaps(BlePairingIoCaps::KEYBOARD_ONLY); + Serial.println("Please enter the same 6-digits key on each side"); + pairingTestRoutine(true); +} + +test(BLE_42_Central_Pairing_Receiption_Central_Io_Caps_Keyboard_Only_Peripheral_Io_Caps_Keyboard_Display) { + BLE.setPairingIoCaps(BlePairingIoCaps::KEYBOARD_ONLY); + pairingTestRoutine(false); +} + +test(BLE_43_Central_Initiate_Pairing_Central_Io_Caps_Keyboard_Display_Peripheral_Io_Caps_None) { + BLE.setPairingIoCaps(BlePairingIoCaps::KEYBOARD_DISPLAY); + pairingTestRoutine(true); +} + +test(BLE_44_Central_Pairing_Receiption_Central_Io_Caps_Keyboard_Display_Peripheral_Io_Caps_Display_Only) { + BLE.setPairingIoCaps(BlePairingIoCaps::KEYBOARD_DISPLAY); + pairingTestRoutine(false); +} + +test(BLE_45_Central_Initiate_Pairing_Central_Io_Caps_Keyboard_Display_Peripheral_Io_Caps_Display_Yesno) { + BLE.setPairingIoCaps(BlePairingIoCaps::KEYBOARD_DISPLAY); + pairingTestRoutine(true); +} + +test(BLE_46_Central_Pairing_Receiption_Central_Io_Caps_Keyboard_Display_Peripheral_Io_Caps_Keyboard_Only) { + BLE.setPairingIoCaps(BlePairingIoCaps::KEYBOARD_DISPLAY); + pairingTestRoutine(false); +} + +test(BLE_47_Central_Initiate_Pairing_Central_Io_Caps_Keyboard_Display_Peripheral_Io_Caps_Keyboard_Display) { + BLE.setPairingIoCaps(BlePairingIoCaps::KEYBOARD_DISPLAY); + pairingTestRoutine(true); +} + +test(BLE_48_Central_Initiate_Pairing_Peripheral_Being_Reject) { + peer = BLE.connect(peerAddr, false); + assertTrue(peer.connected()); + + assertEqual(BLE.startPairing(peer), (int)SYSTEM_ERROR_NONE); + assertTrue(BLE.isPairing(peer)); + assertTrue(waitFor([&]{ return !BLE.isPairing(peer); }, 20000)); + assertFalse(BLE.isPaired(peer)); + + delay(500); + BLE.disconnect(peer); + delay(500); +} + +test(BLE_49_Central_Pairing_Receiption_Reject) { + pairingStatus = 0; + BLE.onPairingEvent([&](const BlePairingEvent& event) { + if (event.type == BlePairingEventType::REQUEST_RECEIVED) { + Serial.println("Reject pairing request."); + BLE.rejectPairing(event.peer); + } else if (event.type == BlePairingEventType::STATUS_UPDATED) { + pairingStatus = event.payload.status.status; + } + }); + + peer = BLE.connect(peerAddr, false); + assertTrue(peer.connected()); + + assertTrue(waitFor([]{ return pairingStatus != 0; }, 5000)); + + delay(500); + BLE.disconnect(peer); + delay(500); } #endif // #if Wiring_BLE == 1 diff --git a/user/tests/wiring/ble_central_peripheral/ble_peripheral/peripheral.cpp b/user/tests/wiring/ble_central_peripheral/ble_peripheral/peripheral.cpp index faee95bac6..55213d5796 100644 --- a/user/tests/wiring/ble_central_peripheral/ble_peripheral/peripheral.cpp +++ b/user/tests/wiring/ble_central_peripheral/ble_peripheral/peripheral.cpp @@ -22,6 +22,7 @@ const String str6("77982c283c65"); const String str7("21ec57d28a0c"); bool str1Rec = false, str2Rec = false, str3Rec = false, str4Rec = false, str5Rec = false, str6Rec = false, str7Rec = false; +bool pairingStarted = false; BleCharacteristic charRead("read", BleCharacteristicProperty::READ, charReadUuid, serviceUuid); @@ -56,6 +57,9 @@ void onDataReceived(const uint8_t* data, size_t len, const BlePeerDevice& peer, if (str == str7) { str7Rec = true; } + if (str == "deadbeef") { + pairingStarted = true; + } } test(BLE_01_Peripheral_Advertising) { @@ -93,79 +97,38 @@ test(BLE_01_Peripheral_Advertising) { } test(BLE_02_Peripheral_Connected) { - size_t wait = 200; // Wait 20s for establishing connection. - while(!BLE.connected() && wait > 0) { - delay(100); - wait--; - } - assertTrue(wait > 0); - + assertTrue(waitFor(BLE.connected, 20000)); Serial.println("BLE connected."); } // For the first data transmission, we need to wait longer to make sure // the Central device has discovered the services and characteristics. test(BLE_03_Peripheral_Receive_Characteristic_With_Write_Property_Auto) { - size_t wait = 100; //Wait for 10s to receive the data from BLE peripheral. - while (!str1Rec && wait > 0) { - delay(100); - wait--; - } - assertTrue(wait > 0); + assertTrue(waitFor([]{ return str1Rec; }, 10000)); } test(BLE_04_Peripheral_Receive_Characteristic_With_Write_Property_Ack) { - size_t wait = 20; //Wait for 2s to receive the data from BLE peripheral. - while (!str2Rec && wait > 0) { - delay(100); - wait--; - } - assertTrue(wait > 0); + assertTrue(waitFor([]{ return str2Rec; }, 2000)); } test(BLE_05_Peripheral_Receive_Characteristic_With_Write_Wo_Rsp_Property_Auto) { - size_t wait = 20; //Wait for 2s to receive the data from BLE peripheral. - while (!str3Rec && wait > 0) { - delay(100); - wait--; - } - assertTrue(wait > 0); + assertTrue(waitFor([]{ return str3Rec; }, 2000)); } test(BLE_06_Peripheral_Receive_Characteristic_With_Write_Wo_Rsp_Property_Nack) { - size_t wait = 20; //Wait for 2s to receive the data from BLE peripheral. - while (!str4Rec && wait > 0) { - delay(100); - wait--; - } - assertTrue(wait > 0); + assertTrue(waitFor([]{ return str4Rec; }, 2000)); } test(BLE_07_Peripheral_Receive_Characteristic_With_Write_Write_Wo_Rsp_Property_Auto) { - size_t wait = 20; //Wait for 2s to receive the data from BLE peripheral. - while (!str5Rec && wait > 0) { - delay(100); - wait--; - } - assertTrue(wait > 0); + assertTrue(waitFor([]{ return str5Rec; }, 2000)); } test(BLE_08_Peripheral_Receive_Characteristic_With_Write_Write_Wo_Rsp_Property_Ack) { - size_t wait = 20; //Wait for 2s to receive the data from BLE peripheral. - while (!str6Rec && wait > 0) { - delay(100); - wait--; - } - assertTrue(wait > 0); + assertTrue(waitFor([]{ return str6Rec; }, 2000)); } test(BLE_09_Peripheral_Receive_Characteristic_With_Write_Write_Wo_Rsp_Property_Nack) { - size_t wait = 20; //Wait for 2s to receive the data from BLE peripheral. - while (!str7Rec && wait > 0) { - delay(100); - wait--; - } - assertTrue(wait > 0); + assertTrue(waitFor([]{ return str7Rec; }, 2000)); } test(BLE_10_Peripheral_Notify_Characteristic_With_Notify_Property_Auto) { @@ -222,5 +185,220 @@ test(BLE_18_Peripheral_Notify_Characteristic_With_Notify_Indicate_Property_Nack) assertTrue(ret == (int)str.length()); } +test(BLE_19_Peripheral_Pairing_Sync) { + // The central will perform some service and characteristic discovery tests + // before starting thee pairing tests + assertTrue(waitFor([]{ return pairingStarted; }, 20000)); + assertTrue(waitFor([]{ return !BLE.connected(); }, 5000)); +} + +static bool pairingRequested = false; +static int pairingStatus = -1; +static BlePeerDevice peer; + +static void pairingTestRoutine(bool request) { + pairingRequested = false; + pairingStatus = -1; + + BLE.onPairingEvent([&](const BlePairingEvent& event) { + if (event.type == BlePairingEventType::REQUEST_RECEIVED) { + pairingRequested = true; + peer = event.peer; + // Serial.println("Request received"); + } else if (event.type == BlePairingEventType::STATUS_UPDATED) { + pairingStatus = event.payload.status.status; + // Serial.println("status updateed"); + } else if (event.type == BlePairingEventType::PASSKEY_DISPLAY) { + Serial.print("Passkey display: "); + for (uint8_t i = 0; i < BLE_PAIRING_PASSKEY_LEN; i++) { + Serial.printf("%c", event.payload.passkey[i]); + } + Serial.println(""); + } else if (event.type == BlePairingEventType::PASSKEY_INPUT) { + Serial.print("Passkey input: "); + uint8_t i = 0; + uint8_t passkey[BLE_PAIRING_PASSKEY_LEN]; + while (i < BLE_PAIRING_PASSKEY_LEN) { + if (Serial.available()) { + passkey[i] = Serial.read(); + Serial.write(passkey[i++]); + } + } + Serial.println(""); + BLE.setPairingPasskey(event.peer, passkey); + } + }); + + assertTrue(waitFor(BLE.connected, 20000)); + + if (request) { + peer = BLE.peerCentral(); + assertEqual(BLE.startPairing(peer), (int)SYSTEM_ERROR_NONE); + } else { + assertTrue(waitFor([&]{ return pairingRequested; }, 20000)); + } + assertTrue(BLE.isPairing(peer)); + assertTrue(waitFor([&]{ return !BLE.isPairing(peer); }, 20000)); + assertTrue(BLE.isPaired(peer)); + assertEqual(pairingStatus, (int)SYSTEM_ERROR_NONE); + + assertTrue(waitFor([]{ return !BLE.connected(); }, 5000)); +} + +test(BLE_20_Peripheral_Pairing_Receiption_Central_Io_Caps_None_Peripheral_Io_Caps_None) { + BLE.setPairingIoCaps(BlePairingIoCaps::NONE); + pairingTestRoutine(false); +} + +test(BLE_21_Peripheral_Initiate_Pairing_Central_Io_Caps_None_Peripheral_Io_Caps_Dispaly_Only) { + BLE.setPairingIoCaps(BlePairingIoCaps::DISPLAY_ONLY); + pairingTestRoutine(true); +} + +test(BLE_22_Peripheral_Pairing_Receiption_Cnetral_Io_Caps_None_Peripheral_Io_Caps_Dispaly_Yesno) { + BLE.setPairingIoCaps(BlePairingIoCaps::DISPLAY_YESNO); + pairingTestRoutine(false); +} + +test(BLE_23_Peripheral_Initiate_Pairing_Central_Io_Caps_None_Peripheral_Io_Caps_Keyboard_Only) { + BLE.setPairingIoCaps(BlePairingIoCaps::KEYBOARD_ONLY); + pairingTestRoutine(true); +} + +test(BLE_24_Peripheral_Pairing_Receiption_Central_Io_Caps_None_Peripheral_Io_Caps_Keyboard_Dispaly) { + BLE.setPairingIoCaps(BlePairingIoCaps::KEYBOARD_DISPLAY); + pairingTestRoutine(false); +} + +test(BLE_25_Peripheral_Initiate_Pairing_Central_Io_Caps_Display_Only_Peripheral_Io_Caps_None) { + BLE.setPairingIoCaps(BlePairingIoCaps::NONE); + pairingTestRoutine(true); +} + +test(BLE_26_Peripheral_Pairing_Receiption_Central_Io_Caps_Display_Only_Peripheral_Io_Caps_Dispaly_Only) { + BLE.setPairingIoCaps(BlePairingIoCaps::DISPLAY_ONLY); + pairingTestRoutine(false); +} + +test(BLE_27_Peripheral_Initiate_Pairing_Central_Io_Caps_Display_Only_Peripheral_Io_Caps_Display_Yesno) { + BLE.setPairingIoCaps(BlePairingIoCaps::DISPLAY_YESNO); + pairingTestRoutine(true); +} + +test(BLE_28_Peripheral_Pairing_Receiption_Central_Io_Caps_Display_Only_Peripheral_Io_Caps_Keyboard_Only) { + BLE.setPairingIoCaps(BlePairingIoCaps::KEYBOARD_ONLY); + pairingTestRoutine(false); +} + +test(BLE_29_Peripheral_Initiate_Pairing_Central_Io_Caps_Display_Only_Peripheral_Io_Caps_Keyboard_Display) { + BLE.setPairingIoCaps(BlePairingIoCaps::KEYBOARD_DISPLAY); + pairingTestRoutine(true); +} + +test(BLE_30_Peripheral_Pairing_Receiption_Central_Io_Caps_Display_Yesno_Peripheral_Io_Caps_None) { + BLE.setPairingIoCaps(BlePairingIoCaps::NONE); + pairingTestRoutine(false); +} + +test(BLE_31_Peripheral_Initiate_Pairing_Central_Io_Caps_Display_Yesno_Peripheral_Io_Caps_Display_Only) { + BLE.setPairingIoCaps(BlePairingIoCaps::DISPLAY_ONLY); + pairingTestRoutine(true); +} + +test(BLE_32_Peripheral_Pairing_Receiption_Central_Io_Caps_Display_Yesno_Peripheral_Io_Caps_Display_Yesno) { + BLE.setPairingIoCaps(BlePairingIoCaps::DISPLAY_YESNO); + pairingTestRoutine(false); +} + +test(BLE_33_Peripheral_Initiate_Pairing_Central_Io_Caps_Display_Yesno_Peripheral_Io_Caps_Keyboard_Only) { + BLE.setPairingIoCaps(BlePairingIoCaps::KEYBOARD_ONLY); + pairingTestRoutine(true); +} + +test(BLE_34_Peripheral_Pairing_Receiption_Central_Io_Caps_Display_Yesno_Peripheral_Io_Caps_Keyboard_Display) { + BLE.setPairingIoCaps(BlePairingIoCaps::KEYBOARD_DISPLAY); + pairingTestRoutine(false); +} + +test(BLE_35_Peripheral_Initiate_Pairing_Central_Io_Caps_Keyboard_Only_Peripheral_Io_Caps_None) { + BLE.setPairingIoCaps(BlePairingIoCaps::NONE); + pairingTestRoutine(true); +} + +test(BLE_36_Peripheral_Pairing_Receiption_Central_Io_Caps_Keyboard_Only_Peripheral_Io_Caps_Display_Only) { + BLE.setPairingIoCaps(BlePairingIoCaps::DISPLAY_ONLY); + pairingTestRoutine(false); +} + +test(BLE_37_Peripheral_Initiate_Pairing_Central_Io_Caps_Keyboard_Only_Peripheral_Io_Caps_Display_Yesno) { + BLE.setPairingIoCaps(BlePairingIoCaps::DISPLAY_YESNO); + pairingTestRoutine(true); +} + +test(BLE_38_Peripheral_Pairing_Receiption_Central_Io_Caps_Keyboard_Only_Peripheral_Io_Caps_Keyboard_Only) { + BLE.setPairingIoCaps(BlePairingIoCaps::KEYBOARD_ONLY); + Serial.println("Please enter the same 6-digits key on each side"); + pairingTestRoutine(false); +} + +test(BLE_39_Peripheral_Initiate_Pairing_Central_Io_Caps_Keyboard_Only_Peripheral_Io_Caps_Keyboard_Display) { + BLE.setPairingIoCaps(BlePairingIoCaps::KEYBOARD_DISPLAY); + pairingTestRoutine(true); +} + +test(BLE_40_Peripheral_Pairing_Receiption_Central_Io_Caps_Keyboard_Display_Peripheral_Io_Caps_None) { + BLE.setPairingIoCaps(BlePairingIoCaps::NONE); + pairingTestRoutine(false); +} + +test(BLE_41_Peripheral_Initiate_Pairing_Central_Io_Caps_Keyboard_Display_Peripheral_Io_Caps_Display_Only) { + BLE.setPairingIoCaps(BlePairingIoCaps::DISPLAY_ONLY); + pairingTestRoutine(true); +} + +test(BLE_42_Peripheral_Pairing_Receiption_Central_Io_Caps_Keyboard_Display_Peripheral_Io_Caps_Display_Yesno) { + BLE.setPairingIoCaps(BlePairingIoCaps::DISPLAY_YESNO); + pairingTestRoutine(false); +} + +test(BLE_43_Peripheral_Initiate_Pairing_Central_Io_Caps_Keyboard_Display_Peripheral_Io_Caps_Keyboard_Only) { + BLE.setPairingIoCaps(BlePairingIoCaps::KEYBOARD_ONLY); + pairingTestRoutine(true); +} + +test(BLE_44_Peripheral_Pairing_Receiption_Central_Io_Caps_Keyboard_Display_Peripheral_Io_Caps_Keyboard_Display) { + BLE.setPairingIoCaps(BlePairingIoCaps::KEYBOARD_DISPLAY); + pairingTestRoutine(false); +} + +test(BLE_45_Peripheral_Pairing_Receiption_Reject) { + pairingStatus = 0; + BLE.onPairingEvent([&](const BlePairingEvent& event) { + if (event.type == BlePairingEventType::REQUEST_RECEIVED) { + Serial.println("Reject pairing request."); + BLE.rejectPairing(event.peer); + } else if (event.type == BlePairingEventType::STATUS_UPDATED) { + pairingStatus = event.payload.status.status; + } + }); + + assertTrue(waitFor(BLE.connected, 20000)); + + assertTrue(waitFor([]{ return pairingStatus != 0; }, 5000)); + + assertTrue(waitFor([]{ return !BLE.connected(); }, 5000)); +} + +test(BLE_46_Peripheral_Initiate_Pairing_Being_Rejected) { + assertTrue(waitFor(BLE.connected, 20000)); + + peer = BLE.peerCentral(); + assertEqual(BLE.startPairing(peer), (int)SYSTEM_ERROR_NONE); + + assertTrue(BLE.isPairing(peer)); + assertTrue(waitFor([&]{ return !BLE.isPairing(peer); }, 20000)); + assertFalse(BLE.isPaired(peer)); +} + #endif // #if Wiring_BLE == 1 diff --git a/wiring/inc/spark_wiring_ble.h b/wiring/inc/spark_wiring_ble.h index de887640f9..cc15944dbe 100644 --- a/wiring/inc/spark_wiring_ble.h +++ b/wiring/inc/spark_wiring_ble.h @@ -147,6 +147,39 @@ enum class BleTxRxType : uint8_t { NACK = 2 }; +enum class BlePairingIoCaps : uint8_t { + NONE = BLE_IO_CAPS_NONE, + DISPLAY_ONLY = BLE_IO_CAPS_DISPLAY_ONLY, + DISPLAY_YESNO = BLE_IO_CAPS_DISPLAY_YESNO, + KEYBOARD_ONLY = BLE_IO_CAPS_KEYBOARD_ONLY, + KEYBOARD_DISPLAY = BLE_IO_CAPS_KEYBOARD_DISPLAY +}; + +enum class BlePairingEventType : uint8_t { + REQUEST_RECEIVED = BLE_EVT_PAIRING_REQUEST_RECEIVED, + PASSKEY_DISPLAY = BLE_EVT_PAIRING_PASSKEY_DISPLAY, + PASSKEY_INPUT = BLE_EVT_PAIRING_PASSKEY_INPUT, + STATUS_UPDATED = BLE_EVT_PAIRING_STATUS_UPDATED +}; + +struct BlePairingStatus { + int status; + bool bonded; + bool lesc; +}; + +union BlePairingEventPayload { + const uint8_t* passkey; + BlePairingStatus status; +}; + +struct BlePairingEvent { + BlePeerDevice& peer; + BlePairingEventType type; + size_t payloadLen; + BlePairingEventPayload payload; +}; + typedef hal_ble_conn_handle_t BleConnectionHandle; typedef hal_ble_attr_handle_t BleAttributeHandle; @@ -155,6 +188,13 @@ typedef void (*BleOnScanResultCallback)(const BleScanResult* result, void* conte typedef void (*BleOnScanResultCallbackRef)(const BleScanResult& result, void* context); typedef void (*BleOnConnectedCallback)(const BlePeerDevice& peer, void* context); typedef void (*BleOnDisconnectedCallback)(const BlePeerDevice& peer, void* context); +typedef void (*BleOnPairingEventCallback)(const BlePairingEvent& event, void* context); + +typedef std::function BleOnDataReceivedStdFunction; +typedef std::function BleOnScanResultStdFunction; +typedef std::function BleOnConnectedStdFunction; +typedef std::function BleOnDisconnectedStdFunction; +typedef std::function BleOnPairingEventStdFunction; class BleAdvertisingParams : public hal_ble_adv_params_t { }; @@ -455,19 +495,19 @@ class BleCharacteristic { BleCharacteristic(EnumFlags properties, const String& desc, BleOnDataReceivedCallback callback = nullptr, void* context = nullptr) : BleCharacteristic(properties, desc.c_str(), callback, context) { } - BleCharacteristic(EnumFlags properties, const char* desc, std::function callback); - BleCharacteristic(EnumFlags properties, const String& desc, std::function callback) + BleCharacteristic(EnumFlags properties, const char* desc, const BleOnDataReceivedStdFunction& callback); + BleCharacteristic(EnumFlags properties, const String& desc, const BleOnDataReceivedStdFunction& callback) : BleCharacteristic(properties, desc.c_str(), callback) { } template BleCharacteristic(EnumFlags properties, const String& desc, void(T::*callback)(const uint8_t*, size_t, const BlePeerDevice& peer), T* instance) - : BleCharacteristic(properties, desc, std::bind(callback, instance, _1, _2, _3)) { + : BleCharacteristic(properties, desc, (callback && instance) ? std::bind(callback, instance, _1, _2, _3) : (BleOnDataReceivedStdFunction)nullptr) { } template BleCharacteristic(EnumFlags properties, const char* desc, void(T::*callback)(const uint8_t*, size_t, const BlePeerDevice& peer), T* instance) - : BleCharacteristic(properties, desc, std::bind(callback, instance, _1, _2, _3)) { + : BleCharacteristic(properties, desc, (callback && instance) ? std::bind(callback, instance, _1, _2, _3) : (BleOnDataReceivedStdFunction)nullptr) { } template @@ -483,25 +523,25 @@ class BleCharacteristic { } template - BleCharacteristic(const char* desc, EnumFlags properties, T1 charUuid, T2 svcUuid, std::function callback) { + BleCharacteristic(const char* desc, EnumFlags properties, T1 charUuid, T2 svcUuid, const BleOnDataReceivedStdFunction& callback) { BleUuid cUuid(charUuid); BleUuid sUuid(svcUuid); construct(desc, properties, cUuid, sUuid, callback); } template - BleCharacteristic(const String& desc, EnumFlags properties, T1 charUuid, T2 svcUuid, std::function callback) + BleCharacteristic(const String& desc, EnumFlags properties, T1 charUuid, T2 svcUuid, const BleOnDataReceivedStdFunction& callback) : BleCharacteristic(desc.c_str(), properties, charUuid, svcUuid, callback) { } template BleCharacteristic(const char* desc, EnumFlags properties, T1 charUuid, T2 svcUuid, void(T3::*callback)(const uint8_t*, size_t, const BlePeerDevice& peer), T3* instance) - : BleCharacteristic(desc, properties, charUuid, svcUuid, std::bind(callback, instance, _1, _2, _3)) { + : BleCharacteristic(desc, properties, charUuid, svcUuid, (callback && instance) ? std::bind(callback, instance, _1, _2, _3) : (BleOnDataReceivedStdFunction)nullptr) { } template BleCharacteristic(const String& desc, EnumFlags properties, T1 charUuid, T2 svcUuid, void(T3::*callback)(const uint8_t*, size_t, const BlePeerDevice& peer), T3* instance) - : BleCharacteristic(desc, properties, charUuid, svcUuid, std::bind(callback, instance, _1, _2, _3)) { + : BleCharacteristic(desc, properties, charUuid, svcUuid, (callback && instance) ? std::bind(callback, instance, _1, _2, _3) : (BleOnDataReceivedStdFunction)nullptr) { } ~BleCharacteristic(); @@ -545,11 +585,11 @@ class BleCharacteristic { } void onDataReceived(BleOnDataReceivedCallback callback, void* context = nullptr); - void onDataReceived(std::function callback); + void onDataReceived(const BleOnDataReceivedStdFunction& callback); template void onDataReceived(void(T::*callback)(const uint8_t*, size_t, const BlePeerDevice& peer), T* instance) { - onDataReceived(std::bind(callback, instance, _1, _2, _3)); + onDataReceived((callback && instance) ? std::bind(callback, instance, _1, _2, _3) :(BleOnDataReceivedStdFunction) nullptr); } BleCharacteristicImpl* impl() const { @@ -562,7 +602,7 @@ class BleCharacteristic { BleOnDataReceivedCallback callback, void* context); void construct(const char* desc, EnumFlags properties, - BleUuid& charUuid, BleUuid& svcUuid, std::function callback); + BleUuid& charUuid, BleUuid& svcUuid, const BleOnDataReceivedStdFunction& callback); std::shared_ptr impl_; }; @@ -909,24 +949,24 @@ class BleLocalDevice { // Scanning control int scan(BleOnScanResultCallback callback, void* context = nullptr) const; int scan(BleOnScanResultCallbackRef callback, void* context = nullptr) const; - int scan(const std::function& callback) const; + int scan(const BleOnScanResultStdFunction& callback) const; int scan(BleScanResult* results, size_t resultCount) const; Vector scan() const; template - int scan(void(T::*callback)(const BleScanResult&), T* instance) { - return scan(std::bind(callback, instance, _1)); + int scan(void(T::*callback)(const BleScanResult&), T* instance) const { + return scan((callback && instance) ? std::bind(callback, instance, _1) : (BleOnScanResultStdFunction)nullptr); } int scanWithFilter(const BleScanFilter& filter, BleOnScanResultCallback callback, void* context = nullptr) const; int scanWithFilter(const BleScanFilter& filter, BleOnScanResultCallbackRef callback, void* context = nullptr) const; - int scanWithFilter(const BleScanFilter& filter, const std::function& callback) const; + int scanWithFilter(const BleScanFilter& filter, const BleOnScanResultStdFunction& callback) const; int scanWithFilter(const BleScanFilter& filter, BleScanResult* results, size_t resultCount) const; Vector scanWithFilter(const BleScanFilter& filter) const; template - int scanWithFilter(const BleScanFilter& filter, void(T::*callback)(const BleScanResult&), T* instance) { - return scanWithFilter(filter, std::bind(callback, instance, _1)); + int scanWithFilter(const BleScanFilter& filter, void(T::*callback)(const BleScanResult&), T* instance) const { + return scanWithFilter(filter, (callback && instance) ? std::bind(callback, instance, _1) : (BleOnScanResultStdFunction)nullptr); } int stopScanning() const; @@ -946,17 +986,17 @@ class BleLocalDevice { BleCharacteristic addCharacteristic(EnumFlags properties, const char* desc, BleOnDataReceivedCallback callback = nullptr, void* context = nullptr); BleCharacteristic addCharacteristic(EnumFlags properties, const String& desc, BleOnDataReceivedCallback callback = nullptr, void* context = nullptr); - BleCharacteristic addCharacteristic(EnumFlags properties, const char* desc, std::function callback); - BleCharacteristic addCharacteristic(EnumFlags properties, const String& desc, std::function callback); + BleCharacteristic addCharacteristic(EnumFlags properties, const char* desc, const BleOnDataReceivedStdFunction& callback); + BleCharacteristic addCharacteristic(EnumFlags properties, const String& desc, const BleOnDataReceivedStdFunction& callback); template BleCharacteristic addCharacteristic(EnumFlags properties, const char* desc, void(T::*callback)(const uint8_t*, size_t, const BlePeerDevice& peer), T* instance) { - return addCharacteristic(properties, desc, std::bind(callback, instance, _1, _2, _3)); + return addCharacteristic(properties, desc, (callback && instance) ? std::bind(callback, instance, _1, _2, _3) : (BleOnDataReceivedStdFunction)nullptr); } template BleCharacteristic addCharacteristic(EnumFlags properties, const String& desc, void(T::*callback)(const uint8_t*, size_t, const BlePeerDevice& peer), T* instance) { - return addCharacteristic(properties, desc, std::bind(callback, instance, _1, _2, _3)); + return addCharacteristic(properties, desc, (callback && instance) ? std::bind(callback, instance, _1, _2, _3) : (BleOnDataReceivedStdFunction)nullptr); } template @@ -972,25 +1012,25 @@ class BleLocalDevice { } template - BleCharacteristic addCharacteristic(const char* desc, EnumFlags properties, T1 charUuid, T2 svcUuid, std::function callback) { + BleCharacteristic addCharacteristic(const char* desc, EnumFlags properties, T1 charUuid, T2 svcUuid, const BleOnDataReceivedStdFunction& callback) { BleCharacteristic characteristic(desc, properties, charUuid, svcUuid, callback); addCharacteristic(characteristic); return characteristic; } template - BleCharacteristic addCharacteristic(const String& desc, EnumFlags properties, T1 charUuid, T2 svcUuid, std::function callback) { + BleCharacteristic addCharacteristic(const String& desc, EnumFlags properties, T1 charUuid, T2 svcUuid, const BleOnDataReceivedStdFunction& callback) { return addCharacteristic(desc.c_str(), properties, charUuid, svcUuid, callback); } template BleCharacteristic addCharacteristic(const char* desc, EnumFlags properties, T1 charUuid, T2 svcUuid, void(T3::*callback)(const uint8_t*, size_t, const BlePeerDevice& peer), T3* instance) { - return addCharacteristic(desc, properties, charUuid, svcUuid, std::bind(callback, instance, _1, _2, _3)); + return addCharacteristic(desc, properties, charUuid, svcUuid, (callback && instance) ? std::bind(callback, instance, _1, _2, _3) : (BleOnDataReceivedStdFunction)nullptr); } template BleCharacteristic addCharacteristic(const String& desc, EnumFlags properties, T1 charUuid, T2 svcUuid, void(T3::*callback)(const uint8_t*, size_t, const BlePeerDevice& peer), T3* instance) { - return addCharacteristic(desc, properties, charUuid, svcUuid, std::bind(callback, instance, _1, _2, _3)); + return addCharacteristic(desc, properties, charUuid, svcUuid, (callback && instance) ? std::bind(callback, instance, _1, _2, _3) : (BleOnDataReceivedStdFunction)nullptr); } // Access connection parameters @@ -1002,6 +1042,21 @@ class BleLocalDevice { BlePeerDevice connect(const BleAddress& addr, uint16_t interval, uint16_t latency, uint16_t timeout, bool automatic = true) const; BlePeerDevice connect(const BleAddress& addr, bool automatic = true) const; + int setPairingIoCaps(BlePairingIoCaps ioCaps) const; + int startPairing(const BlePeerDevice& peer) const; + int rejectPairing(const BlePeerDevice& peer) const; + int setPairingPasskey(const BlePeerDevice& peer, const uint8_t* passkey) const; + bool isPairing(const BlePeerDevice& peer) const; + bool isPaired(const BlePeerDevice& peer) const; + + void onPairingEvent(BleOnPairingEventCallback callback, void* context = nullptr) const; + void onPairingEvent(const BleOnPairingEventStdFunction& callback) const; + + template + void onPairingEvent(void(T::*callback)(const BlePairingEvent& event), T* instance) const { + return onPairingEvent((callback && instance) ? std::bind(callback, instance, _1) : (BleOnPairingEventStdFunction)nullptr); + } + // This only disconnect the peer Central device, i.e. when the local device is acting as BLE Peripheral. int disconnect() const; int disconnect(const BlePeerDevice& peer) const; @@ -1010,19 +1065,19 @@ class BleLocalDevice { bool connected() const; void onConnected(BleOnConnectedCallback callback, void* context = nullptr) const; - void onConnected(const std::function& callback) const; + void onConnected(const BleOnConnectedStdFunction& callback) const; template - void onConnected(void(T::*callback)(const BlePeerDevice& peer), T* instance) { - return onConnected(std::bind(callback, instance, _1)); + void onConnected(void(T::*callback)(const BlePeerDevice& peer), T* instance) const { + return onConnected((callback && instance) ? std::bind(callback, instance, _1) : (BleOnConnectedStdFunction)nullptr); } void onDisconnected(BleOnDisconnectedCallback callback, void* context = nullptr) const; - void onDisconnected(const std::function& callback) const; + void onDisconnected(const BleOnDisconnectedStdFunction& callback) const; template - void onDisconnected(void(T::*callback)(const BlePeerDevice& peer), T* instance) { - return onDisconnected(std::bind(callback, instance, _1)); + void onDisconnected(void(T::*callback)(const BlePeerDevice& peer), T* instance) const { + return onDisconnected((callback && instance) ? std::bind(callback, instance, _1) : (BleOnDisconnectedStdFunction)nullptr); } BlePeerDevice peerCentral() const; diff --git a/wiring/src/spark_wiring_ble.cpp b/wiring/src/spark_wiring_ble.cpp index d559ed019f..0c01fde2f6 100644 --- a/wiring/src/spark_wiring_ble.cpp +++ b/wiring/src/spark_wiring_ble.cpp @@ -860,31 +860,27 @@ class BleCharacteristicImpl { dataReceivedCallback_(nullptr) { } - BleCharacteristicImpl(EnumFlags properties, const char* desc, - BleOnDataReceivedCallback callback, void* context) + BleCharacteristicImpl(EnumFlags properties, const char* desc, BleOnDataReceivedCallback callback, void* context) : BleCharacteristicImpl() { properties_ = properties; description_ = desc; - dataReceivedCallback_ = std::bind(callback, _1, _2, _3, context); + dataReceivedCallback_ = callback ? std::bind(callback, _1, _2, _3, context) : (BleOnDataReceivedStdFunction)nullptr; } - BleCharacteristicImpl(EnumFlags properties, const char* desc, - std::function callback) + BleCharacteristicImpl(EnumFlags properties, const char* desc, const BleOnDataReceivedStdFunction& callback) : BleCharacteristicImpl() { properties_ = properties; description_ = desc; dataReceivedCallback_ = callback; } - BleCharacteristicImpl(const char* desc, EnumFlags properties, BleUuid& charUuid, BleUuid& svcUuid, - BleOnDataReceivedCallback callback, void* context) + BleCharacteristicImpl(const char* desc, EnumFlags properties, BleUuid& charUuid, BleUuid& svcUuid, BleOnDataReceivedCallback callback, void* context) : BleCharacteristicImpl(properties, desc, callback, context) { charUuid_ = charUuid; svcUuid_ = svcUuid; } - BleCharacteristicImpl(const char* desc, EnumFlags properties, BleUuid& charUuid, BleUuid& svcUuid, - std::function callback) + BleCharacteristicImpl(const char* desc, EnumFlags properties, BleUuid& charUuid, BleUuid& svcUuid, const BleOnDataReceivedStdFunction& callback) : BleCharacteristicImpl(properties, desc, callback) { charUuid_ = charUuid; svcUuid_ = svcUuid; @@ -925,10 +921,10 @@ class BleCharacteristicImpl { } void setCallback(BleOnDataReceivedCallback callback, void* context) { - dataReceivedCallback_ = std::bind(callback, _1, _2, _3, context); + dataReceivedCallback_ = callback ? std::bind(callback, _1, _2, _3, context) : (BleOnDataReceivedStdFunction)nullptr; } - void setCallback(std::function callback) { + void setCallback(const BleOnDataReceivedStdFunction& callback) { dataReceivedCallback_ = callback; } @@ -970,7 +966,7 @@ class BleCharacteristicImpl { BleUuid svcUuid_; String description_; static uint16_t defaultUuidCharCount_; - std::function dataReceivedCallback_; + BleOnDataReceivedStdFunction dataReceivedCallback_; }; @@ -1103,7 +1099,8 @@ class BleLocalDeviceImpl { public: BleLocalDeviceImpl() : connectedCallback_(nullptr), - disconnectedCallback_(nullptr) { + disconnectedCallback_(nullptr), + pairingEventCallback_(nullptr) { } ~BleLocalDeviceImpl() = default; @@ -1121,21 +1118,29 @@ class BleLocalDeviceImpl { } void onConnectedCallback(BleOnConnectedCallback callback, void* context) { - connectedCallback_ = std::bind(callback, _1, context); + connectedCallback_ = callback ? std::bind(callback, _1, context) : (BleOnConnectedStdFunction)nullptr; } - void onConnectedCallback(std::function callback) { + void onConnectedCallback(const BleOnConnectedStdFunction& callback) { connectedCallback_ = callback; } void onDisconnectedCallback(BleOnDisconnectedCallback callback, void* context) { - disconnectedCallback_ = std::bind(callback, _1, context);; + disconnectedCallback_ = callback ? std::bind(callback, _1, context) : (BleOnDisconnectedStdFunction)nullptr; } - void onDisconnectedCallback(std::function callback) { + void onDisconnectedCallback(const BleOnDisconnectedStdFunction& callback) { disconnectedCallback_ = callback; } + void onPairingEvent(BleOnPairingEventCallback callback, void* context) { + pairingEventCallback_ = callback ? std::bind(callback, _1, context) : (BleOnPairingEventStdFunction)nullptr; + } + + void onPairingEvent(const BleOnPairingEventStdFunction& callback) { + pairingEventCallback_ = callback; + } + BlePeerDevice* findPeerDevice(BleConnectionHandle connHandle) { for (auto& peer : peers_) { if (peer.impl()->connHandle() == connHandle) { @@ -1186,6 +1191,31 @@ class BleLocalDeviceImpl { } break; } + case BLE_EVT_PAIRING_REQUEST_RECEIVED: + case BLE_EVT_PAIRING_PASSKEY_DISPLAY: + case BLE_EVT_PAIRING_PASSKEY_INPUT: + case BLE_EVT_PAIRING_STATUS_UPDATED: { + BlePeerDevice* peer = impl->findPeerDevice(event->conn_handle); + if (peer) { + if (impl->pairingEventCallback_) { + BlePairingEvent pairingEvent = { + .peer = *peer, + .type = static_cast(event->type) + }; + if (event->type == BLE_EVT_PAIRING_PASSKEY_DISPLAY) { + pairingEvent.payload.passkey = event->params.passkey_display.passkey; + pairingEvent.payloadLen = BLE_PAIRING_PASSKEY_LEN; + } else if (event->type == BLE_EVT_PAIRING_STATUS_UPDATED) { + pairingEvent.payload.status.status = event->params.pairing_status.status; + pairingEvent.payload.status.bonded = event->params.pairing_status.bonded; + pairingEvent.payload.status.lesc = event->params.pairing_status.lesc; + pairingEvent.payloadLen = sizeof(BlePairingStatus); + } + impl->pairingEventCallback_(pairingEvent); + } + } + break; + } default: { break; } @@ -1196,8 +1226,9 @@ class BleLocalDeviceImpl { Vector services_; Vector characteristics_; Vector peers_; - std::function connectedCallback_; - std::function disconnectedCallback_; + BleOnConnectedStdFunction connectedCallback_; + BleOnDisconnectedStdFunction disconnectedCallback_; + BleOnPairingEventStdFunction pairingEventCallback_; }; @@ -1251,8 +1282,7 @@ BleCharacteristic::BleCharacteristic(const BleCharacteristic& characteristic) DEBUG("BleCharacteristic(copy), 0x%08X => 0x%08X -> 0x%08X, count: %d", &characteristic, this, impl(), impl_.use_count()); } -BleCharacteristic::BleCharacteristic(EnumFlags properties, const char* desc, - BleOnDataReceivedCallback callback, void* context) +BleCharacteristic::BleCharacteristic(EnumFlags properties, const char* desc, BleOnDataReceivedCallback callback, void* context) : impl_(std::make_shared(properties, desc, callback, context)) { if (!impl()) { SPARK_ASSERT(false); @@ -1260,8 +1290,7 @@ BleCharacteristic::BleCharacteristic(EnumFlags proper DEBUG("BleCharacteristic(...), 0x%08X -> 0x%08X, count: %d", this, impl(), impl_.use_count()); } -BleCharacteristic::BleCharacteristic(EnumFlags properties, const char* desc, - std::function callback) +BleCharacteristic::BleCharacteristic(EnumFlags properties, const char* desc, const BleOnDataReceivedStdFunction& callback) : impl_(std::make_shared(properties, desc, callback)) { if (!impl()) { SPARK_ASSERT(false); @@ -1269,8 +1298,7 @@ BleCharacteristic::BleCharacteristic(EnumFlags proper DEBUG("BleCharacteristic(...), 0x%08X -> 0x%08X, count: %d", this, impl(), impl_.use_count()); } -void BleCharacteristic::construct(const char* desc, EnumFlags properties, BleUuid& charUuid, BleUuid& svcUuid, - BleOnDataReceivedCallback callback, void* context) { +void BleCharacteristic::construct(const char* desc, EnumFlags properties, BleUuid& charUuid, BleUuid& svcUuid, BleOnDataReceivedCallback callback, void* context) { impl_ = std::make_shared(desc, properties, charUuid, svcUuid, callback, context); if (!impl()) { SPARK_ASSERT(false); @@ -1278,8 +1306,7 @@ void BleCharacteristic::construct(const char* desc, EnumFlags 0x%08X, count: %d", this, impl(), impl_.use_count()); } -void BleCharacteristic::construct(const char* desc, EnumFlags properties, BleUuid& charUuid, BleUuid& svcUuid, - std::function callback) { +void BleCharacteristic::construct(const char* desc, EnumFlags properties, BleUuid& charUuid, BleUuid& svcUuid, const BleOnDataReceivedStdFunction& callback) { impl_ = std::make_shared(desc, properties, charUuid, svcUuid, callback); if (!impl()) { SPARK_ASSERT(false); @@ -1425,7 +1452,7 @@ void BleCharacteristic::onDataReceived(BleOnDataReceivedCallback callback, void* impl()->setCallback(callback, context); } -void BleCharacteristic::onDataReceived(std::function callback) { +void BleCharacteristic::onDataReceived(const BleOnDataReceivedStdFunction& callback) { impl()->setCallback(callback); } @@ -1526,6 +1553,7 @@ class BleDiscoveryDelegator { * SoftDevice events in queue. */ static void onCharacteristicsDiscovered(const hal_ble_char_discovered_evt_t* event, void* context) { + LOG(TRACE, "Characteristic discovered."); BlePeerDeviceImpl* peerImpl = static_cast(context); for (size_t i = 0; i < event->count; i++) { BleCharacteristic characteristic; @@ -1936,7 +1964,7 @@ void BleLocalDevice::onConnected(BleOnConnectedCallback callback, void* context) impl()->onConnectedCallback(callback, context); } -void BleLocalDevice::onConnected(const std::function& callback) const { +void BleLocalDevice::onConnected(const BleOnConnectedStdFunction& callback) const { impl()->onConnectedCallback(callback); } @@ -1944,7 +1972,7 @@ void BleLocalDevice::onDisconnected(BleOnDisconnectedCallback callback, void* co impl()->onDisconnectedCallback(callback, context); } -void BleLocalDevice::onDisconnected(const std::function& callback) const { +void BleLocalDevice::onDisconnected(const BleOnDisconnectedStdFunction& callback) const { impl()->onDisconnectedCallback(callback); } @@ -2212,7 +2240,7 @@ class BleScanDelegator { ~BleScanDelegator() = default; int start(BleOnScanResultCallback callback, void* context) { - scanResultCallback_ = std::bind(callback, _1, context); + scanResultCallback_ = callback ? std::bind(callback, _1, context) : (std::function)nullptr; scanResultCallbackRef_ = nullptr; CHECK(hal_ble_gap_start_scan(onScanResultCallback, this, nullptr)); return foundCount_; @@ -2220,7 +2248,7 @@ class BleScanDelegator { int start(BleOnScanResultCallbackRef callback, void* context) { scanResultCallback_ = nullptr; - scanResultCallbackRef_ = std::bind(callback, _1, context); + scanResultCallbackRef_ = callback ? std::bind(callback, _1, context) : (BleOnScanResultStdFunction)nullptr; CHECK(hal_ble_gap_start_scan(onScanResultCallback, this, nullptr)); return foundCount_; } @@ -2241,7 +2269,7 @@ class BleScanDelegator { return resultsVector_; } - int start(const std::function& callback) { + int start(const BleOnScanResultStdFunction& callback) { scanResultCallback_ = nullptr; scanResultCallbackRef_ = callback; CHECK(hal_ble_gap_start_scan(onScanResultCallback, this, nullptr)); @@ -2433,7 +2461,7 @@ class BleScanDelegator { size_t targetCount_; size_t foundCount_; std::function scanResultCallback_; - std::function scanResultCallbackRef_; + BleOnScanResultStdFunction scanResultCallbackRef_; BleScanFilter filter_; }; @@ -2461,7 +2489,7 @@ int BleLocalDevice::getScanParameters(BleScanParams& params) const { return getScanParameters(¶ms); } -int BleLocalDevice::scan(const std::function& callback) const { +int BleLocalDevice::scan(const BleOnScanResultStdFunction& callback) const { BleScanDelegator scanner; return scanner.start(callback); } @@ -2489,7 +2517,7 @@ Vector BleLocalDevice::scan() const { return scanner.start(); } -int BleLocalDevice::scanWithFilter(const BleScanFilter& filter, const std::function& callback) const { +int BleLocalDevice::scanWithFilter(const BleScanFilter& filter, const BleOnScanResultStdFunction& callback) const { BleScanDelegator scanner; return scanner.setScanFilter(filter).start(callback); } @@ -2558,6 +2586,42 @@ BlePeerDevice BleLocalDevice::connect(const BleAddress& addr, bool automatic) co return connect(addr, nullptr, automatic); } +int BleLocalDevice::setPairingIoCaps(BlePairingIoCaps ioCaps) const { + hal_ble_pairing_config_t config = {}; + config.version = BLE_API_VERSION; + config.size = sizeof(hal_ble_pairing_config_t); + config.io_caps = static_cast(ioCaps); + return hal_ble_gap_set_pairing_config(&config, nullptr); +} + +int BleLocalDevice::startPairing(const BlePeerDevice& peer) const { + return hal_ble_gap_start_pairing(peer.impl()->connHandle(), nullptr); +} + +int BleLocalDevice::rejectPairing(const BlePeerDevice& peer) const { + return hal_ble_gap_reject_pairing(peer.impl()->connHandle(), nullptr); +} + +int BleLocalDevice::setPairingPasskey(const BlePeerDevice& peer, const uint8_t* passkey) const { + return hal_ble_gap_set_pairing_passkey(peer.impl()->connHandle(), passkey, nullptr); +} + +bool BleLocalDevice::isPairing(const BlePeerDevice& peer) const { + return hal_ble_gap_is_pairing(peer.impl()->connHandle(), nullptr); +} + +bool BleLocalDevice::isPaired(const BlePeerDevice& peer) const { + return hal_ble_gap_is_paired(peer.impl()->connHandle(), nullptr); +} + +void BleLocalDevice::onPairingEvent(BleOnPairingEventCallback callback, void* context) const { + impl()->onPairingEvent(callback, context); +} + +void BleLocalDevice::onPairingEvent(const BleOnPairingEventStdFunction& callback) const { + impl()->onPairingEvent(callback); +} + int BleLocalDevice::disconnect() const { WiringBleLock lk; for (auto& p : impl()->peers()) { @@ -2674,14 +2738,14 @@ BleCharacteristic BleLocalDevice::addCharacteristic(EnumFlags properties, const char* desc, std::function callback) { +BleCharacteristic BleLocalDevice::addCharacteristic(EnumFlags properties, const char* desc, const BleOnDataReceivedStdFunction& callback) { WiringBleLock lk; BleCharacteristic characteristic(properties, desc, callback); addCharacteristic(characteristic); return characteristic; } -BleCharacteristic BleLocalDevice::addCharacteristic(EnumFlags properties, const String& desc, std::function callback) { +BleCharacteristic BleLocalDevice::addCharacteristic(EnumFlags properties, const String& desc, const BleOnDataReceivedStdFunction& callback) { WiringBleLock lk; return addCharacteristic(properties, desc.c_str(), callback); }