/*******************************************************************************
  * @file    stm32wlxx_hal_subghz.c
  * @author  MCD Application Team
  * @brief   SUBGHZ HAL module driver.
  *          This file provides firmware functions to manage the following
  *          functionalities of the SUBGHZ peripheral:
  *           + Initialization and de-initialization functions
  *           + IO operation functions
  *           + Peripheral State and Errors functions
  *
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2020 STMicroelectronics.
  * All rights reserved.
  *
  * This software is licensed under terms that can be found in the LICENSE file
  * in the root directory of this software component.
  * If no LICENSE file comes with this software, it is provided AS-IS.
  *
  ******************************************************************************
 @verbatim
 ==============================================================================
                       ##### How to use this driver #####
 ==============================================================================
 [..]
    The SUBGHZ HAL driver can be used as follows:

    (#) Declare a SUBGHZ_HandleTypeDef handle structure, for example:
        SUBGHZ_HandleTypeDef hUserSubghz;

    (#) Initialize the SUBGHZ low level resources by implementing the @ref HAL_SUBGHZ_MspInit() API:
        (##) PWR configuration
            (+++) Enable the SUBGHZSPI interface clock
            (+++) Enable wakeup signal of the Radio peripheral
        (##) NVIC configuration:
            (+++) Enable the NVIC Radio IRQ ITs for CPU1 (EXTI 44)
            (+++) Configure the Radio interrupt priority

    (#) Initialize the SUBGHZ handle and SUBGHZSPI SPI registers by calling the @ref HAL_SUBGHZ_Init(&hUserSubghz),
        configures also the low level Hardware (GPIO, CLOCK, NVIC...etc) by calling
        the customized @ref HAL_SUBGHZ_MspInit() API.

    (#) For SUBGHZ IO operations, polling operation modes is available within this driver :

    *** Polling mode IO operation      ***
    =====================================
    [..]
      (+) Set and execute a command in blocking mode using @ref HAL_SUBGHZ_ExecSetCmd()
      (+) Get a status blocking mode using @ref HAL_SUBGHZ_ExecGetCmd()
      (+) Write a Data Buffer in blocking mode using @ref HAL_SUBGHZ_WriteBuffer()
      (+) Read a Data Buffer  in blocking mode using @ref HAL_SUBGHZ_ReadBuffer()
      (+) Write Registers (more than 1 byte) in blocking mode using @ref HAL_SUBGHZ_WriteRegisters()
      (+) Read Registers (more than 1 byte) in blocking mode using @ref HAL_SUBGHZ_ReadRegisters()
      (+) Write Register (1 byte) in blocking mode using @ref HAL_SUBGHZ_WriteRegister()
      (+) Read Register (1 byte) in blocking mode using @ref HAL_SUBGHZ_ReadRegister()

    *** SUBGHZ HAL driver macros list ***
    =====================================
    [..]
      (+) @ref __HAL_SUBGHZ_RESET_HANDLE_STATE: Reset the SUBGHZ handle state

#if defined(GENERATOR_CALLBACK_REGISTERING_PRESENT)
     *** SUBGHZ Callback registration  ***
     =====================================

    [..]
     The compilation flag USE_HAL_SUBGHZ_REGISTER_CALLBACKS when set to 1
     allows the user to configure dynamically the driver callbacks.
     Use Functions @ref HAL_SUBGHZ_RegisterCallback() or @ref HAL_SUBGHZ_RegisterAddrCallback()
     to register an interrupt callback.

    [..]
     Function @ref HAL_SUBGHZ_RegisterCallback() allows to register following callbacks:
       (+) TxCpltCallback           : callback for Tx Completed.
       (+) RxCpltCallback           : callback for Rx Completed.
       (+) PreambleDetectedCallback : callback for Preamble detected.
       (+) SyncWordValidCallback    : callback for Synchro word valid.
       (+) HeaderValidCallback      : callback for Header valid.
       (+) HeaderErrorCallback      : callback for Header error.
       (+) CRCErrorCallback         : callback for CRC Error.
       (+) RxTxTimeoutCallback      : callback for Rx Tx Timeout.
       (+) MspInitCallback          : callback for Msp Init.
       (+) MspDeInitCallback        : callback for Msp DeInit.
       (+) LrFhssHopCallback        : callback for LoRa Frequency Hopping Spread Spectrum Hopping.
     This function takes as parameters the HAL peripheral handle, the Callback ID
     and a pointer to the user callback function.
    [..]
     For specific callback CADStatusCallback use dedicated register callbacks :
     @ref HAL_SUBGHZ_RegisterCadStatusCallback().
    [..]
     Use function @ref HAL_SUBGHZ_UnRegisterCallback to reset a callback to the default
     weak function.
     @ref HAL_SUBGHZ_UnRegisterCallback takes as parameters the HAL peripheral handle,
     and the Callback ID.
     This function allows to reset following callbacks:
       (+) TxCpltCallback           : callback for Tx Completed.
       (+) RxCpltCallback           : callback for Rx Completed.
       (+) PreambleDetectedCallback : callback for Preamble detected.
       (+) SyncWordValidCallback    : callback for Synchro word valid.
       (+) HeaderValidCallback      : callback for Header valid.
       (+) HeaderErrorCallback      : callback for Header error.
       (+) CRCErrorCallback         : callback for CRC Error.
       (+) RxTxTimeoutCallback      : callback for Rx Tx Timeout.
       (+) MspInitCallback          : callback for Msp Init.
       (+) MspDeInitCallback        : callback for Msp DeInit.
       (+) LrFhssHopCallback        : callback for LoRa Frequency Hopping Spread Spectrum Hopping.
    [..]
     For specific callback CADStatusCallback use dedicated register callbacks :
     @ref HAL_SUBGHZ_UnRegisterCadStatusCallback().
    [..]
     MspInit and MspDeInit functions are reset to the legacy weak functions in the
     @ref HAL_SUBGHZ_Init()/ @ref HAL_SUBGHZ_DeInit() only when these callbacks are null
     (not registered beforehand).
     If MspInit or MspDeInit are not null, the @ref HAL_SUBGHZ_Init()/ @ref HAL_SUBGHZ_DeInit()
     keep and use the user MspInit/MspDeInit callbacks (registered beforehand) whatever the state.

    [..]
     Callbacks for MspInit/MspDeInit functions can be registered/unregistered
     in @ref HAL_SUBGHZ_STATE_READY or @ref HAL_SUBGHZ_STATE_RESET state,
     thus registered (user) MspInit/DeInit callbacks can be used during the Init/DeInit.
     Then, the user first registers the MspInit/MspDeInit user callbacks
     using @ref HAL_SUBGHZ_RegisterCallback() before calling @ref HAL_SUBGHZ_DeInit()
     or @ref HAL_SUBGHZ_Init() function.

    [..]
     When the compilation flag USE_HAL_SUBGHZ_REGISTER_CALLBACKS is set to 0 or
     not defined, the callback registration feature is not available and all callbacks
     are set to the corresponding weak functions.
#endif
  */

/* Includes ------------------------------------------------------------------*/
#include "stm32wlxx_hal.h"

/** @addtogroup STM32WLxx_HAL_Driver
  * @{
  */

/** @defgroup SUBGHZ SUBGHZ
  * @brief SUBGHZ HAL module driver
  * @{
  */
#ifdef HAL_SUBGHZ_MODULE_ENABLED

/* Private typedef -----------------------------------------------------------*/
/* Private defines -----------------------------------------------------------*/
/** @defgroup SUBGHZ_Private_Constants SUBGHZ Private Constants
  * @{
  */
#define SUBGHZ_DEFAULT_TIMEOUT     100U    /* HAL Timeout in ms               */
#define SUBGHZ_DUMMY_DATA          0xFFU   /* SUBGHZSPI Dummy Data use for Tx */
#define SUBGHZ_DEEP_SLEEP_ENABLE   1U      /* SUBGHZ Radio in Deep Sleep      */
#define SUBGHZ_DEEP_SLEEP_DISABLE  0U      /* SUBGHZ Radio not in Deep Sleep  */

/* SystemCoreClock dividers. Corresponding to time execution of while loop.   */
#define SUBGHZ_DEFAULT_LOOP_TIME   ((SystemCoreClock*28U)>>19U)
#define SUBGHZ_RFBUSY_LOOP_TIME    ((SystemCoreClock*24U)>>20U)

/* ~150 us loop delay assuming 10 CPU cycles per iteration (> 20us as per Semtech spec) */
#define SUBGHZ_NSS_LOOP_TIME       ((SystemCoreClock)>>16U)
/**
  * @}
  */

/* Private macros ------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
/* Private function prototypes -----------------------------------------------*/
/** @defgroup SUBGHZ_Private_Functions SUBGHZ Private Functions
  * @{
  */
