this is using the mbed os version 5-13-1
source/BleManager.cpp
- Committer:
- ocomeni
- Date:
- 2019-03-10
- Revision:
- 74:f26e846adfe9
- Child:
- 75:08eff6258e1b
File content as of revision 74:f26e846adfe9:
/* mbed Microcontroller Library
* Copyright (c) 2006-2013 ARM Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifdef BLE_SECURITY_ENABLED
#include <events/mbed_events.h>
#include <mbed.h>
#include "ble/BLE.h"
#include "SecurityManager.h"
#include "pretty_printer.h"
#if MBED_CONF_APP_FILESYSTEM_SUPPORT
#include "LittleFileSystem.h"
#include "HeapBlockDevice.h"
#endif //MBED_CONF_APP_FILESYSTEM_SUPPORT
/** This example demonstrates all the basic setup required
* for pairing and setting up link security both as a central and peripheral
*
* The example is implemented as two classes, one for the peripheral and one
* for central inheriting from a common base. They are run in sequence and
* require a peer device to connect to. During the peripheral device demonstration
* a peer device is required to connect. In the central device demonstration
* this peer device will be scanned for and connected to - therefore it should
* be advertising with the same address as when it connected.
*
* During the test output is written on the serial connection to monitor its
* progress.
*/
static const char DEVICE_NAME[] = "SM_device";
/* we have to specify the disconnect call because of ambiguous overloads */
typedef ble_error_t (Gap::*disconnect_call_t)(ble::connection_handle_t, ble::local_disconnection_reason_t);
const static disconnect_call_t disconnect_call = &Gap::disconnect;
/* for demonstration purposes we will store the peer device address
* of the device that connects to us in the first demonstration
* so we can use its address to reconnect to it later */
static BLEProtocol::AddressBytes_t peer_address;
/** Base class for both peripheral and central. The same class that provides
* the logic for the application also implements the SecurityManagerEventHandler
* which is the interface used by the Security Manager to communicate events
* back to the applications. You can provide overrides for a selection of events
* your application is interested in.
*/
class SMDevice : private mbed::NonCopyable<SMDevice>,
public SecurityManager::EventHandler,
public ble::Gap::EventHandler
{
public:
SMDevice(BLE &ble, events::EventQueue &event_queue, BLEProtocol::AddressBytes_t &peer_address) :
_led1(LED1, 0),
_ble(ble),
_event_queue(event_queue),
_peer_address(peer_address),
_handle(0),
_is_connecting(false) { };
virtual ~SMDevice()
{
if (_ble.hasInitialized()) {
_ble.shutdown();
}
};
/** Start BLE interface initialisation */
void run()
{
ble_error_t error;
/* to show we're running we'll blink every 500ms */
_event_queue.call_every(500, this, &SMDevice::blink);
if (_ble.hasInitialized()) {
printf("Ble instance already initialised.\r\n");
return;
}
/* this will inform us off all events so we can schedule their handling
* using our event queue */
_ble.onEventsToProcess(
makeFunctionPointer(this, &SMDevice::schedule_ble_events)
);
/* handle gap events */
_ble.gap().setEventHandler(this);
error = _ble.init(this, &SMDevice::on_init_complete);
if (error) {
printf("Error returned by BLE::init.\r\n");
return;
}
/* this will not return until shutdown */
_event_queue.dispatch_forever();
};
private:
/** Override to start chosen activity when initialisation completes */
virtual void start() = 0;
/** This is called when BLE interface is initialised and starts the demonstration */
void on_init_complete(BLE::InitializationCompleteCallbackContext *event)
{
ble_error_t error;
if (event->error) {
printf("Error during the initialisation\r\n");
return;
}
/* This path will be used to store bonding information but will fallback
* to storing in memory if file access fails (for example due to lack of a filesystem) */
const char* db_path = "/fs/bt_sec_db";
/* If the security manager is required this needs to be called before any
* calls to the Security manager happen. */
error = _ble.securityManager().init(
true,
false,
SecurityManager::IO_CAPS_NONE,
NULL,
false,
db_path
);
if (error) {
printf("Error during init %d\r\n", error);
return;
}
error = _ble.securityManager().preserveBondingStateOnReset(true);
if (error) {
printf("Error during preserveBondingStateOnReset %d\r\n", error);
}
#if MBED_CONF_APP_FILESYSTEM_SUPPORT
/* Enable privacy so we can find the keys */
error = _ble.gap().enablePrivacy(true);
if (error) {
printf("Error enabling privacy\r\n");
}
Gap::PeripheralPrivacyConfiguration_t configuration_p = {
/* use_non_resolvable_random_address */ false,
Gap::PeripheralPrivacyConfiguration_t::REJECT_NON_RESOLVED_ADDRESS
};
_ble.gap().setPeripheralPrivacyConfiguration(&configuration_p);
Gap::CentralPrivacyConfiguration_t configuration_c = {
/* use_non_resolvable_random_address */ false,
Gap::CentralPrivacyConfiguration_t::RESOLVE_AND_FORWARD
};
_ble.gap().setCentralPrivacyConfiguration(&configuration_c);
/* this demo switches between being master and slave */
_ble.securityManager().setHintFutureRoleReversal(true);
#endif
/* Tell the security manager to use methods in this class to inform us
* of any events. Class needs to implement SecurityManagerEventHandler. */
_ble.securityManager().setSecurityManagerEventHandler(this);
/* gap events also handled by this class */
_ble.gap().setEventHandler(this);
/* print device address */
Gap::AddressType_t addr_type;
Gap::Address_t addr;
_ble.gap().getAddress(&addr_type, addr);
print_address(addr);
/* start test in 500 ms */
_event_queue.call_in(500, this, &SMDevice::start);
};
/** Schedule processing of events from the BLE in the event queue. */
void schedule_ble_events(BLE::OnEventsToProcessCallbackContext *context)
{
_event_queue.call(mbed::callback(&context->ble, &BLE::processEvents));
};
/** Blink LED to show we're running */
void blink(void)
{
_led1 = !_led1;
};
private:
/* Event handler */
/** Respond to a pairing request. This will be called by the stack
* when a pairing request arrives and expects the application to
* call acceptPairingRequest or cancelPairingRequest */
virtual void pairingRequest(
ble::connection_handle_t connectionHandle
) {
printf("Pairing requested - authorising\r\n");
_ble.securityManager().acceptPairingRequest(connectionHandle);
}
/** Inform the application of a successful pairing. Terminate the demonstration. */
virtual void pairingResult(
ble::connection_handle_t connectionHandle,
SecurityManager::SecurityCompletionStatus_t result
) {
if (result == SecurityManager::SEC_STATUS_SUCCESS) {
printf("Pairing successful\r\n");
} else {
printf("Pairing failed\r\n");
}
}
/** Inform the application of change in encryption status. This will be
* communicated through the serial port */
virtual void linkEncryptionResult(
ble::connection_handle_t connectionHandle,
ble::link_encryption_t result
) {
if (result == ble::link_encryption_t::ENCRYPTED) {
printf("Link ENCRYPTED\r\n");
} else if (result == ble::link_encryption_t::ENCRYPTED_WITH_MITM) {
printf("Link ENCRYPTED_WITH_MITM\r\n");
} else if (result == ble::link_encryption_t::NOT_ENCRYPTED) {
printf("Link NOT_ENCRYPTED\r\n");
}
/* disconnect in 2 s */
_event_queue.call_in(
2000,
&_ble.gap(),
disconnect_call,
_handle,
ble::local_disconnection_reason_t(ble::local_disconnection_reason_t::USER_TERMINATION)
);
}
/** This is called by Gap to notify the application we disconnected,
* in our case it ends the demonstration. */
virtual void onDisconnectionComplete(const ble::DisconnectionCompleteEvent &)
{
printf("Diconnected\r\n");
_event_queue.break_dispatch();
};
virtual void onAdvertisingEnd(const ble::AdvertisingEndEvent &)
{
printf("Advertising timed out - aborting\r\n");
_event_queue.break_dispatch();
}
virtual void onScanTimeout(const ble::ScanTimeoutEvent &)
{
printf("Scan timed out - aborting\r\n");
_event_queue.break_dispatch();
}
private:
DigitalOut _led1;
protected:
BLE &_ble;
events::EventQueue &_event_queue;
BLEProtocol::AddressBytes_t &_peer_address;
ble::connection_handle_t _handle;
bool _is_connecting;
};
/** A peripheral device will advertise, accept the connection and request
* a change in link security. */
class SMDevicePeripheral : public SMDevice {
public:
SMDevicePeripheral(BLE &ble, events::EventQueue &event_queue, BLEProtocol::AddressBytes_t &peer_address)
: SMDevice(ble, event_queue, peer_address) { }
virtual void start()
{
/* Set up and start advertising */
uint8_t adv_buffer[ble::LEGACY_ADVERTISING_MAX_SIZE];
/* use the helper to build the payload */
ble::AdvertisingDataBuilder adv_data_builder(
adv_buffer
);
adv_data_builder.setFlags();
adv_data_builder.setName(DEVICE_NAME);
/* Set payload for the set */
ble_error_t error = _ble.gap().setAdvertisingPayload(
ble::LEGACY_ADVERTISING_HANDLE,
adv_data_builder.getAdvertisingData()
);
if (error) {
print_error(error, "Gap::setAdvertisingPayload() failed");
_event_queue.break_dispatch();
return;
}
ble::AdvertisingParameters adv_parameters(
ble::advertising_type_t::CONNECTABLE_UNDIRECTED
);
error = _ble.gap().setAdvertisingParameters(
ble::LEGACY_ADVERTISING_HANDLE,
adv_parameters
);
if (error) {
print_error(error, "Gap::setAdvertisingParameters() failed");
return;
}
error = _ble.gap().startAdvertising(ble::LEGACY_ADVERTISING_HANDLE);
if (error) {
print_error(error, "Gap::startAdvertising() failed");
return;
}
printf("Please connect to device\r\n");
/** This tells the stack to generate a pairingRequest event
* which will require this application to respond before pairing
* can proceed. Setting it to false will automatically accept
* pairing. */
_ble.securityManager().setPairingRequestAuthorisation(true);
};
/** This is called by Gap to notify the application we connected,
* in our case it immediately requests a change in link security */
virtual void onConnectionComplete(const ble::ConnectionCompleteEvent &event)
{
ble_error_t error;
/* remember the device that connects to us now so we can connect to it
* during the next demonstration */
memcpy(_peer_address, event.getPeerAddress().data(), sizeof(_peer_address));
printf("Connected to peer: ");
print_address(event.getPeerAddress().data());
_handle = event.getConnectionHandle();
/* Request a change in link security. This will be done
* indirectly by asking the master of the connection to
* change it. Depending on circumstances different actions
* may be taken by the master which will trigger events
* which the applications should deal with. */
error = _ble.securityManager().setLinkSecurity(
_handle,
SecurityManager::SECURITY_MODE_ENCRYPTION_NO_MITM
);
if (error) {
printf("Error during SM::setLinkSecurity %d\r\n", error);
return;
}
};
};
/** A central device will scan, connect to a peer and request pairing. */
class SMDeviceCentral : public SMDevice {
public:
SMDeviceCentral(BLE &ble, events::EventQueue &event_queue, BLEProtocol::AddressBytes_t &peer_address)
: SMDevice(ble, event_queue, peer_address) { }
virtual void start()
{
ble::ScanParameters params;
ble_error_t error = _ble.gap().setScanParameters(params);
if (error) {
print_error(error, "Error in Gap::startScan %d\r\n");
return;
}
/* start scanning, results will be handled by onAdvertisingReport */
error = _ble.gap().startScan();
if (error) {
print_error(error, "Error in Gap::startScan %d\r\n");
return;
}
printf("Please advertise\r\n");
printf("Scanning for: ");
print_address(_peer_address);
}
private:
/* Gap::EventHandler */
/** Look at scan payload to find a peer device and connect to it */
virtual void onAdvertisingReport(const ble::AdvertisingReportEvent &event)
{
/* don't bother with analysing scan result if we're already connecting */
if (_is_connecting) {
return;
}
/* parse the advertising payload, looking for a discoverable device */
if (memcmp(event.getPeerAddress().data(), _peer_address, sizeof(_peer_address)) == 0) {
ble_error_t error = _ble.gap().stopScan();
if (error) {
print_error(error, "Error caused by Gap::stopScan");
return;
}
ble::ConnectionParameters connection_params(
ble::phy_t::LE_1M,
ble::scan_interval_t(50),
ble::scan_window_t(50),
ble::conn_interval_t(50),
ble::conn_interval_t(100),
ble::slave_latency_t(0),
ble::supervision_timeout_t(100)
);
connection_params.setOwnAddressType(ble::own_address_type_t::RANDOM);
error = _ble.gap().connect(
event.getPeerAddressType(),
event.getPeerAddress(),
connection_params
);
if (error) {
print_error(error, "Error caused by Gap::connect");
return;
}
/* we may have already scan events waiting
* to be processed so we need to remember
* that we are already connecting and ignore them */
_is_connecting = true;
return;
}
}
/** This is called by Gap to notify the application we connected,
* in our case it immediately request pairing */
virtual void onConnectionComplete(const ble::ConnectionCompleteEvent &event)
{
if (event.getStatus() == BLE_ERROR_NONE) {
/* store the handle for future Security Manager requests */
_handle = event.getConnectionHandle();
printf("Connected\r\n");
/* in this example the local device is the master so we request pairing */
ble_error_t error = _ble.securityManager().requestPairing(_handle);
if (error) {
printf("Error during SM::requestPairing %d\r\n", error);
return;
}
/* upon pairing success the application will disconnect */
}
/* failed to connect - restart scan */
ble_error_t error = _ble.gap().startScan();
if (error) {
print_error(error, "Error in Gap::startScan %d\r\n");
return;
}
};
};
#if MBED_CONF_APP_FILESYSTEM_SUPPORT
bool create_filesystem()
{
static LittleFileSystem fs("fs");
/* replace this with any physical block device your board supports (like an SD card) */
static HeapBlockDevice bd(4096, 256);
int err = bd.init();
if (err) {
return false;
}
err = bd.erase(0, bd.size());
if (err) {
return false;
}
err = fs.mount(&bd);
if (err) {
/* Reformat if we can't mount the filesystem */
printf("No filesystem found, formatting...\r\n");
err = fs.reformat(&bd);
if (err) {
return false;
}
}
return true;
}
#endif //MBED_CONF_APP_FILESYSTEM_SUPPORT
int main()
{
BLE& ble = BLE::Instance();
events::EventQueue queue;
#if MBED_CONF_APP_FILESYSTEM_SUPPORT
/* if filesystem creation fails or there is no filesystem the security manager
* will fallback to storing the security database in memory */
if (!create_filesystem()) {
printf("Filesystem creation failed, will use memory storage\r\n");
}
#endif
while(1) {
{
printf("\r\n PERIPHERAL \r\n\r\n");
SMDevicePeripheral peripheral(ble, queue, peer_address);
peripheral.run();
}
{
printf("\r\n CENTRAL \r\n\r\n");
SMDeviceCentral central(ble, queue, peer_address);
central.run();
}
}
return 0;
}
#endif