diff --git a/components/driver/touch_sensor/esp32/touch_sensor.c b/components/driver/touch_sensor/esp32/touch_sensor.c index 14cef17c94a13..5e52782b44e2a 100644 --- a/components/driver/touch_sensor/esp32/touch_sensor.c +++ b/components/driver/touch_sensor/esp32/touch_sensor.c @@ -11,6 +11,7 @@ #include "esp_log.h" #include "sys/lock.h" #include "soc/rtc.h" +#include "soc/soc_caps_full.h" #include "soc/periph_defs.h" #include "freertos/FreeRTOS.h" #include "freertos/semphr.h" @@ -51,8 +52,8 @@ static SemaphoreHandle_t rtc_touch_mux = NULL; static __attribute__((unused)) const char *TOUCH_TAG = "TOUCH_SENSOR"; -#define TOUCH_CHANNEL_CHECK(channel) ESP_RETURN_ON_FALSE(channel < SOC_TOUCH_SENSOR_NUM, ESP_ERR_INVALID_ARG, TOUCH_TAG, "Touch channel error"); -#define TOUCH_CHANNEL_CHECK_ISR(channel) ESP_RETURN_ON_FALSE_ISR(channel < SOC_TOUCH_SENSOR_NUM, ESP_ERR_INVALID_ARG, TOUCH_TAG, "Touch channel error"); +#define TOUCH_CHANNEL_CHECK(channel) ESP_RETURN_ON_FALSE(channel < SOC_MODULE_ATTR(TOUCH, CHAN_NUM), ESP_ERR_INVALID_ARG, TOUCH_TAG, "Touch channel error"); +#define TOUCH_CHANNEL_CHECK_ISR(channel) ESP_RETURN_ON_FALSE_ISR(channel < SOC_MODULE_ATTR(TOUCH, CHAN_NUM), ESP_ERR_INVALID_ARG, TOUCH_TAG, "Touch channel error"); #define TOUCH_NULL_POINTER_CHECK(p, name) ESP_RETURN_ON_FALSE((p), ESP_ERR_INVALID_ARG, TOUCH_TAG, "input param '"name"' is NULL") #define TOUCH_NULL_POINTER_CHECK_ISR(p, name) ESP_RETURN_ON_FALSE_ISR((p), ESP_ERR_INVALID_ARG, TOUCH_TAG, "input param '"name"' is NULL") #define TOUCH_PARAM_CHECK_STR(s) ""s" parameter error" diff --git a/components/driver/touch_sensor/esp32s2/touch_sensor.c b/components/driver/touch_sensor/esp32s2/touch_sensor.c index 8bc29e71d0217..1e92c1b4fe3be 100644 --- a/components/driver/touch_sensor/esp32s2/touch_sensor.c +++ b/components/driver/touch_sensor/esp32s2/touch_sensor.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2016-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2016-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -10,6 +10,7 @@ #include "esp_log.h" #include "sys/lock.h" #include "soc/soc_pins.h" +#include "soc/soc_caps_full.h" #include "soc/rtc_cntl_reg.h" #include "freertos/FreeRTOS.h" #include "freertos/semphr.h" @@ -39,7 +40,7 @@ static __attribute__((unused)) const char *TOUCH_TAG = "TOUCH_SENSOR"; #define TOUCH_CHANNEL_CHECK(channel) do { \ - ESP_RETURN_ON_FALSE(channel < SOC_TOUCH_SENSOR_NUM && channel >= 0, ESP_ERR_INVALID_ARG, TOUCH_TAG, "Touch channel error"); \ + ESP_RETURN_ON_FALSE(channel < SOC_MODULE_ATTR(TOUCH, CHAN_NUM) && channel >= 0, ESP_ERR_INVALID_ARG, TOUCH_TAG, "Touch channel error"); \ ESP_RETURN_ON_FALSE(channel != SOC_TOUCH_DENOISE_CHANNEL, ESP_ERR_INVALID_ARG, TOUCH_TAG, "TOUCH0 is internal denoise channel"); \ } while (0); #define TOUCH_CH_MASK_CHECK(mask) ESP_RETURN_ON_FALSE((mask <= TOUCH_PAD_BIT_MASK_ALL), ESP_ERR_INVALID_ARG, TOUCH_TAG, "touch channel bitmask error"); @@ -101,7 +102,7 @@ esp_err_t touch_pad_isr_register(intr_handler_t fn, void *arg, touch_pad_intr_ma if (intr_mask & TOUCH_PAD_INTR_MASK_TIMEOUT) { en_msk |= RTC_CNTL_TOUCH_TIMEOUT_INT_ST_M; } -#if SOC_TOUCH_PROXIMITY_MEAS_DONE_SUPPORTED +#if TOUCH_LL_SUPPORT_PROX_DONE if (intr_mask & TOUCH_PAD_INTR_MASK_PROXI_MEAS_DONE) { en_msk |= RTC_CNTL_TOUCH_APPROACH_LOOP_DONE_INT_ST_M; } @@ -472,7 +473,7 @@ esp_err_t touch_pad_denoise_read_data(uint32_t *data) esp_err_t touch_pad_waterproof_set_config(const touch_pad_waterproof_t *waterproof) { TOUCH_NULL_POINTER_CHECK(waterproof, "waterproof"); - ESP_RETURN_ON_FALSE(waterproof->guard_ring_pad < SOC_TOUCH_SENSOR_NUM, ESP_ERR_INVALID_ARG, TOUCH_TAG, TOUCH_PARAM_CHECK_STR("pad")); + ESP_RETURN_ON_FALSE(waterproof->guard_ring_pad < SOC_MODULE_ATTR(TOUCH, CHAN_NUM), ESP_ERR_INVALID_ARG, TOUCH_TAG, TOUCH_PARAM_CHECK_STR("pad")); ESP_RETURN_ON_FALSE(waterproof->shield_driver < TOUCH_PAD_SHIELD_DRV_MAX, ESP_ERR_INVALID_ARG, TOUCH_TAG, TOUCH_PARAM_CHECK_STR("shield_driver")); TOUCH_ENTER_CRITICAL(); diff --git a/components/driver/touch_sensor/esp32s3/touch_sensor.c b/components/driver/touch_sensor/esp32s3/touch_sensor.c index 968d0e73d6fa6..071a114f4fc7d 100644 --- a/components/driver/touch_sensor/esp32s3/touch_sensor.c +++ b/components/driver/touch_sensor/esp32s3/touch_sensor.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2016-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2016-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -10,6 +10,7 @@ #include "esp_log.h" #include "sys/lock.h" #include "soc/soc_pins.h" +#include "soc/soc_caps_full.h" #include "soc/rtc_cntl_reg.h" #include "freertos/FreeRTOS.h" #include "freertos/semphr.h" @@ -39,7 +40,7 @@ static __attribute__((unused)) const char *TOUCH_TAG = "TOUCH_SENSOR"; #define TOUCH_CHANNEL_CHECK(channel) do { \ - ESP_RETURN_ON_FALSE(channel < SOC_TOUCH_SENSOR_NUM && channel >= 0, ESP_ERR_INVALID_ARG, TOUCH_TAG, "Touch channel error"); \ + ESP_RETURN_ON_FALSE(channel < SOC_MODULE_ATTR(TOUCH, CHAN_NUM) && channel >= 0, ESP_ERR_INVALID_ARG, TOUCH_TAG, "Touch channel error"); \ ESP_RETURN_ON_FALSE(channel != SOC_TOUCH_DENOISE_CHANNEL, ESP_ERR_INVALID_ARG, TOUCH_TAG, "TOUCH0 is internal denoise channel"); \ } while (0); #define TOUCH_CH_MASK_CHECK(mask) ESP_RETURN_ON_FALSE((mask <= TOUCH_PAD_BIT_MASK_ALL), ESP_ERR_INVALID_ARG, TOUCH_TAG, "touch channel bitmask error"); @@ -80,7 +81,7 @@ esp_err_t touch_pad_isr_register(intr_handler_t fn, void *arg, touch_pad_intr_ma if (intr_mask & TOUCH_PAD_INTR_MASK_TIMEOUT) { en_msk |= RTC_CNTL_TOUCH_TIMEOUT_INT_ST_M; } -#if SOC_TOUCH_PROXIMITY_MEAS_DONE_SUPPORTED +#if TOUCH_LL_SUPPORT_PROX_DONE if (intr_mask & TOUCH_PAD_INTR_MASK_PROXI_MEAS_DONE) { en_msk |= RTC_CNTL_TOUCH_APPROACH_LOOP_DONE_INT_ST_M; } @@ -445,7 +446,7 @@ esp_err_t touch_pad_denoise_read_data(uint32_t *data) esp_err_t touch_pad_waterproof_set_config(const touch_pad_waterproof_t *waterproof) { TOUCH_NULL_POINTER_CHECK(waterproof, "waterproof"); - ESP_RETURN_ON_FALSE(waterproof->guard_ring_pad < SOC_TOUCH_SENSOR_NUM, ESP_ERR_INVALID_ARG, TOUCH_TAG, TOUCH_PARAM_CHECK_STR("pad")); + ESP_RETURN_ON_FALSE(waterproof->guard_ring_pad < SOC_MODULE_ATTR(TOUCH, CHAN_NUM), ESP_ERR_INVALID_ARG, TOUCH_TAG, TOUCH_PARAM_CHECK_STR("pad")); ESP_RETURN_ON_FALSE(waterproof->shield_driver < TOUCH_PAD_SHIELD_DRV_MAX, ESP_ERR_INVALID_ARG, TOUCH_TAG, TOUCH_PARAM_CHECK_STR("shield_driver")); TOUCH_ENTER_CRITICAL(); diff --git a/components/driver/touch_sensor/touch_sensor_common.c b/components/driver/touch_sensor/touch_sensor_common.c index 97d44aaa8981d..7ad4a81c3d426 100644 --- a/components/driver/touch_sensor/touch_sensor_common.c +++ b/components/driver/touch_sensor/touch_sensor_common.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2016-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2016-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -11,6 +11,7 @@ #include "esp_log.h" #include "sys/lock.h" #include "soc/soc_pins.h" +#include "soc/soc_caps_full.h" #include "freertos/FreeRTOS.h" #include "freertos/semphr.h" #include "freertos/timers.h" @@ -29,18 +30,18 @@ static const char *TOUCH_TAG = "TOUCH_SENSOR"; }) #ifdef CONFIG_IDF_TARGET_ESP32 #define TOUCH_CHANNEL_CHECK(channel) do { \ - TOUCH_CHECK(channel < SOC_TOUCH_SENSOR_NUM && channel >= 0, "Touch channel error", ESP_ERR_INVALID_ARG); \ + TOUCH_CHECK(channel < SOC_MODULE_ATTR(TOUCH, CHAN_NUM) && channel >= 0, "Touch channel error", ESP_ERR_INVALID_ARG); \ } while (0); #else // !CONFIG_IDF_TARGET_ESP32 #define TOUCH_CHANNEL_CHECK(channel) do { \ - TOUCH_CHECK(channel < SOC_TOUCH_SENSOR_NUM && channel >= 0, "Touch channel error", ESP_ERR_INVALID_ARG); \ + TOUCH_CHECK(channel < SOC_MODULE_ATTR(TOUCH, CHAN_NUM) && channel >= 0, "Touch channel error", ESP_ERR_INVALID_ARG); \ TOUCH_CHECK(channel != SOC_TOUCH_DENOISE_CHANNEL, "TOUCH0 is internal denoise channel", ESP_ERR_INVALID_ARG); \ } while (0); #endif // CONFIG_IDF_TARGET_ESP32 #define TOUCH_GET_IO_NUM(channel) (touch_sensor_channel_io_map[channel]) -_Static_assert(TOUCH_PAD_MAX == SOC_TOUCH_SENSOR_NUM, "Touch sensor channel number not equal to chip capabilities"); +_Static_assert(TOUCH_PAD_MAX == SOC_MODULE_ATTR(TOUCH, CHAN_NUM), "Touch sensor channel number not equal to chip capabilities"); extern portMUX_TYPE rtc_spinlock; //TODO: Will be placed in the appropriate position after the rtc module is finished. #define TOUCH_ENTER_CRITICAL() portENTER_CRITICAL(&rtc_spinlock) @@ -87,7 +88,7 @@ esp_err_t touch_pad_get_voltage(touch_high_volt_t *refh, touch_low_volt_t *refl, esp_err_t touch_pad_set_cnt_mode(touch_pad_t touch_num, touch_cnt_slope_t slope, touch_tie_opt_t opt) { - TOUCH_CHECK(touch_num < SOC_TOUCH_SENSOR_NUM, "Touch channel error", ESP_ERR_INVALID_ARG); + TOUCH_CHECK(touch_num < SOC_MODULE_ATTR(TOUCH, CHAN_NUM), "Touch channel error", ESP_ERR_INVALID_ARG); TOUCH_CHECK(slope < TOUCH_PAD_SLOPE_MAX, "touch slope error", ESP_ERR_INVALID_ARG); TOUCH_CHECK(opt < TOUCH_PAD_TIE_OPT_MAX, "touch opt error", ESP_ERR_INVALID_ARG); @@ -104,7 +105,7 @@ esp_err_t touch_pad_set_cnt_mode(touch_pad_t touch_num, touch_cnt_slope_t slope, esp_err_t touch_pad_get_cnt_mode(touch_pad_t touch_num, touch_cnt_slope_t *slope, touch_tie_opt_t *opt) { - TOUCH_CHECK(touch_num < SOC_TOUCH_SENSOR_NUM, "Touch channel error", ESP_ERR_INVALID_ARG); + TOUCH_CHECK(touch_num < SOC_MODULE_ATTR(TOUCH, CHAN_NUM), "Touch channel error", ESP_ERR_INVALID_ARG); touch_hal_meas_mode_t meas = {0}; TOUCH_ENTER_CRITICAL(); diff --git a/components/esp_driver_touch_sens/common/touch_sens_private.h b/components/esp_driver_touch_sens/common/touch_sens_private.h index aaa0328ef13d7..27506316676b6 100644 --- a/components/esp_driver_touch_sens/common/touch_sens_private.h +++ b/components/esp_driver_touch_sens/common/touch_sens_private.h @@ -84,7 +84,7 @@ extern portMUX_TYPE g_touch_spinlock; * */ struct touch_sensor_s { - touch_channel_handle_t ch[SOC_TOUCH_SENSOR_NUM]; /*!< Touch sensor channel handles, will be NULL if the channel is not registered */ + touch_channel_handle_t ch[SOC_MODULE_ATTR(TOUCH, CHAN_NUM)]; /*!< Touch sensor channel handles, will be NULL if the channel is not registered */ uint32_t chan_mask; /*!< Enabled channel mask, corresponding bit will be set if the channel is registered */ uint32_t src_freq_hz; /*!< Source clock frequency */ uint32_t interval_freq_hz; /*!< Frequency of the interval clock */ diff --git a/components/esp_driver_touch_sens/hw_ver2/touch_version_specific.c b/components/esp_driver_touch_sens/hw_ver2/touch_version_specific.c index 4882c3b8da7b0..808dc8b6c8170 100644 --- a/components/esp_driver_touch_sens/hw_ver2/touch_version_specific.c +++ b/components/esp_driver_touch_sens/hw_ver2/touch_version_specific.c @@ -68,7 +68,7 @@ void IRAM_ATTR touch_priv_default_intr_handler(void *arg) return; } /* It actually won't be out of range in the real environment, but limit the range to pass the coverity check */ - uint32_t curr_chan_offset = (curr_chan >= SOC_TOUCH_SENSOR_NUM ? SOC_TOUCH_SENSOR_NUM - 1 : curr_chan) - TOUCH_MIN_CHAN_ID; + uint32_t curr_chan_offset = (curr_chan >= SOC_MODULE_ATTR(TOUCH, CHAN_NUM) ? SOC_MODULE_ATTR(TOUCH, CHAN_NUM) - 1 : curr_chan) - TOUCH_MIN_CHAN_ID; data.chan = g_touch->ch[curr_chan_offset]; /* If the channel is not registered, return directly */ if (!data.chan) { @@ -77,7 +77,7 @@ void IRAM_ATTR touch_priv_default_intr_handler(void *arg) data.chan_id = data.chan->id; if (status & TOUCH_LL_INTR_MASK_DONE) { -#if !SOC_TOUCH_PROXIMITY_MEAS_DONE_SUPPORTED +#if !TOUCH_LL_SUPPORT_PROX_DONE /* For the target like ESP32-S2 that don't support proximity done interrupt, Simulate the interrupt by software by judge the scan times. */ if (data.chan->prox_id > 0 && diff --git a/components/hal/esp32/include/hal/touch_sensor_ll.h b/components/hal/esp32/include/hal/touch_sensor_ll.h index 357399fe1ad83..e8dc48be55281 100644 --- a/components/hal/esp32/include/hal/touch_sensor_ll.h +++ b/components/hal/esp32/include/hal/touch_sensor_ll.h @@ -23,6 +23,7 @@ #include "soc/rtc_io_struct.h" #include "soc/rtc_cntl_struct.h" #include "soc/soc_caps.h" +#include "soc/soc_caps_full.h" #include "hal/touch_sens_types.h" #ifdef __cplusplus @@ -33,7 +34,7 @@ extern "C" { #define TOUCH_LL_BIT_SWAP(data, n, m) (((data >> n) & 0x1) == ((data >> m) & 0x1) ? (data) : ((data) ^ ((0x1 <> 1], l_val); diff --git a/components/hal/esp32p4/include/hal/touch_sensor_ll.h b/components/hal/esp32p4/include/hal/touch_sensor_ll.h index 02e9e670ad22d..8ee210efb9985 100644 --- a/components/hal/esp32p4/include/hal/touch_sensor_ll.h +++ b/components/hal/esp32p4/include/hal/touch_sensor_ll.h @@ -24,6 +24,7 @@ #include "soc/touch_struct.h" #include "soc/pmu_struct.h" #include "soc/soc_caps.h" +#include "soc/soc_caps_full.h" #include "hal/touch_sens_types.h" #include "hal/config.h" @@ -46,7 +47,7 @@ extern "C" { #define TOUCH_LL_INTR_MASK_PROX_DONE BIT(5) #define TOUCH_LL_INTR_MASK_ALL (0x3F) -#define TOUCH_LL_FULL_CHANNEL_MASK ((uint16_t)((1U << (SOC_TOUCH_SENSOR_NUM)) - 1) << SOC_TOUCH_MIN_CHAN_ID) +#define TOUCH_LL_FULL_CHANNEL_MASK ((uint16_t)((1U << (SOC_MODULE_ATTR(TOUCH, CHAN_NUM))) - 1) << SOC_TOUCH_MIN_CHAN_ID) #define TOUCH_LL_NULL_CHANNEL (15) // Null Channel id. Used for disabling some functions like sleep/proximity/waterproof #define TOUCH_LL_PAD_MEASURE_WAIT_MAX (0x7FFF) // The timer frequency is 8Mhz, the max value is 0xff @@ -55,6 +56,8 @@ extern "C" { #define TOUCH_LL_TIMEOUT_MAX (0xFFFF) // Max timeout value #define TOUCH_LL_SLP_MEASURE_WAIT_MAX (0x1FF) // Max wait ticks to wait PMU entering HP SLEEP status during the sleep. +#define TOUCH_LL_SUPPORT_PROX_DONE (1) + /** * Enable/disable clock gate of touch sensor. * diff --git a/components/hal/esp32s2/include/hal/touch_sensor_ll.h b/components/hal/esp32s2/include/hal/touch_sensor_ll.h index 13b253d4e4dc1..09e4bf0b395ac 100644 --- a/components/hal/esp32s2/include/hal/touch_sensor_ll.h +++ b/components/hal/esp32s2/include/hal/touch_sensor_ll.h @@ -21,6 +21,7 @@ #include "hal/assert.h" #include "soc/touch_sensor_periph.h" #include "soc/soc_caps.h" +#include "soc/soc_caps_full.h" #include "soc/sens_struct.h" #include "soc/rtc_cntl_struct.h" #include "soc/rtc_io_struct.h" @@ -53,7 +54,7 @@ extern "C" { TOUCH_LL_INTR_MASK_INACTIVE | \ TOUCH_LL_INTR_MASK_TIMEOUT) -#define TOUCH_LL_FULL_CHANNEL_MASK ((uint16_t)((1U << SOC_TOUCH_SENSOR_NUM) - 1)) +#define TOUCH_LL_FULL_CHANNEL_MASK ((uint16_t)((1U << SOC_MODULE_ATTR(TOUCH, CHAN_NUM)) - 1)) #define TOUCH_LL_NULL_CHANNEL (0) // Null Channel id. Used for disabling some functions like sleep/proximity/waterproof #define TOUCH_LL_PAD_MEASURE_WAIT_MAX (0xFF) // The timer frequency is 8Mhz, the max value is 0xff diff --git a/components/hal/esp32s3/include/hal/touch_sensor_ll.h b/components/hal/esp32s3/include/hal/touch_sensor_ll.h index 28b91632c4247..5926d39b503da 100644 --- a/components/hal/esp32s3/include/hal/touch_sensor_ll.h +++ b/components/hal/esp32s3/include/hal/touch_sensor_ll.h @@ -24,6 +24,7 @@ #include "soc/rtc_io_struct.h" #include "soc/sens_struct.h" #include "soc/soc_caps.h" +#include "soc/soc_caps_full.h" #include "hal/touch_sens_types.h" #ifdef __cplusplus @@ -49,12 +50,13 @@ extern "C" { TOUCH_LL_INTR_MASK_TIMEOUT | \ TOUCH_LL_INTR_MASK_PROX_DONE) -#define TOUCH_LL_FULL_CHANNEL_MASK ((uint16_t)((1U << SOC_TOUCH_SENSOR_NUM) - 1)) +#define TOUCH_LL_FULL_CHANNEL_MASK ((uint16_t)((1U << SOC_MODULE_ATTR(TOUCH, CHAN_NUM)) - 1)) #define TOUCH_LL_NULL_CHANNEL (0) // Null Channel id. Used for disabling some functions like sleep/proximity/waterproof #define TOUCH_LL_PAD_MEASURE_WAIT_MAX (0xFF) // The timer frequency is 8Mhz, the max value is 0xff #define TOUCH_LL_ACTIVE_THRESH_MAX (0x3FFFFF) // Max channel active threshold #define TOUCH_LL_TIMEOUT_MAX (0x3FFFFF) // Max timeout value +#define TOUCH_LL_SUPPORT_PROX_DONE (1) /** * Enable/disable clock gate of touch sensor. diff --git a/components/hal/include/hal/touch_sensor_legacy_types.h b/components/hal/include/hal/touch_sensor_legacy_types.h index d7e694ac3ca4b..603db33e7563b 100644 --- a/components/hal/include/hal/touch_sensor_legacy_types.h +++ b/components/hal/include/hal/touch_sensor_legacy_types.h @@ -28,7 +28,7 @@ typedef enum { TOUCH_PAD_NUM7, /*!< Touch pad channel 7 is GPIO27(ESP32) / GPIO7(ESP32-S2) */ TOUCH_PAD_NUM8, /*!< Touch pad channel 8 is GPIO33(ESP32) / GPIO8(ESP32-S2) */ TOUCH_PAD_NUM9, /*!< Touch pad channel 9 is GPIO32(ESP32) / GPIO9(ESP32-S2) */ -#if SOC_TOUCH_SENSOR_NUM > 10 +#if !SOC_IS(ESP32) TOUCH_PAD_NUM10, /*!< Touch channel 10 is GPIO10(ESP32-S2) */ TOUCH_PAD_NUM11, /*!< Touch channel 11 is GPIO11(ESP32-S2) */ TOUCH_PAD_NUM12, /*!< Touch channel 12 is GPIO12(ESP32-S2) */ @@ -111,7 +111,16 @@ typedef enum { } touch_trigger_src_t; /********************************/ -#define TOUCH_PAD_BIT_MASK_ALL ((1< 1 && !SOC_IS(ESP32S2) TOUCH_PAD_INTR_MASK_PROXI_MEAS_DONE = BIT(5), /*!