void              SUBGHZSPI_Init(uint32_t BaudratePrescaler);
void              SUBGHZSPI_DeInit(void);
HAL_StatusTypeDef SUBGHZSPI_Transmit(SUBGHZ_HandleTypeDef *hsubghz, uint8_t Data);
HAL_StatusTypeDef SUBGHZSPI_Receive(SUBGHZ_HandleTypeDef *hsubghz, uint8_t *pData);
HAL_StatusTypeDef SUBGHZ_WaitOnBusy(SUBGHZ_HandleTypeDef *hsubghz);
HAL_StatusTypeDef SUBGHZ_CheckDeviceReady(SUBGHZ_HandleTypeDef *hsubghz);
/**
  * @}
  */

/* Exported functions --------------------------------------------------------*/
/** @defgroup SUBGHZ_Exported_Functions SUBGHZ Exported Functions
  * @{
  */

/** @defgroup SUBGHZ_Exported_Functions_Group1 Initialization and de-initialization functions
  *  @brief    Initialization and Configuration functions
  *
@verbatim
 ===============================================================================
              ##### Initialization and de-initialization functions #####
 ===============================================================================
    [..]  This subsection provides a set of functions allowing to initialize and
          de-initialize the SUBGHZ peripheral:

      (+) User must implement HAL_SUBGHZ_MspInit() function in which he configures
          all related peripherals resources (CLOCK, GPIO, IT and NVIC ).

      (+) Call the function HAL_SUBGHZ_Init() to configure SUBGHZSPI peripheral
          and initialize SUBGHZ Handle.

      (+) Call the function HAL_SUBGHZ_DeInit() to restore the default configuration
          of the SUBGHZ peripheral.

@endverbatim
  * @{
  */

/**
  * @brief  Initialize the SUBGHZ according to the specified parameters
  *         in the SUBGHZ_HandleTypeDef and initialize the associated handle.
  * @param  hsubghz pointer to a SUBGHZ_HandleTypeDef structure that contains
  *         the handle information for SUBGHZ module.
  * @note   In case of exiting from Standby mode, before calling this function,
  *         set the state to HAL_SUBGHZ_STATE_RESET_RF_READY with __HAL_SUBGHZ_RESET_HANDLE_STATE_RF_READY
  *         to avoid the reset of Radio peripheral.
  * @retval HAL status
  */
HAL_StatusTypeDef HAL_SUBGHZ_Init(SUBGHZ_HandleTypeDef *hsubghz)
{
  HAL_StatusTypeDef status;
  __IO uint32_t count;
  HAL_SUBGHZ_StateTypeDef subghz_state;

  /* Check the hsubghz handle allocation */
  if (hsubghz == NULL)
  {
    status = HAL_ERROR;
    return status;
  }
  else
  {
    status = HAL_OK;
  }

  assert_param(IS_SUBGHZSPI_BAUDRATE_PRESCALER(hsubghz->Init.BaudratePrescaler));

  subghz_state = hsubghz->State;
  if ((subghz_state == HAL_SUBGHZ_STATE_RESET) ||
      (subghz_state == HAL_SUBGHZ_STATE_RESET_RF_READY))
  {
    /* Allocate lock resource and initialize it */
    hsubghz->Lock = HAL_UNLOCKED;

#if (USE_HAL_SUBGHZ_REGISTER_CALLBACKS == 1)
    /* Init the SUBGHZ Legacy weak Callback settings */
    hsubghz->TxCpltCallback              = HAL_SUBGHZ_TxCpltCallback;
    hsubghz->RxCpltCallback              = HAL_SUBGHZ_RxCpltCallback;
    hsubghz->PreambleDetectedCallback    = HAL_SUBGHZ_PreambleDetectedCallback;
    hsubghz->SyncWordValidCallback       = HAL_SUBGHZ_SyncWordValidCallback;
    hsubghz->HeaderValidCallback         = HAL_SUBGHZ_HeaderValidCallback;
    hsubghz->HeaderErrorCallback         = HAL_SUBGHZ_HeaderErrorCallback;
    hsubghz->CRCErrorCallback            = HAL_SUBGHZ_CRCErrorCallback;
    hsubghz->CADStatusCallback           = HAL_SUBGHZ_CADStatusCallback;
    hsubghz->RxTxTimeoutCallback         = HAL_SUBGHZ_RxTxTimeoutCallback;
    hsubghz->LrFhssHopCallback           = HAL_SUBGHZ_LrFhssHopCallback;

    if (hsubghz->MspInitCallback == NULL)
    {
      hsubghz->MspInitCallback = HAL_SUBGHZ_MspInit; /* Legacy weak MspInit  */
    }

    /* Init the low level hardware : GPIO, CLOCK, NVIC... */
    hsubghz->MspInitCallback(hsubghz);
#else
    /* Init the low level hardware : GPIO, CLOCK, NVIC... */
    HAL_SUBGHZ_MspInit(hsubghz);
#endif /* USE_HAL_ SUBGHZ_REGISTER_CALLBACKS */

#if defined(CORE_CM0PLUS)
    /* Enable EXTI 44 : Radio IRQ ITs for CPU2 */
    LL_C2_EXTI_EnableIT_32_63(LL_EXTI_LINE_44);
#else
    /* Enable EXTI 44 : Radio IRQ ITs for CPU1 */
    LL_EXTI_EnableIT_32_63(LL_EXTI_LINE_44);
#endif /* CORE_CM0PLUS */
  }

  if (subghz_state == HAL_SUBGHZ_STATE_RESET)
  {
    /* Reinitialize Radio peripheral only if SUBGHZ is in full RESET state */
    hsubghz->State = HAL_SUBGHZ_STATE_BUSY;

    /* De-asserts the reset signal of the Radio peripheral */
    LL_RCC_RF_DisableReset();

    /* Verify that Radio in reset status flag is set */
    count  = SUBGHZ_DEFAULT_TIMEOUT * SUBGHZ_DEFAULT_LOOP_TIME;

    do
    {
      if (count == 0U)
      {
        status  = HAL_ERROR;
        hsubghz->ErrorCode = HAL_SUBGHZ_ERROR_TIMEOUT;
        break;
      }
      count--;
    } while (LL_RCC_IsRFUnderReset() != 0UL);

    /* Asserts the reset signal of the Radio peripheral */
    LL_PWR_UnselectSUBGHZSPI_NSS();

#if defined(CORE_CM0PLUS)
    /* Enable wakeup signal of the Radio peripheral */
    LL_C2_PWR_SetRadioBusyTrigger(LL_PWR_RADIO_BUSY_TRIGGER_WU_IT);
#else
    /* Enable wakeup signal of the Radio peripheral */
    LL_PWR_SetRadioBusyTrigger(LL_PWR_RADIO_BUSY_TRIGGER_WU_IT);
#endif /* CORE_CM0PLUS */
  }

  /* Clear Pending Flag */
  LL_PWR_ClearFlag_RFBUSY();

  if (status == HAL_OK)
  {
    /* Initialize SUBGHZSPI Peripheral */
    SUBGHZSPI_Init(hsubghz->Init.BaudratePrescaler);

    hsubghz->DeepSleep = SUBGHZ_DEEP_SLEEP_ENABLE;
    hsubghz->ErrorCode = HAL_SUBGHZ_ERROR_NONE;
  }

  hsubghz->State = HAL_SUBGHZ_STATE_READY;

  return status;
}

/**
  * @brief  De-Initialize the SUBGHZ peripheral.
  * @param  hsubghz pointer to a SUBGHZ_HandleTypeDef structure that contains
  *         the handle information for SUBGHZ module.
  * @retval HAL status
  */
