| /** |
| * \addtogroup BSP |
| * \{ |
| * \addtogroup DEVICES |
| * \{ |
| * \addtogroup GPIO |
| * \{ |
| * \brief GPIO Control |
| */ |
| |
| /** |
| ***************************************************************************************** |
| * |
| * @file hw_gpio.h |
| * |
| * @brief Definition of API for the GPIO Low Level Driver. |
| * |
| * Copyright (c) 2016, Dialog Semiconductor |
| * All rights reserved. |
| * Redistribution and use in source and binary forms, with or without modification, |
| * are permitted provided that the following conditions are met: |
| * 1. Redistributions of source code must retain the above copyright notice, |
| * this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright notice, |
| * this list of conditions and the following disclaimer in the documentation |
| * and/or other materials provided with the distribution. |
| * 3. Neither the name of the copyright holder nor the names of its contributors |
| * may be used to endorse or promote products derived from this software without |
| * specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
| * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
| * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. |
| * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, |
| * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
| * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, |
| * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, |
| * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
| * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED |
| * OF THE POSSIBILITY OF SUCH DAMAGE. |
| * |
| * |
| ***************************************************************************************** |
| */ |
| |
| #ifndef HW_GPIO_H_ |
| #define HW_GPIO_H_ |
| |
| #if dg_configUSE_HW_GPIO |
| |
| #include <stdbool.h> |
| #include <stdint.h> |
| #include <sdk_defs.h> |
| |
| /* GPIO layout definitions */ |
| |
| /** Number of GPIO ports available */ |
| #define HW_GPIO_NUM_PORTS (5) |
| /** Number of GPIO pins available (cumulative) */ |
| #define HW_GPIO_NUM_PINS (HW_GPIO_PORT_0_NUM_PINS + HW_GPIO_PORT_1_NUM_PINS + \ |
| HW_GPIO_PORT_2_NUM_PINS + HW_GPIO_PORT_3_NUM_PINS + \ |
| HW_GPIO_PORT_4_NUM_PINS) |
| /** Number of GPIO pins available in port 0 */ |
| #define HW_GPIO_PORT_0_NUM_PINS (8) |
| /** Number of GPIO pins available in port 1 */ |
| #define HW_GPIO_PORT_1_NUM_PINS (8) |
| /** Number of GPIO pins available in port 2 */ |
| #define HW_GPIO_PORT_2_NUM_PINS (5) |
| /** Number of GPIO pins available in port 3 */ |
| #define HW_GPIO_PORT_3_NUM_PINS (8) |
| /** Number of GPIO pins available in port 4 */ |
| #define HW_GPIO_PORT_4_NUM_PINS (8) |
| /** Number of pins in each GPIO port */ |
| extern const uint8_t hw_gpio_port_num_pins[HW_GPIO_NUM_PORTS]; |
| |
| |
| /** |
| * \brief GPIO input/output mode |
| * |
| */ |
| typedef enum { |
| HW_GPIO_MODE_INPUT = 0, /**< GPIO as an input */ |
| HW_GPIO_MODE_INPUT_PULLUP = 0x100, /**< GPIO as an input with pull-up */ |
| HW_GPIO_MODE_INPUT_PULLDOWN = 0x200, /**< GPIO as an input with pull-down */ |
| HW_GPIO_MODE_OUTPUT = 0x300, /**< GPIO as an (implicitly push-pull) output */ |
| HW_GPIO_MODE_OUTPUT_PUSH_PULL = 0x300, /**< GPIO as an (explicitly push-pull) output */ |
| HW_GPIO_MODE_OUTPUT_OPEN_DRAIN = 0x700, /**< GPIO as an open-drain output */ |
| } HW_GPIO_MODE; |
| |
| /** |
| * \brief GPIO power source |
| * |
| */ |
| typedef enum { |
| HW_GPIO_POWER_V33 = 0, /**< V33 (3.3 V) power rail */ |
| HW_GPIO_POWER_VDD1V8P = 1, /**< VDD1V8P (1.8 V) power rail */ |
| } HW_GPIO_POWER; |
| |
| /** |
| * \brief GPIO port number |
| * |
| */ |
| typedef enum { |
| HW_GPIO_PORT_0 = 0, /**< GPIO Port 0 */ |
| HW_GPIO_PORT_1 = 1, /**< GPIO Port 1 */ |
| HW_GPIO_PORT_2 = 2, /**< GPIO Port 2 */ |
| HW_GPIO_PORT_3 = 3, /**< GPIO Port 3 */ |
| HW_GPIO_PORT_4 = 4 /**< GPIO Port 4 */ |
| } HW_GPIO_PORT; |
| |
| /** |
| * \brief GPIO pin number |
| * |
| */ |
| typedef enum { |
| HW_GPIO_PIN_0 = 0, /**< GPIO Pin 0 */ |
| HW_GPIO_PIN_1 = 1, /**< GPIO Pin 1 */ |
| HW_GPIO_PIN_2 = 2, /**< GPIO Pin 2 */ |
| HW_GPIO_PIN_3 = 3, /**< GPIO Pin 3 */ |
| HW_GPIO_PIN_4 = 4, /**< GPIO Pin 4 */ |
| HW_GPIO_PIN_5 = 5, /**< GPIO Pin 5 */ |
| HW_GPIO_PIN_6 = 6, /**< GPIO Pin 6 */ |
| HW_GPIO_PIN_7 = 7, /**< GPIO Pin 7 */ |
| } HW_GPIO_PIN; |
| |
| /** |
| * \brief GPIO function |
| * |
| */ |
| typedef enum { |
| HW_GPIO_FUNC_GPIO = 0, /**< GPIO */ |
| HW_GPIO_FUNC_UART_RX = 1, /**< GPIO as UART RX */ |
| HW_GPIO_FUNC_UART_TX = 2, /**< GPIO as UART TX */ |
| HW_GPIO_FUNC_UART_IRDA_RX = 3, /**< GPIO as UART IRDA RX */ |
| HW_GPIO_FUNC_UART_IRDA_TX = 4, /**< GPIO as UART IRDA TX */ |
| HW_GPIO_FUNC_UART2_RX = 5, /**< GPIO as UART2 RX */ |
| HW_GPIO_FUNC_UART2_TX = 6, /**< GPIO as UART2 TX */ |
| HW_GPIO_FUNC_UART2_IRDA_RX = 7, /**< GPIO as UART2 IRDA RX */ |
| HW_GPIO_FUNC_UART2_IRDA_TX = 8, /**< GPIO as UART2 IRDA TX */ |
| HW_GPIO_FUNC_UART2_CTSN = 9, /**< GPIO as UART2 CTSN */ |
| HW_GPIO_FUNC_UART2_RTSN = 10, /**< GPIO as UART2 RTSN */ |
| HW_GPIO_FUNC_SPI_DI = 11, /**< GPIO as SPI DI */ |
| HW_GPIO_FUNC_SPI_DO = 12, /**< GPIO as SPI DO */ |
| HW_GPIO_FUNC_SPI_CLK = 13, /**< GPIO as SPI CLK */ |
| HW_GPIO_FUNC_SPI_EN = 14, /**< GPIO as SPI EN */ |
| HW_GPIO_FUNC_SPI2_DI = 15, /**< GPIO as SPI2 DI */ |
| HW_GPIO_FUNC_SPI2_DO = 16, /**< GPIO as SPI2 DO */ |
| HW_GPIO_FUNC_SPI2_CLK = 17, /**< GPIO as SPI2 CLK */ |
| HW_GPIO_FUNC_SPI2_EN = 18, /**< GPIO as SPI2 EN */ |
| HW_GPIO_FUNC_I2C_SCL = 19, /**< GPIO as I2C SCL */ |
| HW_GPIO_FUNC_I2C_SDA = 20, /**< GPIO as I2C SDA */ |
| HW_GPIO_FUNC_I2C2_SCL = 21, /**< GPIO as I2C2 SCL */ |
| HW_GPIO_FUNC_I2C2_SDA = 22, /**< GPIO as I2C2 SDA */ |
| HW_GPIO_FUNC_PWM0 = 23, /**< GPIO as PWM0 */ |
| HW_GPIO_FUNC_PWM1 = 24, /**< GPIO as PWM1 */ |
| HW_GPIO_FUNC_PWM2 = 25, /**< GPIO as PWM2 */ |
| HW_GPIO_FUNC_PWM3 = 26, /**< GPIO as PWM3 */ |
| HW_GPIO_FUNC_PWM4 = 27, /**< GPIO as PWM4 */ |
| HW_GPIO_FUNC_BLE_DIAG = 28, /**< GPIO as BLE DIAG */ |
| HW_GPIO_FUNC_FTDF_DIAG = 29, /**< GPIO as FTDF DIAG */ |
| HW_GPIO_FUNC_PCM_DI = 30, /**< GPIO as PCM DI */ |
| HW_GPIO_FUNC_PCM_DO = 31, /**< GPIO as PCM DO */ |
| HW_GPIO_FUNC_PCM_FSC = 32, /**< GPIO as PCM FSC */ |
| HW_GPIO_FUNC_PCM_CLK = 33, /**< GPIO as PCM CLK */ |
| HW_GPIO_FUNC_PDM_DI = 34, /**< GPIO as PDM DI */ |
| HW_GPIO_FUNC_PDM_DO = 35, /**< GPIO as PDM DO */ |
| HW_GPIO_FUNC_PDM_CLK = 36, /**< GPIO as PDM CLK */ |
| HW_GPIO_FUNC_USB_SOF = 37, /**< GPIO as USB SOF */ |
| HW_GPIO_FUNC_ADC = 38, /**< GPIO as ADC */ |
| HW_GPIO_FUNC_USB = 38, /**< GPIO as USB */ |
| HW_GPIO_FUNC_QSPI = 38, /**< GPIO as QSPI */ |
| HW_GPIO_FUNC_XTAL32 = 38, /**< GPIO as XTAL32 */ |
| HW_GPIO_FUNC_QUADEC_XA = 39, /**< GPIO as QUADEC XA */ |
| HW_GPIO_FUNC_QUADEC_XB = 40, /**< GPIO as QUADEC XB */ |
| HW_GPIO_FUNC_QUADEC_YA = 41, /**< GPIO as QUADEC YA */ |
| HW_GPIO_FUNC_QUADEC_YB = 42, /**< GPIO as QUADEC YB */ |
| HW_GPIO_FUNC_QUADEC_ZA = 43, /**< GPIO as QUADEC ZA */ |
| HW_GPIO_FUNC_QUADEC_ZB = 44, /**< GPIO as QUADEC ZB */ |
| HW_GPIO_FUNC_IR_OUT = 45, /**< GPIO as IR OUT */ |
| HW_GPIO_FUNC_BREATH = 46, /**< GPIO as BREATH */ |
| HW_GPIO_FUNC_KB_ROW = 47, /**< GPIO as KB ROW */ |
| HW_GPIO_FUNC_COEX_EXT_ACT0 = 48, /**< GPIO as COEX EXT ACT0 */ |
| HW_GPIO_FUNC_COEX_EXT_ACT1 = 49, /**< GPIO as COEX EXT ACT1 */ |
| HW_GPIO_FUNC_COEX_SMART_ACT = 50, /**< GPIO as COEX SMART ACT */ |
| HW_GPIO_FUNC_COEX_SMART_PRI = 51, /**< GPIO as COEX SMART PRI */ |
| HW_GPIO_FUNC_CLOCK = 52, /**< GPIO as CLOCK */ |
| HW_GPIO_FUNC_ONESHOT = 53, /**< GPIO as ONESHOT */ |
| HW_GPIO_FUNC_PWM5 = 54, /**< GPIO as PWM5 */ |
| HW_GPIO_FUNC_PORT0_DCF = 55, /**< GPIO as PORT0 DCF */ |
| HW_GPIO_FUNC_PORT1_DCF = 56, /**< GPIO as PORT1 DCF */ |
| HW_GPIO_FUNC_PORT2_DCF = 57, /**< GPIO as PORT2 DCF */ |
| HW_GPIO_FUNC_PORT3_DCF = 58, /**< GPIO as PORT3 DCF */ |
| HW_GPIO_FUNC_PORT4_DCF = 59, /**< GPIO as PORT4 DCF */ |
| HW_GPIO_FUNC_RF_ANT_TRIM0 = 60, /**< GPIO as RF ANT TRIM0 */ |
| HW_GPIO_FUNC_RF_ANT_TRIM1 = 61, /**< GPIO as RF ANT TRIM1 */ |
| HW_GPIO_FUNC_RF_ANT_TRIM2 = 62 /**< GPIO as RF ANT TRIM2 */ |
| } HW_GPIO_FUNC; |
| |
| /** |
| * \brief GPIO pin configuration |
| * |
| * It's recommended to use \p HW_GPIO_PINCONFIG and \p HW_GPIO_PINCONFIG_RESERVE to set pin entries. |
| * Each configuration must be terminated using \p HW_GPIO_PINCONFIG_END macro. |
| * |
| */ |
| typedef struct { |
| uint8_t pin; /**< pin name, high-nibble is port number and low-nibble is pin */ |
| HW_GPIO_MODE mode; /**< pin mode */ |
| HW_GPIO_FUNC func; /**< pin function */ |
| bool high; /**< initial pin state, true for high and false for low */ |
| bool reserve; /*<< true if pin should be also reserved */ |
| } __attribute__((packed)) gpio_config; |
| |
| /** |
| * \brief GPIO pin configuration for \p gpio_config |
| * |
| * \p xport and \p xpin are specified as symbols from \p HW_GPIO_PORT and \p HW_GPIO_PIN enums |
| * respectively or more conveniently as plain numeric values. |
| * \p xmode and \p xfunc have the same values as defined in \p HW_GPIO_MODE and \p HW_GPIO_FUNC enums |
| * respectively, except they have prefix stripped. |
| * |
| * \param [in] xport port number |
| * \param [in] xpin pin number |
| * \param [in] xmode pin mode |
| * \param [in] xfunc pin function |
| * \param [in] xhigh true for high state, false otherwise |
| * |
| */ |
| #define HW_GPIO_PINCONFIG(xport, xpin, xmode, xfunc, xhigh) \ |
| { \ |
| .pin = (xport << 4) | (xpin & 0x0F), \ |
| .mode = HW_GPIO_MODE_ ## xmode, \ |
| .func = HW_GPIO_FUNC_ ## xfunc, \ |
| .high = xhigh, \ |
| .reserve = false, \ |
| } |
| |
| /** |
| * \brief GPIO pin configuration and reservation for \p gpio_config |
| * |
| * This macro is virtually identical to \p HW_GPIO_PINCONFIG, except it also reserves pin. |
| * |
| * \param [in] xport port number |
| * \param [in] xpin pin number |
| * \param [in] xmode pin mode |
| * \param [in] xfunc pin function |
| * \param [in] xhigh true for high state, false otherwise |
| * |
| * \sa HW_GPIO_PINCONFIG |
| * |
| */ |
| #define HW_GPIO_PINCONFIG_RESERVE(xport, xpin, xmode, xfunc, xhigh) \ |
| { \ |
| .pin = (xport << 4) | (xpin & 0x0F), \ |
| .mode = HW_GPIO_MODE_ ## xmode, \ |
| .func = HW_GPIO_FUNC_ ## xfunc, \ |
| .high = xhigh, \ |
| .reserve = true, \ |
| } |
| |
| /** |
| * \brief Macro to properly terminate array of \p gpio_config definition |
| * |
| */ |
| #define HW_GPIO_PINCONFIG_END \ |
| { \ |
| .pin = 0xFF, \ |
| } |
| |
| /** |
| * \brief GPIO configuration |
| * |
| * This is a shortcut to configure multiple GPIOs in one call. |
| * \p cfg is an array of GPIO pins configuration, it should be terminated by dummy element with |
| * \p pin member set to 0xFF (macro \p HW_GPIO_PINCONFIG can be used for this purpose). |
| * |
| * \param [in] cfg GPIO pins configuration |
| * |
| * \sa hw_gpio_configure_pin |
| * \sa hw_gpio_set_pin_function |
| * |
| */ |
| void hw_gpio_configure(const gpio_config cfg[]); |
| |
| /** |
| * \brief Reserve GPIO pin |
| * |
| * Reserve pin for exclusive usage. |
| * This macro can be used in application peripheral_setup function to detect |
| * usage of same GPIO pin by different applications. |
| * |
| * \param [in] port GPIO port number |
| * \param [in] pin GPIO pin number |
| * |
| * \return true if pin was successfully reserved and setup, false if pin was already reserved |
| * |
| */ |
| bool hw_gpio_reserve_pin(HW_GPIO_PORT port, HW_GPIO_PIN pin); |
| |
| /** |
| * \brief Reserve GPIO pin and set pin function |
| * |
| * Reserve pin and set up it function. If pin was already reserved do nothing. |
| * |
| * \param [in] port GPIO port number |
| * \param [in] pin GPIO pin number |
| * \param [in] mode GPIO access mode |
| * \param [in] function GPIO function |
| * \param [in] high in case of PID_GPIO and OUTPUT value to set on pin |
| * |
| * \return true if pin was successfully reserved and setup, false if pin was already reserved |
| * |
| */ |
| bool hw_gpio_reserve_and_configure_pin(HW_GPIO_PORT port, HW_GPIO_PIN pin, HW_GPIO_MODE mode, |
| HW_GPIO_FUNC function, bool high); |
| |
| /** |
| * \brief Unreserve GPIO pin |
| * |
| * Free reserved pin. If pin was not reserved do nothing. |
| * Configuration of pin does not change just reservation. |
| * |
| * \note If pin was reserved using RESERVE_GPIO it will also be unreserved. |
| * If RESERVE_GPIO was not enabled by compile time flags call to this function |
| * may cause unexpected result. |
| * |
| * \param [in] port GPIO port number |
| * \param [in] pin GPIO pin number |
| * |
| * \sa hw_gpio_reserve_and_configure_pin |
| * \sa hw_gpio_reserve_pin |
| * \sa RESERVE_GPIO |
| * |
| */ |
| void hw_gpio_unreserve_pin(HW_GPIO_PORT port, HW_GPIO_PIN pin); |
| |
| #if (DEBUG_GPIO_ALLOC_MONITOR_ENABLED == 1) |
| /** |
| * \brief Reserve GPIO pin |
| * |
| * Reserve pin for exclusive usage. If pin is already allocated trigger breakpoint. |
| * This macro should be used in application peripheral_setup function to detect |
| * usage of same GPIO pin by different applications. |
| * |
| * If runtime GPIO reservation is needed, use hw_gpio_reserve_pin, |
| * hw_gpio_reserve_and_configure_pin and hw_gpio_unreserve_pin instead. |
| * |
| * \param [in] name parameter ignored, used for debug only |
| * \param [in] port GPIO port number |
| * \param [in] pin GPIO pin number |
| * \param [in] func parameter ignored (for compatibility) |
| * |
| * \sa hw_gpio_reserve_pin |
| * \sa hw_gpio_reserve_and_configure_pin |
| * \sa hw_gpio_unreserve_pin instead |
| * |
| */ |
| #define RESERVE_GPIO(name, port, pin, func) \ |
| do { \ |
| if (!hw_gpio_reserve_pin((port), (pin))) { \ |
| /* If debugger stops at this line, there is configuration problem */ \ |
| /* pin is used without being reserved first */ \ |
| __BKPT(0); /* this pin has not been previously reserved! */ \ |
| } \ |
| } while (0) |
| |
| #else |
| |
| #define RESERVE_GPIO( name, port, pin, func ) \ |
| do { \ |
| \ |
| } while (0) |
| |
| #endif // (DEBUG_GPIO_ALLOC_MONITOR_ENABLED == 1) |
| |
| /** |
| * \brief Set the pin type and mode |
| * |
| * \param [in] port GPIO port |
| * \param [in] pin GPIO pin |
| * \param [in] mode GPIO pin mode |
| * \param [in] function GPIO pin usage |
| * |
| */ |
| void hw_gpio_set_pin_function(HW_GPIO_PORT port, HW_GPIO_PIN pin, HW_GPIO_MODE mode, |
| HW_GPIO_FUNC function); |
| |
| /** |
| * \brief Get the pin type and mode |
| * |
| * \param [in] port GPIO port |
| * \param [in] pin GPIO pin |
| * \param [out] mode GPIO pin mode |
| * \param [out] function GPIO pin usage |
| * |
| */ |
| void hw_gpio_get_pin_function(HW_GPIO_PORT port, HW_GPIO_PIN pin, HW_GPIO_MODE* mode, |
| HW_GPIO_FUNC* function); |
| |
| /** |
| * \brief Combined function to set the state and the type and mode of the GPIO pin |
| * |
| * \param [in] port GPIO port |
| * \param [in] pin GPIO pin |
| * \param [in] mode GPIO pin mode |
| * \param [in] function GPIO pin usage |
| * \param [in] high set to TRUE to set the pin into high else low |
| * |
| */ |
| void hw_gpio_configure_pin(HW_GPIO_PORT port, HW_GPIO_PIN pin, HW_GPIO_MODE mode, |
| HW_GPIO_FUNC function, const bool high); |
| |
| /** |
| * \brief Configure power source for pin output |
| * |
| * \param [in] port GPIO port |
| * \param [in] pin GPIO pin |
| * \param [in] power GPIO power source |
| * |
| */ |
| void hw_gpio_configure_pin_power(HW_GPIO_PORT port, HW_GPIO_PIN pin, HW_GPIO_POWER power); |
| |
| /** |
| * \brief Set a GPIO in high state |
| * |
| * The GPIO should have been previously configured as an output! |
| * |
| * \param [in] port GPIO port |
| * \param [in] pin GPIO pin |
| * |
| */ |
| void hw_gpio_set_active(HW_GPIO_PORT port, HW_GPIO_PIN pin); |
| |
| /** |
| * \brief Set a GPIO in low state |
| * |
| * The GPIO should have been previously configured as an output! |
| * |
| * \param [in] port GPIO port |
| * \param [in] pin GPIO pin |
| * |
| */ |
| void hw_gpio_set_inactive(HW_GPIO_PORT port, HW_GPIO_PIN pin); |
| |
| /** |
| * \brief Get the GPIO status |
| * |
| * The GPIO should have been previously configured as input! |
| * |
| * \param [in] port GPIO port |
| * \param [in] pin GPIO pin |
| * |
| * \return true if the pin is high, false if low |
| * |
| */ |
| bool hw_gpio_get_pin_status(HW_GPIO_PORT port, HW_GPIO_PIN pin); |
| |
| /** |
| * \brief Toggle GPIO pin state |
| * |
| * \param [in] port GPIO port |
| * \param [in] pin GPIO pin |
| * |
| */ |
| void hw_gpio_toggle(HW_GPIO_PORT port, HW_GPIO_PIN pin); |
| |
| /** |
| * \brief Find pins with specific function |
| * |
| * Function searches for pins configured for specific function. |
| * If buf is not NULL and buf_size is greater than 0 pins are stored in buf |
| * high-nibble is port number and low-nibble is pin. |
| * If number of pins found is greater then buf_size only buf_size entries are filled, though |
| * the returned number of found pins is correct. |
| * |
| * \param [in] func function to lookup |
| * \param [out] buf buffer for port-pin pairs that are configured for specific function |
| * \param [in] buf_size size of buf |
| * |
| * \return number of pins with specific function put in buf |
| * 0 - no pin is configured for this function |
| * |
| */ |
| int hw_gpio_get_pins_with_function(HW_GPIO_FUNC func, uint8_t *buf, int buf_size); |
| |
| |
| #endif /* dg_configUSE_HW_GPIO */ |
| |
| #endif /* HW_GPIO_H_ */ |
| |
| /** |
| * \} |
| * \} |
| * \} |
| */ |