text: /**************************************************************************//**
   * @file rail.h
   * @brief The main header file for the RAIL library. It describes the external
   *        APIs available to a RAIL user
   * @copyright Copyright 2015 Silicon Laboratories, Inc. www.silabs.com
   *****************************************************************************/
  
  #ifndef __RAIL_H__
  #define __RAIL_H__
  
  // Get the standard include types
  #include <stdint.h>
  #include <stdbool.h>
  #include <string.h> // For memcpy()
  
  // Get the RAIL-specific structures and types
  #include "rail_chip_specific.h"
  #include "rail_types.h"
  #include "rail_assert_error_codes.h"
  
  #ifdef __cplusplus
  extern "C" {
  #endif
  
  /**
   * @addtogroup RAIL_API RAIL API
   * @brief This is the primary API layer for the Radio Abstraction Interface
   *   Layer (RAIL)
   * @{
   */
  
  /**
   * @defgroup Protocol_Specific Protocol-specific
   * @brief Protocol-specific RAIL APIs
   */
  
  /******************************************************************************
   * General Radio Operation
   *****************************************************************************/
  /**
   * @addtogroup General
   * @brief Basic APIs to set up and interact with the RAIL library
   * @{
   */
  
  /**
   * Gets the version information for the compiled RAIL library.
   *
   * @param[out] version A pointer to \ref RAIL_Version_t structure to
   *   populate with version information.
   * @param[in] verbose Populate \ref RAIL_Version_t struct with verbose
   *   information.
   * @return void.
   *
   * The version information contains a major version number, a minor version
   * number, and a rev (revision) number.
   */
  void RAIL_GetVersion(RAIL_Version_t *version, bool verbose);
  
  /**
   * Initializes RAIL.
   *
   * @param[in,out] railCfg The configuration and state structure for setting up
   *   the library, which contains memory and other options needed by RAIL.
   *   This structure must be allocated in application global read-write
   *   memory. RAIL may modify fields within or referenced by this structure
   *   during its operation.
   * @param[in] cb A callback that notifies the application when the radio is
   *   finished initializing and is ready for further configuration. This
   *   callback is useful for potential transceiver products that require a
   *   power up sequence before further configuration is available. After the
   *   callback fires, the radio is ready for additional configuration before
   *   transmit and receive operations.
   * @return Handle for initialized rail instance or NULL if an
   *   invalid value was passed in the railCfg.
   */
  RAIL_Handle_t RAIL_Init(RAIL_Config_t *railCfg,
                          RAIL_InitCompleteCallbackPtr_t cb);
  
  /**
   * Collects entropy from the radio if available.
   *
   * @param[in] railHandle A RAIL instance handle.
   * @param[out] buffer The buffer to write the collected entropy.
   * @param[in] bytes The number of bytes to fill in in the input buffer.
   * @return Returns the number of bytes of entropy collected. For
   *   chips that don't support entropy collection, the function returns 0.
   *   Values less than the requested amount may also be returned on platforms
   *   that use entropy pools to collect random data periodically.
   *
   * Attempts to fill the provided buffer with the requested number of bytes of
   * entropy. If the requested number of bytes can't be provided, as many
   * bytes as possible will be filled and returned. For chips
   * that do not support this function, 0 bytes are always returned. For
   * information about the specific mechanism for gathering entropy, see
   * documentation for the chip family.
   */
  uint16_t RAIL_GetRadioEntropy(RAIL_Handle_t railHandle,
                                uint8_t *buffer,
                                uint16_t bytes);
  
  /** @} */ // end of group General
  
  /******************************************************************************
   * PTI
   *****************************************************************************/
  /**
   * @addtogroup PTI Packet Trace (PTI)
   * @brief Basic APIs to set up and interact with PTI settings
   * @{
   */
  
  /**
   * Configures PTI pin locations, serial protocols, and baud rates.
   *
   * @param[in] railHandle A RAIL instance handle (currently not used).
   * @param[in] ptiConfig A configuration structure applied to the
   *   relevant PTI registers. A NULL ptiConfig will produce undefined
   *   behavior.
   * @return Status code indicating success of the function call.
   *
   * This method must be called before RAIL_EnablePti() is called.
   * Although a RAIL handle is included for potential future
   * expansion of this function, it is currently not used. That is,
   * there is only one PTI configuration that can be active on a
   * chip, regardless of the number of protocols (unless the application
   * updates the configuration upon a protocol switch),
   * and the configuration is not saved in the RAIL instance. For optimal
   * future compatibility, pass in a chip-specific handle, such as
   * \ref RAIL_EFR32_HANDLE.
   */
  RAIL_Status_t RAIL_ConfigPti(RAIL_Handle_t railHandle,
                               const RAIL_PtiConfig_t *ptiConfig);
  
  /**
   * Gets the currently active PTI configuration.
   *
   * @param[in] railHandle A RAIL instance handle (currently not used).
   * @param[out] ptiConfig A configuration structure filled with the active
   *   PTI configuration.
   * @return RAIL status indicating success of the function call.
   *
   * Although most combinations of configurations can be set, it is safest
   * to call this method after configuration to confirm which values were
   * actually set. As in RAIL_ConfigPti, railHandle is not used. This function
   * always returns the single active PTI configuration regardless of the
   * active protocol. For optimal future compatibility, pass in a chip
   * specific handle, such as \ref RAIL_EFR32_HANDLE.
   */
  RAIL_Status_t RAIL_GetPtiConfig(RAIL_Handle_t railHandle,
                                  RAIL_PtiConfig_t *ptiConfig);
  
  /**
   * Enables Packet Trace Interface (PTI) output of packet data.
   *
   * @param[in] railHandle A RAIL instance handle (currently not used).
   * @param[in] enable PTI is enabled if true; disable if false.
   * @return RAIL status indicating success of the function call.
   *
   * Similarly to having only one PTI configuration per chip,
   * PTI can only be enabled or disabled for all protocols. It cannot
   * be individually set to enabled and disabled per protocol
   * (unless the application switches it when
   * the protocol switches), and enable/disable is not saved as part of the
   * RAIL instance. For optimal future compatibility, pass in a chip-specific
   * handle, such as \ref RAIL_EFR32_HANDLE.
   */
  RAIL_Status_t RAIL_EnablePti(RAIL_Handle_t railHandle,
                               bool enable);
  
  /**
   * Sets a protocol that RAIL outputs on PTI.
   *
   * @param[in] railHandle A RAIL instance handle.
   * @param[in] protocol The enumeration representing which protocol the node is using.
   * @return Status code indicating success of the function call.
   *
   * The protocol is output via PTI for each packet.
   * Before any protocol is set, the default value is \ref
   * RAIL_PTI_PROTOCOL_CUSTOM. Use one of the enumeration values so that
   * the Network Analyzer can decode the packet.
   */
  RAIL_Status_t RAIL_SetPtiProtocol(RAIL_Handle_t railHandle,
                                    RAIL_PtiProtocol_t protocol);
  
  /** @} */ // end of group PTI
  
  /******************************************************************************
   * Antenna Control
   *****************************************************************************/
  /**
   * @addtogroup Antenna_Control Antenna Control
   * @brief Basic APIs to control the Antenna functionality
   * @{
   */
  
  /**
   * Configures Antenna pin locations.
   * @param[in] railHandle A RAIL instance handle.
   * @param[in] config A configuration structure applied to the relevant Antenna
   *   Configuration registers. A NULL config will produce undefined behavior.
   * @return Status code indicating success of the function call.
   *
   * Although a RAIL handle is included for potential future expansion, it is
   * currently not used.
   *
   */
  RAIL_Status_t RAIL_ConfigAntenna(RAIL_Handle_t railHandle,
                                   const RAIL_AntennaConfig_t *config);
  
  /** @} */ // end of group Antenna_Control
  
  /******************************************************************************
   * Radio Configuration
   *****************************************************************************/
  /// @addtogroup Radio_Configuration Radio Configuration
  /// @brief Routines for setting up and querying radio configuration information.
  ///
  /// These routines allow for runtime flexibility in the radio
  /// configuration. Some of the parameters, however, are meant to be generated
  /// from the radio calculator in Simplicity Studio. The basic code to configure
  /// the radio from this calculator output looks like the example below.
  ///
  /// @code{.c}
  /// // Associate a specific channel config with a particular RAIL instance, and
  /// // load the settings that correspond to the first usable channel.
  /// RAIL_ConfigChannels(railHandle, channelConfigs[0]);
  /// @endcode
  ///
  /// For more information about the types of parameters that can be changed in
  /// the other functions and how to use them, see their individual documentation.
  ///
  /// @{
  
  /**
   * Loads a static radio configuration.
   *
   * @param[in] railHandle A RAIL instance handle.
   * @param[in] config A pointer to a radio configuration.
   * @return Status code indicating success of the function call.
   *
   * The configuration passed into this function should be auto-generated
   * and not manually created or edited. By default this function should not be
   * called in RAIL 2.x and later unless a non-default radio configuration needs
   * to be applied. In RAIL 2.x and later, the RAIL_ConfigChannels function
   * applies the default radio configuration automatically.
   */
  RAIL_Status_t RAIL_ConfigRadio(RAIL_Handle_t railHandle,
                                 RAIL_RadioConfig_t config);
  
  /**
   * Modifies the currently configured fixed frame length in bytes.
   *
   * @param[in] railHandle A RAIL instance handle.
   * @param[in] length The expected fixed frame length. A value of 0 is infinite.
   *   A value of RAIL_SETFIXEDLENGTH_INVALID restores the frame's length back to
   *   the length specified by the default frame type configuration.
   * @return Length configured; The new frame length configured into the hardware
   *   for use. 0 if in infinite mode, or RAIL_SETFIXEDLENGTH_INVALID if the frame
   *   length has not yet been overridden by a valid value.
   *
   * Sets the fixed-length configuration for transmit and receive.
   * Be careful when using this function in receive and transmit as this
   * function changes the default frame configuration and remains in force until
   * it is called again with an input value of RAIL_SETFIXEDLENGTH_INVALID. This
   * function will override any fixed or variable length settings from a radio
   * configuration.
   */
  uint16_t RAIL_SetFixedLength(RAIL_Handle_t railHandle, uint16_t length);
  
  /**
   * Configures the channels supported by this device.
   *
   * @param[in] railHandle A RAIL instance handle.
   * @param[in] config A pointer to the channel configuration for your device.
   *   This pointer will be cached in the library so it must
   *   exist for the runtime of the application. Typically, this should be
   *   what is stored in Flash by the configuration tool.
   * @param[in] cb Function called whenever a radio configuration change occurs.
   * @return Returns the first available channel in the configuration.
   *
   * When configuring channels on EFR32, the radio tuner is reconfigured
   * based on the frequency and channel spacing in the channel configuration.
   */
  uint16_t RAIL_ConfigChannels(RAIL_Handle_t railHandle,
                               const RAIL_ChannelConfig_t *config,
                               RAIL_RadioConfigChangedCallback_t cb);
  
  /**
   * Checks to see if the channel exists in RAIL.
   *
   * @param[in] railHandle A RAIL instance handle.
   * @param[in] channel A channel number to check.
   * @return Returns RAIL_STATUS_NO_ERROR if channel exists
   *
   * Returns RAIL_STATUS_INVALID_PARAMETER if the given channel does not exist
   * in the channel configuration currently used or RAIL_STATUS_NO_ERROR if the
   * channel is valid.
   */
  RAIL_Status_t RAIL_IsValidChannel(RAIL_Handle_t railHandle,
                                    uint16_t channel);
  
  /**
   * Causes radio settings associated with a particular channel to be applied to
   * hardware.
   *
   * @param[in] railHandle A RAIL instance handle.
   * @param[in] channel The channel to prepare for use.
   * @return \ref RAIL_STATUS_NO_ERROR on success or
   *   \ref RAIL_STATUS_INVALID_PARAMETER if the given channel does not have an
   *   associated channel configuration entry or
   *   \ref RAIL_STATUS_INVALID_STATE if the specified railHandle is not the
   *   current RAIL handle in use. \ref RAIL_STATUS_INVALID_STATE is only used
   *   in the multiprotocol version of RAIL.
   *
   * This function walks the channelConfigEntry list and applies the configuration
   * associated with the specified channel. This function is used to manually
   * change channels without actually starting a TX or RX operation.
   *
   * A successful return of this function will result in the radio being left in
   * the idle state. An unsuccessful return of this function will not alter the
   * radio state.
   */
  RAIL_Status_t RAIL_PrepareChannel(RAIL_Handle_t railHandle, uint16_t channel);
  
  /**
   * Returns the current RAIL channel.
   *
   * @param[in] railHandle A RAIL instance handle.
   * @param[out] channel The channel for which RAIL is currently configured.
   * @return RAIL_STATUS_NO_ERROR on success or
   *   RAIL_STATUS_INVALID_CALL if the radio is not configured for any channel or
   *   RAIL_STATUS_INVALID_PARAMETER if channel parameter is NULL.
   *
   * This function returns the channel configured for use corresponding to the
   * specified RAIL handle whether or not the specified handle is the active one.
   */
  RAIL_Status_t RAIL_GetChannel(RAIL_Handle_t railHandle, uint16_t *channel);
  
  /**
   * Returns the symbol rate for the current PHY.
   *
   * @param[in] railHandle A RAIL instance handle.
   * @return The symbol rate in symbols per second or 0.
   *
   * The symbol rate is the rate of symbol changes over the air. For non-DSSS
   * PHYs, this is the same as the baudrate. For DSSS PHYs, it is the baudrate
   * divided by the length of a chipping sequence. For more information,
   * see the modem calculator documentation. If the rate cannot be
   * calculated, this function returns 0.
   */
  uint32_t RAIL_GetSymbolRate(RAIL_Handle_t railHandle);
  
  #ifndef DOXYGEN_SHOULD_SKIP_THIS
  /**
   * Calculates the symbol rate for the current PHY.
   *
   * @param[in] railHandle A RAIL instance handle.
   * @return The symbol rate in symbols per second or 0.
   *
   * This function is used to calculate the symbol rate when the radio config does
   * not include that information. In the general case, this function should be
   * implemented automatically in the radio config as a stub.
   */
  uint32_t RAILCb_CalcSymbolRate(RAIL_Handle_t railHandle);
  #endif
  
  /**
   * Returns the bit rate for the current PHY.
   *
   * @param[in] railHandle A RAIL instance handle.
   * @return The bit rate in bits per second or 0.
   *
   * The bit rate is the effective over-the-air data rate. It does not account
   * for extra spreading for forward error correction, and so on, but
   * accounts for modulation schemes, DSSS, and other configurations. For more
   * information, see the modem calculator documentation. If the rate cannot be
   * calculated, this function returns 0.
   */
  uint32_t RAIL_GetBitRate(RAIL_Handle_t railHandle);
  
  #ifndef DOXYGEN_SHOULD_SKIP_THIS
  /**
   * Calculates the bit rate for the current PHY.
   *
   * @param[in] railHandle A RAIL instance handle.
   * @return The bit rate in bits per second or 0.
   *
   * This function is used to calculate the bit rate when the radio config does
   * not include that information. In the general case, this function should be
   * implemented automatically in the radio config as a stub.
   */
  uint32_t RAILCb_CalcBitRate(RAIL_Handle_t railHandle);
  #endif
  /**
   * Sets the PA capacitor tune value for transmit and receive.
   *
   * @param[in] railHandle A RAIL instance handle.
   * @param[in] txPaCtuneValue PA Ctune value for TX mode.
   * @param[in] rxPaCtuneValue PA Ctune value for RX mode.
   * @return Status code indicating success of the function call.
   *
   * Tunes the impedance of the transmit
   * and receive modes by changing the amount of capacitance at
   * the PA output.
   */
  RAIL_Status_t RAIL_SetPaCTune(RAIL_Handle_t railHandle,
                                uint8_t txPaCtuneValue,
                                uint8_t rxPaCtuneValue);
  
  /** @} */ // end of group Radio_Configuration
  
  /******************************************************************************
   * Timing Information
   *****************************************************************************/
  /**
   * @addtogroup System_Timing System Timing
   * @brief Functionality related to the RAIL timer and general system time.
   *
   * These functions can be used to get information about the current system time
   * or to manipulate the RAIL timer.
   *
   * The system time returned by RAIL_GetTime() is in the same timebase that is
   * used throughout RAIL. Any callbacks that return a timestamp, such as
   * rxPacketReceived(), will use the same timebase as will any APIs
   * that accept an absolute time for scheduling their action. Throughout the
   * documentation, the timebase is referred to as the RAIL
   * timebase. The timebase is currently a value in microseconds from chip boot time,
   * which means that it will wrap every 1.19 hours.
   * (`(2^32 - 1) / (3600 sec/hr * 1000000 us/sec)`).
   *
   * The provided timer is hardware-backed and interrupt-driven. It can be used
   * for timing any event in the system, but is especially helpful for
   * timing protocol-based state machines and other systems that interact with
   * the radio. To avoid processing the expiration in interrupt
   * context, leave the timerExpired() callback empty and poll for expiration
   * with the RAIL_IsTimerExpired() function.
   *
   * @{
   */
  
  /**
   * Configures the RAIL software timer feature.
   *
   * @param[in] enable Enables/disables the RAIL multitimer.
   * @return True if the multitimer was successfully enabled/disabled, false
   *   otherwise.
   *
   * Turning this on will add a software timer layer above the physical RAIL timer
   * so that the user can have as many timers as desired. It is not necessary to
   * call this function if the MultiTimer APIs are not used.
   *
   * @note This function must be called before calling \ref RAIL_SetMultiTimer.
   *       This function is a no-op on multiprotocol as this layer is already used
   *       under the hood.
   *       It should not be called while the RAIL timer is running.
   *       Call \ref RAIL_IsTimerRunning before enabling/disabling the multitimer.
   *       If the multitimer is not needed, do not call this function to allow the
   *       code to be dead stripped.
   */
  bool RAIL_ConfigMultiTimer(bool enable);
  
  /**
   * Gets the current RAIL time.
   *
   * @return Returns the RAIL timebase in microseconds. Note that this wraps
   *   after about 1.19 hours since it's stored in a 32bit value.
   *
   * Returns the current time in the RAIL timebase (microseconds). It can be
   * used to compare with packet timestamps or to schedule transmits.
   */
  RAIL_Time_t RAIL_GetTime(void);
  
  /**
   * Sets the current RAIL time.
   *
   * @param[in] time Set the RAIL timebase to this value in microseconds.
   * @return Status code indicating success of the function call.
   *
   * Sets the current time in the RAIL timebase in microseconds.
   */
  RAIL_Status_t RAIL_SetTime(RAIL_Time_t time);
  
  /**
   * Schedules a timer to expire using the RAIL timebase.
   *
   * @param[in] railHandle A RAIL instance handle.
   * @param[in] time The timer's expiration time in the RAIL timebase.
   * @param[in] mode Indicates whether the time argument is an absolute
   *   RAIL time or relative to the current RAIL time. Specifying mode
   *   \ref RAIL_TIME_DISABLED is the same as calling RAIL_CancelTimer().
   * @param[in] cb The callback for RAIL to call when the timer expires.
   * @return RAIL_STATUS_NO_ERROR on success and
   *   RAIL_STATUS_INVALID_PARAMETER if the timer can't be scheduled.
   *
   * Configures a timer to expire after a period in the RAIL timebase.
   * This timer can be used to implement low-level protocol features.
   *
   * @warning Attempting to schedule the timer when it is
   *   still running from a previous request is bad practice, unless the cb callback is
   *   identical to that used in the previous request, in which case the
   *   timer is rescheduled to the new time. Note that if
   *   the original timer expires as it is being rescheduled, the callback
   *   may or may not occur. It is generally good practice to cancel a
   *   running timer before rescheduling it to minimize ambiguity.
   */
  RAIL_Status_t RAIL_SetTimer(RAIL_Handle_t railHandle,
                              RAIL_Time_t time,
                              RAIL_TimeMode_t mode,
                              RAIL_TimerCallback_t cb);
  
  /**
   * Returns the absolute time that the RAIL timer was configured to expire.
   *
   * @param[in] railHandle A RAIL instance handle.
   * @return The absolute time that this timer was set to expire.
   *
   * Provides the absolute time regardless of the \ref RAIL_TimeMode_t that
   * was passed into \ref RAIL_SetTimer. Note that the time might be in the
   * past if the timer has already expired. The return value is undefined if the
   * timer was never set.
   */
  RAIL_Time_t RAIL_GetTimer(RAIL_Handle_t railHandle);
  
  /**
   * Stops the currently scheduled RAIL timer.
   *
   * @param[in] railHandle A RAIL instance handle.
   * @return void.
   *
   * Cancels the timer. If this function is called before the timer expires,
   * the cb callback specified in the earlier RAIL_SetTimer() call will never
   * be called.
   */
  void RAIL_CancelTimer(RAIL_Handle_t railHandle);
  
  /**
   * Checks whether the RAIL timer has expired.
   *
   * @param[in] railHandle A RAIL instance handle.
   * @return True if the previously scheduled timer has expired and false
   *   otherwise.
   *
   * Polling via this function can be used as an alternative to the callback.
   */
  bool RAIL_IsTimerExpired(RAIL_Handle_t railHandle);
  
  /**
   * Checks whether the RAIL timer is currently running.
   *
   * @param[in] railHandle A RAIL instance handle.
   * @return Returns true if the timer is running and false if
   *   the timer has expired or was never set.
   */
  bool RAIL_IsTimerRunning(RAIL_Handle_t railHandle);
  
  /**
   * Starts a multitimer instance.
   *
   * @note
   *    It is legal to start an already running timer. If this is done, the timer
   *    will first be stopped before the new configurations are applied.
   *    If expirationTime is 0, the callback is called
   *    immediately.
   *
   * @param[in,out] tmr A pointer to the timer instance to start.
   * @param[in] expirationTime A time when the timer is set to expire.
   * @param[in] expirationMode Select mode of expirationTime. See \ref
   *            RAIL_TimeMode_t.
   * @param[in] callback A function to call on timer expiry. See \ref
   *            RAIL_MultiTimerCallback_t. NULL is a legal value.
   * @param[in] cbArg An extra callback function parameter for the user application.
   *
   * @return
   *    \ref RAIL_STATUS_NO_ERROR on success.@n
   *    \ref RAIL_STATUS_INVALID_PARAMETER if tmr has an illegal value or if
   *         timeout is in the past.
   */
  RAIL_Status_t RAIL_SetMultiTimer(RAIL_MultiTimer_t *tmr,
                                   RAIL_Time_t expirationTime,
                                   RAIL_TimeMode_t expirationMode,
                                   RAIL_MultiTimerCallback_t callback,
                                   void *cbArg);
  
  /**
   * Stops the currently scheduled RAIL multi timer.
   *
   * @param[in,out] tmr A RAIL timer instance handle.
   *
   * @return
   *    true if the timer was successfully cancelled.
   *    false if the timer was not running.
   *
   * Cancels the timer. If this function is called before the timer expires,
   * the cb callback specified in the earlier RAIL_SetTimer() call will never
   * be called.
   */
  bool RAIL_CancelMultiTimer(RAIL_MultiTimer_t *tmr);
  
  /**
   * Checks if a given timer is running.
   *
   * @param[in] tmr A pointer to the timer structure to query.
   *
   * @return
   *    true if the timer is running.
   *    false if the timer is not running.
   */
  bool RAIL_IsMultiTimerRunning(RAIL_MultiTimer_t *tmr);
  
  /**
   * Checks if a given timer has expired.
   *
   * @param[in] tmr A pointer to the timer structure to query.
   *
   * @return
   *    true if the timer is expired.
   *    false if the timer is running.
   */
  bool RAIL_IsMultiTimerExpired(RAIL_MultiTimer_t *tmr);
  
  /**
   * Gets time left before a given timer instance expires.
   *
   * @param[in] tmr A pointer to the timer structure to query.
   * @param[in] timeMode Indicates how the function provides the time
   *            remaining. By choosing \ref
   *            RAIL_TimeMode_t::RAIL_TIME_ABSOLUTE, the function returns the
   *            absolute expiration time, and by choosing \ref
   *            RAIL_TimeMode_t::RAIL_TIME_DELAY, the function returns the
   *            amount of time remaining before the timer's expiration.
   *
   * @return
   *    Time left expressed in RAIL's time units.
   *    0 if the soft timer is not running or has already expired.
   */
  RAIL_Time_t RAIL_GetMultiTimer(RAIL_MultiTimer_t *tmr,
                                 RAIL_TimeMode_t timeMode);
  
  /**
   * Initializes RAIL timer synchronization.
   *
   * @param[in] railHandle A RAIL instance handle.
   * @param[in] sleepConfig A sleep configuration.
   *
   * @return Status code indicating success of the function call.
   */
  RAIL_Status_t RAIL_ConfigSleep(RAIL_Handle_t railHandle,
                                 RAIL_SleepConfig_t sleepConfig);
  
  /**
   * Stops the RAIL timer and prepares RAIL for sleep.
   *
   * @param[in] wakeupProcessTime Time in microseconds that the application and
   *                              hardware need to recover from sleep state.
   * @param[out] deepSleepAllowed
   *   true - system can go to deep sleep.
   *   false - system should not go to deep sleep. Deep sleep should be blocked
   *           in this case.
   *
   * @return Status code indicating success of the function call.
   *
   * @warning The active RAIL configuration must be idle to enable sleep.
   */
  RAIL_Status_t RAIL_Sleep(uint16_t wakeupProcessTime, bool *deepSleepAllowed);
  
  /**
   * Wakes RAIL from sleep and restarts the RAIL timer.
   *
   * @param[in] elapsedTime Add the sleep duration to the RAIL timer
   *   before restarting the RAIL timer.
   *
   * @return Status code indicating success of the function call.
   *
   * If the timer sync was enabled by \ref RAIL_ConfigSleep, synchronize the RAIL
   * timer using an alternate timer. Otherwise, add elapsedTime to the RAIL
   * timer.
   */
  RAIL_Status_t RAIL_Wake(RAIL_Time_t elapsedTime);
  
  /** @} */ // end of group System_Timing
  
  /******************************************************************************
   * Events
   *****************************************************************************/
  /**
   * @addtogroup Events
   * @brief APIs related to events
   * @{
   */
  
  /**
   * Configures radio events.
   *
   * @param[in] railHandle A RAIL instance handle.
   * @param[in] mask A bitmask of events to configure.
   * @param[in] events A bitmask of events to trigger \ref RAIL_Config_t::eventsCallback
   *   For a full list of available callbacks, see
   *   RAIL_EVENT_* set of defines.
   * @return Status code indicating success of the function call.
   *
   * Sets up which radio interrupts generate a RAIL event. The full list of
   * options is in \ref RAIL_Events_t.
   */
  RAIL_Status_t RAIL_ConfigEvents(RAIL_Handle_t railHandle,
                                  RAIL_Events_t mask,
                                  RAIL_Events_t events);
  
  /** @} */ // end of group Events
  
  /******************************************************************************
   * Data Management
   *****************************************************************************/
  /// @addtogroup Data_Management Data Management
  /// @brief Data management functions
  ///
  /// These functions allow the application to choose how data is presented to the
  /// application. RAIL provides data in a packet-based method or in a FIFO-based
  /// method which gives the application more granularity and responsibility in
  /// managing transmit and receive data and allows packet sizes larger than
  /// the RX or TX FIFO buffers.
  ///
  /// The application can configure RAIL data management through
  /// RAIL_ConfigData().
  /// This function allows the application to specify the type of radio data
  /// (\ref RAIL_TxDataSource_t and \ref RAIL_RxDataSource_t) and the method of
  /// interacting with data (\ref RAIL_DataMethod_t). By default, RAIL
  /// configures TX and RX both with packet data source and packet mode.
  ///
  /// In packet-based data management:
  ///   - Packet lengths are determined from the Radio Configurator configuration
  ///     or after receive packet completion using RAIL_GetRxPacketInfo().
  ///   - Load transmit data with RAIL_WriteTxFifo().
  ///   - Received packet data is made available on successful packet completion
  ///     via \ref RAIL_Config_t::eventsCallback with \ref RAIL_EVENT_RX_PACKET_RECEIVED
  ///     which can then use RAIL_GetRxPacketInfo() and RAIL_GetRxPacketDetailsAlt() to
  ///     access packet information and RAIL_PeekRxPacket() to access packet
  ///     data.
  ///   - Filtered, Aborted, or FrameError received packet data is automatically
  ///     dropped without the application needing to worry about consuming it.
  ///     The application can choose to not even be bothered with the events
  ///     related to such packets: \ref RAIL_EVENT_RX_ADDRESS_FILTERED,
  ///     \ref RAIL_EVENT_RX_PACKET_ABORTED, or \ref RAIL_EVENT_RX_FRAME_ERROR.
  ///
  /// In FIFO-based data management:
  ///   - Packet Lengths are determined from the Radio Configurator configuration
  ///     or by application knowledge of packet payload structure.
  ///   - Load transmit data with RAIL_WriteTxFifo() with reset set to false.
  ///   - Received data can be retrieved prior to packet completion through
  ///     RAIL_ReadRxFifo() and is never dropped on Filtered, Aborted, or
  ///     FrameError packets. The application should enable and handle these
  ///     events so it can flush any packet data it's already retrieved.
  ///   - After packet completion, remaining packet data for Filtered, Aborted,
  ///     or FrameError packets can be either flushed automatically by RAIL
  ///     or consumed by the application just like a successfully received packet,
  ///     as determined from RAIL_GetRxPacketInfo(). RAIL_GetRxPacketDetailsAlt()
  ///     provides packet detailed information only for successfully received
  ///     packets.
  ///   - Set the TX FIFO threshold through RAIL_SetTxFifoThreshold(). The
  ///     \ref RAIL_Config_t::eventsCallback with \ref RAIL_EVENT_TX_FIFO_ALMOST_EMPTY
  ///     will occur telling the application to load more TX packet data, if needed
  ///     to prevent a \ref RAIL_EVENT_TX_UNDERFLOW event from occurring.
  ///   - Set the RX FIFO threshold through RAIL_SetRxFifoThreshold(). The
  ///     \ref RAIL_Config_t::eventsCallback with \ref RAIL_EVENT_RX_FIFO_ALMOST_FULL
  ///     will occur telling the application to consume some RX packet data to prevent a
  ///     \ref RAIL_EVENT_RX_FIFO_OVERFLOW event from occurring.
  ///   - Get RX FIFO count information through
  ///     RAIL_GetRxPacketInfo(\ref RAIL_RX_PACKET_HANDLE_NEWEST)
  ///     (or RAIL_GetRxFifoBytesAvailable()).
  ///   - Get TX FIFO count information through RAIL_GetTxFifoSpaceAvailable().
  ///   - Reset RX and/or TX FIFOs with RAIL_ResetFifo().
  ///
  /// When trying to determine an appropriate threshold, the application needs
  /// to know the size of each FIFO. The receive FIFO is internal to RAIL and its
  /// default size is 512 bytes. The receive FIFO is level-based in that the \ref
  /// RAIL_EVENT_RX_FIFO_ALMOST_FULL event will constantly pend if the threshold
  /// is exceeded. This normally means that inside this event's callback, the
  /// application should empty enough of the FIFO to go under the threshold. To
  /// defer reading the FIFO to main context, the application can disable or
  /// re-enable the receive FIFO threshold event using RAIL_ConfigEvents() with
  /// the mask \ref RAIL_EVENT_RX_FIFO_ALMOST_FULL.
  ///
  /// The transmit FIFO is specified by the application and its size is
  /// the value returned from the most recent call to RAIL_SetTxFifo().
  /// The transmit FIFO is edge-based in that it only provides the \ref
  /// RAIL_EVENT_TX_FIFO_ALMOST_EMPTY event once when the threshold is crossed
  /// in the emptying direction.
  ///
  /// In FIFO mode, FIFOs can store multiple packets. Depending on the
  /// traffic, RAIL can receive multiple packets into the receive FIFO before the
  /// application gets around to reading out the received data from the FIFO.
  /// RAIL_ReadRxFifo() won't allow reading beyond a packet boundary so
  /// process packet completion events promptly. Keep in mind that in FIFO mode,
  /// packet data already read from packets that are subsequently aborted,
  /// frameerror, or filtered should be flushed.
  ///
  /// While RAIL defaults to packet mode, the application can explicitly
  /// initialize RAIL for packet mode in the following manner:
  /// @code{.c}
  /// static const RAIL_DataConfig_t railDataConfig = {
  ///   .txSource = TX_PACKET_DATA,
  ///   .rxSource = RX_PACKET_DATA,
  ///   .txMethod = PACKET_MODE,
  ///   .rxMethod = PACKET_MODE,
  /// };
  ///
  /// status = RAIL_ConfigData(&railDataConfig);
  ///
  /// // Events that can occur in Packet Mode:
  ///    RAIL_EVENT_TX_PACKET_SENT
  ///    RAIL_EVENT_RX_PACKET_RECEIVED
  /// and optionally (packet data automatically dropped):
  ///    RAIL_EVENT_RX_ADDRESS_FILTERED
  ///    RAIL_EVENT_RX_PACKET_ABORTED
  ///    RAIL_EVENT_RX_FRAME_ERROR
  /// @endcode
  ///
  /// Initializing RAIL for FIFO Mode requires a few more function calls:
  /// @code{.c}
  /// static const RAIL_DataConfig_t railDataConfig = {
  ///   .txSource = TX_PACKET_DATA,
  ///   .rxSource = RX_PACKET_DATA,
  ///   .txMethod = FIFO_MODE,
  ///   .rxMethod = FIFO_MODE,
  /// };
  ///
  /// status = RAIL_ConfigData(&railDataConfig);
  ///
  /// // Gets the size of the FIFOs.
  /// // The transmit and receive FIFOs are the same size
  /// uint16_t fifoSize = RAIL_GetTxFifoSpaceAvailable();
  ///
  /// // Sets the transmit and receive FIFO thresholds.
  /// // For this example, set the threshold in the middle of each FIFO.
  /// RAIL_SetRxFifoThreshold(fifoSize / 2);
  /// RAIL_SetTxFifoThreshold(fifoSize / 2);
  ///
  /// // Events that can occur in FIFO mode:
  ///    RAIL_EVENT_TX_FIFO_ALMOST_EMPTY
  ///    RAIL_EVENT_TX_UNDERFLOW
  ///    RAIL_EVENT_TXACK_UNDERFLOW
  ///    RAIL_EVENT_TX_PACKET_SENT
  ///    RAIL_EVENT_RX_FIFO_ALMOST_FULL
  ///    RAIL_EVENT_RX_FIFO_OVERFLOW
  ///    RAIL_EVENT_RX_ADDRESS_FILTERED
  ///    RAIL_EVENT_RX_PACKET_ABORTED
  ///    RAIL_EVENT_RX_FRAME_ERROR
  ///    RAIL_EVENT_RX_PACKET_RECEIVED
  /// @endcode
  ///
  /// On receive, an application can use multiple data sources that
  /// are only compatible with the FIFO method of data delivery. All that differs
  /// from the FIFO mode example above is the RAIL_DataConfig_t::rxSource setting.
  /// IQ data samples are taken at the hardware's oversample rate and the amount
  /// of data can easily overwhelm the CPU processing time. The sample rate
  /// depends on the chosen PHY, as determined by the data rate and the decimation
  /// chain. It is <b>not</b> recommended to use the IQ data source with sample
  /// rates above 300 k samples/second as the CPU might not be able to keep up
  /// with the data. Depending on the application and needed CPU bandwidth, slower
  /// data rates may be required.
  /// @code{.c}
  /// // IQ data is provided into the receive FIFO.
  /// static const RAIL_DataConfig_t railDataConfig = {
  ///   .txSource = TX_PACKET_DATA,
  ///   .rxSource = RX_IQDATA_FILTLSB,
  ///   .txMethod = FIFO_MODE,
  ///   .rxMethod = FIFO_MODE,
  /// };
  ///
  /// // IQ data comes in the following format when reading out of the FIFO:
  /// //------------------------------------
  /// // I[LSB] | I[MSB] | Q[LSB] | Q[MSB] |
  /// //------------------------------------
  /// @endcode
  ///
  /// @note \ref RAIL_DataConfig_t.txMethod and \ref RAIL_DataConfig_t.rxMethod
  ///   must have the same \ref RAIL_DataMethod_t configuration.
  ///
  /// @warning Do not call RAIL_ReadRxFifo() function while in
  /// \ref RAIL_DataMethod_t::PACKET_MODE.
  /// @{
  
  /**
   * RAIL data management configuration
   *
   * @param[in] railHandle A RAIL instance handle.
   * @param[in] dataConfig RAIL data configuration structure.
   * @return Status code indicating success of the function call.
   *
   * This function configures how RAIL manages data. The application can
   * configure RAIL to receive data in a packet-based or FIFO-based format.
   * FIFO mode is necessary to support packets larger than the radio's
   * FIFO buffers.
   *
   * With FIFO mode, the application sets appropriate FIFO thresholds via
   * RAIL_SetTxFifoThreshold() and RAIL_SetRxFifoThreshold() and then
   * enables and handles the \ref RAIL_EVENT_TX_FIFO_ALMOST_EMPTY event
   * callback (to feed more packet data via RAIL_WriteTxFifo() before the
   * FIFO underflows) and the \ref RAIL_EVENT_RX_FIFO_ALMOST_FULL event
   * callback (to consume packet data via RAIL_ReadRxFifo() before the
   * RX FIFO overflows).
   *
   * When configuring TX or RX for FIFO mode, this function resets the configured
   * FIFOs. When configuring TX or RX for Packet mode, this function will reset
   * the corresponding FIFO thresholds such that they won't trigger the
   * \ref RAIL_EVENT_RX_FIFO_ALMOST_FULL or \ref RAIL_EVENT_TX_FIFO_ALMOST_EMPTY
   * events.
   *
   * When \ref RAIL_DataConfig_t.rxMethod is set to \ref
   * RAIL_DataMethod_t.FIFO_MODE, the radio won't drop packet data of
   * aborted or CRC error packets, but will present it to the application
   * to deal with accordingly. On completion of erroneous packets, the
   * \ref RAIL_Config_t::eventsCallback with \ref RAIL_EVENT_RX_PACKET_ABORTED,
   * \ref RAIL_EVENT_RX_FRAME_ERROR, or \ref RAIL_EVENT_RX_ADDRESS_FILTERED will
   * tell the application it can drop any data it read via RAIL_ReadRxFifo() during reception.
   * For CRC error packets when the \ref RAIL_RX_OPTION_IGNORE_CRC_ERRORS
   * RX option is in effect, the application should check for that from the
   * \ref RAIL_RxPacketStatus_t obtained by calling RAIL_GetRxPacketInfo().
   * RAIL will automatically flush any remaining packet data after reporting
   * one of these packet completion events or the application can explicitly
   * flush it by calling RAIL_ReleaseRxPacket().
   *
   * When \ref RAIL_DataConfig_t.rxMethod is set to \ref
   * RAIL_DataMethod_t.PACKET_MODE, the radio will drop all packet data
   * associated with aborted packets including those with CRC errors (unless
   * configured to ignore CRC errors via the
   * \ref RAIL_RX_OPTION_IGNORE_CRC_ERRORS RX option). The application will
   * never have to deal with packet data from these packets.
   *
   * In either mode, the application can set RX options as needed.
   * Packet details are not available for aborted packets.
   */
  RAIL_Status_t RAIL_ConfigData(RAIL_Handle_t railHandle,
                                const RAIL_DataConfig_t *dataConfig);
  
  /**
   * Writes data to the transmit FIFO.
   *
   * @param[in] railHandle A RAIL instance handle.
   * @param[in] dataPtr An application-provided pointer to transmit data.
   * @param[in] writeLength A number of bytes to write to the transmit FIFO.
   * @param[in] reset If true, resets TX FIFO before writing the data.
   * @return The number of bytes written to the transmit FIFO.
   *
   * This function reads data from the provided dataPtr and writes it to the TX
   * FIFO. If the requested writeLength exceeds the current number of bytes open
   * in the transmit FIFO, the function only writes until the transmit FIFO
   * is full. The function returns the number of bytes written to the transmit
   * FIFO or returns zero if railHandle is NULL or if the TX FIFO is full.
   *
   * @note The protocol's packet configuration, as set up by the radio
   *   configurator or via RAIL_SetFixedLength(), determines how many
   *   bytes of data are consumed from the TX FIFO for a successful transmit
   *   operation, not the writeLength value passed in. If not enough data has
   *   been put into the TX FIFO, a \ref RAIL_EVENT_TX_UNDERFLOW event will
   *   occur. If too much data is put into the TX FIFO, the extra data will
   *   either become the first bytes
   *   sent in a subsequent packet, or will be thrown away if the FIFO gets
   *   reset prior to the next transmit. In general, the proper number of
   *   packet bytes to put into the TX FIFO are all payload bytes except for
   *   any CRC bytes, which the packet configuration causes to be sent
   *   automatically.
   *
   * @note This function does not create a critical section but, depending on the
   *   application, a critical section could be appropriate.
   */
  uint16_t RAIL_WriteTxFifo(RAIL_Handle_t railHandle,
                            const uint8_t *dataPtr,
                            uint16_t writeLength,
                            bool reset);
  
  /**
   * Sets the address of the TX FIFO, a circular buffer used for TX data.
   *
   * @param[in] railHandle A RAIL instance handle.
   * @param[in,out] addr A pointer to a read-write memory location in RAM
   *   used as the TX FIFO. This memory must persist until the next call to
   *   this function.
   * @param[in] initLength A number of initial bytes already in the TX FIFO.
   * @param[in] size A desired size of the TX FIFO in bytes.
   * @return Returns the FIFO size in bytes.
   *
   * This function sets the memory location for the TX FIFO. It
   * must be called at least once before any transmit operations occur.
   *
   * The FIFO size can be determined by the return value of this function. The
   * chosen size is determined based on the available FIFO sizes supported by
   * the hardware. For more on supported FIFO sizes and alignments, see
   * chip-specific documentation, such as \ref efr32_main. The returned FIFO size
   * will be the closest allowed size less than or equal to the passed in size
   * parameter, unless the size parameter is smaller than the minimum FIFO size.
   * If the initLength parameter is larger than the returned size, the FIFO will
   * be filled up to its size.
   *
   * A user may write to the custom memory location directly before calling this
   * function, or use \ref RAIL_WriteTxFifo to write to the memory location after
   * calling this function. Users must specify the initLength
   * for previously-written memory to be set in the TX
   * FIFO.
   *
   * This function reserves the block of RAM starting at addr with a length of the
   * returned FIFO size, which is used internally as a circular buffer for the
   * transmit FIFO. It must be able to hold the entire FIFO size. The caller must
   * guarantee that the custom FIFO remains intact and unchanged (except via calls
   * to \ref RAIL_WriteTxFifo) until the next call to this function.
   *
   * @note The protocol's packet configuration, as set up by the radio
   *   configurator or via RAIL_SetFixedLength(), determines how many
   *   bytes of data are consumed from the TX FIFO for a successful transmit
   *   operation, not the initLength value passed in. If not enough data has
   *   been put into the TX FIFO, a \ref RAIL_EVENT_TX_UNDERFLOW event will
   *   occur. If too much data is put into the TX FIFO, the extra data
   *   will either become the first bytes
   *   sent in a subsequent packet, or will be thrown away if the FIFO gets
   *   reset prior to the next transmit. In general, the proper number of
   *   packet bytes to put into the TX FIFO are all payload bytes except for
   *   any CRC bytes which the packet configuration causes to be sent
   *   automatically.
   */
  uint16_t RAIL_SetTxFifo(RAIL_Handle_t railHandle,
                          uint8_t *addr,
                          uint16_t initLength,
                          uint16_t size);
  
  /**
   * Sets the address of the RX FIFO, a circular buffer used for RX data.
   *
   * @param[in] railHandle A RAIL instance handle.
   * @param[in,out] addr A pointer to a read-write memory location in RAM used as
   *   the RX FIFO. This memory must persist until the next call to this function.
   * @param[in,out] size A desired size of the RX FIFO in bytes. This will be
   *   populated with the actual size during the function call.
   * @return Status code indicating success of the function call.
   *
   * This function sets the memory location for the RX FIFO. It
   * must be called at least once before any receive operations occur.
   *
   * The FIFO size can be determined by the value of size after this function. The
   * chosen size is determined based on the available FIFO sizes supported by
   * the hardware. For more on supported FIFO sizes and required alignments, see
   * chip-specific documentation, such as \ref efr32_main. The returned FIFO size
   * will be the closest allowed size less than or equal to the passed in size
   * parameter, unless the size parameter is smaller than the minimum FIFO size.
   *
   * This function reserves the block of RAM starting at addr with a length
   * of size, which is used internally as a circular buffer for the receive FIFO.
   * It must be able to hold the entire FIFO size. The caller must guarantee that
   * the custom FIFO remains intact and unchanged (except via incoming packet data
   * being written) until the next call to this function.
   *
   * In multiprotocol, RAIL currently shares one receive FIFO across all
   * protocols. This function will return \ref RAIL_STATUS_INVALID_STATE if the
   * requested \ref RAIL_Handle_t is not active.
   */
  RAIL_Status_t RAIL_SetRxFifo(RAIL_Handle_t railHandle,
                               uint8_t *addr,
                               uint16_t *size);
  
  /// Sets up the receive FIFO to use. This function is optional to implement.
  ///
  /// @param[in] railHandle A RAIL instance handle.
  /// @return Status code indicating success of the function call.
  ///
  /// This function is called during the \ref RAIL_Init process to set up the FIFO
  /// to use for received packets. If not implemented by the application,
  /// a default implementation from within the RAIL library will be used to
  /// initialize a 512 byte FIFO.
  ///
  /// If this function returns an error, then the RAIL_Init process will fail.
  ///
  /// During this function, the application should generally call
  /// \ref RAIL_SetRxFifo. If that does not happen, then the application needs to
  /// set up the receive FIFO via a call to \ref RAIL_SetRxFifo before attempting
  /// to receive any packets. An example implementation may look like the following:
  /// @code{.c}
  /// #define RX_FIFO_SIZE 1024
  /// static uint8_t rxFifo[RX_FIFO_SIZE];
  ///
  /// RAIL_Status_t RAILCb_SetupRxFifo(RAIL_Handle_t railHandle)
  /// {
  ///   uint16_t rxFifoSize = RX_FIFO_SIZE;
  ///   RAIL_Status_t status = RAIL_SetRxFifo(railHandle, &rxFifo[0], &rxFifoSize);
  ///   if (rxFifoSize != RX_FIFO_SIZE) {
  ///     // We set up an incorrect FIFO size
  ///     return RAIL_STATUS_INVALID_PARAMETER;
  ///   }
  ///   if (status == RAIL_STATUS_INVALID_STATE) {
  ///     // Allow failures due to multiprotocol
  ///     return RAIL_STATUS_NO_ERROR;
  ///   }
  ///   return status;
  /// }
  /// @endcode
  RAIL_Status_t RAILCb_SetupRxFifo(RAIL_Handle_t railHandle);
  
  /**
   * Reads packet data from RAIL's internal receive FIFO buffer.
   * This function can be used in any RX mode, though in Packet
   * mode it can only be used on the oldest unreleased packet whose
   * RAIL_RxPacketStatus_t is among the RAIL_RX_PACKET_READY_ set.
   *
   * @param[in] railHandle A RAIL instance handle.
   * @param[out] dataPtr An application-provided pointer to store data.
   *   If NULL, the data is thrown away rather than copied out.
   * @param[in] readLength A number of packet bytes to read from the FIFO.
   * @return The number of packet bytes read from the receive FIFO.
   *
   * This function reads packet data from the head of receive FIFO and
   * writes it to the provided dataPtr. It does not permit reading more
   * data than is available in the FIFO, nor does it permit reading more
   * data than remains in the oldest unreleased packet.
   *
   * Because this function does not have a critical section, use it
   * only in one context or make sure function calls are protected to prevent
   * buffer corruption.
   *
   * @note When reading data from an arriving packet that is not yet complete,
   *   keep in mind its data is highly suspect because it has not yet passed
   *   any CRC integrity checking. Also note that the packet could be aborted,
   *   cancelled, or fail momentarily, invalidating its data in Packet mode.
   *   Furthermore, there is a small chance towards the end of packet reception
   *   that the RX FIFO could include not only packet data received so far,
   *   but also some raw radio-appended info detail bytes that RAIL's
   *   packet-completion processing will subsequently deal with. It's up to the
   *   application to know its packet format well enough to avoid reading this
   *   info as it will corrupt the packet's details and possibly corrupt the
   *   RX FIFO buffer.
   */
  uint16_t RAIL_ReadRxFifo(RAIL_Handle_t railHandle,
                           uint8_t *dataPtr,
                           uint16_t readLength);
  
  /**
   * Configures the RAIL transmit FIFO almost empty threshold.
   *
   * @param[in] railHandle A RAIL instance handle.
   * @param[in] txThreshold The threshold once fallen under will fire \ref RAIL_Config_t::eventsCallback
   *   with \ref RAIL_EVENT_TX_FIFO_ALMOST_EMPTY set.
   * @return Configured transmit FIFO threshold value.
   *
   * This function configures the threshold for the transmit FIFO. When the count
   * of the transmit FIFO is less than the configured threshold, \ref RAIL_Config_t::eventsCallback
   * will fire with \ref RAIL_EVENT_TX_FIFO_ALMOST_EMPTY set. A value of
   * 0 is invalid and will not change the current configuration.
   */
  uint16_t RAIL_SetTxFifoThreshold(RAIL_Handle_t railHandle,
                                   uint16_t txThreshold);
  
  /**
   * Configures the RAIL receive FIFO almost full threshold.
   *
   * @param[in] railHandle A RAIL instance handle.
   * @param[in] rxThreshold The threshold once exceeded will fire \ref RAIL_Config_t::eventsCallback
   *   with \ref RAIL_EVENT_RX_FIFO_ALMOST_FULL set.
   * @return Configured receive FIFO threshold value.
   *
   * This function configures the threshold for the receive FIFO. When the count
   * of the receive FIFO is greater than the configured threshold,
   * \ref RAIL_Config_t::eventsCallback will fire with
   * \ref RAIL_EVENT_RX_FIFO_ALMOST_FULL set. A value of 0xFFFF is invalid and
   * will not change the current configuration. Depending on the size of the
   * receive FIFO hardware, the maximum value can vary. If the rxThreshold value
   * exceeds the capability of the hardware, the RX threshold will be configured
   * so that it fires only when the FIFO is one byte away from being full.
   */
  uint16_t RAIL_SetRxFifoThreshold(RAIL_Handle_t railHandle,
                                   uint16_t rxThreshold);
  
  /**
   * Gets the RAIL transmit FIFO almost empty threshold value.
   *
   * @param[in] railHandle A RAIL instance handle.
   * @return Configured TX Threshold value.
   *
   * Retrieves the configured TX threshold value.
   */
  uint16_t RAIL_GetTxFifoThreshold(RAIL_Handle_t railHandle);
  
  /**
   * Gets the RAIL receive FIFO almost full threshold value.
   *
   * @param[in] railHandle A RAIL instance handle.
   * @return Configured RX Threshold value.
   *
   * Retrieves the configured RX threshold value.
   */
  uint16_t RAIL_GetRxFifoThreshold(RAIL_Handle_t railHandle);
  
  /**
   * Resets the RAIL FIFOs.
   *
   * @param[in] railHandle A RAIL instance handle.
   * @param[in] txFifo If true, reset the transmit FIFO.
   * @param[in] rxFifo If true, reset the receive FIFO.
   * @return void.
   *
   * This function can reset each FIFO. The application should not reset the RX
   * FIFO while receiving a frame.
   */
  void RAIL_ResetFifo(RAIL_Handle_t railHandle, bool txFifo, bool rxFifo);
  
  /**
   * Gets the number of bytes available in the receive FIFO.
   * This function should only be used in RX FIFO mode. Apps should
   * use RAIL_GetRxPacketInfo() instead.
   *
   * @param[in] railHandle A RAIL instance handle.
   * @return Number of raw bytes in the receive FIFO.
   *
   * @note The number of bytes returned may not just reflect the current
   *   packet's data but could also include raw appended info bytes added
   *   after successful packet reception and bytes from subsequently received
   *   packets. It is up to the app to never try to consume more than the
   *   packet's actual data when using the value returned here in a subsequent
   *   call to RAIL_ReadRxFifo(), otherwise the Rx buffer will be corrupted.
   */
  uint16_t RAIL_GetRxFifoBytesAvailable(RAIL_Handle_t railHandle);
  
  /**
   * Gets the number of bytes open in the transmit FIFO.
   *
   * @param[in] railHandle A RAIL instance handle.
   * @return Number of bytes open in the transmit FIFO.
   *
   * Gets the number of bytes open in the transmit FIFO.
   */
  uint16_t RAIL_GetTxFifoSpaceAvailable(RAIL_Handle_t railHandle);
  
  /** @} */ // end of group Data_Management
  
  /******************************************************************************
   * State Transitions
   *****************************************************************************/
  /**
   * @addtogroup State_Transitions State Transitions
   * @{
   */
  
  /**
   * Configures RAIL automatic state transitions after RX.
   *
   * @param[in] railHandle A RAIL instance handle.
   * @param[in] transitions The state transitions to apply after reception.
   * @return Status code indicating success of the function call.
   *
   * This function fails if unsupported transitions are passed in or if the
   * radio is currently in the RX state. Success can transition to TX, RX, or
   * IDLE, while error can transition to RX or IDLE.
   */
  RAIL_Status_t RAIL_SetRxTransitions(RAIL_Handle_t railHandle,
                                      const RAIL_StateTransitions_t *transitions);
  
  /**
   * Configures RAIL automatic state transitions after TX.
   *
   * @param[in] railHandle A RAIL instance handle.
   * @param[in] transitions The state transitions to apply after transmission.
   * @return Status code indicating a success of the function call.
   *
   * This function fails if unsupported transitions are passed in or if the
   * radio is currently in the TX state. Success and error can each transition
   * to RX or IDLE.
   */
  RAIL_Status_t RAIL_SetTxTransitions(RAIL_Handle_t railHandle,
                                      const RAIL_StateTransitions_t *transitions);
  
  /**
   * Configures RAIL automatic state transition timing.
   *
   * @param[in] railHandle A RAIL instance handle.
   * @param[in,out] timings The timings used to configure the RAIL state
   *   machine. This structure is overwritten with the actual times that were
   *   set, if an input timing is invalid.
   * @return Status code indicating a success of the function call.
   *
   * The timings given are close to the actual transition time. However,
   * a still uncharacterized software overhead occurs. Also, timings are not
   * always adhered to when using an automatic transition after an error, due to
   * the cleanup required to recover from the error.
   */
  RAIL_Status_t RAIL_SetStateTiming(RAIL_Handle_t railHandle,
                                    RAIL_StateTiming_t *timings);
  
  /**
   * Places the radio into an idle state.
   *
   * @param[in] railHandle A RAIL instance handle.
   * @param[in] mode The method for shutting down the radio.
   * @param[in] wait Whether this function should wait for the radio to reach
   *   idle before returning.
   * @return void.
   *
   * This function is used to remove the radio from TX and RX states. How these
   * states are left is defined by the mode parameter.
   *
   * In multiprotocol, this API will also cause the radio to be yielded so that
   * other tasks can be run. See \ref rail_radio_scheduler_yield for more details.
   */
  void RAIL_Idle(RAIL_Handle_t railHandle,
                 RAIL_IdleMode_t mode,
                 bool wait);
  
  /**
   * Gets the current radio state.
   *
   * @param[in] railHandle A RAIL instance handle.
   * @return An enumeration for the current radio state.
   *
   * Returns the state of the radio as a bitmask containing:
   * \ref RAIL_RF_STATE_IDLE, \ref RAIL_RF_STATE_RX, \ref RAIL_RF_STATE_TX,
   * and \ref RAIL_RF_STATE_ACTIVE. \ref RAIL_RF_STATE_IDLE, \ref
   * RAIL_RF_STATE_RX, and \ref RAIL_RF_STATE_TX bits are mutually exclusive.
   * The radio can transition through intermediate states,
   * which are not reported but are instead bucketed into the state
   * being transitioned into. For example, when the transmitter is in the
   * process of shutting down, this function will return TX, as if the
   * shutdown process hadn't started yet.
   */
  RAIL_RadioState_t RAIL_GetRadioState(RAIL_Handle_t railHandle);
  
  /** @} */ // end of group State_Transitions
  
  /******************************************************************************
   * Transmit
   *****************************************************************************/
  /**
   * @addtogroup Transmit
   * @brief APIs related to transmitting data packets
   * @{
   */
  
  /// @addtogroup PA Power Amplifier (PA)
  /// @brief APIs for interacting with one of the on chip PAs.
  ///
  /// These APIs let you configure the on-chip PA to get the appropriate output
  /// power.
  ///
  /// These are the function types:
  ///   1) Configuration functions: These functions set and get configuration
  ///      for the PA. In this case, "configuration" refers to a) indicating
  ///      which PA to use, b) the voltage supplied by your board to the PA,
  ///      and c) the ramp time over which to ramp the PA up to its full
  ///      power.
  ///   2) Power-setting functions: These functions consume the actual
  ///      values written to the PA registers and write them appropriately.
  ///      These values are referred to as "(raw) power levels". The range of
  ///      acceptable values for these functions depends on which PA is
  ///      currently active. The higher the power level set, the higher
  ///      the dBm power output by the chip. However, the mapping
  ///      between dBm and these power levels can vary greatly between
  ///      modules/boards.
  ///   3) Conversion functions: These functions convert
  ///      between the "power levels" discussed previously and the
  ///      dBm values output by the chip. Continue reading for more information
  ///      about unit conversion.
  ///
  /// The accuracy of the chip output power is application-specific.
  /// For some protocols or channels, the protocol itself or
  /// legal limitations require applications to know exactly what power
  /// they're transmitting at, in dBm. Other applications do not have
  /// these restrictions, and users determine power level(s)
  /// that fit their criteria for the trade-off between radio range and
  /// power savings, regardless of what dBm power that maps to.
  ///
  /// \ref RAIL_ConvertRawToDbm and \ref RAIL_ConvertDbmToRaw,
  /// which convert between the dBm power and the raw power levels,
  /// provide a solution that fits all these applications.
  /// The levels of customizability are outlined below:
  ///  1) No customizability needed: for a given dBm value, the result
  ///     of RAIL_ConvertDbmToRaw provides an appropriate
  ///     raw power level that, when written to the registers via
  ///     RAIL_SetPowerLevel, causes the chip to output at that
  ///     dBm power. In this case, no action is needed by the user,
  ///     the WEAK versions of the conversion functions can be used
  ///     and the default include paths in pa_conversions_efr32.h can
  ///     be used.
  ///  2) The mapping of power level to dBm is not good, but the
  ///     level of precision is sufficient: In pa_conversions_efr32.c,
  ///     the WEAK versions of the conversion functions work by using
  ///     8-segment piecewise linear curves to convert between dBm
  ///     and power levels for PA's with hundreds of power levels
  ///     and simple mapping tables for use with PA's with only a few
  ///     levels. If this method is sufficiently precise, but the mapping
  ///     between power levels and dBm is incorrect,
  ///     copy pa_curves_efr32.h into a new file, updating the segments
  ///     to form a better fit (_DCDC_CURVES or _VBAT_CURVES defines) and
  ///     then add the RAIL_PA_CURVES define to your build with the path
  ///     to the new file.
  ///  3) A different level of precision is needed and the fit is bad:
  ///     If the piecewise-linear line segment fit is not appropriate for
  ///     your solution, the functions in pa_conversions_efr32.c can be
  ///     totally rewritten, as long as RAIL_ConvertDbmToRaw and
  ///     RAIL_ConvertRawToDbm have the same signatures. It is completely
  ///     acceptable to re-write these in a way that makes the
  ///     pa_curves_efr32.h and pa_curve_types_efr32.h files referenced in
  ///     pa_conversions_efr32.h unnecessary. Those files are needed solely
  ///     for the provided conversion methods.
  ///  4) dBm values are not necessary: If the application does not require
  ///     dBm values at all, overwrite
  ///     RAIL_ConvertDbmToRaw and RAIL_ConvertRawToDbm with smaller functions
  ///     (i.e., return 0 or whatever was input). These functions are called
  ///     from within the RAIL library, so they can never be deadstripped,
  ///     but making them as small as possible is the best way to reduce code
  ///     size. From there, call RAIL_SetTxPower, without
  ///     converting from a dBm value. If you never want the library to coerce the
  ///     power based on channels, RAIL_ConvertRawToDbm should be overwritten
  ///     to always return 0 and RAIL_ConvertDbmToRaw should be overwritten to
  ///     always return 255.
  ///
  /// The following is example code that shows how to initialize your PA
  /// @code{.c}
  ///
  /// #include "pa_conversions_efr32.h"
  ///
  /// // A helper macro to declare all the curve structures used by the provided
  /// // conversion functions.
  /// RAIL_DECLARE_TX_POWER_VBAT_CURVES(piecewiseSegments, curvesSg, curves24Hp, curves24Lp);
  ///
  /// // Puts the variables declared above into the appropriate structure.
  /// RAIL_TxPowerCurvesConfig_t txPowerCurvesConfig = { curves24Hp, curvesSg, curves24Lp, piecewiseSegments };
  ///
  /// // Saves those curves
  /// // to be referenced when the conversion functions are called.
  /// RAIL_InitTxPowerCurves(&txPowerCurvesConfig);
  ///
  /// // Declares the structure used to configure the PA.
  /// RAIL_TxPowerConfig_t txPowerConfig = { RAIL_TX_POWER_MODE_2P4_HP, 3300, 10 };
  ///
  /// // Initializes the PA. Here, it is assumed that 'railHandle' is a valid RAIL_Handle_t
  /// // that has already been initialized.
  /// RAIL_ConfigTxPower(railHandle, &txPowerConfig);
  ///
  /// // Picks a dBm power to use: 100 deci-dBm = 10 dBm. See docs on RAIL_TxPower_t.
  /// RAIL_TxPower_t power = 100;
  ///
  /// // Gets the config written by RAIL_ConfigTxPower to confirm what was actually set.
  /// RAIL_GetTxPowerConfig(railHandle, &txPowerConfig);
  ///
  /// // RAIL_ConvertDbmToRaw is the default weak version,
  /// // or the customer version, if overwritten.
  /// RAIL_TxPowerLevel_t powerLevel = RAIL_ConvertDbmToRaw(railHandle,
  ///                                                       txPowerConfig.mode,
  ///                                                       power);
  ///
  /// // Writes the result of the conversion to the PA power registers in terms
  /// // of raw power levels.
  /// RAIL_SetTxPower(railHandle, powerLevel);
  /// @endcode
  ///
  /// @note: All lines following "RAIL_TxPower_t power = 100;" can be
  /// replaced with the provided utility function, \ref RAIL_SetTxPowerDbm.
  /// However, the full example here was provided for clarity. See the
  /// documentation on \ref RAIL_SetTxPowerDbm for more details.
  ///
  /// @{
  
  /**
   * Initializes TxPower Settings.
   *
   * @param[in] railHandle A RAIL instance handle.
   * @param[in] config An instance which contains desired initial settings
   *   for the TX amplifier.
   * @return RAIL_Status_t indicating success or an error.
   *
   * These settings include the selection between the multiple TX amplifiers,
   * voltage supplied to the TX power amplifier, and ramp times. This must
   * be called before any transmit occurs, or \ref RAIL_SetTxPower is called.
   * While this function should always be called during initialization,
   * it can also be called any time if these settings need to change to adapt
   * to a different application/protocol. This API will also reset TX Power to
   * its minimum value, so \ref RAIL_SetTxPower must be called afterwards.
   *
   * At times, certain combinations of configurations cannot be achieved.
   * This API attempts to get as close as possible to the requested settings. The
   * following "RAIL_Get..." API can be used to determine what values were set.
   */
  RAIL_Status_t RAIL_ConfigTxPower(RAIL_Handle_t railHandle,
                                   const RAIL_TxPowerConfig_t *config);
  
  /**
   * Gets the TX power settings currently used in the amplifier.
   *
   * @param[in] railHandle A RAIL instance handle.
   * @param[out] config A pointer to memory allocated to hold the current TxPower
   *   configuration structure. A NULL config will produce undefined
   *   behavior.
   * @return RAIL status variable indicating whether or not the get was
   * successful.
   *
   * Note that this API does not return the current TX power, which is separately
   * managed by the \ref RAIL_GetTxPower/\ref RAIL_SetTxPower APIs. This API
   * should be used to determine which values were set as a result of
   * \ref RAIL_ConfigTxPower.
   */
  RAIL_Status_t RAIL_GetTxPowerConfig(RAIL_Handle_t railHandle,
                                      RAIL_TxPowerConfig_t *config);
  
  /**
   * Sets the TX power in units of raw units (see \ref rail_chip_specific.h for
   * value ranges).
   *
   * @param[in] railHandle A RAIL instance handle.
   * @param[in] powerLevel Power in chip-specific \ref RAIL_TxPowerLevel_t units.
   * @return RAIL_Status_t indicating success or an error.
   *
   * To convert between decibels and the integer values that the
   * registers take, call \ref RAIL_ConvertDbmToRaw.
   * A weak version of this function, which works well with our boards is provided. However,
   * customers using a custom board need to characterize
   * chip operation on that board and override the function to convert
   * appropriately from the desired dB values to raw integer values.
   *
   * Depending on the configuration used in \ref RAIL_ConfigTxPower, not all
   * power levels are achievable. This API will get as close as possible to
   * the desired power without exceeding it, and calling \ref RAIL_GetTxPower is
   * the only way to know the exact value written.
   *
   * Calling this function before configuring the PA (i.e., before a successful
   * call to \ref RAIL_ConfigTxPower) will return an error.
   */
  RAIL_Status_t RAIL_SetTxPower(RAIL_Handle_t railHandle,
                                RAIL_TxPowerLevel_t powerLevel);
  
  /**
   * Returns the current power setting of the PA.
   *
   * @param[in] railHandle A RAIL instance handle.
   * @return The chip-specific \ref RAIL_TxPowerLevel_t value of the current
   * transmit power.
   *
   * This API returns the raw value that was set by \ref RAIL_SetTxPower.
   * A weak version of \ref RAIL_ConvertRawToDbm that works
   * with our boards to convert the raw values into actual output dBm values is provided.
   * However, customers using a custom board need to
   * re-characterize the relationship between raw and decibel values and rewrite
   * the provided function.
   *
   * Calling this function before configuring the PA (i.e., before a successful
   * call to \ref RAIL_ConfigTxPower) will return an error
   * (RAIL_TX_POWER_LEVEL_INVALID).
   */
  RAIL_TxPowerLevel_t RAIL_GetTxPower(RAIL_Handle_t railHandle);
  
  /**
   * Converts raw values written to registers to decibel value (in units of
   * deci-dBm).
   *
   * @param[in] railHandle A RAIL instance handle.
   * @param[in] mode PA mode for which to convert.
   * @param[in] powerLevel A raw amplifier register value to be converted to
   *   deci-dBm.
   * @return raw amplifier values converted to units of deci-dBm.
   *
   * A weak version of this function is provided that is tuned
   * to provide accurate values for our boards. For a
   * custom board, the relationship between what is written to the TX amplifier
   * and the actual output power should be re-characterized and implemented in an
   * overriding version of \ref RAIL_ConvertRawToDbm. For minimum code size and
   * best speed, use only raw values with the TxPower API and override this
   * function with a smaller function. In the weak version provided with the RAIL
   * library, railHandle is only used to indicate to the user from where the
   * function was called, so it is OK to use either a real protocol handle, or one
   * of the chip-specific ones, such as \ref RAIL_EFR32_HANDLE.
   *
   * Although the definitions of this function may change, the signature
   * must be as declared here.
   */
  RAIL_TxPower_t RAIL_ConvertRawToDbm(RAIL_Handle_t railHandle,
                                      RAIL_TxPowerMode_t mode,
                                      RAIL_TxPowerLevel_t powerLevel);
  
  /**
   * Converts the desired decibel value (in units of deci-dBm)
   * to raw integer values used by the TX amplifier registers.
   *
   * @param[in] railHandle A RAIL instance handle.
   * @param[in] mode PA mode for which to do the conversion.
   * @param[in] power Desired dBm values in units of deci-dBm.
   * @return deci-dBm value converted to a raw
   *   integer value that can be used directly with \ref RAIL_SetTxPower.
   *
   * A weak version of this function is provided that is tuned
   * to provide accurate values for our boards. For a
   * custom board, the relationship between what is written to the TX amplifier
   * and the actual output power should be characterized and implemented in an
   * overriding version of \ref RAIL_ConvertDbmToRaw. For minimum code size and
   * best speed use only raw values with the TxPower API and override this
   * function with a smaller function. In the weak version provided with the RAIL
   * library, railHandle is only used to indicate to the user from where the
   * function was called, so it is OK to use either a real protocol handle, or one
   * of the chip-specific ones, such as \ref RAIL_EFR32_HANDLE.
   *
   * Although the definitions of this function may change, the signature
   * must be as declared here.
   *
   * @note This function is called from within the RAIL library for
   *   comparison between channel limitations and current power. It will
   *   throw an assert if you haven't called RAIL_InitTxPowerCurves
   *   which initializes the mappings between raw power levels and
   *   actual dBm powers. To avoid the assert, ensure that the
   *   maxPower of all channel config entries is \ref RAIL_TX_POWER_MAX
   *   or above, or override this function to always return 255.
   */
  RAIL_TxPowerLevel_t RAIL_ConvertDbmToRaw(RAIL_Handle_t railHandle,
                                           RAIL_TxPowerMode_t mode,
                                           RAIL_TxPower_t power);
  
  /** @} */ // end of group PA
  
  /// Sets the TX power in terms of deci-dBm instead of raw power level.
  ///
  /// @param[in] railHandle A RAIL instance handle.
  /// @param[in] power A desired deci-dBm power to be set.
  /// @return RAIL Status variable indicate whether setting the
  ///   power was successful.
  ///
  /// This is a utility function for user convenience. Normally, to set TX
  /// power in dBm, the user would have to do the following:
  ///
  /// @code{.c}
  /// RAIL_TxPower_t power = 100; // 100 deci-dBm, 10 dBm
  /// RAIL_TxPowerConfig_t txPowerConfig;
  /// RAIL_GetTxPowerConfig(railHandle, &txPowerConfig);
  /// // RAIL_ConvertDbmToRaw will be the weak version provided by Silicon Labs
  /// // by default, or the customer version, if overwritten.
  /// RAIL_TxPowerLevel_t powerLevel = RAIL_ConvertDbmToRaw(railHandle,
  ///                                                       txPowerConfig.mode,
  ///                                                       power);
  /// RAIL_SetTxPower(railHandle, powerLevel);
  /// @endcode
  ///
  /// This function wraps all those calls in a single function with power passed in
  /// as a parameter.
  ///
  RAIL_Status_t RAIL_SetTxPowerDbm(RAIL_Handle_t railHandle,
                                   RAIL_TxPower_t power);
  
  /// Gets the TX power in terms of deci-dBm instead of raw power level.
  ///
  /// @param[in] railHandle A RAIL instance handle.
  /// @return The current output power in deci-dBm.
  ///
  /// This is a utility function for user convenience. Normally, to get TX
  /// power in dBm, the user would have to do the following:
  ///
  /// @code{.c}
  /// RAIL_TxPowerLevel_t powerLevel = RAIL_GetTxPower(railHandle);
  /// RAIL_TxPowerConfig_t txPowerConfig;
  /// RAIL_GetTxPowerConfig(railHandle, &txPowerConfig);
  /// // RAIL_ConvertRawToDbm will be the weak version provided by Silicon Labs
  /// // by default, or the customer version, if overwritten.
  /// RAIL_TxPower_t power = RAIL_ConvertRawToDbm(railHandle,
  ///                                             txPowerConfig.mode,
  ///                                             power);
  /// return power;
  /// @endcode
  ///
  /// This function wraps all those calls in a single function with power returned
  /// as the result.
  ///
  RAIL_TxPower_t RAIL_GetTxPowerDbm(RAIL_Handle_t railHandle);
  
  /**
   * Starts a non-blocking transmit.
   *
   * @param[in] railHandle A RAIL instance handle.
   * @param[in] channel Define the channel to transmit on.
   * @param[in] options TX options to be applied to this transmit only.
   * @param[in] schedulerInfo Information to allow the radio scheduler to place
   *   this transmit appropriately. This is only used in multiprotocol version of
   *   RAIL and may be set to NULL in all other versions.
   * @return Status code indicating success of the function call. If successfully
   *   initiated, transmit completion or failure will be reported by a later
   *   \ref RAIL_Config_t::eventsCallback with the appropriate \ref RAIL_Events_t.
   *
   * Will begin transmission of the payload previously loaded via
   * \ref RAIL_WriteTxFifo() immediately, or right after a packet currently being
   * received is completed.
   *
   * Returns an error if a previous transmit is still in progress.
   * If changing channels, any ongoing packet reception is aborted.
   *
   * In multiprotocol, ensure that the radio is properly yielded after this
   * operation completes. See \ref rail_radio_scheduler_yield for more details.
   */
  RAIL_Status_t RAIL_StartTx(RAIL_Handle_t railHandle,
                             uint16_t channel,
                             RAIL_TxOptions_t options,
                             const RAIL_SchedulerInfo_t *schedulerInfo);
  
  /**
   * Stops an active or pending transmit
   *
   * @param[in] railHandle A RAIL instance handle.
   * @param[in] mode Configure the types of transmits to stop.
   * @return \ref RAIL_STATUS_NO_ERROR if the transmit was successfully
   *   cancelled.
   *   Returns \ref RAIL_STATUS_INVALID_STATE if the requested
   *                transmit mode cannot be stopped.
   *
   * @note This function will stop autoAcks in active transmit.
   */
  RAIL_Status_t RAIL_StopTx(RAIL_Handle_t railHandle, RAIL_StopMode_t mode);
  
  /**
   * Schedules sending a packet.
   *
   * @param[in] railHandle A RAIL instance handle.
   * @param[in] channel Define the channel to transmit on.
   * @param[in] options TX options to be applied to this transmit only.
   * @param[in] config A pointer to the \ref RAIL_ScheduleTxConfig_t
   *   structure containing when the transmit should occur.
   * @param[in] schedulerInfo Information to allow the radio scheduler to place
   *   this transmit appropriately. This is only used in multiprotocol version of
   *   RAIL and may be set to NULL in all other versions.
   * @return Status code indicating success of the function call. If successfully
   *   initiated, a transmit completion or failure will be reported by a later
   *   \ref RAIL_Config_t::eventsCallback with the appropriate \ref RAIL_Events_t.
   *
   * Will begin transmission of the payload previously loaded via
   * \ref RAIL_WriteTxFifo() at the scheduled time.
   * The time (in microseconds) as well as whether that time is absolute or
   * relative, is specified using the \ref RAIL_ScheduleTxConfig_t structure.
   * Also specified in this structure is what to do if a scheduled transmit
   * fires in the midst of receiving a packet.
   *
   * Returns an error if a previous transmit is still in progress.
   * If changing channels, any ongoing packet reception is aborted.
   *
   * In multiprotocol, ensure that the radio is properly yielded after this
   * operation completes. See \ref rail_radio_scheduler_yield for more details.
   */
  RAIL_Status_t RAIL_StartScheduledTx(RAIL_Handle_t railHandle,
                                      uint16_t channel,
                                      RAIL_TxOptions_t options,
                                      const RAIL_ScheduleTxConfig_t *config,
                                      const RAIL_SchedulerInfo_t *schedulerInfo);
  
  /**
   * Starts a non-blocking Transmit using CSMA.
   *
   * @param[in] railHandle A RAIL instance handle.
   * @param[in] channel Define the channel to transmit on.
   * @param[in] options TX options to be applied to this transmit only.
   * @param[in] csmaConfig A pointer to the RAIL_CsmaConfig_t structure
   *   describing the CSMA parameters to use for this transmit.
   * @param[in] schedulerInfo Information to allow the radio scheduler to place
   *   this transmit appropriately. This is only used in multiprotocol version of
   *   RAIL and may be set to NULL in all other versions.
   * @return Status code indicating success of the function call. If successfully
   *   initiated, a transmit completion or failure will be reported by a later
   *   \ref RAIL_Config_t::eventsCallback with the appropriate \ref RAIL_Events_t.
   *
   * Performs the Carrier Sense Multiple Access (CSMA) algorithm and, if
   * the channel is deemed clear (RSSI below the specified threshold), it will
   * commence transmission of the payload previously loaded via
   * RAIL_WriteTxFifo().
   * Packets can be received during CSMA backoff periods if receive is active
   * throughout the CSMA process. This will happen either by starting the CSMA
   * process while receive is already active, or if the csmaBackoff time in
   * the \ref RAIL_CsmaConfig_t is less than the idleToRx time (set by
   * RAIL_SetStateTiming()). If the csmaBackoff time is greater than the
   * idleToRx time, receive will only be active during CSMA's clear channel
   * assessments.
   *
   * If the CSMA algorithm deems the channel busy, the \ref RAIL_Config_t::eventsCallback
   * occurs with \ref RAIL_EVENT_TX_CHANNEL_BUSY, and the contents
   * of the TX FIFO remain intact.
   *
   * Returns an error if a previous transmit is still in progress.
   * If changing channels, any ongoing packet reception is aborted.
   *
   * In multiprotocol, ensure that the radio is properly yielded after this
   * operation completes. See \ref rail_radio_scheduler_yield for more details.
   */
  RAIL_Status_t RAIL_StartCcaCsmaTx(RAIL_Handle_t railHandle,
                                    uint16_t channel,
                                    RAIL_TxOptions_t options,
                                    const RAIL_CsmaConfig_t *csmaConfig,
                                    const RAIL_SchedulerInfo_t *schedulerInfo);
  
  /**
   * Starts a non-blocking Transmit using LBT.
   *
   * @param[in] railHandle A RAIL instance handle.
   * @param[in] channel Define the channel to transmit on.
   * @param[in] options TX options to be applied to this transmit only.
   * @param[in] lbtConfig A pointer to the RAIL_LbtConfig_t structure
   *   describing the LBT parameters to use for this transmit.
   * @param[in] schedulerInfo Information to allow the radio scheduler to place
   *   this transmit appropriately. This is only used in multiprotocol version of
   *   RAIL and may be set to NULL in all other versions.
   * @return Status code indicating success of the function call. If successfully
   *   initiated, a transmit completion or failure will be reported by a later
   *   \ref RAIL_Config_t::eventsCallback with the appropriate \ref RAIL_Events_t.
   *
   * Performs the Listen Before Talk (LBT) algorithm and, if the channel
   * is deemed clear (RSSI below the specified threshold), it will commence
   * transmission of the payload previously loaded via RAIL_WriteTxFifo().
   * Packets can be received during LBT backoff periods if receive is active
   * throughout the LBT process. This will happen either by starting the LBT
   * process while receive is already active, or if the lbtBackoff time in
   * the \ref RAIL_LbtConfig_t is less than the idleToRx time (set by
   * RAIL_SetStateTiming()). If the lbtBackoff time is greater than the
   * idleToRx time, receive will only be active during LBT's clear channel
   * assessments.
   *
   * If the LBT algorithm deems the channel busy, the \ref RAIL_Config_t::eventsCallback occurs with
   * \ref RAIL_EVENT_TX_CHANNEL_BUSY, and the contents
   * of the TX FIFO remain intact.
   *
   * Returns an error if a previous transmit is still in progress.
   * If changing channels, any ongoing packet reception is aborted.
   *
   * In multiprotocol, ensure that the radio is properly yielded after this
   * operation completes. See \ref rail_radio_scheduler_yield for more details.
   */
  RAIL_Status_t RAIL_StartCcaLbtTx(RAIL_Handle_t railHandle,
                                   uint16_t channel,
                                   RAIL_TxOptions_t options,
                                   const RAIL_LbtConfig_t *lbtConfig,
                                   const RAIL_SchedulerInfo_t *schedulerInfo);
  
  /**
   * Sets the CCA threshold in dBm.
   *
   * @param[in] railHandle A RAIL instance handle.
   * @param[in] ccaThresholdDbm The CCA threshold in dBm.
   * @return Status code indicating success of the function call.
   *
   * Unlike RAIL_StartCcaCsmaTx() or RAIL_StartCcaLbtTx(), which can cause a
   * transmit, this function only modifies the CCA threshold. A possible
   * use case for this function involves setting the CCA threshold to invalid RSSI
   * of -128 which blocks transmission by preventing clear channel assessments
   * from succeeding.
   */
  RAIL_Status_t RAIL_SetCcaThreshold(RAIL_Handle_t railHandle,
                                     int8_t ccaThresholdDbm);
  
  /**
   * Gets detailed information about the last packet transmitted.
   *
   * @param[in] railHandle A RAIL instance handle.
   * @param[in,out] pPacketDetails An application-provided pointer to store
   *   RAIL_TxPacketDetails_t corresponding to the transmit event.
   *   The isAck and timeSent fields totalPacketBytes and timePosition
   *   must be initialized prior to each call:
   *   - isAck true to obtain details about the most recent ACK transmit,
   *     false to obtain details about the most recent app-initiated transmit.
   *   - totalPacketBytes with the total number of bytes of the transmitted
   *     packet for RAIL to use when calculating the specified timestamp.
   *     This should account for all bytes sent over the air after the
   *     Preamble and Sync word(s), including CRC bytes.
   *   - timePosition with a \ref RAIL_PacketTimePosition_t value specifying
   *     the packet position to put in the timeSent field on return.
   *     This field will also be updated with the actual position corresponding
   *     to the timeSent value filled in.
   * @return \ref RAIL_STATUS_NO_ERROR if pPacketDetails was filled in,
   *   or an appropriate error code otherwise.
   *
   * @note Consider using \ref RAIL_GetTxPacketDetailsAlt for smaller code size.
   *
   * This function can only be called from callback context for either
   * \ref RAIL_EVENT_TX_PACKET_SENT or \ref RAIL_EVENT_TXACK_PACKET_SENT
   * events.
   */
  RAIL_Status_t RAIL_GetTxPacketDetails(RAIL_Handle_t railHandle,
                                        RAIL_TxPacketDetails_t *pPacketDetails);
  
  /**
   * Gets detailed information about the last packet transmitted.
   *
   * @param[in] railHandle A RAIL instance handle.
   * @param[in] isAck True to obtain details about the most recent ACK transmit.
   *   False to obtain details about the most recent app-initiated transmit.
   * @param[out] pPacketTime An application-provided non-NULL pointer to store a
   *   RAIL_Time_t corresponding to the transmit event. This will be populated
   *   with a timestamp corresponding to an arbitrary location in the packet. Call
   *   \ref RAIL_GetTxTimePreambleStart, \ref RAIL_GetTxTimeSyncWordEnd, or
   *   \ref RAIL_GetTxTimeFrameEnd to adjust the timestamp for different locations
   *   in the packet.
   * @return \ref RAIL_STATUS_NO_ERROR if pPacketTime was filled in,
   *   or an appropriate error code otherwise.
   *
   * This function can only be called from callback context for either
   * \ref RAIL_EVENT_TX_PACKET_SENT or \ref RAIL_EVENT_TXACK_PACKET_SENT
   * events.
   */
  RAIL_Status_t RAIL_GetTxPacketDetailsAlt(RAIL_Handle_t railHandle,
                                           bool isAck,
                                           RAIL_Time_t *pPacketTime);
  
  /**
   * Adjust a RAIL TX timestamp to refer to the start of the preamble.
   *
   * @param[in] railHandle A RAIL instance handle.
   * @param[in] totalPacketBytes The total number of bytes of the transmitted
   *   packet for RAIL to use when calculating the specified timestamp. This
   *   should account for all bytes transmitted over the air after the Preamble
   *   and Sync word(s), including CRC bytes.
   * @param[in, out] pPacketTime The time that was returned in a
   *   \ref RAIL_Time_t from a previous call to \ref RAIL_GetTxPacketDetailsAlt
   *   for this same packet. After this function, the time at that location will
   *   be updated with the time that the preamble for this packet started on air.
   *   Must be non-NULL.
   * @return \ref RAIL_STATUS_NO_ERROR if pPacketTime was successfully calculated,
   *   or an appropriate error code otherwise.
   *
   * This timestamp adjustment API should be called in the same interrupt context
   * as \ref RAIL_GetTxPacketDetailsAlt in order to get the timestamp for the
   * beginning of the preamble.
   */
  RAIL_Status_t RAIL_GetTxTimePreambleStart(RAIL_Handle_t railHandle,
                                            uint16_t totalPacketBytes,
                                            RAIL_Time_t *pPacketTime);
  
  /**
   * Adjust a RAIL TX timestamp to refer to the end of the sync word
   *
   * @param[in] railHandle A RAIL instance handle.
   * @param[in] totalPacketBytes The total number of bytes of the transmitted
   *   packet for RAIL to use when calculating the specified timestamp. This
   *   should account for all bytes transmitted over the air after the Preamble
   *   and Sync word(s), including CRC bytes.
   * @param[in, out] pPacketTime The time that was returned in a
   *   \ref RAIL_Time_t from a previous call to \ref RAIL_GetTxPacketDetailsAlt
   *   for this same packet. After this function, the time at that location will
   *   be updated with the time that the sync word for this packet finished on
   *   air.  Must be non-NULL.
   * @return \ref RAIL_STATUS_NO_ERROR if pPacketTime was successfully calculated,
   *   or an appropriate error code otherwise.
   *
   * This timestamp adjustment API should be called in the same interrupt context
   * as \ref RAIL_GetTxPacketDetailsAlt in order to get the timestamp for the
   * end of the sync word.
   */
  RAIL_Status_t RAIL_GetTxTimeSyncWordEnd(RAIL_Handle_t railHandle,
                                          uint16_t totalPacketBytes,
                                          RAIL_Time_t *pPacketTime);
  
  /**
   * Adjust a RAIL TX timestamp to refer to the end of frame
   *
   * @param[in] railHandle A RAIL instance handle.
   * @param[in] totalPacketBytes The total number of bytes of the transmitted
   *   packet for RAIL to use when calculating the specified timestamp. This
   *   should account for all bytes transmitted over the air after the Preamble
   *   and Sync word(s), including CRC bytes.
   * @param[in, out] pPacketTime The time that was returned in a
   *   \ref RAIL_Time_t from a previous call to \ref RAIL_GetTxPacketDetailsAlt
   *   for this same packet. After this function, the time at that location will
   *   be updated with the time that this packet finished on air. Must be
   *   non-NULL.
   * @return \ref RAIL_STATUS_NO_ERROR if pPacketTime was successfully calculated,
   *   or an appropriate error code otherwise.
   *
   * This timestamp adjustment API should be called in the same interrupt context
   * as \ref RAIL_GetTxPacketDetailsAlt in order to get the timestamp for the
   * end of the frame.
   */
  RAIL_Status_t RAIL_GetTxTimeFrameEnd(RAIL_Handle_t railHandle,
                                       uint16_t totalPacketBytes,
                                       RAIL_Time_t *pPacketTime);
  /**
   * Prevent the radio from starting a transmit.
   *
   * @param[in] railHandle A RAIL instance handle.
   * @param[in] enable Enable/Disable TX hold off.
   * @return void.
   *
   * Enable TX hold off to prevent the radio from starting any transmits.
   * Disable TX hold off to allow the radio to transmit again.
   * Attempting to transmit with the TX hold off enabled will result in
   * \ref RAIL_EVENT_TX_BLOCKED and/or \ref RAIL_EVENT_TXACK_BLOCKED
   * events.
   *
   * @note This function does not affect a transmit that has already started.
   *   To stop an already-started transmission, use RAIL_Idle() with
   *   \ref RAIL_IDLE_ABORT.
   */
  void RAIL_EnableTxHoldOff(RAIL_Handle_t railHandle, bool enable);
  
  /**
   * Check whether or not TX hold off is enabled.
   *
   * @param[in] railHandle A RAIL instance handle.
   * @return Returns true if TX hold off is enabled, false otherwise.
   *
   * TX hold off can be enabled/disabled using \ref RAIL_EnableTxHoldOff.
   * Attempting to transmit with the TX hold off enabled will block the
   * transmission and result in \ref RAIL_EVENT_TX_BLOCKED
   * and/or \ref RAIL_EVENT_TXACK_BLOCKED events.
   */
  bool RAIL_IsTxHoldOffEnabled(RAIL_Handle_t railHandle);
  
  /** @} */ // end of group Transmit
  
  /******************************************************************************
   * Receive
   *****************************************************************************/
  /**
   * @addtogroup Receive
   * @brief APIs related to packet receive
   * @{
   */
  
  /**
   * Configures receive options.
   *
   * @param[in] railHandle A RAIL instance handle.
   * @param[in] mask A bitmask containing which options should be modified.
   * @param[in] options A bitmask containing desired configuration settings.
   *   Bit positions for each option are found in the \ref RAIL_RxOptions_t.
   * @return Status code indicating success of the function call.
   *
   * Configures the radio receive flow based on the list of available options.
   * Only the options indicated by the mask parameter will be affected. Pass
   * \ref RAIL_RX_OPTIONS_ALL to set all parameters.
   * The previous settings may affect the current frame if a packet is
   * received during this configuration.
   */
  RAIL_Status_t RAIL_ConfigRxOptions(RAIL_Handle_t railHandle,
                                     RAIL_RxOptions_t mask,
                                     RAIL_RxOptions_t options);
  
  #ifndef DOXYGEN_SHOULD_SKIP_THIS
  /**
   * Function implemented in the radio config for frame type length handling
   *
   * @param[in] railHandle A RAIL instance handle.
   * @param[in] frameType A frame type configuration structure.
   * @return void.
   *
   * Currently, the frame type passed in only handles packet length decoding. If
   * NULL is passed into this function, it clears any currently configured
   * frame type settings. This will either be implemented as an empty function in
   * the radio configuration if it is not needed, to assist in dead code
   * elimination.
   */
  void RAILCb_ConfigFrameTypeLength(RAIL_Handle_t railHandle,
                                    const RAIL_FrameType_t *frameType);
  #endif
  
  /**
   * Starts the receiver on a specific channel.
   *
   * @param[in] railHandle A RAIL instance handle.
   * @param[in] channel The channel to listen on.
   * @param[in] schedulerInfo Information to allow the radio scheduler to place
   *   this receive appropriately. This is only used in multiprotocol version of
   *   RAIL and may be set to NULL in all other versions.
   * @return Status code indicating success of the function call.
   *
   * This is a non-blocking function. Whenever a packet is received, \ref RAIL_Config_t::eventsCallback
   * will fire with \ref RAIL_EVENT_RX_PACKET_RECEIVED set. If you call
   * this while not idle but with a different channel, any ongoing
   * receive or transmit operation will be aborted.
   */
  RAIL_Status_t RAIL_StartRx(RAIL_Handle_t railHandle,
                             uint16_t channel,
                             const RAIL_SchedulerInfo_t *schedulerInfo);
  
  /**
   * Schedules a receive window for some future time.
   *
   * @param[in] railHandle A RAIL instance handle.
   * @param[in] channel A channel to listen on.
   * @param[in] cfg The configuration structure to define the receive window.
   * @param[in] schedulerInfo Information to allow the radio scheduler to place
   *   this receive appropriately. This is only used in multiprotocol version of
   *   RAIL and may be set to NULL in all other versions.
   * @return Status code indicating success of the function call.
   *
   * This API immediately changes the channel and schedules receive to start
   * at the specified time and end at the given end time. If you do not specify
   * an end time, you may call this API later with an end time as long as you set
   * the start time to disabled. You can also terminate the receive
   * operation immediately using the RAIL_Idle() function. Note that relative
   * end times are always relative to the start unless no start time is
   * specified. If changing channels, aborts any ongoing packet transmission or
   * reception.
   *
   * In multiprotocol, ensure that you properly yield the radio after this
   * call. See \ref rail_radio_scheduler_yield for more details.
   */
  RAIL_Status_t RAIL_ScheduleRx(RAIL_Handle_t railHandle,
                                uint16_t channel,
                                const RAIL_ScheduleRxConfig_t *cfg,
                                const RAIL_SchedulerInfo_t *schedulerInfo);
  
  /**
   * Gets basic information about a pending or received packet.
   * This function can be used in any RX mode; it does not free up any
   * internal resources.
   *
   * @param[in] railHandle A RAIL instance handle.
   * @param[in] packetHandle A packet handle for the unreleased packet as
   *   returned from a previous call, or sentinel values
   *   \ref RAIL_RX_PACKET_HANDLE_OLDEST or \ref RAIL_RX_PACKET_HANDLE_NEWEST.
   * @param[out] pPacketInfo An application-provided pointer to store
   *   \ref RAIL_RxPacketInfo_t for the requested packet. Must be non-NULL.
   * @return The packet handle for the requested packet:
   *   if packetHandle was one of the sentinel values, returns the actual
   *   packet handle for that packet, otherwise returns packetHandle.
   *   It may return \ref RAIL_RX_PACKET_HANDLE_INVALID to indicate an error.
   *
   * @note When getting information about an arriving packet that is not yet complete
   *   (i.e., pPacketInfo->packetStatus == \ref RAIL_RX_PACKET_RECEIVING), keep
   *   in mind its data is highly suspect because it has not yet passed any CRC
   *   integrity checking. Also note that the packet could be aborted, cancelled, or
   *   fail momentarily, invalidating its data in Packet mode. Furthermore, there
   *   is a small chance towards the end of packet reception that the filled-in
   *   RAIL_RxPacketInfo_t could include not only packet data received so far,
   *   but also some raw radio-appended info detail bytes that RAIL's
   *   packet-completion processing will subsequently deal with. It's up to the
   *   application to know its packet format well enough to avoid confusing such
   *   info as packet data.
   */
  RAIL_RxPacketHandle_t RAIL_GetRxPacketInfo(RAIL_Handle_t railHandle,
                                             RAIL_RxPacketHandle_t packetHandle,
                                             RAIL_RxPacketInfo_t *pPacketInfo);
  
  /**
   * Convenience helper function to copy a full packet to a user-specified
   * contiguous buffer.
   *
   * @param[out] pDest An application-provided pointer to a buffer of at
   *   least pPacketInfo->packetBytes in size to store the packet data
   *   contiguously. This buffer must never overlay RAIL's Rx FIFO buffer.
   *   Exactly pPacketInfo->packetBytes of packet data will be written into it.
   * @param[out] pPacketInfo
   *   \ref RAIL_RxPacketInfo_t for the requested packet.
   * @return void.
   *
   * @note This helper is intended to be expedient. As a result, it does not
   *   check the validity of its arguments,
   *   so don't pass either as NULL, and don't
   *   pass a pDest pointer to a buffer that's too small for the packet's data.
   * @note If only a portion of the packet is needed, use RAIL_PeekRxPacket()
   *   instead.
   */
  static inline
  void RAIL_CopyRxPacket(uint8_t *pDest,
                         const RAIL_RxPacketInfo_t *pPacketInfo)
  {
    memcpy(pDest, pPacketInfo->firstPortionData, pPacketInfo->firstPortionBytes);
    if (pPacketInfo->lastPortionData != NULL) {
      memcpy(pDest + pPacketInfo->firstPortionBytes,
             pPacketInfo->lastPortionData,
             pPacketInfo->packetBytes - pPacketInfo->firstPortionBytes);
    }
  }
  
  /**
   * Gets detailed information about a ready packet received (one whose
   * \ref RAIL_RxPacketStatus_t is among the RAIL_RX_PACKET_READY_ set).
   * This function can be used in any RX mode; it does not free up any
   * internal resources.
   *
   * @param[in] railHandle A RAIL instance handle.
   * @param[in] packetHandle A packet handle for the unreleased packet as
   *   returned from a previous call to RAIL_GetRxPacketInfo() or
   *   RAIL_HoldRxPacket(), or sentinel values \ref RAIL_RX_PACKET_HANDLE_OLDEST
   *   or \ref RAIL_RX_PACKET_HANDLE_NEWEST.
   * @param[in,out] pPacketDetails An application-provided non-NULL pointer to
   *   store \ref RAIL_RxPacketDetails_t for the requested packet.
   *   The timeReceived fields totalPacketBytes and timePosition must be
   *   initialized prior to each call:
   *   - totalPacketBytes with the total number of bytes of the received
   *     packet for RAIL to use when calculating the specified timestamp.
   *     This should account for all bytes received over the air after the
   *     Preamble and Sync word(s), including CRC bytes.
   *   - timePosition with a \ref RAIL_PacketTimePosition_t value specifying
   *     the packet position to put in the timeReceived field on return.
   *     This field will also be updated with the actual position corresponding
   *     to the timeReceived value filled in.
   * @return \ref RAIL_STATUS_NO_ERROR if pPacketDetails was filled in,
   *   or an appropriate error code otherwise.
   *
   * @note Consider using \ref RAIL_GetRxPacketDetailsAlt for smaller code size.
   */
  RAIL_Status_t RAIL_GetRxPacketDetails(RAIL_Handle_t railHandle,
                                        RAIL_RxPacketHandle_t packetHandle,
                                        RAIL_RxPacketDetails_t *pPacketDetails);
  
  /**
   * Get detailed information about a ready packet received (one whose
   * \ref RAIL_RxPacketStatus_t is among the RAIL_RX_PACKET_READY_ set).
   * This function can be used in any RX mode; it does not free up any
   * internal resources.
   *
   * @param[in] railHandle A RAIL instance handle.
   * @param[in] packetHandle A packet handle for the unreleased packet as
   *   returned from a previous call to RAIL_GetRxPacketInfo() or
   *   RAIL_HoldRxPacket(), or sentinel values \ref RAIL_RX_PACKET_HANDLE_OLDEST
   *   or \ref RAIL_RX_PACKET_HANDLE_NEWEST.
   * @param[out] pPacketDetails An application-provided non-NULL pointer to store
   *   \ref RAIL_RxPacketDetails_t for the requested packet. The timeReceived
   *   field packetTime will be populated with a timestamp corresponding to an
   *   arbitrary location in the packet. Call \ref RAIL_GetRxTimePreambleStart,
   *   \ref RAIL_GetRxTimeSyncWordEnd, or \ref RAIL_GetRxTimeFrameEnd to adjust
   *   that timestamp for different locations in the packet.
   * @return \ref RAIL_STATUS_NO_ERROR if pPacketDetails was filled in,
   *   or an appropriate error code otherwise.
   *
   * This alternative API allows for smaller code size by deadstripping the
   * timestamp adjustment algorithms which are not in use.
   */
  RAIL_Status_t RAIL_GetRxPacketDetailsAlt(RAIL_Handle_t railHandle,
                                           RAIL_RxPacketHandle_t packetHandle,
                                           RAIL_RxPacketDetails_t *pPacketDetails);
  
  /**
   * Adjust a RAIL RX timestamp to refer to the start of the preamble.
   *
   * @param[in] railHandle A RAIL instance handle.
   * @param[in] totalPacketBytes The total number of bytes of the received packet
   *   for RAIL to use when calculating the specified timestamp. This should
   *   account for all bytes received over the air after the Preamble and Sync
   *   word(s), including CRC bytes.
   * @param[in, out] pPacketTime The time that was returned in the
   *   \ref RAIL_PacketTimeStamp_t::packetTime field of
   *   \ref RAIL_RxPacketDetails_t::timeReceived from a previous call to
   *   \ref RAIL_GetRxPacketDetailsAlt for this same packet. After this
   *   function, the time at that location will be updated with the time that the
   *   preamble for this packet started on air. Must be non-NULL.
   * @return \ref RAIL_STATUS_NO_ERROR if pPacketTime was successfully calculated,
   *   or an appropriate error code otherwise.
   *
   * This API must be called while the given railHandle is active, or it will
   * return an error code of \ref RAIL_STATUS_INVALID_STATE.
   */
  RAIL_Status_t RAIL_GetRxTimePreambleStart(RAIL_Handle_t railHandle,
                                            uint16_t totalPacketBytes,
                                            RAIL_Time_t *pPacketTime);
  
  /**
   * Adjust a RAIL RX timestamp to refer to the end of the sync word
   *
   * @param[in] railHandle A RAIL instance handle.
   * @param[in] totalPacketBytes The total number of bytes of the received packet
   *   for RAIL to use when calculating the specified timestamp. This should
   *   account for all bytes received over the air after the Preamble and Sync
   *   word(s), including CRC bytes.
   * @param[in, out] pPacketTime The time that was returned in the
   *   \ref RAIL_PacketTimeStamp_t::packetTime field of
   *   \ref RAIL_RxPacketDetails_t::timeReceived from a previous call to
   *   \ref RAIL_GetRxPacketDetailsAlt for this same packet. After this
   *   function, the time at that location will be updated with the time that the
   *   sync word for this packet finished on air. Must be non-NULL.
   * @return \ref RAIL_STATUS_NO_ERROR if pPacketTime was successfully calculated,
   *   or an appropriate error code otherwise.
   *
   * This API must be called while the given railHandle is active, or it will
   * return an error code of \ref RAIL_STATUS_INVALID_STATE.
   */
  RAIL_Status_t RAIL_GetRxTimeSyncWordEnd(RAIL_Handle_t railHandle,
                                          uint16_t totalPacketBytes,
                                          RAIL_Time_t *pPacketTime);
  
  /**
   * Adjust a RAIL RX timestamp to refer to the end of frame
   *
   * @param[in] railHandle A RAIL instance handle.
   * @param[in] totalPacketBytes The total number of bytes of the received packet
   *   for RAIL to use when calculating the specified timestamp. This should
   *   account for all bytes received over the air after the Preamble and Sync
   *   word(s), including CRC bytes.
   * @param[in, out] pPacketTime The time that was returned in the
   *   \ref RAIL_PacketTimeStamp_t::packetTime field of
   *   \ref RAIL_RxPacketDetails_t::timeReceived from a previous call to
   *   \ref RAIL_GetRxPacketDetailsAlt for this same packet. After this
   *   function, the time at that location will be updated with the time that this
   *   packet finished on air. Must be non-NULL.
   * @return \ref RAIL_STATUS_NO_ERROR if pPacketTime was successfully calculated,
   *   or an appropriate error code otherwise.
   *
   * This API must be called while the given railHandle is active, or it will
   * return an error code of \ref RAIL_STATUS_INVALID_STATE.
   */
  RAIL_Status_t RAIL_GetRxTimeFrameEnd(RAIL_Handle_t railHandle,
                                       uint16_t totalPacketBytes,
                                       RAIL_Time_t *pPacketTime);
  
  /**
   * Place a temporary hold on this packet's data and information resources
   * within RAIL.
   * This function can only be called from within RAIL callback context.
   * It can be used in any RX mode.
   *
   * Normally, when RAIL issues its callback indicating a packet is ready
   * or aborted, it expects the application's callback to retrieve and
   * copy (or discard) the packet's information and data, and will free up
   * its internal packet data after the callback returns. This function
   * tells RAIL to hold onto those resources after the callback returns in
   * case the application wants to defer processing the packet to a later
   * time, e.g., outside of callback context.
   *
   * @param[in] railHandle A RAIL instance handle.
   * @return The packet handle for the packet associated with the callback,
   *   \ref RAIL_RX_PACKET_HANDLE_INVALID if no such packet yet exists or
   *   railHandle is not active.
   */
  RAIL_RxPacketHandle_t RAIL_HoldRxPacket(RAIL_Handle_t railHandle);
  
  /**
   * Copies 'len' bytes of packet data starting from 'offset' from the
   * receive FIFO. Those bytes remain valid for re-peeking.
   *
   * @param[in] railHandle A RAIL instance handle.
   * @param[in] packetHandle A packet handle as returned from a previous
   *   RAIL_GetRxPacketInfo() or RAIL_HoldRxPacket() call, or
   *   sentinel values \ref RAIL_RX_PACKET_HANDLE_OLDEST
   *   or \ref RAIL_RX_PACKET_HANDLE_NEWEST.
   * @param[out] pDst A pointer to the location where the received bytes will
   *   be copied. If NULL, no copying occurs.
   * @param[in] len A number of packet data bytes to copy.
   * @param[in] offset A byte offset within remaining packet data from which
   *   to copy.
   * @return Number of packet bytes copied.
   *
   * @note Peek does not permit peeking beyond the requested packet's
   *   available packet data (though there is a small chance it might
   *   for a \ref RAIL_RX_PACKET_HANDLE_NEWEST packet at the very end of
   *   still being received). Nor can one peek into already-consumed data read
   *   by RAIL_ReadRxFifo(). len and offset are relative to the remaining data
   *   available in the packet, if any was already consumed by RAIL_ReadRxFifo().
   */
  uint16_t RAIL_PeekRxPacket(RAIL_Handle_t railHandle,
                             RAIL_RxPacketHandle_t packetHandle,
                             uint8_t *pDst,
                             uint16_t len,
                             uint16_t offset);
  
  /**
   * Releases RAIL's internal resources for the packet.
   * This function must be called for any packet previously held via
   * RAIL_HoldRxPacket(). It may optionally be called within a
   * callback context to release RAIL resources sooner than at
   * callback completion time when not holding the packet.
   * This function can be used in any RX mode.
   *
   * @param[in] railHandle A RAIL instance handle.
   * @param[in] packetHandle A packet handle as returned from a previous
   *   RAIL_HoldRxPacket() call, or sentinel values
   *   \ref RAIL_RX_PACKET_HANDLE_OLDEST or \ref RAIL_RX_PACKET_HANDLE_NEWEST.
   *   The latter might be used within RAIL callback context to explicitly
   *   release the packet associated with the callback early, before it's
   *   released automatically by RAIL on callback return (unless explicitly
   *   held).
   * @return \ref RAIL_STATUS_NO_ERROR if the held packet was released
   *   or an appropriate error code otherwise.
   */
  RAIL_Status_t RAIL_ReleaseRxPacket(RAIL_Handle_t railHandle,
                                     RAIL_RxPacketHandle_t packetHandle);
  
  /**
   * Returns the current raw RSSI.
   *
   * @param[in] railHandle A RAIL instance handle.
   * @param[in] wait if false returns instant RSSI with no checks.
   * @return \ref RAIL_RSSI_INVALID if the receiver is disabled and an RSSI
   *   value can't be obtained. Otherwise, return the RSSI in quarter dBm, dbm*4.
   *
   * Gets the current RSSI value. This value represents the current energy of the
   * channel. It can change rapidly and will be low if no RF energy is
   * in the current channel. The function from the value reported to dBm is an
   * offset dependent on the PHY and the PCB layout. Users should characterize the
   * RSSI received on their hardware and apply an offset in the application to
   * account for board and PHY parameters. 'Wait' argument doesn't guarantee
   * a valid RSSI reading.'Wait' being true gives assurance that that the RSSI is
   * current and not a stale value from a previous radio state. If GetRssi is called
   * during RX-to-RX, RX-to-IDLE, or RX-to-TX transition, the RSSI is considered
   * stale and \ref RAIL_RSSI_INVALID is returned if wait=true. 'Wait' being false
   * will return either the current RSSI or a stale RSSI measurement (if called during
   * RX-to-RX, RX-to-IDLE, or RX-to-TX transition). \ref RAIL_RSSI_INVALID is
   * returned if radio hasn't been in RX longer than 'idleToRx' time
   * (see \ref RAIL_StateTiming_t), regardless of wait argument.
   *
   * In multiprotocol, this function returns \ref RAIL_RSSI_INVALID
   * immediately if railHandle is not the current active \ref RAIL_Handle_t.
   * Additionally, 'wait' should never be set 'true' in multiprotocol
   * as the wait time is not consistent, so scheduling a scheduler
   * slot cannot be done accurately.
   */
  int16_t RAIL_GetRssi(RAIL_Handle_t railHandle, bool wait);
  
  /**
   * Starts the RSSI averaging over a specified time in us.
   *
   * @param[in] railHandle A RAIL instance handle.
   * @param[in] channel The physical channel to set.
   * @param[in] averagingTimeUs Averaging time in microseconds.
   * @param[in] schedulerInfo Information to allow the radio scheduler to place
   *   this operation appropriately. This is only used in multiprotocol version of
   *   RAIL and may be set to NULL in all other versions.
   * @return Status code indicating success of the function call.
   *
   * Starts a non-blocking hardware-based RSSI averaging mechanism. Only a single
   * instance of RSSI averaging can be run at any time and the radio must be idle
   * to start.
   *
   * In multiprotocol, this is a scheduled event. It will start when railHandle
   * becomes active. railHandle needs to stay active until the averaging
   * completes. If the averaging is interrupted, calls to
   * \ref RAIL_GetAverageRssi will return \ref RAIL_RSSI_INVALID.
   *
   * Also in multiprotocol, the user is required to call \ref RAIL_YieldRadio
   * after this event completes (i.e., when \ref RAIL_EVENT_RSSI_AVERAGE_DONE
   * occurs).
   */
  RAIL_Status_t RAIL_StartAverageRssi(RAIL_Handle_t railHandle,
                                      uint16_t channel,
                                      RAIL_Time_t averagingTimeUs,
                                      const RAIL_SchedulerInfo_t *schedulerInfo);
  
  /**
   * Queries whether the RSSI averaging is done.
   *
   * @param[in] railHandle A RAIL instance handle.
   * @return Returns true if done and false otherwise.
   *
   * This function can be used to poll for completion of the RSSI averaging
   * to avoid relying on an interrupt-based callback.
   */
  bool RAIL_IsAverageRssiReady(RAIL_Handle_t railHandle);
  
  /**
   * Gets the RSSI averaged over a specified time in us.
   *
   * @param[in] railHandle A RAIL instance handle.
   * @return Return \ref RAIL_RSSI_INVALID if the receiver is disabled
   *   an RSSI value can't be obtained. Otherwise, return the RSSI in
   *   quarter dBm,dbm*4.
   *
   * Gets the hardware RSSI average after issuing RAIL_StartAverageRssi.
   * It should be used after \ref RAIL_StartAverageRssi.
   */
  int16_t RAIL_GetAverageRssi(RAIL_Handle_t railHandle);
  
  /**
   * Sets the RSSI offset.
   *
   * @param[in] railHandle a RAIL instance handle.
   * @param[in] rssiOffset desired offset to be added to the RSSI measurements.
   * @return Status code indicating success of the function call.
   *
   * Adds an offset to the RSSI in dBm. This offset affects all functionality that
   * depends on RSSI values, such as CCA fuctions. The offset should not be
   * modified dynamically during the RX of a packet.
   */
  RAIL_Status_t RAIL_SetRssiOffset(RAIL_Handle_t railHandle, int8_t rssiOffset);
  
  /**
   * Gets the RSSI offset.
   *
   * @param[in] railHandle a RAIL instance handle.
   * @return rssiOffset in dBm corresponding to the current handle.
   *
   */
  int8_t RAIL_GetRssiOffset(RAIL_Handle_t railHandle);
  
  /******************************************************************************
   * Address Filtering (RX)
   *****************************************************************************/
  /**
   * @addtogroup Address_Filtering Address Filtering
   * @brief Configuration APIs for receive packet address filtering.
   *
   * The address filtering code examines the packet as follows.
   *
   * | `Bytes: 0 - 255` | `0 - 8`  | `0 - 255` | `0 - 8`  | `Variable` |
   * |:----------------:|---------:|----------:|---------:|:----------:|
   * | `Data0`          | `Field0` | `Data1`   | `Field1` | `Data2`    |
   *
   * In the above structure, anything listed as DataN is an optional section of
   * bytes that RAIL will not process for address filtering. The FieldN segments
   * reference specific sections in the packet that will each be interpreted
   * as an address during address filtering. The application may submit up to
   * four addresses to attempt to match each field segment and each address may
   * have a size of up to 8 bytes. To set up address filtering, first configure
   * the locations and length of the addresses in the packet. Next, configure
   * which combinations of matches in Field0 and Field1 should constitute an
   * address match. Last, enter addresses into tables for each field and
   * enable them. The first two of these are part of the \ref RAIL_AddrConfig_t
   * structure while the second part is configured at runtime using the
   * RAIL_SetAddressFilterAddress() API. A brief description of each
   * configuration is listed below.
   *
   * The offsets and sizes of the fields
   * are assumed fixed for the RAIL address filter. To set them, specify
   * arrays for these values in the sizes and offsets entries in the
   * \ref RAIL_AddrConfig_t structure. A size of zero indicates that a field is
   * disabled. The start offset for a field is relative to the previous start
   * offset and, if you're using FrameType decoding, the first start offset is
   * relative to the end of the byte containing the frame type.
   *
   * Configuring which combinations of Field0 and Field1 constitute a match is
   * the most complex portion of the address filter. The easiest way to think
   * about this is with a truth table. If you consider each of the four possible
   * address entries in a field, you can have a match on any one of those or a
   * match for none of them. This can be represented as a 4-bit mask where 1
   * indicates a match and 0 indicates no match. Representing the Field0 match
   * options as rows and the Field1 options as columns results in a truth table
   * as shown below.
   *
   * |          | 0000 | 0001 | 0010 | 0100 | 1000 |
   * |----------|------|------|------|------|------|
   * | __0000__ | bit0 | bit1 | bit2 | bit3 | bit4 |
   * | __0001__ | bit5 | bit6 | bit7 | bit8 | bit9 |
   * | __0010__ | bit10| bit11| bit12| bit13| bit14|
   * | __0100__ | bit15| bit16| bit17| bit18| bit19|
   * | __1000__ | bit20| bit21| bit22| bit23| bit24|
   *
   * Because this is only 25 bits, it can be represented in one 32-bit integer
   * where 1 indicates a filter pass and 0 indicates a filter fail. This is the
   * matchTable parameter in the configuration structure and is used during
   * filtering. For common simple configurations two defines are provided with
   * the truth tables as shown below. The first is \ref
   * ADDRCONFIG_MATCH_TABLE_SINGLE_FIELD, which can be used if only using
   * one address field (either field). If using two fields and want to
   * force in the same address entry in each field, use the second define: \ref
   * ADDRCONFIG_MATCH_TABLE_DOUBLE_FIELD. For more complex systems,
   * create a valid custom table.
   *
   * @note Address filtering does not function reliably with PHYs that use a data
   *   rate greater than 500 kbps. If this is a requirement,
   *   filtering must currently be done by the application.
   *
   * @{
   */
  
  /**
   * Configures address filtering.
   *
   * @param[in] railHandle A RAIL instance handle.
   * @param[in] addrConfig The configuration structure, which defines how
   *   addresses are setup in your packets.
   * @return Status code indicating success of the function call.
   *
   * This function must be called to set up address filtering. You may call it
   * multiple times but all previous information is wiped out each time you call
   * and any configured addresses must be reset.
   */
  RAIL_Status_t RAIL_ConfigAddressFilter(RAIL_Handle_t railHandle,
                                         const RAIL_AddrConfig_t *addrConfig);
  
  /**
   * Enables address filtering.
   *
   * @param[in] railHandle A RAIL instance handle.
   * @param[in] enable An argument to indicate whether or not to enable address
   *   filtering.
   * @return True if address filtering was enabled to start with and false
   *   otherwise.
   *
   * Only allow packets through that pass the current address filtering
   * configuration. This does not reset or change the configuration so you can
   * set that up before turning on this feature.
   */
  bool RAIL_EnableAddressFilter(RAIL_Handle_t railHandle, bool enable);
  
  /**
   * Returns whether address filtering is currently enabled.
   *
   * @param[in] railHandle A RAIL instance handle.
   * @return True if address filtering is enabled and false otherwise.
   */
  bool RAIL_IsAddressFilterEnabled(RAIL_Handle_t railHandle);
  
  /**
   * Resets the address filtering configuration.
   *
   * @param[in] railHandle A RAIL instance handle.
   * @return void.
   *
   * Resets all structures related to address filtering. This does not disable
   * address filtering. It leaves the radio in a state where no packets
   * pass filtering.
   */
  void RAIL_ResetAddressFilter(RAIL_Handle_t railHandle);
  
  /**
   * Sets an address for filtering in hardware.
   *
   * @param[in] railHandle A RAIL instance handle.
   * @param[in] field Indicates an address field for this address.
   * @param[in] index Indicates a match entry for this address for a
   *   given field.
   * @param[in] value A pointer to the address data. This must be at least as
   *   long as the size specified in RAIL_ConfigAddressFilter().
   * @param[in] enable A boolean to indicate whether this address should be
   *   enabled immediately.
   * @return Status code indicating success of the function call.
   *
   * This function loads the given address into hardware for filtering and
   * starts filtering if you set the enable parameter to true. Otherwise,
   * call RAIL_EnableAddressFilterAddress() to turn it on later.
   */
  RAIL_Status_t RAIL_SetAddressFilterAddress(RAIL_Handle_t railHandle,
                                             uint8_t field,
                                             uint8_t index,
                                             const uint8_t *value,
                                             bool enable);
  
  /**
   * Enables address filtering for the specified address.
   *
   * @param[in] railHandle A RAIL instance handle.
   * @param[in] enable An argument to indicate whether or not to enable address
   *   filtering.
   * @param[in] field Indicates an address for the address.
   * @param[in] index Indicates a match entry in the given field you want to enable.
   * @return Status code indicating success of the function call.
   */
  RAIL_Status_t RAIL_EnableAddressFilterAddress(RAIL_Handle_t railHandle,
                                                bool enable,
                                                uint8_t field,
                                                uint8_t index);
  
  /** @} */ // end of group Address_Filtering
  
  /** @} */ // end of group Receive
  
  /******************************************************************************
   * Auto Acking
   *****************************************************************************/
  /// @addtogroup Auto_Ack Auto ACK
  /// @brief APIs for configuring auto ACK functionality
  ///
  /// These APIs are used to configure the radio for auto acknowledgment
  /// features. Auto ACK inherently changes how the underlying state machine
  /// behaves so users should not modify RAIL_SetRxTransitions() and
  /// RAIL_SetTxTransitions() while using auto ACK features.
  ///
  /// @code{.c}
  /// // Go to RX after ACK operation.
  /// RAIL_AutoAckConfig_t autoAckConfig = {
  ///   .enable = true,
  ///   .ackTimeout = 1000,
  ///   // "error" param ignored
  ///   .rxTransitions = { RAIL_RF_STATE_RX, RAIL_RF_STATE_RX},
  ///   // "error" param ignored
  ///   .txTransitions = { RAIL_RF_STATE_RX, RAIL_RF_STATE_RX}
  /// };
  ///
  /// RAIL_Status_t status = RAIL_ConfigAutoAck(railHandle, &autoAckConfig);
  ///
  /// uint8_t ackData[] = {0x05, 0x02, 0x10, 0x00};
  ///
  /// RAIL_Status_t status = RAIL_WriteAutoAckFifo(ackData, sizeof(ackData));
  /// @endcode
  ///
  /// The acknowledgment transmits based on the frame format configured via
  /// the Radio Configurator. For example, if the frame format is using a variable
  /// length scheme, the ACK will be sent according to that scheme. If a 10-byte
  /// packet is loaded into the ACK, but the variable length field of the ACK
  /// payload specifies a length of 5, only 5 bytes will transmit for the ACK.
  /// The converse is also true, if the frame length is configured to be a fixed
  /// 10-byte packet but only 5 bytes are loaded into the ACK buffer, a TX
  /// underflow occurs during the ACK transmit.
  ///
  /// Unlike in non-ACK mode, ACK mode will always return to a single
  /// state after all ACK sequences complete, regardless of whether
  /// the ACK was successfully received/sent or not. See the documentation
  /// of RAIL_ConfigAutoAck for configuration information. To
  /// not auto acknowledge a series of packets after transmit
  /// or receive, call RAIL_PauseTxAutoAck(true) or RAIL_PauseRxAutoAck(true).
  /// When auto acking is paused, after receiving or transmitting (also
  /// regardless of success) a packet, the radio transitions to the same single
  /// state it always defaults to while acking. To return to
  /// normal state transition logic outside of acking, call
  /// RAIL_ConfigAutoAck with the "enable" field false and specify the
  /// desired transitions in the rxTransitions and txTransitions fields.
  /// To get out of a paused state and resume auto acking, call
  /// RAIL_PauseTxAutoAck(false) or RAIL_PauseRxAutoAck(false).
  ///
  /// Applications can cancel the transmission of an ACK with
  /// RAIL_CancelAutoAck(). Conversely, applications can control if a transmit
  /// operation should wait for an ACK after transmitting by using
  /// the \ref RAIL_TX_OPTION_WAIT_FOR_ACK bit.
  ///
  /// If the ACK payload is dynamic, the application must call
  /// RAIL_WriteAutoAckFifo() with the appropriate ACK payload after the
  /// application processes the receive. RAIL can auto ACK from the normal
  /// transmit buffer if RAIL_UseTxFifoForAutoAck() is called before the radio
  /// transmits the ACK. Ensure the transmit buffer contains data loaded by
  /// RAIL_WriteTxFifo().
  ///
  /// Standard-based protocols that contain auto ACK functionality are normally
  /// configured in the protocol-specific configuration function. For example,
  /// RAIL_IEEE802154_Init() provides auto ACK configuration parameters in \ref
  /// RAIL_IEEE802154_Config_t and should only be configured through that
  /// function. It is not advisable to call both RAIL_IEEE802154_Init() and
  /// RAIL_ConfigAutoAck(). However, ACK modification functions are still valid to
  /// use with protocol-specific ACKs. To cancel an IEEE 802.15.4 ACK transmit, use
  /// RAIL_CancelAutoAck().
  ///
  /// @{
  
  /// Configures and enables auto acknowledgment.
  ///
  /// @param[in] railHandle A RAIL instance handle.
  /// @param[in] config Auto ACK configuration structure.
  /// @return Status code indicating success of the function call.
  ///
  /// Configures the RAIL state machine to for hardware-accelerated auto
  /// acknowledgment. ACK timing parameters are defined in the configuration
  /// structure.
  ///
  /// While auto acking is enabled, do not call the following RAIL functions:
  ///   - RAIL_SetRxTransitions()
  ///   - RAIL_SetTxTransitions()
  ///
  /// Note, that if you are enabling auto ACK (i.e., "enable" field is true)
  /// The "error" fields of rxTransitions and txTransitions are ignored.
  /// After all ACK sequences, (success or fail) the state machine will return
  /// the radio to the "success" state. If you need information about the
  /// actual success of the ACK sequence, use RAIL events such as
  /// \ref RAIL_EVENT_TXACK_PACKET_SENT to make sure an ACK was sent, or
  /// \ref RAIL_EVENT_RX_ACK_TIMEOUT to make sure that an ACK was received
  /// within the specified timeout.
  ///
  /// To set a certain turnaround time (i.e., txToRx and rxToTx
  /// in \ref RAIL_StateTiming_t), make txToRx lower than
  /// desired to ensure you get to RX in time to receive the ACK.
  /// Silicon Labs recommends setting 10 us lower than desired:
  ///
  /// @code{.c}
  /// void setAutoAckStateTimings()
  /// {
  ///   RAIL_StateTiming_t timings;
  ///
  ///   // User is already in auto ACK and wants a turnaround of 192 us.
  ///   timings.rxToTx = 192;
  ///   timings.txToRx = 192 - 10;
  ///
  ///   // Set other fields of timings...
  ///   timings.idleToRx = 100;
  ///   timings.idleToTx = 100;
  ///   timings.rxSearchTimeout = 0;
  ///   timings.txToRxSearchTimeout = 0;
  ///
  ///   RAIL_SetStateTiming(railHandle, &timings);
  /// }
  /// @endcode
  ///
  /// As opposed to an explicit "Disable" API, set the "enable"
  /// field of the RAIL_AutoAckConfig_t to false. Then, auto ACK will be
  /// disabled and state transitions will be returned to the values set
  /// in \ref RAIL_AutoAckConfig_t. When disabling, the "ackTimeout" field
  /// isn't used.
  ///
  RAIL_Status_t RAIL_ConfigAutoAck(RAIL_Handle_t railHandle,
                                   const RAIL_AutoAckConfig_t *config);
  
  /**
   * Returns the enable status of the auto ACK feature.
   *
   * @param[in] railHandle A RAIL instance handle.
   * @return true if auto ACK is enabled, false if disabled.
   */
  bool RAIL_IsAutoAckEnabled(RAIL_Handle_t railHandle);
  
  /**
   * Loads the auto ACK buffer with ACK data.
   *
   * @param[in] railHandle A RAIL instance handle.
   * @param[in] ackData A pointer to ACK data to transmit.
   * @param[in] ackDataLen The number of bytes in ACK data.
   * @return Status code indicating success of the function call.
   *
   * If the ACK buffer is available for updates, load the ACK buffer with data.
   */
  RAIL_Status_t RAIL_WriteAutoAckFifo(RAIL_Handle_t railHandle,
                                      const uint8_t *ackData,
                                      uint8_t ackDataLen);
  
  /**
   * Pauses/resumes RX auto ACK functionality.
   *
   * @param[in] railHandle A RAIL instance handle.
   * @param[in] pause Pause or resume RX auto acking.
   * @return void.
   *
   * When RX auto acking is paused, the radio transitions to default
   * state after receiving a packet and does not transmit an ACK.
   * When RX auto ACK is resumed, the radio resumes automatically acking
   * every successfully received packet.
   */
  void RAIL_PauseRxAutoAck(RAIL_Handle_t railHandle,
                           bool pause);
  
  /**
   * Returns whether the RX auto ACK is paused.
   *
   * @param[in] railHandle A RAIL instance handle.
   * @return true if RX auto ACK is paused, false if not paused.
   */
  bool RAIL_IsRxAutoAckPaused(RAIL_Handle_t railHandle);
  
  /**
   * Pauses/resumes TX auto ACK functionality.
   *
   * @param[in] railHandle A RAIL instance handle.
   * @param[in] pause Pause or resume TX auto acking.
   * @return void.
   *
   * When TX auto acking is paused, the radio transitions to a default
   * state after transmitting a packet and does not wait for an ACK. When TX
   * auto ACK is resumed, the radio resumes automatically waiting for
   * an ACK after a successful transmit.
   */
  void RAIL_PauseTxAutoAck(RAIL_Handle_t railHandle, bool pause);
  
  /**
   * Returns whether the TX auto ACK is paused.
   *
   * @param[in] railHandle A RAIL instance handle.
   * @return true if TX auto ACK is paused, false if not paused.
   */
  bool RAIL_IsTxAutoAckPaused(RAIL_Handle_t railHandle);
  
  /**
   * Modifies the upcoming ACK to use the TX Buffer.
   *
   * @param[in] railHandle A RAIL instance handle.
   * @return Status code indicating success of the function call. The call will
   *   fail if it is too late to modify the outgoing ACK.
   *
   * This function allows the application to use the normal TX buffer as the data
   * source for the upcoming ACK. The ACK modification to use the TX buffer only
   * applies to one ACK transmission.
   *
   * This function only returns true if the following conditions are met:
   *   - Radio has not already decided to use the ACK buffer AND
   *   - Radio is either looking for sync, receiving the packet after sync, or in
   *     the Rx2Tx turnaround before the ACK is sent.
   */
  RAIL_Status_t RAIL_UseTxFifoForAutoAck(RAIL_Handle_t railHandle);
  
  /**
   * Cancels the upcoming ACK.
   *
   * @param[in] railHandle A RAIL instance handle.
   * @return Status code indicating success of the function call. This call will
   *   fail if it is too late to modify the outgoing ACK.
   *
   * This function allows the application to cancel the upcoming automatic
   * acknowledgment.
   *
   * This function only returns true if the following conditions are met:
   *   - Radio has not already decided to transmit the ACK AND
   *   - Radio is either looking for sync, receiving the packet after sync or in
   *     the Rx2Tx turnaround before the ACK is sent.
   */
  RAIL_Status_t RAIL_CancelAutoAck(RAIL_Handle_t railHandle);
  
  /**
   * Returns whether the radio is currently waiting for an ACK.
   *
   * @param[in] railHandle A RAIL instance handle.
   * @return True if radio is waiting for ACK, false if radio is not waiting for
   *   an ACK.
   *
   * This function allows the application to query whether the radio is currently
   * waiting for an ACK after a transmit operation.
   */
  bool RAIL_IsAutoAckWaitingForAck(RAIL_Handle_t railHandle);
  
  /** @} */ // end of group Auto_Ack
  
  /******************************************************************************
   * Calibration
   *****************************************************************************/
  /// @addtogroup Calibration
  /// @brief APIs for calibrating the radio
  /// @{
  ///
  /// These APIs can be used to calibrate the radio. The RAIL library
  /// determines which calibrations are necessary. Calibrations can
  /// be enabled/disabled with the RAIL_CalMask_t parameter.
  ///
  /// Some calibrations produce values that can be saved and reapplied to
  /// avoid repeating the calibration process.
  ///
  /// Calibrations can either be run with \ref RAIL_Calibrate, or with the
  /// individual chip-specific calibration routines. An example for running code
  /// with \ref RAIL_Calibrate looks like:
  ///
  /// @code{.c}
  /// static RAIL_CalValues_t calValues = RAIL_CALVALUES_UNINIT;
  ///
  /// void RAILCb_Event(RAIL_Handle_t railHandle, RAIL_Events_t events) {
  ///   // Omitting other event handlers
  ///   if (events & RAIL_EVENT_CAL_NEEDED) {
  ///     // Run all pending calibrations, and save the results
  ///     RAIL_Calibrate(railHandle, &calValues, RAIL_CAL_ALL_PENDING);
  ///   }
  /// }
  /// @endcode
  ///
  /// Alternatively, if the image rejection calibration for your chip can be
  /// determined ahead of time, such as by running the calibration on a separate
  /// firmware image on each chip, the following calibration process will
  /// result in smaller code.
  ///
  /// @code{.c}
  /// static uint32_t imageRejection = IRCAL_VALUE;
  ///
  /// void RAILCb_Event(RAIL_Handle_t railHandle, RAIL_Events_t events) {
  ///   // Omitting other event handlers
  ///   if (events & RAIL_EVENT_CAL_NEEDED) {
  ///     RAIL_CalMask_t pendingCals = RAIL_GetPendingCal(railHandle);
  ///     // Disable the radio if we have to do an offline calibration
  ///     if (pendingCals & RAIL_CAL_TEMP_VC0) {
  ///       RAIL_CalibrateTemp(railHandle);
  ///     }
  ///     if (pendingCals & RAIL_CAL_ONETIME_IRCAL) {
  ///       RAIL_ApplyIrCalibration(railHandle, imageRejection);
  ///     }
  ///   }
  /// }
  /// @endcode
  
  /**
   * Initializes RAIL calibration.
   *
   * @param[in] railHandle A RAIL instance handle.
   * @param[in] calEnable A bitmask that indicates which calibrations
   *   to enable for a callback notification.
   *   The exact meaning of these bits is chip-specific.
   * @return Status code indicating success of the function call.
   *
   * Calibration initialization provides the calibration settings that
   * correspond to the current radio configuration.
   */
  RAIL_Status_t RAIL_ConfigCal(RAIL_Handle_t railHandle,
                               RAIL_CalMask_t calEnable);
  
  /**
   * Starts the calibration process.
   *
   * @param[in] railHandle A RAIL instance handle.
   * @param[in,out] calValues A structure of calibration values to apply.
   *   If a valid calibration structure is provided and the structure
   *   contains valid calibration values, those values will be applied to the
   *   hardware and the RAIL library will cache those values for use again later.
   *   If a valid calibration structure is provided and the structure
   *   contains a calibration value of \ref RAIL_CAL_INVALID_VALUE for the
   *   desired calibration, the desired calibration will run, the calibration
   *   values structure will be updated with a valid calibration value, and the
   *   RAIL library will cache that value for use again later.
   *   If a NULL pointer is provided, the desired calibration will run
   *   and the RAIL library will cache that value for use again later. However,
   *   the valid calibration value will not be returned to the application.
   * @param[in] calForce A mask to force specific calibration(s) to execute.
   *   To run all pending calibrations, use the value \ref RAIL_CAL_ALL_PENDING.
   *   Only the calibrations specified will run, even if not enabled during
   *   initialization.
   * @return Status code indicating success of the function call.
   *
   * If calibrations were performed previously and the application saves the
   * calibration values (i.e., call this function with a calibration values
   * structure containing calibration values of \ref RAIL_CAL_INVALID_VALUE
   * before a reset), the application can later bypass the time it would normally
   * take to recalibrate hardware by reusing previous calibration values (i.e.,
   * call this function with a calibration values structure containing valid
   * calibration values after a reset).
   *
   * If multiple protocols are used, this function will return
   * \ref RAIL_STATUS_INVALID_STATE if it is called and the given railHandle is
   * not active. The caller must attempt to re-call this function later.
   *
   * @note Instead of this function, consider using the individual chip-specific
   *   functions. Using the individual functions will allow for better
   *   dead-stripping if not all calibrations are run.
   * @note Some calibrations should only be executed when the radio is IDLE. See
   *   chip-specific documentation for more details.
   */
  RAIL_Status_t RAIL_Calibrate(RAIL_Handle_t railHandle,
                               RAIL_CalValues_t *calValues,
                               RAIL_CalMask_t calForce);
  
  /**
   * Returns the current set of pending calibrations.
   *
   * @param[in] railHandle A RAIL instance handle.
   * @return A mask of all pending calibrations that the user has been asked to
   *   perform.
   *
   * This function returns a full set of pending calibrations. The only way
   * to clear pending calibrations is to perform them using the \ref
   * RAIL_Calibrate() API with the appropriate list of calibrations.
   */
  RAIL_CalMask_t RAIL_GetPendingCal(RAIL_Handle_t railHandle);
  
  /**
   * Enables/disables the PA calibration.
   *
   * @param[in] enable Enables/disables the PA calibration.
   * @return void.
   *
   * Enabling will ensure that the PA power remains constant chip-to-chip.
   * By default, this feature is disabled after reset.
   *
   * @note This function should be called before \ref RAIL_ConfigTxPower() if this
   *   feature is desired.
   */
  void RAIL_EnablePaCal(bool enable);
  
  /** @} */ // end of group Calibration
  
  /******************************************************************************
   * RF Sense Structures
   *****************************************************************************/
  /**
   * @addtogroup Rf_Sense RF Sense
   * @{
   */
  
  /**
   * Starts/stops the RF Sense functionality for use during low-energy sleep modes.
   *
   * @param[in] railHandle A RAIL instance handle.
   * @param[in] band The frequency band(s) on which to sense the RF energy.
   *   To stop RF Sense, specify \ref RAIL_RFSENSE_OFF.
   * @param[in] senseTime The time (in microseconds) the RF energy must be
   *   continually detected to be considered "sensed".
   * @param[in] cb \ref RAIL_RfSense_CallbackPtr_t is called when the RF is
   *   sensed. Set null if polling via \ref RAIL_IsRfSensed().
   * @return The actual senseTime used, which may be different than
   *   requested due to limitations of the hardware. If 0, RF sense was
   *   disabled or could not be enabled (no callback will be issued).
   *
   * The EFR32 has the ability to sense the presence of RF Energy above -20 dBm
   * within either or both the 2.4 GHz and Sub-GHz bands and trigger an event
   * if that energy is continuously present for certain durations of time.
   *
   * @note After RF energy has been sensed, the RF Sense is automatically
   *   disabled. RAIL_StartRfSense() must be called again to reactivate it.
   *
   * @note Packet reception is not guaranteed to work correctly once RF Sense is
   *   enabled. To be safe, an application should turn this on only after idling
   *   the radio to stop receive and turn it off before attempting to restart
   *   receive. Since EM4 sleep causes the chip to come up through the reset
   *   vector any wake from EM4 must also shut off RF Sense to ensure proper
   *   receive functionality.
   *
   * @warning RF Sense functionality is only guaranteed from 0 to 85 degrees
   *   Celsius. RF Sense should be disabled outside of this temperature range.
   */
  RAIL_Time_t RAIL_StartRfSense(RAIL_Handle_t railHandle,
                                RAIL_RfSenseBand_t band,
                                RAIL_Time_t senseTime,
                                RAIL_RfSense_CallbackPtr_t cb);
  
  /**
   * Checks if the RF was sensed.
   *
   * @param[in] railHandle A RAIL instance handle.
   * @return true if RF was sensed since the last call to \ref RAIL_StartRfSense.
   *   False otherwise.
   *
   * This function is useful if \ref RAIL_StartRfSense is called with a null
   * callback. It is generally used after EM4 reboot but can be used any time.
   */
  bool RAIL_IsRfSensed(RAIL_Handle_t railHandle);
  
  /** @} */ // end of group Rf_Sense
  
  /******************************************************************************
   * Multiprotocol-specific Functions
   *****************************************************************************/
  /**
   * @addtogroup Multiprotocol
   * @brief Multiprotocol scheduler APIs to support multiple time-sliced PHYs.
   * @{
   */
  
  /**
   * Yields the radio to other configurations.
   *
   * @param[in] railHandle A RAIL instance handle.
   * @return void.
   *
   * This function is used to indicate that the previous transmit or scheduled
   * receive operation has completed. It must be used in multiprotocol RAIL since
   * the scheduler assumes that any transmit or receive operation that is started
   * can go on indefinitely based on state transitions and your protocol.
   * RAIL will not allow a lower priority tasks to run until this is called so it
   * can negatively impact performance of those protocols if this is omitted or
   * delayed. It is also possible to simply call the \ref RAIL_Idle() API to
   * both terminate the operation and idle the radio. In single-protocol RAIL
   * this API does nothing.
   *
   * See \ref rail_radio_scheduler_yield for more details.
   */
  void RAIL_YieldRadio(RAIL_Handle_t railHandle);
  
  /**
   * Gets the status of the RAIL scheduler.
   *
   * @param[in] railHandle A RAIL instance handle.
   * @return \ref RAIL_SchedulerStatus_t status.
   *
   * This function can only be called from a callback context after the
   * \ref RAIL_EVENT_SCHEDULER_STATUS event occurs.
   */
  RAIL_SchedulerStatus_t RAIL_GetSchedulerStatus(RAIL_Handle_t railHandle);
  
  /**
   * Change the priority of a specified task type in multiprotocol.
   *
   * @param[in] railHandle A RAIL instance handle.
   * @param[in] priority Desired new priority for the railHandle's active task
   * @param[in] taskType Type of task whose priority should be updated
   * @return \ref RAIL_Status_t indicating success of the function call.
   *
   * While the application can use this function however it likes, a major use
   * case is being able to increase an infinite receive priority while receiving
   * a packet. In other words, a given RAIL_Handle_t can maintain a very low
   * priority background receive, but upon getting a
   * \ref RAIL_EVENT_RX_SYNC1_DETECT_SHIFT or
   * \ref RAIL_EVENT_RX_SYNC2_DETECT_SHIFT event, the app can call this function
   * to increase the background RX priority to lower the risk another protocol
   * might preempt during packet reception.
   */
  RAIL_Status_t RAIL_SetTaskPriority(RAIL_Handle_t railHandle,
                                     uint8_t priority,
                                     RAIL_TaskType_t taskType);
  
  /** @} */ // end of group Multiprotocol
  
  /******************************************************************************
   * Diagnostic
   *****************************************************************************/
  /**
   * @addtogroup Diagnostic
   * @brief APIs for diagnostic and test chip modes
   * @{
   */
  
  /**
   * Enables or disables direct mode for RAIL.
   *
   * @param[in] railHandle A RAIL instance handle.
   * @param[in] enable Whether or not to enable direct mode.
   * @return \ref RAIL_STATUS_NO_ERROR on success and an error code on failure.
   *
   * @warning This API configures fixed pins for TX data in, RX data out,
   *   and RX clock out. There should be more control over these pins in the
   *   future but they are currently fixed. Also, this API is not safe to
   *   use in a true multiprotocol app.
   *
   * In this mode, packets are output and input directly to the radio via GPIO
   * and RAIL packet handling is ignored. On the EFR32, the DIN pin in TX is
   * EFR32_PC10, which corresponds to EXP_HEADER15/WSTKP12, and the DOUT pin in
   * RX is EFR32_PC11, which corresponds to EXP_HEADER16/WSTKP13.
   */
  RAIL_Status_t RAIL_EnableDirectMode(RAIL_Handle_t railHandle,
                                      bool enable);
  
  /**
   * Sets the crystal tuning.
   *
   * @param[in] railHandle A RAIL instance handle.
   * @param[in] tune A chip-dependent crystal capacitor bank tuning parameter.
   * @return Status code indicating success of the function call.
   *
   * Tunes the crystal that the radio depends on to change the location of the
   * center frequency for transmitting and receiving. This function will only
   * succeed if the radio is idle at the time of the call.
   *
   * @note This function proportionally affects the entire chip's timing
   *   across all its peripherals, including radio tuning and channel spacing.
   *   A separate function, \ref RAIL_SetFreqOffset(), can be used to adjust
   *   just the radio tuner without disturbing channel spacing or other chip
   *   peripheral timing.
   */
  RAIL_Status_t RAIL_SetTune(RAIL_Handle_t railHandle, uint32_t tune);
  
  /**
   * Gets the crystal tuning.
   *
   * @param[in] railHandle A RAIL instance handle.
   * @return A chip-dependent crystal capacitor bank tuning parameter.
   *
   * Retrieves the current tuning value used by the crystal that the radio
   * depends on.
   */
  uint32_t RAIL_GetTune(RAIL_Handle_t railHandle);
  
  /**
   * Gets the frequency offset.
   *
   * @param[in] railHandle A RAIL instance handle.
   * @return Returns the measured frequency offset on a received packet.
   *   The units are described in the \ref RAIL_FrequencyOffset_t
   *   documentation. If this returns \ref RAIL_FREQUENCY_OFFSET_INVALID,
   *   it was called while the radio wasn't active and there is no way
   *   to get the frequency offset.
   *
   * Retrieves the measured frequency offset used during the previous
   * received packet, which includes the current radio frequency offset
   * (see \ref RAIL_SetFreqOffset()). If the chip has not been in RX,
   * it returns the nominal radio frequency offset.
   */
  RAIL_FrequencyOffset_t RAIL_GetRxFreqOffset(RAIL_Handle_t railHandle);
  
  /**
   * Sets the nominal radio frequency offset.
   *
   * @param[in] railHandle A RAIL instance handle.
   * @param[in] freqOffset \ref RAIL_FrequencyOffset_t parameter (signed, 2's
   *   complement).
   * @return Status code indicating success of the function call.
   *
   * This function is used to adjust the radio's tuning frequency slightly up or down.
   * It might be used in conjunction with \ref RAIL_GetRxFreqOffset() after
   * receiving a packet from a peer to adjust the tuner to better match the
   * peer's tuned frequency.
   *
   * @note Unlike \ref RAIL_SetTune(), which affects the entire chip's
   *   timing including radio tuning and channel spacing, this function
   *   only affects radio tuning without disturbing channel spacing or
   *   other chip peripheral timing.
   */
  RAIL_Status_t RAIL_SetFreqOffset(RAIL_Handle_t railHandle,
                                   RAIL_FrequencyOffset_t freqOffset);
  
  /**
   * Starts transmitting a stream on a certain channel.
   *
   * @param[in] railHandle A RAIL instance handle.
   * @param[in] channel A channel on which to emit a stream.
   * @param[in] mode Choose the stream mode (PN9, and so on).
   * @return Status code indicating success of the function call.
   *
   * Begins streaming onto the given channel. The sources can either be an
   * unmodulated carrier wave or an encoded stream of bits from a PN9 source.
   * All ongoing radio operations will be stopped before transmission begins.
   */
  RAIL_Status_t RAIL_StartTxStream(RAIL_Handle_t railHandle,
                                   uint16_t channel,
                                   RAIL_StreamMode_t mode);
  
  /**
   * Stops stream transmission.
   *
   * @param[in] railHandle A RAIL instance handle.
   * @return Status code indicating success of the function call.
   *
   * Halts the transmission started by RAIL_StartTxStream().
   */
  RAIL_Status_t RAIL_StopTxStream(RAIL_Handle_t railHandle);
  
  /**
   * Configures the verification of radio memory contents.
   *
   * @param[in] railHandle A RAIL instance handle.
   * @param[in,out] configVerify A configuration structure made available to
   *   RAIL to perform radio state verification. This structure must be
   *   allocated in application global read-write memory. RAIL may modify
   *   fields within or referenced by this structure during its operation.
   * @param[in] radioConfig A pointer to a radioConfig that is to be used as a
   *   white list for verifying memory contents.
   * @param[in] cb A callback that notifies the application of a mismatch in
   *   expected vs actual memory contents. A NULL parameter may be passed in
   *   if a callback is not provided by the application.
   * @return \ref RAIL_STATUS_NO_ERROR if setup of the verification feature
   *   successfully occurred.
   *   \ref RAIL_STATUS_INVALID_PARAMETER is returned if the provided railHandle
   *   or configVerify structures are invalid.
   */
  RAIL_Status_t RAIL_ConfigVerification(RAIL_Handle_t railHandle,
                                        RAIL_VerifyConfig_t *configVerify,
                                        const uint32_t *radioConfig,
                                        RAIL_VerifyCallbackPtr_t cb);
  
  /**
   * Verifies radio memory contents.
   *
   * @param[in,out] configVerify A configuration structure made available to
   *   RAIL to perform radio state verification. This structure must be
   *   allocated in application global read-write memory. RAIL may modify
   *   fields within or referenced by this structure during its operation.
   * @param[in] durationUs The duration (in microseconds) for how long memory
   *   verification should occur before returning to the application. A value of
   *   RAIL_VERIFY_DURATION_MAX indicates that all memory contents should be
   *   verified before returning to the application.
   * @param[in] restart This flag only has meaning if a previous call of this
   *   function returned \ref RAIL_STATUS_SUSPENDED. By restarting (true), the
   *   verification process starts over from the beginning, or by resuming
   *   where verification left off after being suspended (false), verification
   *   can proceed towards completion.
   * @return \ref RAIL_STATUS_NO_ERROR if the contents of all applicable
   *   memory locations have been verified.
   *   \ref RAIL_STATUS_SUSPENDED is returned if the provided test duration
   *   expired but the time was not sufficient to verify all memory contents.
   *   By calling \ref RAIL_Verify again, further verification will commence.
   *   \ref RAIL_STATUS_INVALID_PARAMETER is returned if the provided
   *   verifyConfig structure pointer is not configured for use by the active
   *   RAIL handle.
   *   \ref RAIL_STATUS_INVALID_STATE is returned if any of the verified
   *   memory contents are different from their reference values.
   */
  RAIL_Status_t RAIL_Verify(RAIL_VerifyConfig_t *configVerify,
                            uint32_t durationUs,
                            bool restart);
  
  /** @} */ // end of group Diagnostic
  
  #ifndef DOXYGEN_SHOULD_SKIP_THIS
  
  /******************************************************************************
   * Debug
   *****************************************************************************/
  /**
   * @addtogroup Debug
   * @brief APIs for debugging
   * @{
   */
  
  /**
   * Configures the debug mode for the radio library. Do not use this function
   * unless instructed by Silicon Labs.
   * @param[in] railHandle A RAIL instance handle.
   * @param[in] debugMode Debug mode to enter.
   * @return Status code indicating success of the function call.
   */
  RAIL_Status_t RAIL_SetDebugMode(RAIL_Handle_t railHandle, uint32_t debugMode);
  
  /**
   * Returns the debug mode for the radio library. Do not use this function
   * unless instructed by Silicon Labs.
   * @param[in] railHandle A RAIL instance handle.
   * @return Debug mode for the radio library.
   */
  uint32_t RAIL_GetDebugMode(RAIL_Handle_t railHandle);
  
  /**
   * Overrides the radio base frequency.
   *
   * @param[in] railHandle A RAIL instance handle.
   * @param[in] freq A desired frequency in Hz.
   * @return Status code indicating success of the function call.
   *
   * Sets the radio to transmit at the frequency given. This function can only
   * be used while in \ref RAIL_DEBUG_MODE_FREQ_OVERRIDE. The given frequency
   * needs to be close to the base frequency of the current PHY.
   */
  RAIL_Status_t RAIL_OverrideDebugFrequency(RAIL_Handle_t railHandle,
                                            uint32_t freq);
  
  /** @} */ // end of group Debug
  
  #endif//DOXYGEN_SHOULD_SKIP_THIS
  
  /******************************************************************************
   * Assertion Callback
   *****************************************************************************/
  /**
   * @addtogroup Assertions
   * @brief Callbacks called by assertions
   *
   * This assertion framework was implemented for the purpose of not only being
   * able to assert that certain conditions be true in a block of code, but also
   * to be able to handle them more appropriately. In previous implementations,
   * the behavior upon a failed assert would be to hang in a while(1) loop.
   * However, with the callback, each assert is given a unique error code so that
   * they can be handled on a more case-by-case basis. For documentation on each
   * of the errors, see the rail_assert_error_codes.h file.
   * RAIL_ASSERT_ERROR_MESSAGES[errorCode] gives the explanation of the error.
   * With asserts built into the library, customers can choose how to handle each
   * error inside the callback.
   *
   * @{
   */
  
  /**
   * Callback called upon failed assertion.
   *
   * @param[in] railHandle A RAIL instance handle.
   * @param[in] errorCode Value passed in by the calling assertion API indicating
   *   the RAIL error that is indicated by the failing assertion.
   * @return void.
   */
  void RAILCb_AssertFailed(RAIL_Handle_t railHandle,
                           RAIL_AssertErrorCodes_t errorCode);
  
  /** @} */ // end of group Assertions
  
  /** @} */ // end of group RAIL_API
  
  #ifdef __cplusplus
  }
  #endif
  
  #endif // __RAIL_H__
  