HAL_StatusTypeDef HAL_SUBGHZ_DeInit(SUBGHZ_HandleTypeDef *hsubghz)
{
  HAL_StatusTypeDef status;
  __IO uint32_t count;

  /* Check the SUBGHZ handle allocation */
  if (hsubghz == NULL)
  {
    status = HAL_ERROR;
    return status;
  }
  else
  {
    status = HAL_OK;
  }

  hsubghz->State = HAL_SUBGHZ_STATE_BUSY;

  /* DeInitialize SUBGHZSPI Peripheral */
  SUBGHZSPI_DeInit();

#if (USE_HAL_SUBGHZ_REGISTER_CALLBACKS == 1)
  if (hsubghz->MspDeInitCallback == NULL)
  {
    hsubghz->MspDeInitCallback = HAL_SUBGHZ_MspDeInit; /* Legacy weak MspDeInit  */
  }

  /* DeInit the low level hardware: GPIO, CLOCK, NVIC... */
  hsubghz->MspDeInitCallback(hsubghz);
#else
  /* DeInit the low level hardware: GPIO, CLOCK, NVIC... */
  HAL_SUBGHZ_MspDeInit(hsubghz);
#endif /* USE_HAL_SUBGHZ_REGISTER_CALLBACKS */

#if defined(CORE_CM0PLUS)
  /* Disable EXTI 44 : Radio IRQ ITs for CPU2 */
  LL_C2_EXTI_DisableIT_32_63(LL_EXTI_LINE_44);

  /* Disable wakeup signal of the Radio peripheral */
  LL_C2_PWR_SetRadioBusyTrigger(LL_PWR_RADIO_BUSY_TRIGGER_NONE);
#else
  /* Disable EXTI 44 : Radio IRQ ITs for CPU1 */
  LL_EXTI_DisableIT_32_63(LL_EXTI_LINE_44);

  /* Disable wakeup signal of the Radio peripheral */
  LL_PWR_SetRadioBusyTrigger(LL_PWR_RADIO_BUSY_TRIGGER_NONE);
#endif /* CORE_CM0PLUS */

  /* Clear Pending Flag */
  LL_PWR_ClearFlag_RFBUSY();

  /* Re-asserts the reset signal of the Radio peripheral */
  LL_RCC_RF_EnableReset();

  /* Verify that Radio in reset status flag is set */
  count  = SUBGHZ_DEFAULT_TIMEOUT * SUBGHZ_DEFAULT_LOOP_TIME;

  do
  {
    if (count == 0U)
    {
      status  = HAL_ERROR;
      hsubghz->ErrorCode = HAL_SUBGHZ_ERROR_TIMEOUT;
      break;
    }
    count--;
  } while (LL_RCC_IsRFUnderReset() != 1UL);

  hsubghz->ErrorCode = HAL_SUBGHZ_ERROR_NONE;
  hsubghz->State     = HAL_SUBGHZ_STATE_RESET;

  /* Release Lock */
  __HAL_UNLOCK(hsubghz);

  return status;
}

/**
  * @brief  Initialize the SUBGHZ MSP.
  * @param  hsubghz pointer to a SUBGHZ_HandleTypeDef structure that contains
  *         the handle information for SUBGHZ module.
  * @retval None
  */
__weak void HAL_SUBGHZ_MspInit(SUBGHZ_HandleTypeDef *hsubghz)
{
  /* NOTE : This function should not be modified, when the callback is needed,
            the HAL_SUBGHZ_MspInit should be implemented in the user file
   */

  /* Prevent unused argument(s) compilation warning */
  UNUSED(hsubghz);
}

/**
  * @brief  De-Initialize the SUBGHZ MSP.
  * @param  hsubghz pointer to a SUBGHZ_HandleTypeDef structure that contains
  *         the handle information for SUBGHZ module.
  * @retval None
  */
__weak void HAL_SUBGHZ_MspDeInit(SUBGHZ_HandleTypeDef *hsubghz)
{
  /* NOTE : This function should not be modified, when the callback is needed,
            the HAL_SUBGHZ_MspDeInit should be implemented in the user file
   */

  /* Prevent unused argument(s) compilation warning */
  UNUSED(hsubghz);
}

#if (USE_HAL_SUBGHZ_REGISTER_CALLBACKS == 1)
/**
  * @brief  Register a User SUBGHZ Callback
  *         To be used instead of the weak predefined callback
  * @param  hsubghz pointer to a SUBGHZ_HandleTypeDef structure that contains
  *         the configuration information for the specified SUBGHZ.
  * @param  CallbackID ID of the callback to be registered
  * @param  pCallback pointer to the Callback function
  * @retval HAL status
  */
HAL_StatusTypeDef HAL_SUBGHZ_RegisterCallback(SUBGHZ_HandleTypeDef *hsubghz,
                                              HAL_SUBGHZ_CallbackIDTypeDef CallbackID,
                                              pSUBGHZ_CallbackTypeDef pCallback)
{
  HAL_StatusTypeDef status = HAL_OK;

  if (pCallback == NULL)
  {
    /* Update the error code */
    hsubghz->ErrorCode |= HAL_SUBGHZ_ERROR_INVALID_CALLBACK;

    return HAL_ERROR;
  }

  /* Process locked */
  __HAL_LOCK(hsubghz);

  if (HAL_SUBGHZ_STATE_READY == hsubghz->State)
  {
    switch (CallbackID)
    {
      case HAL_SUBGHZ_TX_COMPLETE_CB_ID :
        hsubghz->TxCpltCallback = pCallback;
        break;

      case HAL_SUBGHZ_RX_COMPLETE_CB_ID :
        hsubghz->RxCpltCallback = pCallback;
        break;

      case HAL_SUBGHZ_PREAMBLE_DETECTED_CB_ID :
        hsubghz->PreambleDetectedCallback = pCallback;
        break;

      case HAL_SUBGHZ_SYNCWORD_VALID_CB_ID :
        hsubghz->SyncWordValidCallback = pCallback;
        break;

      case HAL_SUBGHZ_HEADER_VALID_CB_ID :
        hsubghz->HeaderValidCallback = pCallback;
        break;

      case HAL_SUBGHZ_HEADER_ERROR_CB_ID :
        hsubghz->HeaderErrorCallback = pCallback;
        break;

      case HAL_SUBGHZ_CRC_ERROR_CB_ID :
        hsubghz->CRCErrorCallback = pCallback;
        break;

      case HAL_SUBGHZ_RX_TX_TIMEOUT_CB_ID :
        hsubghz->RxTxTimeoutCallback = pCallback;
        break;

      case HAL_SUBGHZ_MSPINIT_CB_ID :
        hsubghz->MspInitCallback = pCallback;
        break;

      case HAL_SUBGHZ_MSPDEINIT_CB_ID :
        hsubghz->MspDeInitCallback = pCallback;
        break;

      case HAL_SUBGHZ_LR_FHSS_HOP_CB_ID :
        hsubghz->LrFhssHopCallback = pCallback;
        break;

      default :
        /* Update the error code */
        hsubghz->ErrorCode = HAL_SUBGHZ_ERROR_INVALID_CALLBACK;

        /* Return error status */
        status =  HAL_ERROR;
        break;
    }
  }
  else if (HAL_SUBGHZ_STATE_RESET == hsubghz->State)
  {
    switch (CallbackID)
    {
      case HAL_SUBGHZ_MSPINIT_CB_ID :
        hsubghz->MspInitCallback = pCallback;
        break;

      case HAL_SUBGHZ_MSPDEINIT_CB_ID :
        hsubghz->MspDeInitCallback = pCallback;
        break;

      default :
        /* Update the error code */
        hsubghz->ErrorCode = HAL_SUBGHZ_ERROR_INVALID_CALLBACK;

        /* Return error status */
        status =  HAL_ERROR;
        break;
    }
  }
  else
  {
    /* Update the error code */
    hsubghz->ErrorCode = HAL_SUBGHZ_ERROR_INVALID_CALLBACK;

    /* Return error status */
    status =  HAL_ERROR;
  }

  /* Release Lock */
  __HAL_UNLOCK(hsubghz);

  return status;
}

/**
  * @brief  Unregister an SUBGHZ Callback
  *         SUBGHZ callback is redirected to the weak predefined callback
  * @param  hsubghz pointer to a SUBGHZ_HandleTypeDef structure that contains
  *         the configuration information for the specified SUBGHZ.
  * @param  CallbackID ID of the callback to be unregistered
  * @retval HAL status
  */
