Attempting to publish a tree

Dependencies:   nrf51-sdk

Dependents:   microbit-dal

Fork of nRF51822 by Lancaster University

Revision:
616:a8f9b022d8fd
Parent:
615:65ea2acfc6a2
--- a/source/nRF5xGap.h	Wed Apr 06 22:38:43 2016 +0100
+++ b/source/nRF5xGap.h	Wed Apr 06 22:39:17 2016 +0100
@@ -17,7 +17,23 @@
 #ifndef __NRF5x_GAP_H__
 #define __NRF5x_GAP_H__
 
-#include "mbed.h"
+#ifdef YOTTA_CFG_MBED_OS
+    #include "mbed-drivers/mbed.h"
+#else
+    #include "mbed.h"
+#endif
+#ifndef YOTTA_CFG_WHITELIST_MAX_SIZE
+    #define YOTTA_CFG_WHITELIST_MAX_SIZE BLE_GAP_WHITELIST_ADDR_MAX_COUNT
+#elif YOTTA_CFG_WHITELIST_MAX_SIZE > BLE_GAP_WHITELIST_ADDR_MAX_COUNT
+    #undef YOTTA_CFG_WHITELIST_MAX_SIZE
+    #define YOTTA_CFG_WHITELIST_MAX_SIZE BLE_GAP_WHITELIST_ADDR_MAX_COUNT
+#endif
+#ifndef YOTTA_CFG_IRK_TABLE_MAX_SIZE
+    #define YOTTA_CFG_IRK_TABLE_MAX_SIZE BLE_GAP_WHITELIST_IRK_MAX_COUNT
+#elif YOTTA_CFG_IRK_TABLE_MAX_SIZE > BLE_GAP_WHITELIST_IRK_MAX_COUNT
+    #undef YOTTA_CFG_IRK_TABLE_MAX_SIZE
+    #define YOTTA_CFG_IRK_TABLE_MAX_SIZE BLE_GAP_WHITELIST_IRK_MAX_COUNT
+#endif
 #include "ble/blecommon.h"
 #include "ble.h"
 #include "ble/GapAdvertisingParams.h"
@@ -26,7 +42,11 @@
 #include "ble/GapScanningParams.h"
 
 #include "nrf_soc.h"
+
+extern "C" {
 #include "ble_radio_notification.h"
+}
+
 #include "btle_security.h"
 
 void radioNotificationStaticCallback(bool param);
