forked from xuos/xiuos
578 lines
14 KiB
C
578 lines
14 KiB
C
/* SPDX-License-Identifier: BSD-3-Clause */
|
|
/*
|
|
* Copyright (c) 2020-2021 Rockchip Electronics Co., Ltd.
|
|
*/
|
|
|
|
#include "hal_base.h"
|
|
#include "hal_pinctrl.h"
|
|
#include "hal_gpio.h"
|
|
|
|
/** @addtogroup RK_HAL_Driver
|
|
* @{
|
|
*/
|
|
|
|
/** @addtogroup GPIO
|
|
* @{
|
|
*/
|
|
|
|
/** @defgroup GPIO_How_To_Use How To Use
|
|
* @{
|
|
|
|
The GPIO driver can be used as follows:
|
|
|
|
APIs for GPIO io read write:
|
|
|
|
1. HAL_GPIO_GetPinLevel() to get EXT port level.
|
|
2. HAL_GPIO_SetPinLevel() to set io level.
|
|
3. HAL_GPIO_SetPinDirection() to set io direction.
|
|
|
|
APIs for GPIO IRQ:
|
|
|
|
1. HAL_GPIO_EnableIRQ() to enable a GPIO IRQ.
|
|
2. HAL_GPIO_DisableIRQ() to disable a GPIO IRQ.
|
|
3. HAL_GPIO_IRQHandler() to handle GPIO IRQ isr.
|
|
4. HAL_GPIO_IRQDispatch() to dispatch GPIO IRQ, should be implemented by User.
|
|
|
|
Please open the macro definition HAL_GPIO_VIRTUAL_MODEL_FEATURE_ENABLED to support
|
|
|
|
APIs for GPIO virtual model:
|
|
|
|
1. HAL_GPIO_EnableVirtualModel() to enable a GPIO virtual model.
|
|
2. HAL_GPIO_DisableVirtualModel() to disable a GPIO virtual model.
|
|
3. HAL_GPIO_SetVirtualModel() to configure GPIO pins virtual model.
|
|
|
|
@} */
|
|
|
|
/** @defgroup GPIO_Private_Definition Private Definition
|
|
* @{
|
|
*/
|
|
/********************* Private MACRO Definition ******************************/
|
|
#define UNUSED(X) (void)(X) /* To avoid gcc/g++ warnings */
|
|
|
|
/********************* Private Function Definition ***************************/
|
|
|
|
/**
|
|
* @brief Set the GPIO IRQ end of interrupt(EOI).
|
|
* @param pGPIO: The pointer of GPIO struct.
|
|
* @param pin: The pin bit defined in @ref ePINCTRL_GPIO_PINS.
|
|
*/
|
|
static void GPIO_SetEOI(struct GPIO_REG *pGPIO, ePINCTRL_GPIO_PINS pin)
|
|
{
|
|
#if (GPIO_VER_ID == 0x01000C2BU)
|
|
if (IS_GPIO_HIGH_PIN(pin)) {
|
|
pin &= 0xFFFF0000;
|
|
pGPIO->PORT_EOI_H = pin | (pin >> 16);
|
|
} else {
|
|
pin &= 0x0000FFFF;
|
|
pGPIO->PORT_EOI_L = pin | (pin << 16);
|
|
}
|
|
#else
|
|
{
|
|
pGPIO->PORTA_EOI = pin;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/**
|
|
* @brief Get GPIO all pins irq type.
|
|
* @param pGPIO: the GPIO struct.
|
|
* @return uint32_t: type value.
|
|
*/
|
|
static uint32_t GPIO_GetIntType(struct GPIO_REG *pGPIO)
|
|
{
|
|
uint32_t type;
|
|
|
|
#if (GPIO_VER_ID == 0x01000C2BU)
|
|
type = (pGPIO->INT_TYPE_L & 0xffff);
|
|
type |= ((pGPIO->INT_TYPE_H & 0xffff) << 16);
|
|
type |= (pGPIO->INT_BOTHEDGE_L & 0xffff);
|
|
type |= ((pGPIO->INT_BOTHEDGE_H & 0xffff) << 16);
|
|
#else
|
|
type = pGPIO->INTTYPE_LEVEL;
|
|
#ifdef GPIO_INT_BOTHEDGE_OFFSET
|
|
type |= pGPIO->INT_BOTHEDGE;
|
|
#endif
|
|
#endif
|
|
|
|
return type;
|
|
}
|
|
|
|
/**
|
|
* @brief Get GPIO all pins irq status.
|
|
* @param pGPIO: the GPIO struct.
|
|
* @return uint32_t: status value.
|
|
*/
|
|
static uint32_t GPIO_GetIntStatus(struct GPIO_REG *pGPIO)
|
|
{
|
|
return pGPIO->INT_STATUS;
|
|
}
|
|
|
|
/** @} */
|
|
/********************* Public Function Definition ***************************/
|
|
|
|
/** @defgroup GPIO_Exported_Functions_Group1 State and Errors Functions
|
|
|
|
This section provides functions allowing to get the status of the module:
|
|
|
|
* @{
|
|
*/
|
|
|
|
/**
|
|
* @brief GPIO Configure IRQ trigger type.
|
|
* @param pGPIO: The pointer of GPIO struct.
|
|
* @param pin: The pin bit defined in @ref ePINCTRL_GPIO_PINS.
|
|
* @param mode: The value defined in @ref eGPIO_intType.
|
|
* @return HAL_Status.
|
|
*/
|
|
HAL_Status HAL_GPIO_SetIntType(struct GPIO_REG *pGPIO, ePINCTRL_GPIO_PINS pin, eGPIO_intType mode)
|
|
{
|
|
uint32_t both = 0, type = 0, plar = 0;
|
|
|
|
UNUSED(both);
|
|
|
|
switch (mode) {
|
|
case GPIO_INT_TYPE_EDGE_RISING:
|
|
type = 1;
|
|
plar = 1;
|
|
both = 0;
|
|
break;
|
|
case GPIO_INT_TYPE_EDGE_FALLING:
|
|
type = 1;
|
|
plar = 0;
|
|
both = 0;
|
|
break;
|
|
case GPIO_INT_TYPE_LEVEL_HIGH:
|
|
type = 0;
|
|
plar = 1;
|
|
both = 0;
|
|
break;
|
|
case GPIO_INT_TYPE_LEVEL_LOW:
|
|
type = 0;
|
|
plar = 0;
|
|
both = 0;
|
|
break;
|
|
case GPIO_INT_TYPE_EDGE_BOTH:
|
|
type = 0;
|
|
plar = 0;
|
|
both = 1;
|
|
break;
|
|
default:
|
|
|
|
return HAL_INVAL;
|
|
}
|
|
|
|
#if (GPIO_VER_ID == 0x01000C2BU)
|
|
if (IS_GPIO_HIGH_PIN(pin)) {
|
|
pin &= 0xFFFF0000;
|
|
pGPIO->INT_TYPE_H = (type) ? (pin | (pin >> 16)) : (pin);
|
|
pGPIO->INT_POLARITY_H = (plar) ? (pin | (pin >> 16)) : (pin);
|
|
pGPIO->INT_BOTHEDGE_H = (both) ? (pin | (pin >> 16)) : (pin);
|
|
} else {
|
|
pin &= 0x0000FFFF;
|
|
pGPIO->INT_TYPE_L = (type) ? (pin | (pin << 16)) : (pin << 16);
|
|
pGPIO->INT_POLARITY_L = (plar) ? (pin | (pin << 16)) : (pin << 16);
|
|
pGPIO->INT_BOTHEDGE_L = (both) ? (pin | (pin << 16)) : (pin << 16);
|
|
}
|
|
#else
|
|
{
|
|
pGPIO->INTTYPE_LEVEL = (type) ? (pin) : (pGPIO->INTTYPE_LEVEL & ~(pin));
|
|
pGPIO->INT_POLARITY = (plar) ? (pin) : (pGPIO->INT_POLARITY & ~(pin));
|
|
#ifdef GPIO_INT_BOTHEDGE_OFFSET
|
|
pGPIO->INT_BOTHEDGE = (both) ? (pin) : (pGPIO->INT_BOTHEDGE & ~(pin));
|
|
#endif
|
|
}
|
|
#endif
|
|
|
|
return HAL_OK;
|
|
}
|
|
|
|
/**
|
|
* @brief Set GPIO direction.
|
|
* @param pGPIO: the GPIO struct.
|
|
* @param pin: The pin bit defined in @ref ePINCTRL_GPIO_PINS.
|
|
* @param direction: direction value defined in @ref eGPIO_pinDirection.
|
|
* @return HAL_Status: HAL_OK if success.
|
|
*/
|
|
HAL_Status HAL_GPIO_SetPinDirection(struct GPIO_REG *pGPIO, ePINCTRL_GPIO_PINS pin, eGPIO_pinDirection direction)
|
|
{
|
|
#if (GPIO_VER_ID == 0x01000C2BU)
|
|
if (IS_GPIO_HIGH_PIN(pin)) {
|
|
pin &= 0xFFFF0000;
|
|
pGPIO->SWPORT_DDR_H = (direction == GPIO_OUT) ? (pin | (pin >> 16)) : (pin);
|
|
} else {
|
|
pin &= 0x0000FFFF;
|
|
pGPIO->SWPORT_DDR_L = (direction == GPIO_OUT) ? (pin | (pin << 16)) : (pin << 16);
|
|
}
|
|
#else
|
|
if (direction == GPIO_OUT) {
|
|
pGPIO->SWPORTA_DDR |= pin;
|
|
} else {
|
|
pGPIO->SWPORTA_DDR &= ~pin;
|
|
}
|
|
#endif
|
|
|
|
return HAL_OK;
|
|
}
|
|
|
|
/**
|
|
* @brief Set GPIO direction.
|
|
* @param pGPIO: the GPIO struct.
|
|
* @param mPins: The pins defined in @ref ePINCTRL_GPIO_PINS.
|
|
* @param direction: value defined in @ref eGPIO_pinDirection.
|
|
* @return HAL_Status: HAL_OK if success.
|
|
*/
|
|
HAL_Status HAL_GPIO_SetPinsDirection(struct GPIO_REG *pGPIO, uint32_t mPins, eGPIO_pinDirection direction)
|
|
{
|
|
uint8_t pin;
|
|
HAL_Status rc;
|
|
|
|
HAL_ASSERT(IS_GPIO_INSTANCE(pGPIO));
|
|
|
|
for (pin = 0; pin < 32; pin++) {
|
|
if (mPins & (1 << pin)) {
|
|
rc = HAL_GPIO_SetPinDirection(pGPIO, (1 << pin), direction);
|
|
if (rc) {
|
|
return rc;
|
|
}
|
|
}
|
|
}
|
|
|
|
return HAL_OK;
|
|
}
|
|
|
|
/**
|
|
* @brief Get GPIO Pin data direction value.
|
|
* @param pGPIO: the GPIO struct.
|
|
* @param pin: The pin bit defined in @ref ePINCTRL_GPIO_PINS.
|
|
* @retval eGPIO_pinDirection: data direction value.
|
|
*/
|
|
eGPIO_pinDirection HAL_GPIO_GetPinDirection(struct GPIO_REG *pGPIO, ePINCTRL_GPIO_PINS pin)
|
|
{
|
|
eGPIO_pinDirection direction;
|
|
uint32_t value;
|
|
|
|
#if (GPIO_VER_ID == 0x01000C2BU)
|
|
value = IS_GPIO_HIGH_PIN(pin) ? (pGPIO->SWPORT_DDR_H & (pin >> 16)) : (pGPIO->SWPORT_DDR_L & pin);
|
|
#else
|
|
value = pGPIO->SWPORTA_DDR & pin;
|
|
#endif
|
|
|
|
if (value != (uint32_t)GPIO_IN) {
|
|
direction = GPIO_OUT;
|
|
} else {
|
|
direction = GPIO_IN;
|
|
}
|
|
|
|
return direction;
|
|
}
|
|
|
|
/**
|
|
* @brief Set GPIO pin level.
|
|
* @param pGPIO: The pointer of GPIO struct.
|
|
* @param pin: The pin bit defined in @ref ePINCTRL_GPIO_PINS.
|
|
* @param level: The level defined in @ref eGPIO_pinLevel.
|
|
* @return HAL_Status.
|
|
*/
|
|
HAL_Status HAL_GPIO_SetPinLevel(struct GPIO_REG *pGPIO, ePINCTRL_GPIO_PINS pin, eGPIO_pinLevel level)
|
|
{
|
|
#if (GPIO_VER_ID == 0x01000C2BU)
|
|
if (IS_GPIO_HIGH_PIN(pin)) {
|
|
pin &= 0xFFFF0000;
|
|
pGPIO->SWPORT_DR_H = (level == GPIO_HIGH) ? (pin | (pin >> 16)) : (pin);
|
|
} else {
|
|
pin &= 0x0000FFFF;
|
|
pGPIO->SWPORT_DR_L = (level == GPIO_HIGH) ? (pin | (pin << 16)) : (pin << 16);
|
|
}
|
|
#else
|
|
if (level == GPIO_HIGH) {
|
|
pGPIO->SWPORTA_DR |= pin;
|
|
} else {
|
|
pGPIO->SWPORTA_DR &= ~pin;
|
|
}
|
|
#endif
|
|
|
|
return HAL_OK;
|
|
}
|
|
|
|
/**
|
|
* @brief Set GPIO pin level.
|
|
* @param pGPIO: The pointer of GPIO struct.
|
|
* @param mPins: The pins defined in @ref ePINCTRL_GPIO_PINS.
|
|
* @param level: The level defined in @ref eGPIO_pinLevel.
|
|
* @return HAL_Status.
|
|
*/
|
|
HAL_Status HAL_GPIO_SetPinsLevel(struct GPIO_REG *pGPIO, uint32_t mPins, eGPIO_pinLevel level)
|
|
{
|
|
uint8_t pin;
|
|
HAL_Status rc;
|
|
|
|
HAL_ASSERT(IS_GPIO_INSTANCE(pGPIO));
|
|
|
|
for (pin = 0; pin < 32; pin++) {
|
|
if (mPins & (1 << pin)) {
|
|
rc = HAL_GPIO_SetPinLevel(pGPIO, (1 << pin), level);
|
|
if (rc) {
|
|
return rc;
|
|
}
|
|
}
|
|
}
|
|
|
|
return HAL_OK;
|
|
}
|
|
|
|
/** @} */
|
|
|
|
/** @defgroup GPIO_Exported_Functions_Group2 IO Functions
|
|
|
|
This section provides functions allowing to IO controlling:
|
|
|
|
* @{
|
|
*/
|
|
|
|
/**
|
|
* @brief Get GPIO Pin data value.
|
|
* @param pGPIO: the GPIO struct.
|
|
* @param pin: The pin bit defined in @ref ePINCTRL_GPIO_PINS.
|
|
* @retval eGPIO_pinLevel: data value.
|
|
*/
|
|
eGPIO_pinLevel HAL_GPIO_GetPinData(struct GPIO_REG *pGPIO, ePINCTRL_GPIO_PINS pin)
|
|
{
|
|
eGPIO_pinLevel level;
|
|
uint32_t value;
|
|
|
|
#if (GPIO_VER_ID == 0x01000C2BU)
|
|
value = IS_GPIO_HIGH_PIN(pin) ? (pGPIO->SWPORT_DR_H & (pin >> 16)) : (pGPIO->SWPORT_DR_L & pin);
|
|
#else
|
|
value = pGPIO->SWPORTA_DR & pin;
|
|
#endif
|
|
|
|
if (value != (uint32_t)GPIO_LOW) {
|
|
level = GPIO_HIGH;
|
|
} else {
|
|
level = GPIO_LOW;
|
|
}
|
|
|
|
return level;
|
|
}
|
|
|
|
/**
|
|
* @brief Get GPIO Pin ext bank level.
|
|
* @param pGPIO: the GPIO struct.
|
|
* @param pin: The pin bit defined in @ref ePINCTRL_GPIO_PINS.
|
|
* @retval GPIO_PinState: ext bank value.
|
|
*/
|
|
eGPIO_pinLevel HAL_GPIO_GetPinLevel(struct GPIO_REG *pGPIO, ePINCTRL_GPIO_PINS pin)
|
|
{
|
|
uint32_t value;
|
|
|
|
#if (GPIO_VER_ID == 0x01000C2BU)
|
|
value = (pGPIO->EXT_PORT & pin);
|
|
#else
|
|
value = (pGPIO->EXT_PORTA & pin);
|
|
#endif
|
|
|
|
return (value == (uint32_t)GPIO_LOW) ? GPIO_LOW : GPIO_HIGH;
|
|
}
|
|
|
|
/**
|
|
* @brief Get GPIO Pin ext bank level.
|
|
* @param pGPIO: the GPIO struct.
|
|
* @retval uint32_t: ext bank value.
|
|
*/
|
|
uint32_t HAL_GPIO_GetBankLevel(struct GPIO_REG *pGPIO)
|
|
{
|
|
uint32_t value;
|
|
|
|
#if (GPIO_VER_ID == 0x01000C2BU)
|
|
value = (pGPIO->EXT_PORT);
|
|
#else
|
|
value = (pGPIO->EXT_PORTA);
|
|
#endif
|
|
|
|
return value;
|
|
}
|
|
/** @} */
|
|
|
|
/** @defgroup GPIO_Exported_Functions_Group3 Other Functions
|
|
* @{
|
|
*/
|
|
|
|
/**
|
|
* @brief Set GPIO irq enable.
|
|
* @param pGPIO: The pointer of GPIO struct.
|
|
* @param pin: The pin bit defined in @ref ePINCTRL_GPIO_PINS.
|
|
*/
|
|
void HAL_GPIO_EnableIRQ(struct GPIO_REG *pGPIO, ePINCTRL_GPIO_PINS pin)
|
|
{
|
|
#if (GPIO_VER_ID == 0x01000C2BU)
|
|
if (IS_GPIO_HIGH_PIN(pin)) {
|
|
pin &= 0xFFFF0000;
|
|
#ifndef HAL_GPIO_IRQ_GROUP_MODULE_ENABLED
|
|
pGPIO->INT_MASK_H = pin;
|
|
#endif
|
|
pGPIO->INT_EN_H = pin | (pin >> 16);
|
|
} else {
|
|
pin &= 0x0000FFFF;
|
|
#ifndef HAL_GPIO_IRQ_GROUP_MODULE_ENABLED
|
|
pGPIO->INT_MASK_L = pin << 16;
|
|
#endif
|
|
pGPIO->INT_EN_L = pin | (pin << 16);
|
|
}
|
|
#else
|
|
{
|
|
pGPIO->INTEN |= pin;
|
|
pGPIO->INTMASK &= ~pin;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/**
|
|
* @brief Set GPIO irq disable.
|
|
* @param pGPIO: The pointer of GPIO struct.
|
|
* @param pin: The pin bit defined in @ref ePINCTRL_GPIO_PINS.
|
|
*/
|
|
void HAL_GPIO_DisableIRQ(struct GPIO_REG *pGPIO, ePINCTRL_GPIO_PINS pin)
|
|
{
|
|
#if (GPIO_VER_ID == 0x01000C2BU)
|
|
if (IS_GPIO_HIGH_PIN(pin)) {
|
|
pin &= 0xFFFF0000;
|
|
pGPIO->INT_EN_H = pin;
|
|
#ifndef HAL_GPIO_IRQ_GROUP_MODULE_ENABLED
|
|
pGPIO->INT_MASK_H = pin | (pin >> 16);
|
|
#endif
|
|
} else {
|
|
pin &= 0x0000FFFF;
|
|
pGPIO->INT_EN_L = pin << 16;
|
|
#ifndef HAL_GPIO_IRQ_GROUP_MODULE_ENABLED
|
|
pGPIO->INT_MASK_L = pin | (pin << 16);
|
|
#endif
|
|
}
|
|
#else
|
|
{
|
|
pGPIO->INTEN &= ~pin;
|
|
pGPIO->INTMASK |= pin;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/**
|
|
* @brief GPIO IRQ callbacks.
|
|
* @param bank: The bank id.
|
|
* @param pin: The true pin index, 0~31.
|
|
* NOTE: This function Should not be modified, when the callback is needed,
|
|
* the HAL_GPIO_IRQDispatch could be implemented in the user file.
|
|
*/
|
|
__attribute__((weak)) void HAL_GPIO_IRQDispatch(eGPIO_bankId bank, uint32_t pin)
|
|
{
|
|
UNUSED(bank);
|
|
UNUSED(pin);
|
|
}
|
|
|
|
/**
|
|
* @brief GPIO IRQ hanlder.
|
|
* @param pGPIO: The pointer of GPIO struct.
|
|
* @param bank: The bank id.
|
|
*/
|
|
void HAL_GPIO_IRQHandler(struct GPIO_REG *pGPIO, eGPIO_bankId bank)
|
|
{
|
|
uint32_t stat, type, clear;
|
|
uint32_t i;
|
|
uint32_t pin;
|
|
|
|
stat = GPIO_GetIntStatus(pGPIO);
|
|
type = GPIO_GetIntType(pGPIO);
|
|
|
|
/* Then process each pending GPIO interrupt */
|
|
for (i = 0x0U; i < PIN_NUMBER_PER_BANK && stat != 0; i++) {
|
|
clear = 0x1U << i;
|
|
pin = HAL_BIT(i);
|
|
|
|
if ((stat & clear) != 0x0U) {
|
|
/* If gpio is Edge-sensitive triggered, clear eoi */
|
|
if (type & clear) {
|
|
GPIO_SetEOI(pGPIO, pin);
|
|
}
|
|
|
|
/* Remove the pending interrupt bit from the clear */
|
|
stat &= ~clear;
|
|
|
|
/* And disptach the GPIO interrupt to the handler */
|
|
HAL_GPIO_IRQDispatch(bank, i);
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifdef HAL_GPIO_VIRTUAL_MODEL_FEATURE_ENABLED
|
|
|
|
/**
|
|
* @brief GPIO virtual model enable.
|
|
* @param pGPIO: The pointer of GPIO struct.
|
|
* @return HAL_Status.
|
|
*/
|
|
HAL_Status HAL_GPIO_EnableVirtualModel(struct GPIO_REG *pGPIO)
|
|
{
|
|
#if (GPIO_VER_ID == 0x01000C2BU)
|
|
pGPIO->GPIO_VIRTUAL_EN = 0x10001;
|
|
|
|
return HAL_OK;
|
|
#endif
|
|
|
|
return HAL_ERROR;
|
|
}
|
|
|
|
/**
|
|
* @brief GPIO virtual model disable.
|
|
* @param pGPIO: The pointer of GPIO struct.
|
|
* @return HAL_Status.
|
|
*/
|
|
HAL_Status HAL_GPIO_DisableVirtualModel(struct GPIO_REG *pGPIO)
|
|
{
|
|
#if (GPIO_VER_ID == 0x01000C2BU)
|
|
pGPIO->GPIO_VIRTUAL_EN = 0x10000;
|
|
|
|
return HAL_OK;
|
|
#endif
|
|
|
|
return HAL_ERROR;
|
|
}
|
|
|
|
/**
|
|
* @brief GPIO Configure pins for virtual model.
|
|
* @param pGPIO: The pointer of GPIO struct.
|
|
* @param pins: The pin bit defined in @ref ePINCTRL_GPIO_PINS.
|
|
* @param vmode: The value defined in @ref eGPIO_VirtualModel.
|
|
* @return HAL_Status.
|
|
*/
|
|
HAL_Status HAL_GPIO_SetVirtualModel(struct GPIO_REG *pGPIO, ePINCTRL_GPIO_PINS pin, eGPIO_VirtualModel vmodel)
|
|
{
|
|
#if (GPIO_VER_ID == 0x01000C2BU)
|
|
uint32_t low_pins, high_pins;
|
|
|
|
low_pins = pin & 0x0000ffff;
|
|
high_pins = (pin & 0xffff0000) >> 16;
|
|
|
|
/* Support OS_A and OS_B */
|
|
if (vmodel == GPIO_VIRTUAL_MODEL_OS_B) {
|
|
pGPIO->GPIO_REG_GROUP_L = low_pins << 16;
|
|
pGPIO->GPIO_REG_GROUP_H = high_pins << 16;
|
|
} else {
|
|
pGPIO->GPIO_REG_GROUP_L = low_pins | (low_pins << 16);
|
|
pGPIO->GPIO_REG_GROUP_H = high_pins | (high_pins << 16);
|
|
}
|
|
|
|
return HAL_OK;
|
|
#endif
|
|
|
|
return HAL_ERROR;
|
|
}
|
|
|
|
#endif /* HAL_GPIO_VIRTUAL_MODEL_FEATURE_ENABLED */
|
|
|
|
/** @} */
|
|
|
|
/** @} */
|
|
|
|
/** @} */
|
|
|
|
|