HAL_StatusTypeDef HAL_SUBGHZ_UnRegisterCallback(SUBGHZ_HandleTypeDef *hsubghz,
                                                HAL_SUBGHZ_CallbackIDTypeDef CallbackID)
{
  HAL_StatusTypeDef status = HAL_OK;

  /* Process locked */
  __HAL_LOCK(hsubghz);

  if (HAL_SUBGHZ_STATE_READY == hsubghz->State)
  {
    /* call legacy weak callback function */
    switch (CallbackID)
    {
      case HAL_SUBGHZ_TX_COMPLETE_CB_ID :
        hsubghz->TxCpltCallback = HAL_SUBGHZ_TxCpltCallback;
        break;

      case HAL_SUBGHZ_RX_COMPLETE_CB_ID :
        hsubghz->RxCpltCallback = HAL_SUBGHZ_RxCpltCallback;
        break;

      case HAL_SUBGHZ_PREAMBLE_DETECTED_CB_ID :
        hsubghz->PreambleDetectedCallback = HAL_SUBGHZ_PreambleDetectedCallback;
        break;

      case HAL_SUBGHZ_SYNCWORD_VALID_CB_ID :
        hsubghz->SyncWordValidCallback = HAL_SUBGHZ_SyncWordValidCallback;
        break;

      case HAL_SUBGHZ_HEADER_VALID_CB_ID :
        hsubghz->HeaderValidCallback = HAL_SUBGHZ_HeaderValidCallback;
        break;

      case HAL_SUBGHZ_HEADER_ERROR_CB_ID :
        hsubghz->HeaderErrorCallback = HAL_SUBGHZ_HeaderErrorCallback;
        break;

      case HAL_SUBGHZ_CRC_ERROR_CB_ID :
        hsubghz->CRCErrorCallback = HAL_SUBGHZ_CRCErrorCallback;
        break;

      case HAL_SUBGHZ_RX_TX_TIMEOUT_CB_ID :
        hsubghz->RxTxTimeoutCallback = HAL_SUBGHZ_RxTxTimeoutCallback;
        break;

      case HAL_SUBGHZ_MSPINIT_CB_ID :
        hsubghz->MspInitCallback = HAL_SUBGHZ_MspInit;
        break;

      case HAL_SUBGHZ_MSPDEINIT_CB_ID :
        hsubghz->MspDeInitCallback = HAL_SUBGHZ_MspDeInit;
        break;

      case HAL_SUBGHZ_LR_FHSS_HOP_CB_ID :
        hsubghz->LrFhssHopCallback = HAL_SUBGHZ_LrFhssHopCallback;
        break;

      default :
        /* Update the error code */
        hsubghz->ErrorCode = HAL_SUBGHZ_ERROR_INVALID_CALLBACK;

        /* Return error status */
        status =  HAL_ERROR;
        break;
    }
  }
  else if (HAL_SUBGHZ_STATE_RESET == hsubghz->State)
  {
    switch (CallbackID)
    {
      case HAL_SUBGHZ_MSPINIT_CB_ID :
        hsubghz->MspInitCallback = HAL_SUBGHZ_MspInit;
        break;

      case HAL_SUBGHZ_MSPDEINIT_CB_ID :
        hsubghz->MspDeInitCallback = HAL_SUBGHZ_MspDeInit;
        break;

      default :
        /* Update the error code */
        hsubghz->ErrorCode = HAL_SUBGHZ_ERROR_INVALID_CALLBACK;

        /* Return error status */
        status =  HAL_ERROR;
        break;
    }
  }
  else
  {
    /* Update the error code */
    hsubghz->ErrorCode = HAL_SUBGHZ_ERROR_INVALID_CALLBACK;

    /* Return error status */
    status =  HAL_ERROR;
  }

  /* Release Lock */
  __HAL_UNLOCK(hsubghz);

  return status;
}

/**
  * @brief  Register the channel activity detection status SUBGHZ Callback
  *         To be used instead of the weak HAL_SUBGHZ_AddrCallback() predefined callback
  * @param  hsubghz Pointer to a SUBGHZ_HandleTypeDef structure that contains
  *                the configuration information for the specified SUBGHZ.
  * @param  pCallback pointer to the CAD Status Callback function
  * @retval HAL status
  */
HAL_StatusTypeDef HAL_SUBGHZ_RegisterCadStatusCallback(SUBGHZ_HandleTypeDef *hsubghz,
                                                       pSUBGHZ_CadStatusCallbackTypeDef pCallback)
{
  HAL_StatusTypeDef status = HAL_OK;

  if (pCallback == NULL)
  {
    /* Update the error code */
    hsubghz->ErrorCode = HAL_SUBGHZ_ERROR_INVALID_CALLBACK;

    return HAL_ERROR;
  }
  /* Process locked */
  __HAL_LOCK(hsubghz);

  if (HAL_SUBGHZ_STATE_READY == hsubghz->State)
  {
    hsubghz->CADStatusCallback = pCallback;
  }
  else
  {
    /* Update the error code */
    hsubghz->ErrorCode = HAL_SUBGHZ_ERROR_INVALID_CALLBACK;

    /* Return error status */
    status =  HAL_ERROR;
  }

  /* Release Lock */
  __HAL_UNLOCK(hsubghz);
  return status;
}

/**
  * @brief  UnRegister the channel activity detection status SUBGHZ Callback
  * @param  hsubghz Pointer to a SUBGHZ_HandleTypeDef structure that contains
  *                the configuration information for the specified SUBGHZ.
  * @retval HAL status
  */
HAL_StatusTypeDef HAL_SUBGHZ_UnRegisterCadStatusCallback(SUBGHZ_HandleTypeDef *hsubghz)
{
  HAL_StatusTypeDef status = HAL_OK;

  /* Process locked */
  __HAL_LOCK(hsubghz);

  if (HAL_SUBGHZ_STATE_READY == hsubghz->State)
  {
    hsubghz->CADStatusCallback = HAL_SUBGHZ_CADStatusCallback; /* Legacy weak AddrCallback */
  }
  else
  {
    /* Update the error code */
    hsubghz->ErrorCode = HAL_SUBGHZ_ERROR_INVALID_CALLBACK;

    /* Return error status */
    status =  HAL_ERROR;
  }

  /* Release Lock */
  __HAL_UNLOCK(hsubghz);
  return status;
}
#endif /* USE_HAL_SUBGHZ_REGISTER_CALLBACKS */
/**
  * @}
  */

/** @defgroup SUBGHZ_Exported_Functions_Group2 IO operation functions
  *  @brief   Data transfers functions
  *
@verbatim
  ==============================================================================
                      ##### IO operation functions #####
 ===============================================================================
 [..]
    This subsection provides a set of functions allowing to manage the SUBGHZ
    data transfers.

    [..] The SUBGHZ supports Read and Write operation:

    (#) There are four modes of transfer:
       (++) Set operation: The Set Command operation is performed in polling mode.
            The HAL status of command processing is returned by the same function
            after finishing transfer.
       (++) Get operation: The Get Status operation is performed using polling mode
            These API update buffer in parameter to retrieve status of command.
            These API return the HAL status
       (++) Write operation: The write operation is performed in polling mode.
            The HAL status of all data processing is returned by the same function
            after finishing transfer.
       (++) Read operation: The read operation is performed using polling mode
            These APIs return the HAL status.

    (#) Blocking mode functions are :
        (++) HAL_SUBGHZ_ExecSetCmd(
        (++) HAL_SUBGHZ_ExecGetCmd()
        (++) HAL_SUBGHZ_WriteBuffer()
        (++) HAL_SUBGHZ_ReadBuffer()
        (++) HAL_SUBGHZ_WriteRegisters()
        (++) HAL_SUBGHZ_ReadRegisters()
        (++) HAL_SUBGHZ_WriteRegister()
        (++) HAL_SUBGHZ_ReadRegister()

@endverbatim
  * @{
  */

/**
  * @brief  Write data buffer at an Address to configurate the peripheral
  * @param  hsubghz pointer to a SUBGHZ_HandleTypeDef structure that contains
  *         the configuration information for the specified SUBGHZ.
  * @param  Address register to configurate
  * @param  pBuffer pointer to a data buffer
  * @param  Size    amount of data to be sent
  * @retval HAL status
  */
HAL_StatusTypeDef HAL_SUBGHZ_WriteRegisters(SUBGHZ_HandleTypeDef *hsubghz,
                                            uint16_t Address,
                                            uint8_t *pBuffer,
                                            uint16_t Size)
{
  HAL_StatusTypeDef status;

  if (hsubghz->State == HAL_SUBGHZ_STATE_READY)
  {
    /* Process Locked */
    __HAL_LOCK(hsubghz);

    hsubghz->State = HAL_SUBGHZ_STATE_BUSY;

    (void)SUBGHZ_CheckDeviceReady(hsubghz);

    /* NSS = 0 */
    LL_PWR_SelectSUBGHZSPI_NSS();

    (void)SUBGHZSPI_Transmit(hsubghz, SUBGHZ_RADIO_WRITE_REGISTER);
    (void)SUBGHZSPI_Transmit(hsubghz, (uint8_t)((Address & 0xFF00U) >> 8U));
    (void)SUBGHZSPI_Transmit(hsubghz, (uint8_t)(Address & 0x00FFU));

    for (uint16_t i = 0U; i < Size; i++)
    {
      (void)SUBGHZSPI_Transmit(hsubghz, pBuffer[i]);
    }

    /* NSS = 1 */
    LL_PWR_UnselectSUBGHZSPI_NSS();

    (void)SUBGHZ_WaitOnBusy(hsubghz);

    if (hsubghz->ErrorCode != HAL_SUBGHZ_ERROR_NONE)
    {
      status = HAL_ERROR;
    }
    else
    {
      status = HAL_OK;
    }

    hsubghz->State = HAL_SUBGHZ_STATE_READY;

    /* Process Unlocked */
    __HAL_UNLOCK(hsubghz);

    return status;
  }
  else
  {
    return HAL_BUSY;
  }
}

/**
  * @brief  Read data register at an Address in the peripheral
  * @param  hsubghz pointer to a SUBGHZ_HandleTypeDef structure that contains
  *         the configuration information for the specified SUBGHZ.
  * @param  Address register to configurate
  * @param  pBuffer pointer to a data buffer
  * @param  Size    amount of data to be sent
  * @retval HAL status
  */