@@ -40,8 +60,6 @@
 class nRF5xGap : public Gap
 {
 public:
-    static nRF5xGap &getInstance();
-
     /* Functions that must be implemented from Gap */
     virtual ble_error_t setAddress(AddressType_t  type,  const Address_t address);
     virtual ble_error_t getAddress(AddressType_t *typeP, Address_t address);
@@ -53,7 +71,7 @@
 
     virtual ble_error_t startAdvertising(const GapAdvertisingParams &);
     virtual ble_error_t stopAdvertising(void);
-    virtual ble_error_t connect(const Address_t, Gap::AddressType_t peerAddrType, const ConnectionParams_t *connectionParams, const GapScanningParams *scanParams);
+    virtual ble_error_t connect(const Address_t, BLEProtocol::AddressType_t peerAddrType, const ConnectionParams_t *connectionParams, const GapScanningParams *scanParams);
     virtual ble_error_t disconnect(Handle_t connectionHandle, DisconnectionReason_t reason);
     virtual ble_error_t disconnect(DisconnectionReason_t reason);
 
@@ -72,6 +90,23 @@
     virtual ble_error_t setPreferredConnectionParams(const ConnectionParams_t *params);
     virtual ble_error_t updateConnectionParams(Handle_t handle, const ConnectionParams_t *params);
 
+    virtual ble_error_t reset(void);
+
+    /*
+     * The following functions are part of the whitelisting experimental API.
+     * Therefore, this functionality can change in the near future.
+     */
+    virtual uint8_t getMaxWhitelistSize(void) const;
+    virtual ble_error_t getWhitelist(Gap::Whitelist_t &whitelistOut) const;
+    virtual ble_error_t setWhitelist(const Gap::Whitelist_t &whitelistIn);
+
+    virtual ble_error_t setAdvertisingPolicyMode(AdvertisingPolicyMode_t mode);
+    virtual ble_error_t setScanningPolicyMode(ScanningPolicyMode_t mode);
+    virtual ble_error_t setInitiatorPolicyMode(InitiatorPolicyMode_t mode);
+    virtual Gap::AdvertisingPolicyMode_t getAdvertisingPolicyMode(void) const;
+    virtual Gap::ScanningPolicyMode_t getScanningPolicyMode(void) const;
+    virtual Gap::InitiatorPolicyMode_t getInitiatorPolicyMode(void) const;
+
     virtual ble_error_t initRadioNotification(void) {
         if (ble_radio_notification_init(NRF_APP_PRIORITY_HIGH, NRF_RADIO_NOTIFICATION_DISTANCE_800US, radioNotificationStaticCallback) == NRF_SUCCESS) {
             return BLE_ERROR_NONE;
@@ -82,45 +117,125 @@
 
 /* Observer role is not supported by S110, return BLE_ERROR_NOT_IMPLEMENTED */
 #if !defined(TARGET_MCU_NRF51_16K_S110) && !defined(TARGET_MCU_NRF51_32K_S110)
-    virtual ble_error_t startRadioScan(const GapScanningParams &scanningParams) {
-        ble_gap_scan_params_t scanParams = {
-            .active      = scanningParams.getActiveScanning(), /**< If 1, perform active scanning (scan requests). */
-            .selective   = 0,    /**< If 1, ignore unknown devices (non whitelisted). */
-            .p_whitelist = NULL, /**< Pointer to whitelist, NULL if none is given. */
-            .interval    = scanningParams.getInterval(),  /**< Scan interval between 0x0004 and 0x4000 in 0.625ms units (2.5ms to 10.24s). */
-            .window      = scanningParams.getWindow(),    /**< Scan window between 0x0004 and 0x4000 in 0.625ms units (2.5ms to 10.24s). */
-            .timeout     = scanningParams.getTimeout(),   /**< Scan timeout between 0x0001 and 0xFFFF in seconds, 0x0000 disables timeout. */
-        };
-
-        if (sd_ble_gap_scan_start(&scanParams) != NRF_SUCCESS) {
-            return BLE_ERROR_PARAM_OUT_OF_RANGE;
-        }
-
-        return BLE_ERROR_NONE;
-    }
-
-    virtual ble_error_t stopScan(void) {
-        if (sd_ble_gap_scan_stop() == NRF_SUCCESS) {
-            return BLE_ERROR_NONE;
-        }
-
-        return BLE_STACK_BUSY;
-    }
+    virtual ble_error_t startRadioScan(const GapScanningParams &scanningParams);
+    virtual ble_error_t stopScan(void);
 #endif
 
 private:
+    /*
+     * Whitelisting API related structures and helper functions.
+     */
+
+    /* Policy modes set by the user. By default these are set to ignore the whitelist */
+    Gap::AdvertisingPolicyMode_t advertisingPolicyMode;
+    Gap::ScanningPolicyMode_t    scanningPolicyMode;
+
+    /* Internal representation of a whitelist */
+    uint8_t         whitelistAddressesSize;
+    ble_gap_addr_t  whitelistAddresses[YOTTA_CFG_WHITELIST_MAX_SIZE];
+
+    /*
+     * An internal function used to populate the ble_gap_whitelist_t that will be used by
+     * the SoftDevice for filtering requests. This function is needed because for the BLE
+     * API the whitelist is just a collection of keys, but for the stack it also includes
+     * the IRK table.
+     */
+    ble_error_t generateStackWhitelist(ble_gap_whitelist_t &whitelist);
+
+private:
+    bool    radioNotificationCallbackParam; /* parameter to be passed into the Timeout-generated radio notification callback. */
+    Timeout radioNotificationTimeout;
+
+    /*
+     * A helper function to post radio notification callbacks with low interrupt priority.
+     */
+    void postRadioNotificationCallback(void) {
+#ifdef YOTTA_CFG_MBED_OS
+        /*
+         * In mbed OS, all user-facing BLE events (interrupts) are posted to the
+         * MINAR scheduler to be executed as callbacks in thread mode. MINAR guards
+         * its critical sections from interrupts by acquiring CriticalSectionLock,
+         * which results in a call to sd_nvic_critical_region_enter(). Thus, it is
+         * safe to invoke MINAR APIs from interrupt context as long as those
+         * interrupts are blocked by sd_nvic_critical_region_enter().
+         *
+         * Radio notifications are a special case for the above. The Radio
+         * Notification IRQ is handled at a very high priority--higher than the
+         * level blocked by sd_nvic_critical_region_enter(). Thus Radio Notification
+         * events can preempt MINAR's critical sections. Using MINAR APIs (such as
+         * posting an event) directly in processRadioNotification() may result in a
+         * race condition ending in a hard-fault.
+         *
+         * The solution is to *not* call MINAR APIs directly from the Radio
+         * Notification handling; i.e. to do the bulk of RadioNotification
+         * processing at a reduced priority which respects MINAR's critical
+         * sections. Unfortunately, on a cortex-M0, there is no clean way to demote
+         * priority for the currently executing interrupt--we wouldn't want to
+         * demote the radio notification handling anyway because it is sensitive to
+         * timing, and the system expects to finish this handling very quickly. The
+         * workaround is to employ a Timeout to trigger
+         * postRadioNotificationCallback() after a very short delay (~0 us) and post
+         * the MINAR callback that context.
+         *
+         * !!!WARNING!!! Radio notifications are very time critical events. The
+         * current solution is expected to work under the assumption that
+         * postRadioNotificationCalback() will be executed BEFORE the next radio
+         * notification event is generated.
+         */
+        minar::Scheduler::postCallback(
+            mbed::util::FunctionPointer1<void, bool>(&radioNotificationCallback, &FunctionPointerWithContext<bool>::call).bind(radioNotificationCallbackParam)
+        );
+#else
+        /*
+         * In mbed classic, all user-facing BLE events execute callbacks in interrupt
+         * mode. Radio Notifications are a special case because its IRQ is handled at
+         * a very high priority. Thus Radio Notification events can preempt other
+         * operations that require interaction with the SoftDevice such as advertising
+         * payload updates and changing the Gap state. Therefore, executing a Radio
+         * Notification callback directly from processRadioNotification() may result
+         * in a race condition ending in a hard-fault.
+         *
+         * The solution is to *not* execute the Radio Notification callback directly
+         * from the Radio Notification handling; i.e. to do the bulk of the
+         * Radio Notification processing at a reduced priority. Unfortunately, on a
+         * cortex-M0, there is no clean way to demote priority for the currently
+         * executing interrupt--we wouldn't want to demote the radio notification
+         * handling anyway because it is sensitive to timing, and the system expects
+         * to finish this handling very quickly. The workaround is to employ a Timeout
+         * to trigger postRadioNotificationCallback() after a very short delay (~0 us)
+         * and execute the callback in that context.
+         *
+         * !!!WARNING!!! Radio notifications are very time critical events. The
+         * current solution is expected to work under the assumption that
+         * postRadioNotificationCalback() will be executed BEFORE the next radio
+         * notification event is generated.
+         */
+        radioNotificationCallback.call(radioNotificationCallbackParam);
+#endif /* #ifdef YOTTA_CFG_MBED_OS */
+    }
+
     /**
      * A helper function to process radio-notification events; to be called internally.
      * @param param [description]
      */
     void processRadioNotificationEvent(bool param) {
-        radioNotificationCallback.call(param);
+        radioNotificationCallbackParam = param;
+        radioNotificationTimeout.attach_us(this, &nRF5xGap::postRadioNotificationCallback, 0);
     }
     friend void radioNotificationStaticCallback(bool param); /* allow invocations of processRadioNotificationEvent() */
 
 private:
     uint16_t m_connectionHandle;
-    nRF5xGap() {
+
+    /*
+     * Allow instantiation from nRF5xn when required.
+     */
+    friend class nRF5xn;
+
+    nRF5xGap() :
+        advertisingPolicyMode(Gap::ADV_POLICY_IGNORE_WHITELIST),
+        scanningPolicyMode(Gap::SCAN_POLICY_IGNORE_WHITELIST),
+        whitelistAddressesSize(0) {
         m_connectionHandle = BLE_CONN_HANDLE_INVALID;
     }