MicroBit as BLE gamepad
Fork of nRF51822 by
source/nRF5xGap.h@561:613dbbdeed27, 2016-01-11 (annotated)
- Committer:
- vcoubard
- Date:
- Mon Jan 11 10:19:15 2016 +0000
- Revision:
- 561:613dbbdeed27
- Parent:
- 550:bcb632fc92df
- Child:
- 563:9c4b96f7be8d
Synchronized with git rev 7bf81e7e
Author: Andres Amaya Garcia
Improve shutdown to clear BLE API and not just SD
Improve the shutdown functionality, such that a call to ble.shutdown() from
the user application clears the API and nRF5x state and NOT only the
SoftDevice. To achieve this the following changes are introduced:
* Add a protected member cleanup() to nRF5xGap, nRF5xGattClient,
nRF5xGattServer, nRF5xSecurityManager and nRF5xServiceDiscovery.
* Modify the shutdown() implementation in nRF5xn such that it also calls the
static member shutdown() exposed by the BLE API in Gap.h, SecurityManager.h,
GattClient.h and GattServer.h.
* Modify nRF5xGattClient, nRF5xGattServer and nRF5xSecurityManager
classes so that they dynamically create their respective objects only if
needed. Previously the GattClient, GattServer and SecurityManager objects were
declared as static, which means that they were always present even though they
were not always needed. This increases memory consumption unnecessarily.
Furthermore, pointers to the object instances are stored in static members of
the classes as specified by the BLE API base classes. This ensures that
calls to shutdown do not require calls to getInstance() functions that would
otherwise result in undesired memory allocations.
* nRF5xGap object is always needed, so this remains allocated statically. But
the reference in Gap is pointed to this object.
The shutdown procedure is as follows:
1. The user calls ble.shutdown() which executes the code in nRF5xn::shutdown()
1. The SoftDevice is shutdown
1. The static members of Gap.h, SecurityManager.h, GattClient.h and
GattServer.h are called to clean up their own state.
If at any point an error occur during the last step, BLE_ERROR_INVALID_STATE is
returned.
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
vcoubard | 541:884f95bf5351 | 1 | /* mbed Microcontroller Library |
vcoubard | 541:884f95bf5351 | 2 | * Copyright (c) 2006-2013 ARM Limited |
vcoubard | 541:884f95bf5351 | 3 | * |
vcoubard | 541:884f95bf5351 | 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
vcoubard | 541:884f95bf5351 | 5 | * you may not use this file except in compliance with the License. |
vcoubard | 541:884f95bf5351 | 6 | * You may obtain a copy of the License at |
vcoubard | 541:884f95bf5351 | 7 | * |
vcoubard | 541:884f95bf5351 | 8 | * http://www.apache.org/licenses/LICENSE-2.0 |
vcoubard | 541:884f95bf5351 | 9 | * |
vcoubard | 541:884f95bf5351 | 10 | * Unless required by applicable law or agreed to in writing, software |
vcoubard | 541:884f95bf5351 | 11 | * distributed under the License is distributed on an "AS IS" BASIS, |
vcoubard | 541:884f95bf5351 | 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
vcoubard | 541:884f95bf5351 | 13 | * See the License for the specific language governing permissions and |
vcoubard | 541:884f95bf5351 | 14 | * limitations under the License. |
vcoubard | 541:884f95bf5351 | 15 | */ |
vcoubard | 541:884f95bf5351 | 16 | |
vcoubard | 541:884f95bf5351 | 17 | #ifndef __NRF5x_GAP_H__ |
vcoubard | 541:884f95bf5351 | 18 | #define __NRF5x_GAP_H__ |
vcoubard | 541:884f95bf5351 | 19 | |
vcoubard | 541:884f95bf5351 | 20 | #include "mbed.h" |
vcoubard | 541:884f95bf5351 | 21 | #include "ble/blecommon.h" |
vcoubard | 541:884f95bf5351 | 22 | #include "ble.h" |
vcoubard | 541:884f95bf5351 | 23 | #include "ble/GapAdvertisingParams.h" |
vcoubard | 541:884f95bf5351 | 24 | #include "ble/GapAdvertisingData.h" |
vcoubard | 541:884f95bf5351 | 25 | #include "ble/Gap.h" |
vcoubard | 541:884f95bf5351 | 26 | #include "ble/GapScanningParams.h" |
vcoubard | 541:884f95bf5351 | 27 | |
vcoubard | 541:884f95bf5351 | 28 | #include "nrf_soc.h" |
vcoubard | 549:3f782c64d014 | 29 | |
vcoubard | 549:3f782c64d014 | 30 | extern "C" { |
vcoubard | 541:884f95bf5351 | 31 | #include "ble_radio_notification.h" |
vcoubard | 549:3f782c64d014 | 32 | } |
vcoubard | 549:3f782c64d014 | 33 | |
vcoubard | 541:884f95bf5351 | 34 | #include "btle_security.h" |
vcoubard | 541:884f95bf5351 | 35 | |
vcoubard | 541:884f95bf5351 | 36 | void radioNotificationStaticCallback(bool param); |
vcoubard | 541:884f95bf5351 | 37 | |
vcoubard | 541:884f95bf5351 | 38 | /**************************************************************************/ |
vcoubard | 541:884f95bf5351 | 39 | /*! |
vcoubard | 541:884f95bf5351 | 40 | \brief |
vcoubard | 541:884f95bf5351 | 41 | |
vcoubard | 541:884f95bf5351 | 42 | */ |
vcoubard | 541:884f95bf5351 | 43 | /**************************************************************************/ |
vcoubard | 541:884f95bf5351 | 44 | class nRF5xGap : public Gap |
vcoubard | 541:884f95bf5351 | 45 | { |
vcoubard | 541:884f95bf5351 | 46 | public: |
vcoubard | 541:884f95bf5351 | 47 | static nRF5xGap &getInstance(); |
vcoubard | 541:884f95bf5351 | 48 | |
vcoubard | 541:884f95bf5351 | 49 | /* Functions that must be implemented from Gap */ |
vcoubard | 541:884f95bf5351 | 50 | virtual ble_error_t setAddress(AddressType_t type, const Address_t address); |
vcoubard | 541:884f95bf5351 | 51 | virtual ble_error_t getAddress(AddressType_t *typeP, Address_t address); |
vcoubard | 541:884f95bf5351 | 52 | virtual ble_error_t setAdvertisingData(const GapAdvertisingData &, const GapAdvertisingData &); |
vcoubard | 541:884f95bf5351 | 53 | |
vcoubard | 541:884f95bf5351 | 54 | virtual uint16_t getMinAdvertisingInterval(void) const {return GapAdvertisingParams::ADVERTISEMENT_DURATION_UNITS_TO_MS(BLE_GAP_ADV_INTERVAL_MIN);} |
vcoubard | 541:884f95bf5351 | 55 | virtual uint16_t getMinNonConnectableAdvertisingInterval(void) const {return GapAdvertisingParams::ADVERTISEMENT_DURATION_UNITS_TO_MS(BLE_GAP_ADV_NONCON_INTERVAL_MIN);} |
vcoubard | 541:884f95bf5351 | 56 | virtual uint16_t getMaxAdvertisingInterval(void) const {return GapAdvertisingParams::ADVERTISEMENT_DURATION_UNITS_TO_MS(BLE_GAP_ADV_INTERVAL_MAX);} |
vcoubard | 541:884f95bf5351 | 57 | |
vcoubard | 541:884f95bf5351 | 58 | virtual ble_error_t startAdvertising(const GapAdvertisingParams &); |
vcoubard | 541:884f95bf5351 | 59 | virtual ble_error_t stopAdvertising(void); |
vcoubard | 541:884f95bf5351 | 60 | virtual ble_error_t connect(const Address_t, Gap::AddressType_t peerAddrType, const ConnectionParams_t *connectionParams, const GapScanningParams *scanParams); |
vcoubard | 541:884f95bf5351 | 61 | virtual ble_error_t disconnect(Handle_t connectionHandle, DisconnectionReason_t reason); |
vcoubard | 541:884f95bf5351 | 62 | virtual ble_error_t disconnect(DisconnectionReason_t reason); |
vcoubard | 541:884f95bf5351 | 63 | |
vcoubard | 541:884f95bf5351 | 64 | virtual ble_error_t setDeviceName(const uint8_t *deviceName); |
vcoubard | 541:884f95bf5351 | 65 | virtual ble_error_t getDeviceName(uint8_t *deviceName, unsigned *lengthP); |
vcoubard | 541:884f95bf5351 | 66 | virtual ble_error_t setAppearance(GapAdvertisingData::Appearance appearance); |
vcoubard | 541:884f95bf5351 | 67 | virtual ble_error_t getAppearance(GapAdvertisingData::Appearance *appearanceP); |
vcoubard | 541:884f95bf5351 | 68 | |
vcoubard | 541:884f95bf5351 | 69 | virtual ble_error_t setTxPower(int8_t txPower); |
vcoubard | 541:884f95bf5351 | 70 | virtual void getPermittedTxPowerValues(const int8_t **valueArrayPP, size_t *countP); |
vcoubard | 541:884f95bf5351 | 71 | |
vcoubard | 541:884f95bf5351 | 72 | void setConnectionHandle(uint16_t con_handle); |
vcoubard | 541:884f95bf5351 | 73 | uint16_t getConnectionHandle(void); |
vcoubard | 541:884f95bf5351 | 74 | |
vcoubard | 541:884f95bf5351 | 75 | virtual ble_error_t getPreferredConnectionParams(ConnectionParams_t *params); |
vcoubard | 541:884f95bf5351 | 76 | virtual ble_error_t setPreferredConnectionParams(const ConnectionParams_t *params); |
vcoubard | 541:884f95bf5351 | 77 | virtual ble_error_t updateConnectionParams(Handle_t handle, const ConnectionParams_t *params); |
vcoubard | 541:884f95bf5351 | 78 | |
vcoubard | 541:884f95bf5351 | 79 | virtual ble_error_t initRadioNotification(void) { |
vcoubard | 541:884f95bf5351 | 80 | if (ble_radio_notification_init(NRF_APP_PRIORITY_HIGH, NRF_RADIO_NOTIFICATION_DISTANCE_800US, radioNotificationStaticCallback) == NRF_SUCCESS) { |
vcoubard | 541:884f95bf5351 | 81 | return BLE_ERROR_NONE; |
vcoubard | 541:884f95bf5351 | 82 | } |
vcoubard | 541:884f95bf5351 | 83 | |
vcoubard | 541:884f95bf5351 | 84 | return BLE_ERROR_UNSPECIFIED; |
vcoubard | 541:884f95bf5351 | 85 | } |
vcoubard | 541:884f95bf5351 | 86 | |
vcoubard | 541:884f95bf5351 | 87 | /* Observer role is not supported by S110, return BLE_ERROR_NOT_IMPLEMENTED */ |
vcoubard | 541:884f95bf5351 | 88 | #if !defined(TARGET_MCU_NRF51_16K_S110) && !defined(TARGET_MCU_NRF51_32K_S110) |
vcoubard | 541:884f95bf5351 | 89 | virtual ble_error_t startRadioScan(const GapScanningParams &scanningParams) { |
vcoubard | 541:884f95bf5351 | 90 | ble_gap_scan_params_t scanParams = { |
vcoubard | 541:884f95bf5351 | 91 | .active = scanningParams.getActiveScanning(), /**< If 1, perform active scanning (scan requests). */ |
vcoubard | 541:884f95bf5351 | 92 | .selective = 0, /**< If 1, ignore unknown devices (non whitelisted). */ |
vcoubard | 541:884f95bf5351 | 93 | .p_whitelist = NULL, /**< Pointer to whitelist, NULL if none is given. */ |
vcoubard | 541:884f95bf5351 | 94 | .interval = scanningParams.getInterval(), /**< Scan interval between 0x0004 and 0x4000 in 0.625ms units (2.5ms to 10.24s). */ |
vcoubard | 541:884f95bf5351 | 95 | .window = scanningParams.getWindow(), /**< Scan window between 0x0004 and 0x4000 in 0.625ms units (2.5ms to 10.24s). */ |
vcoubard | 541:884f95bf5351 | 96 | .timeout = scanningParams.getTimeout(), /**< Scan timeout between 0x0001 and 0xFFFF in seconds, 0x0000 disables timeout. */ |
vcoubard | 541:884f95bf5351 | 97 | }; |
vcoubard | 541:884f95bf5351 | 98 | |
vcoubard | 541:884f95bf5351 | 99 | if (sd_ble_gap_scan_start(&scanParams) != NRF_SUCCESS) { |
vcoubard | 541:884f95bf5351 | 100 | return BLE_ERROR_PARAM_OUT_OF_RANGE; |
vcoubard | 541:884f95bf5351 | 101 | } |
vcoubard | 541:884f95bf5351 | 102 | |
vcoubard | 541:884f95bf5351 | 103 | return BLE_ERROR_NONE; |
vcoubard | 541:884f95bf5351 | 104 | } |
vcoubard | 541:884f95bf5351 | 105 | |
vcoubard | 541:884f95bf5351 | 106 | virtual ble_error_t stopScan(void) { |
vcoubard | 541:884f95bf5351 | 107 | if (sd_ble_gap_scan_stop() == NRF_SUCCESS) { |
vcoubard | 541:884f95bf5351 | 108 | return BLE_ERROR_NONE; |
vcoubard | 541:884f95bf5351 | 109 | } |
vcoubard | 541:884f95bf5351 | 110 | |
vcoubard | 541:884f95bf5351 | 111 | return BLE_STACK_BUSY; |
vcoubard | 541:884f95bf5351 | 112 | } |
vcoubard | 541:884f95bf5351 | 113 | #endif |
vcoubard | 541:884f95bf5351 | 114 | |
vcoubard | 561:613dbbdeed27 | 115 | protected: |
vcoubard | 561:613dbbdeed27 | 116 | virtual ble_error_t cleanup(void); |
vcoubard | 561:613dbbdeed27 | 117 | |
vcoubard | 541:884f95bf5351 | 118 | private: |
vcoubard | 546:1e147322b2b5 | 119 | bool radioNotificationCallbackParam; /* parameter to be passed into the Timeout-generated radio notification callback. */ |
vcoubard | 546:1e147322b2b5 | 120 | Timeout radioNotificationTimeout; |
vcoubard | 546:1e147322b2b5 | 121 | |
vcoubard | 546:1e147322b2b5 | 122 | /* |
vcoubard | 550:bcb632fc92df | 123 | * A helper function to post radio notification callbacks with low interrupt priority. |
vcoubard | 546:1e147322b2b5 | 124 | */ |
vcoubard | 546:1e147322b2b5 | 125 | void postRadioNotificationCallback(void) { |
vcoubard | 550:bcb632fc92df | 126 | #ifdef YOTTA_CFG_MBED_OS |
vcoubard | 550:bcb632fc92df | 127 | /* |
vcoubard | 550:bcb632fc92df | 128 | * In mbed OS, all user-facing BLE events (interrupts) are posted to the |
vcoubard | 550:bcb632fc92df | 129 | * MINAR scheduler to be executed as callbacks in thread mode. MINAR guards |
vcoubard | 550:bcb632fc92df | 130 | * its critical sections from interrupts by acquiring CriticalSectionLock, |
vcoubard | 550:bcb632fc92df | 131 | * which results in a call to sd_nvic_critical_region_enter(). Thus, it is |
vcoubard | 550:bcb632fc92df | 132 | * safe to invoke MINAR APIs from interrupt context as long as those |
vcoubard | 550:bcb632fc92df | 133 | * interrupts are blocked by sd_nvic_critical_region_enter(). |
vcoubard | 550:bcb632fc92df | 134 | * |
vcoubard | 550:bcb632fc92df | 135 | * Radio notifications are a special case for the above. The Radio |
vcoubard | 550:bcb632fc92df | 136 | * Notification IRQ is handled at a very high priority--higher than the |
vcoubard | 550:bcb632fc92df | 137 | * level blocked by sd_nvic_critical_region_enter(). Thus Radio Notification |
vcoubard | 550:bcb632fc92df | 138 | * events can preempt MINAR's critical sections. Using MINAR APIs (such as |
vcoubard | 550:bcb632fc92df | 139 | * posting an event) directly in processRadioNotification() may result in a |
vcoubard | 550:bcb632fc92df | 140 | * race condition ending in a hard-fault. |
vcoubard | 550:bcb632fc92df | 141 | * |
vcoubard | 550:bcb632fc92df | 142 | * The solution is to *not* call MINAR APIs directly from the Radio |
vcoubard | 550:bcb632fc92df | 143 | * Notification handling; i.e. to do the bulk of RadioNotification |
vcoubard | 550:bcb632fc92df | 144 | * processing at a reduced priority which respects MINAR's critical |
vcoubard | 550:bcb632fc92df | 145 | * sections. Unfortunately, on a cortex-M0, there is no clean way to demote |
vcoubard | 550:bcb632fc92df | 146 | * priority for the currently executing interrupt--we wouldn't want to |
vcoubard | 550:bcb632fc92df | 147 | * demote the radio notification handling anyway because it is sensitive to |
vcoubard | 550:bcb632fc92df | 148 | * timing, and the system expects to finish this handling very quickly. The |
vcoubard | 550:bcb632fc92df | 149 | * workaround is to employ a Timeout to trigger |
vcoubard | 550:bcb632fc92df | 150 | * postRadioNotificationCallback() after a very short delay (~0 us) and post |
vcoubard | 550:bcb632fc92df | 151 | * the MINAR callback that context. |
vcoubard | 550:bcb632fc92df | 152 | * |
vcoubard | 550:bcb632fc92df | 153 | * !!!WARNING!!! Radio notifications are very time critical events. The |
vcoubard | 550:bcb632fc92df | 154 | * current solution is expected to work under the assumption that |
vcoubard | 550:bcb632fc92df | 155 | * postRadioNotificationCalback() will be executed BEFORE the next radio |
vcoubard | 550:bcb632fc92df | 156 | * notification event is generated. |
vcoubard | 550:bcb632fc92df | 157 | */ |
vcoubard | 546:1e147322b2b5 | 158 | minar::Scheduler::postCallback( |
vcoubard | 546:1e147322b2b5 | 159 | mbed::util::FunctionPointer1<void, bool>(&radioNotificationCallback, &FunctionPointerWithContext<bool>::call).bind(radioNotificationCallbackParam) |
vcoubard | 546:1e147322b2b5 | 160 | ); |
vcoubard | 550:bcb632fc92df | 161 | #else |
vcoubard | 550:bcb632fc92df | 162 | /* |
vcoubard | 550:bcb632fc92df | 163 | * In mbed classic, all user-facing BLE events execute callbacks in interrupt |
vcoubard | 550:bcb632fc92df | 164 | * mode. Radio Notifications are a special case because its IRQ is handled at |
vcoubard | 550:bcb632fc92df | 165 | * a very high priority. Thus Radio Notification events can preempt other |
vcoubard | 550:bcb632fc92df | 166 | * operations that require interaction with the SoftDevice such as advertising |
vcoubard | 550:bcb632fc92df | 167 | * payload updates and changing the Gap state. Therefore, executing a Radio |
vcoubard | 550:bcb632fc92df | 168 | * Notification callback directly from processRadioNotification() may result |
vcoubard | 550:bcb632fc92df | 169 | * in a race condition ending in a hard-fault. |
vcoubard | 550:bcb632fc92df | 170 | * |
vcoubard | 550:bcb632fc92df | 171 | * The solution is to *not* execute the Radio Notification callback directly |
vcoubard | 550:bcb632fc92df | 172 | * from the Radio Notification handling; i.e. to do the bulk of the |
vcoubard | 550:bcb632fc92df | 173 | * Radio Notification processing at a reduced priority. Unfortunately, on a |
vcoubard | 550:bcb632fc92df | 174 | * cortex-M0, there is no clean way to demote priority for the currently |
vcoubard | 550:bcb632fc92df | 175 | * executing interrupt--we wouldn't want to demote the radio notification |
vcoubard | 550:bcb632fc92df | 176 | * handling anyway because it is sensitive to timing, and the system expects |
vcoubard | 550:bcb632fc92df | 177 | * to finish this handling very quickly. The workaround is to employ a Timeout |
vcoubard | 550:bcb632fc92df | 178 | * to trigger postRadioNotificationCallback() after a very short delay (~0 us) |
vcoubard | 550:bcb632fc92df | 179 | * and execute the callback in that context. |
vcoubard | 550:bcb632fc92df | 180 | * |
vcoubard | 550:bcb632fc92df | 181 | * !!!WARNING!!! Radio notifications are very time critical events. The |
vcoubard | 550:bcb632fc92df | 182 | * current solution is expected to work under the assumption that |
vcoubard | 550:bcb632fc92df | 183 | * postRadioNotificationCalback() will be executed BEFORE the next radio |
vcoubard | 550:bcb632fc92df | 184 | * notification event is generated. |
vcoubard | 550:bcb632fc92df | 185 | */ |
vcoubard | 550:bcb632fc92df | 186 | radioNotificationCallback.call(radioNotificationCallbackParam); |
vcoubard | 550:bcb632fc92df | 187 | #endif /* #ifdef YOTTA_CFG_MBED_OS */ |
vcoubard | 546:1e147322b2b5 | 188 | } |
vcoubard | 546:1e147322b2b5 | 189 | |
vcoubard | 541:884f95bf5351 | 190 | /** |
vcoubard | 541:884f95bf5351 | 191 | * A helper function to process radio-notification events; to be called internally. |
vcoubard | 541:884f95bf5351 | 192 | * @param param [description] |
vcoubard | 541:884f95bf5351 | 193 | */ |
vcoubard | 541:884f95bf5351 | 194 | void processRadioNotificationEvent(bool param) { |
vcoubard | 546:1e147322b2b5 | 195 | radioNotificationCallbackParam = param; |
vcoubard | 546:1e147322b2b5 | 196 | radioNotificationTimeout.attach_us(this, &nRF5xGap::postRadioNotificationCallback, 0); |
vcoubard | 541:884f95bf5351 | 197 | } |
vcoubard | 541:884f95bf5351 | 198 | friend void radioNotificationStaticCallback(bool param); /* allow invocations of processRadioNotificationEvent() */ |
vcoubard | 541:884f95bf5351 | 199 | |
vcoubard | 541:884f95bf5351 | 200 | private: |
vcoubard | 541:884f95bf5351 | 201 | uint16_t m_connectionHandle; |
vcoubard | 541:884f95bf5351 | 202 | nRF5xGap() { |
vcoubard | 541:884f95bf5351 | 203 | m_connectionHandle = BLE_CONN_HANDLE_INVALID; |
vcoubard | 541:884f95bf5351 | 204 | } |
vcoubard | 541:884f95bf5351 | 205 | |
vcoubard | 541:884f95bf5351 | 206 | nRF5xGap(nRF5xGap const &); |
vcoubard | 541:884f95bf5351 | 207 | void operator=(nRF5xGap const &); |
vcoubard | 541:884f95bf5351 | 208 | }; |
vcoubard | 541:884f95bf5351 | 209 | |
rgrover1 | 388:db85a09c27ef | 210 | #endif // ifndef __NRF5x_GAP_H__ |