HAL_StatusTypeDef HAL_SUBGHZ_ReadRegisters(SUBGHZ_HandleTypeDef *hsubghz,
                                           uint16_t Address,
                                           uint8_t *pBuffer,
                                           uint16_t Size)
{
  HAL_StatusTypeDef status;
  uint8_t *pData = pBuffer;

  if (hsubghz->State == HAL_SUBGHZ_STATE_READY)
  {
    /* Process Locked */
    __HAL_LOCK(hsubghz);

    (void)SUBGHZ_CheckDeviceReady(hsubghz);

    /* NSS = 0 */
    LL_PWR_SelectSUBGHZSPI_NSS();

    (void)SUBGHZSPI_Transmit(hsubghz, SUBGHZ_RADIO_READ_REGISTER);
    (void)SUBGHZSPI_Transmit(hsubghz, (uint8_t)((Address & 0xFF00U) >> 8U));
    (void)SUBGHZSPI_Transmit(hsubghz, (uint8_t)(Address & 0x00FFU));
    (void)SUBGHZSPI_Transmit(hsubghz, 0U);

    for (uint16_t i = 0U; i < Size; i++)
    {
      (void)SUBGHZSPI_Receive(hsubghz, (pData));
      pData++;
    }

    /* NSS = 1 */
    LL_PWR_UnselectSUBGHZSPI_NSS();

    (void)SUBGHZ_WaitOnBusy(hsubghz);

    if (hsubghz->ErrorCode != HAL_SUBGHZ_ERROR_NONE)
    {
      status = HAL_ERROR;
    }
    else
    {
      status = HAL_OK;
    }

    hsubghz->State = HAL_SUBGHZ_STATE_READY;

    /* Process Unlocked */
    __HAL_UNLOCK(hsubghz);

    return status;
  }
  else
  {
    return HAL_BUSY;
  }
}

/**
  * @brief  Write one data at an Address to configurate the peripheral
  * @param  hsubghz pointer to a SUBGHZ_HandleTypeDef structure that contains
  *         the configuration information for the specified SUBGHZ.
  * @param  Address register to configurate
  * @param  Value data
  * @retval HAL status
  */
HAL_StatusTypeDef HAL_SUBGHZ_WriteRegister(SUBGHZ_HandleTypeDef *hsubghz,
                                           uint16_t Address,
                                           uint8_t Value)
{
  return (HAL_SUBGHZ_WriteRegisters(hsubghz, Address, &Value, 1U));
}

/**
  * @brief  Read data register at an Address in the peripheral
  * @param  hsubghz pointer to a SUBGHZ_HandleTypeDef structure that contains
  *         the configuration information for the specified SUBGHZ.
  * @param  Address register to configurate
  * @param  pValue pointer to a data
  * @retval HAL status
  */
HAL_StatusTypeDef HAL_SUBGHZ_ReadRegister(SUBGHZ_HandleTypeDef *hsubghz,
                                          uint16_t Address,
                                          uint8_t *pValue)
{
  return (HAL_SUBGHZ_ReadRegisters(hsubghz, Address, pValue, 1U));
}

/**
  * @brief  Send a command to configure the peripheral
  * @param  hsubghz pointer to a SUBGHZ_HandleTypeDef structure that contains
  *         the configuration information for the specified SUBGHZ.
  * @param  Command configuration for peripheral
  * @param  pBuffer pointer to a data buffer
  * @param  Size    amount of data to be sent
  * @retval HAL status
  */
HAL_StatusTypeDef HAL_SUBGHZ_ExecSetCmd(SUBGHZ_HandleTypeDef *hsubghz,
                                        SUBGHZ_RadioSetCmd_t Command,
                                        uint8_t *pBuffer,
                                        uint16_t Size)
{
  HAL_StatusTypeDef status;

  /* LORA Modulation not available on STM32WLx4xx devices */
  assert_param(IS_SUBGHZ_MODULATION_SUPPORTED(Command, pBuffer[0U]));

  if (hsubghz->State == HAL_SUBGHZ_STATE_READY)
  {
    /* Process Locked */
    __HAL_LOCK(hsubghz);

    /* Need to wakeup Radio if already in Sleep at startup */
    (void)SUBGHZ_CheckDeviceReady(hsubghz);

    if ((Command == RADIO_SET_SLEEP) || (Command == RADIO_SET_RXDUTYCYCLE))
    {
      hsubghz->DeepSleep = SUBGHZ_DEEP_SLEEP_ENABLE;
    }
    else
    {
      hsubghz->DeepSleep = SUBGHZ_DEEP_SLEEP_DISABLE;
    }

    /* NSS = 0 */
    LL_PWR_SelectSUBGHZSPI_NSS();

    (void)SUBGHZSPI_Transmit(hsubghz, (uint8_t)Command);

    for (uint16_t i = 0U; i < Size; i++)
    {
      (void)SUBGHZSPI_Transmit(hsubghz, pBuffer[i]);
    }

    /* NSS = 1 */
    LL_PWR_UnselectSUBGHZSPI_NSS();

    if (Command != RADIO_SET_SLEEP)
    {
      (void)SUBGHZ_WaitOnBusy(hsubghz);
    }

    if (hsubghz->ErrorCode != HAL_SUBGHZ_ERROR_NONE)
    {
      status = HAL_ERROR;
    }
    else
    {
      status = HAL_OK;
    }

    hsubghz->State = HAL_SUBGHZ_STATE_READY;

    /* Process Unlocked */
    __HAL_UNLOCK(hsubghz);

    return status;
  }
  else
  {
    return HAL_BUSY;
  }
}

/**
  * @brief  Retrieve a status from the peripheral
  * @param  hsubghz pointer to a SUBGHZ_HandleTypeDef structure that contains
  *         the configuration information for the specified SUBGHZ.
  * @param  Command configuration for peripheral
  * @param  pBuffer pointer to a data buffer
  * @param  Size    amount of data to be sent
  * @retval HAL status
  */
HAL_StatusTypeDef HAL_SUBGHZ_ExecGetCmd(SUBGHZ_HandleTypeDef *hsubghz,
                                        SUBGHZ_RadioGetCmd_t Command,
                                        uint8_t *pBuffer,
                                        uint16_t Size)
{
  HAL_StatusTypeDef status;
  uint8_t *pData = pBuffer;

  if (hsubghz->State == HAL_SUBGHZ_STATE_READY)
  {
    /* Process Locked */
    __HAL_LOCK(hsubghz);

    (void)SUBGHZ_CheckDeviceReady(hsubghz);

    /* NSS = 0 */
    LL_PWR_SelectSUBGHZSPI_NSS();

    (void)SUBGHZSPI_Transmit(hsubghz, (uint8_t)Command);

    /* Use to flush the Status (First byte) receive from SUBGHZ as not use */
    (void)SUBGHZSPI_Transmit(hsubghz, 0x00U);

    for (uint16_t i = 0U; i < Size; i++)
    {
      (void)SUBGHZSPI_Receive(hsubghz, (pData));
      pData++;
    }

    /* NSS = 1 */
    LL_PWR_UnselectSUBGHZSPI_NSS();

    (void)SUBGHZ_WaitOnBusy(hsubghz);

    if (hsubghz->ErrorCode != HAL_SUBGHZ_ERROR_NONE)
    {
      status = HAL_ERROR;
    }
    else
    {
      status = HAL_OK;
    }

    hsubghz->State = HAL_SUBGHZ_STATE_READY;

    /* Process Unlocked */
    __HAL_UNLOCK(hsubghz);

    return status;
  }
  else
  {
    return HAL_BUSY;
  }
}

/**
  * @brief  Write data buffer inside payload of peripheral
  * @param  hsubghz pointer to a SUBGHZ_HandleTypeDef structure that contains
  *         the configuration information for the specified SUBGHZ.
  * @param  Offset  Offset inside payload
  * @param  pBuffer pointer to a data buffer
  * @param  Size    amount of data to be sent
  * @retval HAL status
  */
HAL_StatusTypeDef HAL_SUBGHZ_WriteBuffer(SUBGHZ_HandleTypeDef *hsubghz,
                                         uint8_t Offset,
                                         uint8_t *pBuffer,
                                         uint16_t Size)
{
  HAL_StatusTypeDef status;

  if (hsubghz->State == HAL_SUBGHZ_STATE_READY)
  {
    /* Process Locked */
    __HAL_LOCK(hsubghz);

    (void)SUBGHZ_CheckDeviceReady(hsubghz);

    /* NSS = 0 */
    LL_PWR_SelectSUBGHZSPI_NSS();

    (void)SUBGHZSPI_Transmit(hsubghz, SUBGHZ_RADIO_WRITE_BUFFER);
    (void)SUBGHZSPI_Transmit(hsubghz, Offset);

    for (uint16_t i = 0U; i < Size; i++)
    {
      (void)SUBGHZSPI_Transmit(hsubghz, pBuffer[i]);
    }
    /* NSS = 1 */
    LL_PWR_UnselectSUBGHZSPI_NSS();

    (void)SUBGHZ_WaitOnBusy(hsubghz);

    if (hsubghz->ErrorCode != HAL_SUBGHZ_ERROR_NONE)
    {
      status = HAL_ERROR;
    }
    else
    {
      status = HAL_OK;
    }

    hsubghz->State = HAL_SUBGHZ_STATE_READY;

    /* Process Unlocked */
    __HAL_UNLOCK(hsubghz);

    return status;
  }
  else
  {
    return HAL_BUSY;
  }
}

/**
  * @brief  Read data buffer inside payload of peripheral
  * @param  hsubghz pointer to a SUBGHZ_HandleTypeDef structure that contains
  *         the configuration information for the specified SUBGHZ.
  * @param  Offset  Offset inside payload
  * @param  pBuffer pointer to a data buffer
  * @param  Size    amount of data to be sent
  * @retval HAL status
  */
HAL_StatusTypeDef HAL_SUBGHZ_ReadBuffer(SUBGHZ_HandleTypeDef *hsubghz,
                                        uint8_t Offset,
                                        uint8_t *pBuffer,
                                        uint16_t Size)
{
  HAL_StatusTypeDef status;
  uint8_t *pData = pBuffer;

  if (hsubghz->State == HAL_SUBGHZ_STATE_READY)
  {
    /* Process Locked */
    __HAL_LOCK(hsubghz);

    (void)SUBGHZ_CheckDeviceReady(hsubghz);

    /* NSS = 0 */
    LL_PWR_SelectSUBGHZSPI_NSS();

    (void)SUBGHZSPI_Transmit(hsubghz, SUBGHZ_RADIO_READ_BUFFER);
    (void)SUBGHZSPI_Transmit(hsubghz, Offset);
    (void)SUBGHZSPI_Transmit(hsubghz, 0x00U);

    for (uint16_t i = 0U; i < Size; i++)
    {
      (void)SUBGHZSPI_Receive(hsubghz, (pData));
      pData++;
    }

    /* NSS = 1 */
    LL_PWR_UnselectSUBGHZSPI_NSS();

    (void)SUBGHZ_WaitOnBusy(hsubghz);

    if (hsubghz->ErrorCode != HAL_SUBGHZ_ERROR_NONE)
    {
      status = HAL_ERROR;
    }
    else
    {
      status = HAL_OK;
    }

    hsubghz->State = HAL_SUBGHZ_STATE_READY;

    /* Process Unlocked */
    __HAL_UNLOCK(hsubghz);

    return status;
  }
  else
  {
    return HAL_BUSY;
  }
}

/**
  * @brief  Handle SUBGHZ interrupt request.
  * @param  hsubghz pointer to a SUBGHZ_HandleTypeDef structure that contains
  *               the configuration information for the specified SUBGHZ module.
  * @retval None
  */
void HAL_SUBGHZ_IRQHandler(SUBGHZ_HandleTypeDef *hsubghz)
{
  uint8_t tmpisr[2U] = {0U};
  uint16_t itsource;

  /* Retrieve Interrupts from SUBGHZ Irq Register */
  (void)HAL_SUBGHZ_ExecGetCmd(hsubghz, RADIO_GET_IRQSTATUS, tmpisr, 2U);
  itsource = tmpisr[0U];
  itsource = (itsource << 8U) | tmpisr[1U];

  /* Clear SUBGHZ Irq Register */
  (void)HAL_SUBGHZ_ExecSetCmd(hsubghz, RADIO_CLR_IRQSTATUS, tmpisr, 2U);

  /* Packet transmission completed Interrupt */
  if (SUBGHZ_CHECK_IT_SOURCE(itsource, SUBGHZ_IT_TX_CPLT) != RESET)
  {
#if (USE_HAL_SUBGHZ_REGISTER_CALLBACKS == 1U)
    hsubghz->TxCpltCallback(hsubghz);
#else
    HAL_SUBGHZ_TxCpltCallback(hsubghz);
#endif /* USE_HAL_SUBGHZ_REGISTER_CALLBACKS */
  }

  /* Packet received Interrupt */
  if ((SUBGHZ_CHECK_IT_SOURCE(itsource, SUBGHZ_IT_RX_CPLT) != RESET))
  {
    if (SUBGHZ_CHECK_IT_SOURCE(itsource, SUBGHZ_IT_CRC_ERROR) != RESET)
    {
      hsubghz->ErrorCode |= HAL_SUBGHZ_ERROR_CRC_MISMATCH;
    }
#if (USE_HAL_SUBGHZ_REGISTER_CALLBACKS == 1U)
    hsubghz->RxCpltCallback(hsubghz);
#else
    HAL_SUBGHZ_RxCpltCallback(hsubghz);
#endif /* USE_HAL_SUBGHZ_REGISTER_CALLBACKS */
  }

  /* Preamble Detected Interrupt */
  if (SUBGHZ_CHECK_IT_SOURCE(itsource, SUBGHZ_IT_PREAMBLE_DETECTED) != RESET)
  {
#if (USE_HAL_SUBGHZ_REGISTER_CALLBACKS == 1U)
    hsubghz->PreambleDetectedCallback(hsubghz);
#else
    HAL_SUBGHZ_PreambleDetectedCallback(hsubghz);
#endif /* USE_HAL_SUBGHZ_REGISTER_CALLBACKS */
  }

  /*  Valid sync word detected Interrupt */
  if (SUBGHZ_CHECK_IT_SOURCE(itsource, SUBGHZ_IT_SYNCWORD_VALID) != RESET)
  {
#if (USE_HAL_SUBGHZ_REGISTER_CALLBACKS == 1U)
    hsubghz->SyncWordValidCallback(hsubghz);
#else
    HAL_SUBGHZ_SyncWordValidCallback(hsubghz);
#endif /* USE_HAL_SUBGHZ_REGISTER_CALLBACKS */
  }

  /* Valid LoRa header received Interrupt */
  if (SUBGHZ_CHECK_IT_SOURCE(itsource, SUBGHZ_IT_HEADER_VALID) != RESET)
  {
#if (USE_HAL_SUBGHZ_REGISTER_CALLBACKS == 1U)
    hsubghz->HeaderValidCallback(hsubghz);
#else
    HAL_SUBGHZ_HeaderValidCallback(hsubghz);
#endif /* USE_HAL_SUBGHZ_REGISTER_CALLBACKS */
  }

  /* LoRa header CRC error Interrupt */
  if (SUBGHZ_CHECK_IT_SOURCE(itsource, SUBGHZ_IT_HEADER_ERROR) != RESET)
  {
#if (USE_HAL_SUBGHZ_REGISTER_CALLBACKS == 1U)
    hsubghz->HeaderErrorCallback(hsubghz);
#else
    HAL_SUBGHZ_HeaderErrorCallback(hsubghz);
#endif /* USE_HAL_SUBGHZ_REGISTER_CALLBACKS */
  }

  /* Wrong CRC received Interrupt */
  if (SUBGHZ_CHECK_IT_SOURCE(itsource, SUBGHZ_IT_CRC_ERROR) != RESET)
  {
#if (USE_HAL_SUBGHZ_REGISTER_CALLBACKS == 1U)
    hsubghz->CRCErrorCallback(hsubghz);
#else
    HAL_SUBGHZ_CRCErrorCallback(hsubghz);
#endif /* USE_HAL_SUBGHZ_REGISTER_CALLBACKS */
  }

  /* Channel activity detection finished Interrupt */
  if (SUBGHZ_CHECK_IT_SOURCE(itsource, SUBGHZ_IT_CAD_DONE) != RESET)
  {
#if (USE_HAL_SUBGHZ_REGISTER_CALLBACKS == 1U)
    /* Channel activity Detected Interrupt */
    if (SUBGHZ_CHECK_IT_SOURCE(itsource, SUBGHZ_IT_CAD_ACTIVITY_DETECTED) != RESET)
    {
      hsubghz->CADStatusCallback(hsubghz, HAL_SUBGHZ_CAD_DETECTED);
    }
    else
    {
      hsubghz->CADStatusCallback(hsubghz, HAL_SUBGHZ_CAD_CLEAR);
    }
#else
    /* Channel activity Detected Interrupt */
    if (SUBGHZ_CHECK_IT_SOURCE(itsource, SUBGHZ_IT_CAD_ACTIVITY_DETECTED) != RESET)
    {
      HAL_SUBGHZ_CADStatusCallback(hsubghz, HAL_SUBGHZ_CAD_DETECTED);
    }
    else
    {
      HAL_SUBGHZ_CADStatusCallback(hsubghz, HAL_SUBGHZ_CAD_CLEAR);
    }
#endif /* USE_HAL_SUBGHZ_REGISTER_CALLBACKS */
  }

  /* Rx or Tx Timeout Interrupt */
  if (SUBGHZ_CHECK_IT_SOURCE(itsource, SUBGHZ_IT_RX_TX_TIMEOUT) != RESET)
  {
#if (USE_HAL_SUBGHZ_REGISTER_CALLBACKS == 1U)
    hsubghz->RxTxTimeoutCallback(hsubghz);
#else
    HAL_SUBGHZ_RxTxTimeoutCallback(hsubghz);
#endif /* USE_HAL_SUBGHZ_REGISTER_CALLBACKS */
  }

  /* LR_FHSS Hop interrupt */
  if (SUBGHZ_CHECK_IT_SOURCE(itsource, SUBGHZ_IT_LR_FHSS_HOP) != RESET)
  {
#if (USE_HAL_SUBGHZ_REGISTER_CALLBACKS == 1U)
    hsubghz->LrFhssHopCallback(hsubghz);
#else
    HAL_SUBGHZ_LrFhssHopCallback(hsubghz);
#endif /* USE_HAL_SUBGHZ_REGISTER_CALLBACKS */
  }
}

/**
  * @brief  Packet transmission completed callback.
  * @param  hsubghz pointer to a SUBGHZ_HandleTypeDef structure that contains
  *               the configuration information for SUBGHZ module.
  * @retval None
  */
__weak void HAL_SUBGHZ_TxCpltCallback(SUBGHZ_HandleTypeDef *hsubghz)
{
  /* Prevent unused argument(s) compilation warning */
  UNUSED(hsubghz);

  /* NOTE : This function should not be modified, when the callback is needed,
            the HAL_SUBGHZ_TxCpltCallback should be implemented in the user file
   */
}

/**
  * @brief  Packet received callback.
  * @param  hsubghz pointer to a SUBGHZ_HandleTypeDef structure that contains
  *               the configuration information for SUBGHZ module.
  * @retval None
  */
__weak void HAL_SUBGHZ_RxCpltCallback(SUBGHZ_HandleTypeDef *hsubghz)
{
  /* Prevent unused argument(s) compilation warning */
  UNUSED(hsubghz);

  /* NOTE : This function should not be modified, when the callback is needed,
            the HAL_SUBGHZ_RxCpltCallback should be implemented in the user file
   */
}

/**
  * @brief  Preamble Detected callback.
  * @param  hsubghz pointer to a SUBGHZ_HandleTypeDef structure that contains
  *               the configuration information for SUBGHZ module.
  * @retval None
  */
__weak void HAL_SUBGHZ_PreambleDetectedCallback(SUBGHZ_HandleTypeDef *hsubghz)
{
  /* Prevent unused argument(s) compilation warning */
  UNUSED(hsubghz);

  /* NOTE : This function should not be modified, when the callback is needed,
            the HAL_SUBGHZ_PreambleDetectedCallback should be implemented in the user file
   */
}

/**
  * @brief  Valid sync word detected callback.
  * @param  hsubghz pointer to a SUBGHZ_HandleTypeDef structure that contains
  *               the configuration information for SUBGHZ module.
  * @retval None
  */
__weak void HAL_SUBGHZ_SyncWordValidCallback(SUBGHZ_HandleTypeDef *hsubghz)
{
  /* Prevent unused argument(s) compilation warning */
  UNUSED(hsubghz);

  /* NOTE : This function should not be modified, when the callback is needed,
            the HAL_SUBGHZ_SyncWordValidCallback should be implemented in the user file
   */
}

/**
  * @brief  Valid LoRa header received callback.
  * @param  hsubghz pointer to a SUBGHZ_HandleTypeDef structure that contains
  *               the configuration information for SUBGHZ module.
  * @retval None
  */
__weak void HAL_SUBGHZ_HeaderValidCallback(SUBGHZ_HandleTypeDef *hsubghz)
{
  /* Prevent unused argument(s) compilation warning */
  UNUSED(hsubghz);

  /* NOTE : This function should not be modified, when the callback is needed,
            the HAL_SUBGHZ_HeaderValidCallback should be implemented in the user file
   */
}

/**
  * @brief  LoRa header CRC error callback.
  * @param  hsubghz pointer to a SUBGHZ_HandleTypeDef structure that contains
  *               the configuration information for SUBGHZ module.
  * @retval None
  */
__weak void HAL_SUBGHZ_HeaderErrorCallback(SUBGHZ_HandleTypeDef *hsubghz)
{
  /* Prevent unused argument(s) compilation warning */
  UNUSED(hsubghz);

  /* NOTE : This function should not be modified, when the callback is needed,
            the HAL_SUBGHZ_HeaderErrorCallback should be implemented in the user file
   */
}

/**
  * @brief  Wrong CRC received callback.
  * @param  hsubghz pointer to a SUBGHZ_HandleTypeDef structure that contains
  *               the configuration information for SUBGHZ module.
  * @retval None
  */
__weak void HAL_SUBGHZ_CRCErrorCallback(SUBGHZ_HandleTypeDef *hsubghz)
{
  /* Prevent unused argument(s) compilation warning */
  UNUSED(hsubghz);

  /* NOTE : This function should not be modified, when the callback is needed,
            the HAL_SUBGHZ_CRCErrorCallback should be implemented in the user file
   */
}

/**
  * @brief  Channel activity detection status callback.
  * @note   Unified callback for CAD Done and CAD activity interrupts.
  * @param  hsubghz pointer to a SUBGHZ_HandleTypeDef structure that contains
  *               the configuration information for SUBGHZ module.
  * @param  cadstatus reports whether activity is detected or not
  * @retval None
  */
__weak void HAL_SUBGHZ_CADStatusCallback(SUBGHZ_HandleTypeDef *hsubghz,
                                         HAL_SUBGHZ_CadStatusTypeDef cadstatus)
{
  /* Prevent unused argument(s) compilation warning */
  UNUSED(hsubghz);

  UNUSED(cadstatus);
  /* NOTE : This function should not be modified, when the callback is needed,
            the HAL_SUBGHZ_CADStatusCallback should be implemented in the user file
   */
}

/**
  * @brief  Rx or Tx Timeout callback.
  * @param  hsubghz pointer to a SUBGHZ_HandleTypeDef structure that contains
  *               the configuration information for SUBGHZ module.
  * @retval None
  */
__weak void HAL_SUBGHZ_RxTxTimeoutCallback(SUBGHZ_HandleTypeDef *hsubghz)
{
  /* Prevent unused argument(s) compilation warning */
  UNUSED(hsubghz);

  /* NOTE : This function should not be modified, when the callback is needed,
            the HAL_SUBGHZ_RxTxTimeoutCallback should be implemented in the user file
   */
}

/**
  * @brief  LR FHSS Hop callback.
  * @param  hsubghz pointer to a SUBGHZ_HandleTypeDef structure that contains
  *               the configuration information for SUBGHZ module.
  * @retval None
  */
__weak void HAL_SUBGHZ_LrFhssHopCallback(SUBGHZ_HandleTypeDef *hsubghz)
{
  /* Prevent unused argument(s) compilation warning */
  UNUSED(hsubghz);

  /* NOTE : This function should not be modified, when the callback is needed,
            the HAL_SUBGHZ_LrFhssHopCallback should be implemented in the user file
   */
}
/**
  * @}
  */

/** @defgroup SUBGHZ_Exported_Functions_Group3 Peripheral State and Errors functions
  * @brief   SUBGHZ control functions
  *
@verbatim
 ===============================================================================
                      ##### Peripheral State and Errors functions #####
 ===============================================================================
    [..]
    This subsection provides a set of functions allowing to control the SUBGHZ.
     (+) HAL_SUBGHZ_GetState() API can be helpful to check in run-time the state of the SUBGHZ peripheral
     (+) HAL_SUBGHZ_GetError() check in run-time Errors occurring during communication
@endverbatim
  * @{
  */

/**
  * @brief  Return the SUBGHZ handle state.
  * @param  hsubghz pointer to a SUBGHZ_HandleTypeDef structure that contains
  *         the handle information for SUBGHZ module.
  * @retval SUBGHZ state
  */
HAL_SUBGHZ_StateTypeDef HAL_SUBGHZ_GetState(const SUBGHZ_HandleTypeDef *hsubghz)
{
  /* Return SUBGHZ handle state */
  return hsubghz->State;
}

/**
  * @brief  Return the SUBGHZ error code.
  * @param  hsubghz pointer to a SUBGHZ_HandleTypeDef structure that contains
  *         the handle information for SUBGHZ module.
  * @retval SUBGHZ error code in bitmap format
  */
uint32_t HAL_SUBGHZ_GetError(const SUBGHZ_HandleTypeDef *hsubghz)
{
  /* Return SUBGHZ ErrorCode */
  return hsubghz->ErrorCode;
}

/**
  * @}
  */

/**
  * @}
  */

/** @addtogroup SUBGHZ_Private_Functions
  * @brief   Private functions
  * @{
  */

/**
  * @brief  Initializes the SUBGHZSPI peripheral
  * @param  BaudratePrescaler SPI Baudrate prescaler
  * @retval None
  */
void SUBGHZSPI_Init(uint32_t BaudratePrescaler)
{
  /* Check the parameters */
  assert_param(IS_SUBGHZ_ALL_INSTANCE(SUBGHZSPI));

  /* Disable SUBGHZSPI Peripheral */
  CLEAR_BIT(SUBGHZSPI->CR1, SPI_CR1_SPE);

  /*----------------------- SPI CR1 Configuration ----------------------------*
   *             SPI Mode: Master                                             *
   *   Communication Mode: 2 lines (Full-Duplex)                              *
   *       Clock polarity: Low                                                *
   *                phase: 1st Edge                                           *
   *       NSS management: Internal (Done with External bit inside PWR        *
   *  Communication speed: BaudratePrescaler                             *
   *            First bit: MSB                                                *
   *      CRC calculation: Disable                                            *
   *--------------------------------------------------------------------------*/
  WRITE_REG(SUBGHZSPI->CR1, (SPI_CR1_MSTR | SPI_CR1_SSI | BaudratePrescaler | SPI_CR1_SSM));

  /*----------------------- SPI CR2 Configuration ----------------------------*
   *            Data Size: 8bits                                              *
   *              TI Mode: Disable                                            *
   *            NSS Pulse: Disable                                            *
   *    Rx FIFO Threshold: 8bits                                              *
   *--------------------------------------------------------------------------*/
  WRITE_REG(SUBGHZSPI->CR2, (SPI_CR2_FRXTH |  SPI_CR2_DS_0 | SPI_CR2_DS_1 | SPI_CR2_DS_2));

  /* Enable SUBGHZSPI Peripheral */
  SET_BIT(SUBGHZSPI->CR1, SPI_CR1_SPE);
}

/**
  * @brief  DeInitializes the SUBGHZSPI peripheral
  * @retval None
  */
void  SUBGHZSPI_DeInit(void)
{
  /* Check the parameters */
  assert_param(IS_SUBGHZ_ALL_INSTANCE(SUBGHZSPI));

  /* Disable SUBGHZSPI Peripheral */
  CLEAR_BIT(SUBGHZSPI->CR1, SPI_CR1_SPE);
}

/**
  * @brief  Transmit data through SUBGHZSPI peripheral
  * @param  hsubghz pointer to a SUBGHZ_HandleTypeDef structure that contains
  *         the handle information for SUBGHZ module.
  * @param  Data  data to transmit
  * @retval HAL status
  */
HAL_StatusTypeDef SUBGHZSPI_Transmit(SUBGHZ_HandleTypeDef *hsubghz,
                                     uint8_t Data)
{
  HAL_StatusTypeDef status = HAL_OK;
  __IO uint32_t count;

  /* Handle Tx transmission from SUBGHZSPI peripheral to Radio ****************/
  /* Initialize Timeout */
  count = SUBGHZ_DEFAULT_TIMEOUT * SUBGHZ_DEFAULT_LOOP_TIME;

  /* Wait until TXE flag is set */
  do
  {
    if (count == 0U)
    {
      status = HAL_ERROR;
      hsubghz->ErrorCode = HAL_SUBGHZ_ERROR_TIMEOUT;
      break;
    }
    count--;
  } while (READ_BIT(SUBGHZSPI->SR, SPI_SR_TXE) != (SPI_SR_TXE));

  /* Transmit Data*/
#if defined (__GNUC__)
  __IO uint8_t *spidr = ((__IO uint8_t *)&SUBGHZSPI->DR);
  *spidr = Data;
#else
  *((__IO uint8_t *)&SUBGHZSPI->DR) = Data;
#endif /* __GNUC__ */

  /* Handle Rx transmission from SUBGHZSPI peripheral to Radio ****************/
  /* Initialize Timeout */
  count = SUBGHZ_DEFAULT_TIMEOUT * SUBGHZ_DEFAULT_LOOP_TIME;

  /* Wait until RXNE flag is set */
  do
  {
    if (count == 0U)
    {
      status = HAL_ERROR;
      hsubghz->ErrorCode = HAL_SUBGHZ_ERROR_TIMEOUT;
      break;
    }
    count--;
  } while (READ_BIT(SUBGHZSPI->SR, SPI_SR_RXNE) != (SPI_SR_RXNE));

  /* Flush Rx data */
  READ_REG(SUBGHZSPI->DR);

  return status;
}

/**
  * @brief  Receive data through SUBGHZSPI peripheral
  * @param  hsubghz pointer to a SUBGHZ_HandleTypeDef structure that contains
  *         the handle information for SUBGHZ module.
  * @param  pData  pointer on data to receive
  * @retval HAL status
  */
HAL_StatusTypeDef SUBGHZSPI_Receive(SUBGHZ_HandleTypeDef *hsubghz,
                                    uint8_t *pData)
{
  HAL_StatusTypeDef status = HAL_OK;
  __IO uint32_t count;

  /* Handle Tx transmission from SUBGHZSPI peripheral to Radio ****************/
  /* Initialize Timeout */
  count = SUBGHZ_DEFAULT_TIMEOUT * SUBGHZ_DEFAULT_LOOP_TIME;

  /* Wait until TXE flag is set */
  do
  {
    if (count == 0U)
    {
      status = HAL_ERROR;
      hsubghz->ErrorCode = HAL_SUBGHZ_ERROR_TIMEOUT;
      break;
    }
    count--;
  } while (READ_BIT(SUBGHZSPI->SR, SPI_SR_TXE) != (SPI_SR_TXE));

  /* Transmit Data*/
#if defined (__GNUC__)
  __IO uint8_t *spidr = ((__IO uint8_t *)&SUBGHZSPI->DR);
  *spidr = SUBGHZ_DUMMY_DATA;
#else
  *((__IO uint8_t *)&SUBGHZSPI->DR) = SUBGHZ_DUMMY_DATA;
#endif /* __GNUC__ */

  /* Handle Rx transmission from SUBGHZSPI peripheral to Radio ****************/
  /* Initialize Timeout */
  count = SUBGHZ_DEFAULT_TIMEOUT * SUBGHZ_DEFAULT_LOOP_TIME;

  /* Wait until RXNE flag is set */
  do
  {
    if (count == 0U)
    {
      status = HAL_ERROR;
      hsubghz->ErrorCode = HAL_SUBGHZ_ERROR_TIMEOUT;
      break;
    }
    count--;
  } while (READ_BIT(SUBGHZSPI->SR, SPI_SR_RXNE) != (SPI_SR_RXNE));

  /* Retrieve pData */
  *pData = (uint8_t)(READ_REG(SUBGHZSPI->DR));

  return status;
}

/**
  * @brief  Check if peripheral is ready
  * @param  hsubghz pointer to a SUBGHZ_HandleTypeDef structure that contains
  *         the handle information for SUBGHZ module.
  * @retval HAL status
  */
HAL_StatusTypeDef SUBGHZ_CheckDeviceReady(SUBGHZ_HandleTypeDef *hsubghz)
{
  __IO uint32_t count;

  /* Wakeup radio in case of sleep mode: Select-Unselect radio */
  if (hsubghz->DeepSleep == SUBGHZ_DEEP_SLEEP_ENABLE)
  {
    /* Initialize NSS switch Delay */
    count  = SUBGHZ_NSS_LOOP_TIME;

    /* NSS = 0; */
    LL_PWR_SelectSUBGHZSPI_NSS();

    /* Wait Radio wakeup */
    do
    {
      count--;
    } while (count != 0UL);

    /* NSS = 1 */
    LL_PWR_UnselectSUBGHZSPI_NSS();
  }
  return (SUBGHZ_WaitOnBusy(hsubghz));
}

/**
  * @brief  Wait busy flag low from peripheral
  * @param  hsubghz pointer to a SUBGHZ_HandleTypeDef structure that contains
  *         the handle information for SUBGHZ module.
  * @retval HAL status
  */
HAL_StatusTypeDef SUBGHZ_WaitOnBusy(SUBGHZ_HandleTypeDef *hsubghz)
{
  HAL_StatusTypeDef status;
  __IO uint32_t count;
  uint32_t mask;

  status = HAL_OK;
  count  = SUBGHZ_DEFAULT_TIMEOUT * SUBGHZ_RFBUSY_LOOP_TIME;

  /* Wait until Busy signal is set */
  do
  {
    mask = LL_PWR_IsActiveFlag_RFBUSYMS();

    if (count == 0U)
    {
      status  = HAL_ERROR;
      hsubghz->ErrorCode = HAL_SUBGHZ_ERROR_RF_BUSY;
      break;
    }
    count--;
  } while ((LL_PWR_IsActiveFlag_RFBUSYS()& mask) == 1UL);

  return status;
}
/**
  * @}
  */

#endif /* HAL_SUBGHZ_MODULE_ENABLED */

/**
  * @}
  */

/**
  * @}
  */
