diff --git a/core/SConscript.bootloader_emu b/core/SConscript.bootloader_emu index 67c8588caa..0723bf05b1 100644 --- a/core/SConscript.bootloader_emu +++ b/core/SConscript.bootloader_emu @@ -112,7 +112,6 @@ SOURCE_MOD += [ 'embed/lib/image.c', 'embed/lib/mini_printf.c', 'embed/lib/terminal.c', - 'embed/lib/touch.c', 'embed/lib/unit_variant.c', 'vendor/micropython/lib/uzlib/adler32.c', 'vendor/micropython/lib/uzlib/crc32.c', @@ -150,11 +149,10 @@ SOURCE_TREZORHAL = [ 'embed/trezorhal/unix/fault_handlers.c', 'embed/trezorhal/unix/flash.c', 'embed/trezorhal/unix/flash_otp.c', - 'embed/trezorhal/unix/touch/touch.c', - 'embed/trezorhal/unix/rng.c', - 'embed/trezorhal/unix/usb.c', 'embed/trezorhal/unix/random_delays.c', + 'embed/trezorhal/unix/rng.c', 'embed/trezorhal/unix/secret.c', + 'embed/trezorhal/unix/usb.c', ] if NEW_RENDERING: diff --git a/core/SConscript.unix b/core/SConscript.unix index 3a9fe3f88b..000ce6e844 100644 --- a/core/SConscript.unix +++ b/core/SConscript.unix @@ -419,20 +419,19 @@ SOURCE_MICROPYTHON = [ SOURCE_UNIX = [ 'embed/trezorhal/unix/boot_args.c', 'embed/trezorhal/unix/common.c', - 'embed/trezorhal/unix/flash.c', 'embed/trezorhal/unix/flash_otp.c', + 'embed/trezorhal/unix/flash.c', 'embed/trezorhal/unix/random_delays.c', 'embed/trezorhal/unix/rng.c', 'embed/trezorhal/unix/usb.c', - 'embed/trezorhal/unix/touch/touch.c', - 'embed/unix/main.c', 'embed/unix/main_main.c', + 'embed/unix/main.c', 'embed/unix/profile.c', - 'vendor/micropython/shared/runtime/gchelper_generic.c', 'vendor/micropython/ports/unix/alloc.c', 'vendor/micropython/ports/unix/gccollect.c', 'vendor/micropython/ports/unix/input.c', 'vendor/micropython/ports/unix/unix_mphal.c', + 'vendor/micropython/shared/runtime/gchelper_generic.c', ] if NEW_RENDERING: diff --git a/core/embed/bootloader/bootui.c b/core/embed/bootloader/bootui.c index 0048ea2129..15d74d4c15 100644 --- a/core/embed/bootloader/bootui.c +++ b/core/embed/bootloader/bootui.c @@ -154,16 +154,16 @@ static void ui_screen_boot_wait(int wait_seconds) { void ui_click(void) { // flush touch events if any - while (touch_read()) { + while (touch_get_event()) { } // wait for TOUCH_START - while ((touch_read() & TOUCH_START) == 0) { + while ((touch_get_event() & TOUCH_START) == 0) { } // wait for TOUCH_END - while ((touch_read() & TOUCH_END) == 0) { + while ((touch_get_event() & TOUCH_END) == 0) { } // flush touch events if any - while (touch_read()) { + while (touch_get_event()) { } } diff --git a/core/embed/bootloader/main.c b/core/embed/bootloader/main.c index d54e0ba838..80911534c1 100644 --- a/core/embed/bootloader/main.c +++ b/core/embed/bootloader/main.c @@ -398,7 +398,6 @@ int bootloader_main(void) { unit_variant_init(); #ifdef USE_TOUCH - touch_power_on(); #ifdef TREZOR_MODEL_T3T1 // on T3T1, tester needs to run without touch, so making an exception // until unit variant is written in OTP @@ -514,17 +513,22 @@ int bootloader_main(void) { uint32_t touched = 0; #ifdef USE_TOUCH if (firmware_present == sectrue && stay_in_bootloader != sectrue) { - touch_wait_until_ready(); + // Wait until the touch controller is ready + // (on hardware this may take a while) + while (touch_ready() != sectrue) { + hal_delay(1); + } +#ifdef TREZOR_EMULATOR + hal_delay(500); +#endif + // Give the touch controller time to report events + // if someone touches the screen for (int i = 0; i < 10; i++) { - touched = touch_is_detected() | touch_read(); - if (touched) { + if (touch_activity() == sectrue) { + touched = 1; break; } -#ifdef TREZOR_EMULATOR - hal_delay(25); -#else - hal_delay_us(1000); -#endif + hal_delay(5); } } #elif defined USE_BUTTON diff --git a/core/embed/bootloader_ci/main.c b/core/embed/bootloader_ci/main.c index 33c6b70038..8f5436400c 100644 --- a/core/embed/bootloader_ci/main.c +++ b/core/embed/bootloader_ci/main.c @@ -206,7 +206,6 @@ int main(void) { random_delays_init(); #ifdef USE_TOUCH touch_init(); - touch_power_on(); #endif #ifdef USE_HASH_PROCESSOR diff --git a/core/embed/extmod/modtrezorio/modtrezorio-poll.h b/core/embed/extmod/modtrezorio/modtrezorio-poll.h index 77ffaa547a..a5f1bc89c2 100644 --- a/core/embed/extmod/modtrezorio/modtrezorio-poll.h +++ b/core/embed/extmod/modtrezorio/modtrezorio-poll.h @@ -85,9 +85,7 @@ STATIC mp_obj_t mod_trezorio_poll(mp_obj_t ifaces, mp_obj_t list_ref, } #if defined USE_TOUCH else if (iface == TOUCH_IFACE) { - - const uint32_t evt = touch_read(); - + const uint32_t evt = touch_get_event(); if (evt) { // ignore TOUCH_MOVE events if they are too frequent if ((evt & TOUCH_MOVE) == 0 || diff --git a/core/embed/lib/touch.c b/core/embed/lib/touch.c deleted file mode 100644 index 197d209f67..0000000000 --- a/core/embed/lib/touch.c +++ /dev/null @@ -1,22 +0,0 @@ - -#ifdef USE_TOUCH -#include "touch.h" - -uint32_t touch_click(void) { - uint32_t r = 0; - // flush touch events if any - while (touch_read()) { - } - // wait for TOUCH_START - while ((touch_read() & TOUCH_START) == 0) { - } - // wait for TOUCH_END - while (((r = touch_read()) & TOUCH_END) == 0) { - } - // flush touch events if any - while (touch_read()) { - } - // return last touch coordinate - return r; -} -#endif diff --git a/core/embed/prodtest/.changelog.d/3900.fixed b/core/embed/prodtest/.changelog.d/3900.fixed new file mode 100644 index 0000000000..07815ecf41 --- /dev/null +++ b/core/embed/prodtest/.changelog.d/3900.fixed @@ -0,0 +1 @@ +Fix TOUCH VERSION command diff --git a/core/embed/prodtest/main.c b/core/embed/prodtest/main.c index ced3767a46..af6c6794d0 100644 --- a/core/embed/prodtest/main.c +++ b/core/embed/prodtest/main.c @@ -312,15 +312,15 @@ static secbool touch_click_timeout(uint32_t *touch, uint32_t timeout_ms) { uint32_t deadline = HAL_GetTick() + timeout_ms; uint32_t r = 0; - while (touch_read()) + while (touch_get_event()) ; - while ((touch_read() & TOUCH_START) == 0) { + while ((touch_get_event() & TOUCH_START) == 0) { if (HAL_GetTick() > deadline) return secfalse; } - while (((r = touch_read()) & TOUCH_END) == 0) { + while (((r = touch_get_event()) & TOUCH_END) == 0) { if (HAL_GetTick() > deadline) return secfalse; } - while (touch_read()) + while (touch_get_event()) ; *touch = r; @@ -348,7 +348,7 @@ static void test_touch(const char *args) { } display_refresh(); - touch_power_on(); + touch_init(); uint32_t evt = 0; if (touch_click_timeout(&evt, timeout * 1000)) { @@ -361,20 +361,20 @@ static void test_touch(const char *args) { display_clear(); display_refresh(); - touch_power_off(); + touch_deinit(); } static void test_sensitivity(const char *args) { int v = atoi(args); - touch_power_on(); - touch_sensitivity(v & 0xFF); + touch_init(); + touch_set_sensitivity(v & 0xFF); display_clear(); display_refresh(); for (;;) { - uint32_t evt = touch_read(); + uint32_t evt = touch_get_event(); if (evt & TOUCH_START || evt & TOUCH_MOVE) { int x = touch_unpack_x(evt); int y = touch_unpack_y(evt); @@ -387,14 +387,14 @@ static void test_sensitivity(const char *args) { } } - touch_power_off(); + touch_deinit(); } static void touch_version(void) { - touch_power_on(); + touch_init(); uint8_t version = touch_get_version(); vcp_println("OK %d", version); - touch_power_off(); + touch_deinit(); } #endif diff --git a/core/embed/rust/build.rs b/core/embed/rust/build.rs index e0de9d1681..9d664cd788 100644 --- a/core/embed/rust/build.rs +++ b/core/embed/rust/build.rs @@ -442,7 +442,7 @@ fn generate_trezorhal_bindings() { //usb .allowlist_function("usb_configured") // touch - .allowlist_function("touch_read") + .allowlist_function("touch_get_event") // button .allowlist_function("button_read") // haptic diff --git a/core/embed/rust/src/trezorhal/io.rs b/core/embed/rust/src/trezorhal/io.rs index 7f9af446fb..6ece57df9f 100644 --- a/core/embed/rust/src/trezorhal/io.rs +++ b/core/embed/rust/src/trezorhal/io.rs @@ -1,8 +1,8 @@ use super::ffi; #[cfg(feature = "touch")] -pub fn io_touch_read() -> u32 { - unsafe { ffi::touch_read() } +pub fn io_touch_get_event() -> u32 { + unsafe { ffi::touch_get_event() } } #[cfg(feature = "button")] diff --git a/core/embed/rust/src/ui/layout/simplified.rs b/core/embed/rust/src/ui/layout/simplified.rs index 176de577ad..a966e1083a 100644 --- a/core/embed/rust/src/ui/layout/simplified.rs +++ b/core/embed/rust/src/ui/layout/simplified.rs @@ -1,7 +1,7 @@ #[cfg(feature = "button")] use crate::trezorhal::io::io_button_read; #[cfg(feature = "touch")] -use crate::trezorhal::io::io_touch_read; +use crate::trezorhal::io::io_touch_get_event; #[cfg(feature = "button")] use crate::ui::event::ButtonEvent; #[cfg(feature = "touch")] @@ -56,7 +56,7 @@ fn button_eval() -> Option { #[cfg(feature = "touch")] fn touch_eval() -> Option { - let event = io_touch_read(); + let event = io_touch_get_event(); if event == 0 { return None; } diff --git a/core/embed/trezorhal/stm32f4/touch/ft6x36.c b/core/embed/trezorhal/stm32f4/touch/ft6x36.c index eb2b4cf30a..eb1d18f0a5 100644 --- a/core/embed/trezorhal/stm32f4/touch/ft6x36.c +++ b/core/embed/trezorhal/stm32f4/touch/ft6x36.c @@ -20,34 +20,105 @@ #include STM32_HAL_H #include TREZOR_BOARD +#include #include #include "common.h" -#include "secbool.h" #include "ft6x36.h" #include "i2c.h" #include "touch.h" -#define TOUCH_ADDRESS \ - (0x38U << 1) // the HAL requires the 7-bit address to be shifted by one bit -#define TOUCH_PACKET_SIZE 7U -#define EVENT_PRESS_DOWN 0x00U -#define EVENT_CONTACT 0x80U -#define EVENT_LIFT_UP 0x40U -#define EVENT_NO_EVENT 0xC0U -#define GESTURE_NO_GESTURE 0x00U -#define X_POS_MSB (touch_data[3] & 0x0FU) -#define X_POS_LSB (touch_data[4]) -#define Y_POS_MSB (touch_data[5] & 0x0FU) -#define Y_POS_LSB (touch_data[6]) +typedef struct { + // Set if the driver is initialized + secbool initialized; + // Set if the driver is ready to report touches. + // FT6X36 needs about 300ms after power-up to stabilize. + secbool ready; + // Captured tick counter when `touch_init()` was called + uint32_t init_ticks; + // Time (in ticks) when touch_get_event() was called last time + uint32_t poll_ticks; + // Set if the touch controller is currently touched + // (respectively, the we detected a touch event) + bool pressed; + // Previously reported x-coordinate + uint16_t last_x; + // Previously reported y-coordinate + uint16_t last_y; -#define EVENT_OLD_TIMEOUT_MS 50 -#define EVENT_MISSING_TIMEOUT_MS 50 +} touch_driver_t; -static uint32_t touch_init_ticks = 0; +// Touch driver instance +static touch_driver_t g_touch_driver = { + .initialized = secfalse, +}; -static void touch_default_pin_state(void) { +// Reads a subsequent registers from the FT6X36. +// +// Returns: `sectrue` if the register was read +// successfully, `secfalse` otherwise. +// +// If the I2C bus is busy, the function will cycle the +// bus and retry the operation. +static secbool ft6x36_read_regs(uint8_t reg, uint8_t* value, size_t count) { + uint16_t i2c_bus = TOUCH_I2C_INSTANCE; + uint8_t i2c_addr = FT6X36_I2C_ADDR; + uint8_t txdata[] = {reg}; + uint8_t retries = 3; + + do { + int result = i2c_transmit(i2c_bus, i2c_addr, txdata, sizeof(txdata), 10); + if (HAL_OK == result) { + result = i2c_receive(i2c_bus, i2c_addr, value, count, 10); + } + + if (HAL_OK == result) { + // success + return sectrue; + } else if (HAL_BUSY == result && retries > 0) { + // I2C bus is busy, cycle it and try again + i2c_cycle(i2c_bus); + retries--; + } else { + // Aother error or retries exhausted + return secfalse; + } + } while (1); +} + +// Writes a register to the FT6X36. +// +// Returns: `sectrue` if the register was written +// successfully, `secfalse` otherwise. +// +// If the I2C bus is busy, the function will cycle the +// bus and retry the operation. +static secbool ft6x36_write_reg(uint8_t reg, uint8_t value) { + uint16_t i2c_bus = TOUCH_I2C_INSTANCE; + uint8_t i2c_addr = FT6X36_I2C_ADDR; + uint8_t txdata[] = {reg, value}; + uint8_t retries = 3; + + do { + int result = i2c_transmit(i2c_bus, i2c_addr, txdata, sizeof(txdata), 10); + if (HAL_OK == result) { + // success + return sectrue; + } else if (HAL_BUSY == result && retries > 0) { + // I2C bus is busy, cycle it and try again + i2c_cycle(i2c_bus); + retries--; + } else { + // Another error or retries exhausted + return secfalse; + } + } while (1); +} + +// Powers down the touch controller and puts all +// the pins in the proper state to save power. +static void ft6x36_power_down(void) { GPIO_PinState state = HAL_GPIO_ReadPin(TOUCH_ON_PORT, TOUCH_ON_PIN); // set power off and other pins as per section 3.5 of FT6236 datasheet @@ -74,227 +145,291 @@ static void touch_default_pin_state(void) { GPIO_InitStructure.Pin = TOUCH_ON_PIN; HAL_GPIO_Init(TOUCH_ON_PORT, &GPIO_InitStructure); - // in-case power was on, or CTPM was active make sure to wait long enough - // for these changes to take effect. a reset needs to be low for - // a minimum of 5ms. also wait for power circuitry to stabilize (if it - // changed). - HAL_Delay(10); - if (state == GPIO_PIN_SET) { - HAL_Delay(90); // add 90 ms for circuitry to stabilize (being conservative) + // 90 ms for circuitry to stabilize (being conservative) + hal_delay(90); } } -static void touch_active_pin_state(void) { - HAL_GPIO_WritePin(TOUCH_ON_PORT, TOUCH_ON_PIN, GPIO_PIN_RESET); // CTP_ON - HAL_Delay(10); // we need to wait until the circuit fully kicks-in +// Powers up the touch controller and do proper reset sequence +// +// `ft6x36_power_down()` must be called before calling this first time function +// to properly initialize the GPIO pins. +static void ft6x36_power_up(void) { + // Ensure the touch controller is in reset state + HAL_GPIO_WritePin(TOUCH_RST_PORT, TOUCH_RST_PIN, GPIO_PIN_RESET); + // Power up the touch controller + HAL_GPIO_WritePin(TOUCH_ON_PORT, TOUCH_ON_PIN, GPIO_PIN_RESET); + // Wait until the circuit fully kicks-in + // (5ms is the minimum time required for the reset signal to be effective) + hal_delay(10); + + // Enable intterrupt input GPIO_InitTypeDef GPIO_InitStructure = {0}; - - // capacitive touch panel module (CTPM) interrupt (INT) input GPIO_InitStructure.Mode = GPIO_MODE_IT_RISING; GPIO_InitStructure.Pull = GPIO_PULLUP; GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_LOW; GPIO_InitStructure.Pin = TOUCH_INT_PIN; HAL_GPIO_Init(TOUCH_INT_PORT, &GPIO_InitStructure); + + // Release touch controller from reset + HAL_GPIO_WritePin(TOUCH_RST_PORT, TOUCH_RST_PIN, GPIO_PIN_SET); + + // Wait for the touch controller to boot up + hal_delay(5); + + // Clear the flag indicating rising edge on INT_PIN __HAL_GPIO_EXTI_CLEAR_FLAG(TOUCH_INT_PIN); - - HAL_GPIO_WritePin(TOUCH_RST_PORT, TOUCH_RST_PIN, - GPIO_PIN_SET); // release CTPM reset - - touch_init_ticks = hal_ticks_ms(); - - HAL_Delay(5); } -secbool touch_set_mode(void) { - // set register 0xA4 G_MODE to interrupt trigger mode (0x01). basically, CTPM - // generates a pulse when new data is available - uint8_t touch_panel_config[] = {0xA4, 0x01}; - for (int i = 0; i < 3; i++) { - if (HAL_OK == i2c_transmit(TOUCH_I2C_INSTANCE, TOUCH_ADDRESS, - touch_panel_config, sizeof(touch_panel_config), - 10)) { - return sectrue; - } - i2c_cycle(TOUCH_I2C_INSTANCE); - } - - return secfalse; -} - -void touch_power_on(void) { - touch_default_pin_state(); - - // turn on CTP circuitry - touch_active_pin_state(); -} - -void touch_power_off(void) { - // turn off CTP circuitry - HAL_Delay(50); - touch_default_pin_state(); -} - -secbool touch_init(void) { - GPIO_InitTypeDef GPIO_InitStructure = {0}; - - // PC4 capacitive touch panel module (CTPM) interrupt (INT) input - GPIO_InitStructure.Mode = GPIO_MODE_IT_RISING; - GPIO_InitStructure.Pull = GPIO_PULLUP; - GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_LOW; - GPIO_InitStructure.Pin = TOUCH_INT_PIN; - HAL_GPIO_Init(TOUCH_INT_PORT, &GPIO_InitStructure); - __HAL_GPIO_EXTI_CLEAR_FLAG(TOUCH_INT_PIN); - - if (sectrue != touch_set_mode()) { - return secfalse; - } - return touch_sensitivity(TOUCH_SENSITIVITY); -} - -void touch_wait_until_ready(void) { - // wait for the touch controller to be ready - while (hal_ticks_ms() - touch_init_ticks < 310) { - HAL_Delay(1); - } -} - -secbool touch_sensitivity(uint8_t value) { - // set panel threshold (TH_GROUP) - default value is 0x12 - uint8_t touch_panel_threshold[] = {0x80, value}; - for (int i = 0; i < 3; i++) { - if (HAL_OK == i2c_transmit(TOUCH_I2C_INSTANCE, TOUCH_ADDRESS, - touch_panel_threshold, - sizeof(touch_panel_threshold), 10)) { - return sectrue; - } - i2c_cycle(TOUCH_I2C_INSTANCE); - } - - return secfalse; -} - -uint32_t touch_is_detected(void) { - // check the interrupt line coming in from the CTPM. - // the line make a short pulse, which sets an interrupt flag when new data is - // available. - // Reference section 1.2 of "Application Note for FT6x06 CTPM". we - // configure the touch controller to use "interrupt trigger mode". - +// Checks if the touch controller has an interrupt pending +// which indicates that new data is available. +// +// The function clears the interrupt flag if it was set so the +// next call returns `false` if no new impulses were detected. +static bool ft6x36_test_and_clear_interrupt(void) { uint32_t event = __HAL_GPIO_EXTI_GET_FLAG(TOUCH_INT_PIN); if (event != 0) { __HAL_GPIO_EXTI_CLEAR_FLAG(TOUCH_INT_PIN); } - return event; + return event != 0; } -uint32_t check_timeout(uint32_t prev, uint32_t timeout) { - uint32_t current = hal_ticks_ms(); - uint32_t diff = current - prev; +// Configures the touch controller to the funtional state. +static secbool ft6x36_configure(void) { + const static uint8_t config[] = { + // Set touch controller to the interrupt trigger mode. + // Basically, CTPM generates a pulse when new data is available. + FT6X36_REG_G_MODE, + 0x01, + FT6X36_REG_TH_GROUP, + TOUCH_SENSITIVITY, + }; - if (diff >= timeout) { - return 1; - } + _Static_assert(sizeof(config) % 2 == 0); - return 0; -} + for (int i = 0; i < sizeof(config); i += 2) { + uint8_t reg = config[i]; + uint8_t value = config[i + 1]; -uint32_t touch_read(void) { - static uint8_t touch_data[TOUCH_PACKET_SIZE], - previous_touch_data[TOUCH_PACKET_SIZE]; - static uint32_t xy; - static uint32_t last_check_time = 0; - static uint32_t last_event_time = 0; - static int touching = 0; - - uint32_t detected = touch_is_detected(); - - if (detected == 0) { - last_check_time = hal_ticks_ms(); - - if (touching && check_timeout(last_event_time, EVENT_MISSING_TIMEOUT_MS)) { - // we didn't detect an event for a long time, but there was an active - // touch: send END event, as we probably missed the END event - touching = 0; - return TOUCH_END | xy; + if (sectrue != ft6x36_write_reg(reg, value)) { + return secfalse; } - - return 0; } - if ((touching == 0) && - (check_timeout(last_check_time, EVENT_OLD_TIMEOUT_MS))) { - // we have detected an event, but it might be too old, rather drop it - // (only dropping old events if there was no touch active) - last_check_time = hal_ticks_ms(); - return 0; + return sectrue; +} + +secbool touch_init(void) { + touch_driver_t* driver = &g_touch_driver; + + if (sectrue == driver->initialized) { + // The driver is already initialized + return sectrue; } - last_check_time = hal_ticks_ms(); + // Initialize GPIO to the default configuration + // (touch controller is powered down) + ft6x36_power_down(); - uint8_t outgoing[] = {0x00}; // start reading from address 0x00 - int result = i2c_transmit(TOUCH_I2C_INSTANCE, TOUCH_ADDRESS, outgoing, - sizeof(outgoing), 1); - if (result != HAL_OK) { - if (result == HAL_BUSY) i2c_cycle(TOUCH_I2C_INSTANCE); - return 0; + // Power up the touch controller and perform the reset sequence + ft6x36_power_up(); + + // Configure the touch controller + if (sectrue != ft6x36_configure()) { + ft6x36_power_down(); + return secfalse; } - if (HAL_OK != i2c_receive(TOUCH_I2C_INSTANCE, TOUCH_ADDRESS, touch_data, - TOUCH_PACKET_SIZE, 1)) { - return 0; // read failure + driver->init_ticks = hal_ticks_ms(); + driver->poll_ticks = driver->init_ticks; + driver->initialized = sectrue; + + return sectrue; +} + +void touch_deinit(void) { + touch_driver_t* driver = &g_touch_driver; + + if (sectrue == driver->initialized) { + // Do not need to deinitialized the controller + // just power it off + ft6x36_power_down(); + + memset(driver, 0, sizeof(touch_driver_t)); + } +} + +secbool touch_ready(void) { + touch_driver_t* driver = &g_touch_driver; + + if (sectrue == driver->initialized && sectrue != driver->ready) { + // FT6X36 does not report events for 300ms + // after it is released from the reset state + if ((int)(hal_ticks_ms() - driver->init_ticks) >= 310) { + driver->ready = sectrue; + } } - last_event_time = hal_ticks_ms(); + return driver->ready; +} - if (0 == memcmp(previous_touch_data, touch_data, TOUCH_PACKET_SIZE)) { - return 0; // same data, filter it out +secbool touch_set_sensitivity(uint8_t value) { + touch_driver_t* driver = &g_touch_driver; + + if (sectrue == driver->initialized) { + return ft6x36_write_reg(FT6X36_REG_TH_GROUP, value); } else { - memcpy(previous_touch_data, touch_data, TOUCH_PACKET_SIZE); + return secfalse; } - - const uint32_t number_of_touch_points = - touch_data[2] & 0x0F; // valid values are 0, 1, 2 (invalid 0xF before - // first touch) (tested with FT6206) - const uint32_t event_flag = touch_data[3] & 0xC0; - if (touch_data[1] == GESTURE_NO_GESTURE) { - xy = touch_pack_xy((X_POS_MSB << 8) | X_POS_LSB, - (Y_POS_MSB << 8) | Y_POS_LSB); - if ((number_of_touch_points == 1) && (event_flag == EVENT_PRESS_DOWN)) { - touching = 1; - return TOUCH_START | xy; - } else if ((number_of_touch_points == 1) && (event_flag == EVENT_CONTACT)) { - if (touching) { - return TOUCH_MOVE | xy; - } else { - touching = 1; - return TOUCH_START | xy; - } - } else if ((number_of_touch_points == 0) && (event_flag == EVENT_LIFT_UP)) { - touching = 0; - return TOUCH_END | xy; - } - } - - return 0; } uint8_t touch_get_version(void) { - uint8_t version = 0; - uint8_t outgoing[] = {0xA6}; // start reading from address 0xA6 - int result = i2c_transmit(TOUCH_I2C_INSTANCE, TOUCH_ADDRESS, outgoing, - sizeof(outgoing), 1); - if (result != HAL_OK) { - if (result == HAL_BUSY) i2c_cycle(TOUCH_I2C_INSTANCE); + touch_driver_t* driver = &g_touch_driver; + + if (sectrue != driver->initialized) { return 0; } - if (HAL_OK != i2c_receive(TOUCH_I2C_INSTANCE, TOUCH_ADDRESS, &version, - sizeof(version), 1)) { - return 0; // read failure + // After powering up the touch controller, we need to wait + // for an unspecified amount of time (~100ms) before attempting + // to read the firmware version. If we try to read too soon, we get 0x00 + // and the chip behaves unpredictably. + while (sectrue != touch_ready()) { + hal_delay(1); } - return version; + uint8_t fw_version = 0; + + if (sectrue != ft6x36_read_regs(FT6X36_REG_FIRMID, &fw_version, 1)) { + ft6x36_power_down(); + return secfalse; + } + + return fw_version; +} + +secbool touch_activity(void) { + touch_driver_t* driver = &g_touch_driver; + + if (sectrue == driver->initialized) { + if (ft6x36_test_and_clear_interrupt()) { + return sectrue; + } + } + + return secfalse; +} + +uint32_t touch_get_event(void) { + touch_driver_t* driver = &g_touch_driver; + + if (sectrue != driver->initialized) { + return 0; + } + + // Content of registers 0x00 - 0x06 read from the touch controller + uint8_t regs[7]; + + // Ensure the registers are within the bounds + _Static_assert(sizeof(regs) > FT6X63_REG_GEST_ID); + _Static_assert(sizeof(regs) > FT6X63_REG_TD_STATUS); + _Static_assert(sizeof(regs) > FT6X63_REG_P1_XH); + _Static_assert(sizeof(regs) > FT6X63_REG_P1_XL); + _Static_assert(sizeof(regs) > FT6X63_REG_P1_YH); + _Static_assert(sizeof(regs) > FT6X63_REG_P1_YL); + + uint32_t ticks = hal_ticks_ms(); + + // Test if the touch_get_event() is starving (not called frequently enough) + bool starving = (int32_t)(ticks - driver->poll_ticks) > 300 /* ms */; + driver->poll_ticks = ticks; + + // Fast track: if there is no new event and the touch controller + // is not touched, we do not need to read the registers + if (!ft6x36_test_and_clear_interrupt() && !driver->pressed) { + return 0; + } + + // Read the set of registers containing touch event and coordinates + if (sectrue != ft6x36_read_regs(0x00, regs, sizeof(regs))) { + // Failed to read the touch registers + return 0; + } + + // Extract gesture ID (FT6X63_GESTURE_xxx) + uint8_t gesture = regs[FT6X63_REG_GEST_ID]; + + if (gesture != FT6X36_GESTURE_NONE) { + // This is here for unknown historical reasons + // It seems we can't get here with FT6X36 + return 0; + } + + // Extract number of touches (0, 1, 2) or 0x0F before + // the first touch (tested with FT6206) + uint8_t nb_touches = regs[FT6X63_REG_TD_STATUS] & 0x0F; + + // Extract event flags (one of press down, contact, lift up) + uint8_t flags = regs[FT6X63_REG_P1_XH] & FT6X63_EVENT_MASK; + + // Extract touch coordinates + uint16_t x = ((regs[FT6X63_REG_P1_XH] & 0x0F) << 8) | regs[FT6X63_REG_P1_XL]; + uint16_t y = ((regs[FT6X63_REG_P1_YH] & 0x0F) << 8) | regs[FT6X63_REG_P1_YL]; + + uint32_t event = 0; + + uint32_t xy = touch_pack_xy(x, y); + + if ((nb_touches == 1) && (flags == FT6X63_EVENT_PRESS_DOWN)) { + if (!driver->pressed) { + // Finger was just pressed down + event = TOUCH_START | xy; + } else { + // It looks like we have missed the lift up event + // We should send the TOUCH_END event here with old coordinates + event = TOUCH_END | touch_pack_xy(driver->last_x, driver->last_y); + } + } else if ((nb_touches == 1) && (flags == FT6X63_EVENT_CONTACT)) { + if (driver->pressed) { + if ((x != driver->last_x) || (y != driver->last_y)) { + // Report the move event only if the coordinates + // have changed + event = TOUCH_MOVE | xy; + } + } else { + // We have missed the press down event, we have to simulate it. + // But ensure we don't simulate TOUCH_START if touch_get_event() is not + // called frequently enough to not produce false events. + if (!starving) { + event = TOUCH_START | xy; + } + } + } else if ((nb_touches == 0) && (flags == FT6X63_EVENT_LIFT_UP)) { + if (driver->pressed) { + // Finger was just lifted up + event = TOUCH_END | xy; + } else { + // 1. Most likely, we have missed the PRESS_DOWN event. + // Touch duration was too short (< 20ms) to be worth reporting. + // 2. Finger is still lifted up. Since we have already sent the + // TOUCH_END event => no event needed. This should not happen + // since two consecutive LIFT_UPs are not possible due to + // testing the interrupt line before reading the registers. + } + } + + // remember the last state + if ((event & TOUCH_START) || (event & TOUCH_MOVE)) { + driver->pressed = true; + } else if (event & TOUCH_END) { + driver->pressed = false; + } + + driver->last_x = x; + driver->last_y = y; + + return event; } diff --git a/core/embed/trezorhal/stm32f4/touch/ft6x36.h b/core/embed/trezorhal/stm32f4/touch/ft6x36.h index 6f24a048be..8629a12693 100644 --- a/core/embed/trezorhal/stm32f4/touch/ft6x36.h +++ b/core/embed/trezorhal/stm32f4/touch/ft6x36.h @@ -20,4 +20,57 @@ #ifndef _TOUCH_FT6X36_H #define _TOUCH_FT6X36_H +// I2C address of the FT6X36 on the I2C bus. +// `<< 1` is required because the HAL expects the address to be shifted by 1. +#define FT6X36_I2C_ADDR (0x38 << 1) + +// ------------------------------------------------------------ +// FT6X36 registers +// ------------------------------------------------------------ + +// Gesture ID (see `FT6X36_GESTURE_xxx`) +#define FT6X63_REG_GEST_ID 0x01 + +// TD_STATUS (number of touch points in lower 4 bits) +#define FT6X63_REG_TD_STATUS 0x02 + +// Event flags in higher 2 bits (see `FT6X63_EVENT_xxx`) +// MSB of touch x-coordinate in lower 4 bits +#define FT6X63_REG_P1_XH 0x03 + +// LSB of touch x-coordinate +#define FT6X63_REG_P1_XL 0x04 + +// MSB of touch y-coordinate in lower 4 bits +#define FT6X63_REG_P1_YH 0x05 + +// LSB of touch y-coordinate +#define FT6X63_REG_P1_YL 0x06 + +// Threshold for touch detection +#define FT6X36_REG_TH_GROUP 0x80 + +// Mode register +// 0x00 - interrupt polling mode +// 0x01 - interrupt trigger mode +#define FT6X36_REG_G_MODE 0xA4 + +// Firmware version +#define FT6X36_REG_FIRMID 0xA6 + +// ------------------------------------------------------------ +// Event bits (see FT6X63_REG_P1_XH) +// ------------------------------------------------------------ + +#define FT6X63_EVENT_PRESS_DOWN 0x00 +#define FT6X63_EVENT_CONTACT 0x80 +#define FT6X63_EVENT_LIFT_UP 0x40 +#define FT6X63_EVENT_MASK 0xC0 + +// ------------------------------------------------------------ +// Gesture types (see FT6X63_REG_GEST_ID) +// ------------------------------------------------------------ + +#define FT6X36_GESTURE_NONE 0x00 + #endif diff --git a/core/embed/trezorhal/stm32f4/touch/stmpe811.c b/core/embed/trezorhal/stm32f4/touch/stmpe811.c index 4a503b1ec7..1d198513e2 100644 --- a/core/embed/trezorhal/stm32f4/touch/stmpe811.c +++ b/core/embed/trezorhal/stm32f4/touch/stmpe811.c @@ -320,26 +320,6 @@ uint16_t IOE_ReadMultiple(uint8_t Addr, uint8_t Reg, uint8_t *pBuffer, */ void IOE_Delay(uint32_t Delay) { HAL_Delay(Delay); } -static void touch_active_pin_state(void) { - // HAL_GPIO_WritePin(GPIOB, GPIO_PIN_10, GPIO_PIN_RESET); // CTP_ON/PB10 - // HAL_Delay(10); // we need to wait until the circuit fully kicks-in - - GPIO_InitTypeDef GPIO_InitStructure = {0}; - - // PC4 capacitive touch panel module (CTPM) interrupt (INT) input - GPIO_InitStructure.Mode = GPIO_MODE_IT_FALLING; - GPIO_InitStructure.Pull = GPIO_PULLUP; - GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_LOW; - GPIO_InitStructure.Pin = GPIO_PIN_15; - HAL_GPIO_Init(GPIOA, &GPIO_InitStructure); - __HAL_GPIO_EXTI_CLEAR_FLAG(GPIO_PIN_15); - - // HAL_GPIO_WritePin(GPIOC, GPIO_PIN_5, GPIO_PIN_SET); // release CTPM reset - // HAL_Delay(310); // "Time of starting to report point after resetting" min - // is - // 300ms, giving an extra 10ms -} - /** * @brief Enable the AF for the selected IO pin(s). * @param DeviceAddr: Device address on communication Bus. @@ -439,17 +419,6 @@ void touch_set_mode(void) { IOE_Delay(2); } -void touch_power_on(void) { - // turn on CTP circuitry - touch_active_pin_state(); - HAL_Delay(50); -} - -void touch_power_off(void) { - // turn off CTP circuitry - HAL_Delay(50); -} - /** * @brief Reset the stmpe811 by Software. * @param DeviceAddr: Device address on communication Bus. @@ -470,55 +439,7 @@ void stmpe811_Reset() { IOE_Delay(2); } -secbool touch_init(void) { - GPIO_InitTypeDef GPIO_InitStructure = {0}; - - __HAL_RCC_GPIOA_CLK_ENABLE(); - - // PC4 capacitive touch panel module (CTPM) interrupt (INT) input - GPIO_InitStructure.Mode = GPIO_MODE_IT_RISING; - GPIO_InitStructure.Pull = GPIO_PULLUP; - GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_LOW; - GPIO_InitStructure.Pin = GPIO_PIN_15; - HAL_GPIO_Init(GPIOA, &GPIO_InitStructure); - __HAL_GPIO_EXTI_CLEAR_FLAG(GPIO_PIN_15); - - stmpe811_Reset(); - touch_set_mode(); - touch_sensitivity(0x06); - - return sectrue; -} - -secbool touch_sensitivity(uint8_t value) { - // set panel threshold (TH_GROUP) - default value is 0x12 - // uint8_t touch_panel_threshold[] = {0x80, value}; - // ensure(sectrue * - // (HAL_OK == HAL_I2C_Master_Transmit( - // &I2c_handle, TOUCH_ADDRESS, touch_panel_threshold, - // sizeof(touch_panel_threshold), 10)), - // NULL); - return sectrue; -} - -uint32_t touch_is_detected(void) { - uint8_t state = ((IOE_Read(TS_I2C_ADDRESS, STMPE811_REG_TSC_CTRL) & - (uint8_t)STMPE811_TS_CTRL_STATUS) == (uint8_t)0x80); - return state > 0; -} - -uint32_t touch_active(void) { - // check the interrupt line coming in from the CTPM. - // the line make a short pulse, which sets an interrupt flag when new data is - // available. - // Reference section 1.2 of "Application Note for FT6x06 CTPM". we - // configure the touch controller to use "interrupt trigger mode". - - // uint32_t event = __HAL_GPIO_EXTI_GET_FLAG(GPIO_PIN_15); - // if (event != 0) { - // __HAL_GPIO_EXTI_CLEAR_FLAG(GPIO_PIN_15); - // } - +static uint32_t touch_active(void) { uint8_t state; uint8_t ret = 0; @@ -539,17 +460,6 @@ uint32_t touch_active(void) { return ret; } -uint32_t check_timeout(uint32_t prev, uint32_t timeout) { - uint32_t current = hal_ticks_ms(); - uint32_t diff = current - prev; - - if (diff >= timeout) { - return 1; - } - - return 0; -} - /** * @brief Get the touch screen X and Y positions values * @param DeviceAddr: Device address on communication Bus. @@ -647,62 +557,87 @@ void BSP_TS_GetState(TS_StateTypeDef *TsState) { } } -uint32_t touch_read(void) { - TS_StateTypeDef state = {0}; - static uint32_t xy = 0; - static TS_StateTypeDef state_last = {0}; - // static uint16_t first = 1; - static uint16_t touching = 0; +typedef struct { + // Set if driver is initialized + secbool initialized; + // Last lower-level driver state + TS_StateTypeDef prev_state; - if (!touch_is_detected()) { - if (touching) { - // touch end - memcpy(&state_last, &state, sizeof(state)); - touching = 0; - return TOUCH_END | xy; - } - return 0; +} touch_driver_t; + +// Touch driver instance +static touch_driver_t g_touch_driver = { + .initialized = secfalse, +}; + +secbool touch_init(void) { + touch_driver_t *driver = &g_touch_driver; + + if (driver->initialized != sectrue) { + stmpe811_Reset(); + touch_set_mode(); + + driver->initialized = sectrue; } - BSP_TS_GetState(&state); + return driver->initialized; +} - if (state.TouchDetected == 0) { - return 0; +void touch_deinit(void) { + touch_driver_t *driver = &g_touch_driver; + + if (driver->initialized == sectrue) { + // Not implemented properly + + memset(driver, 0, sizeof(touch_driver_t)); } +} - // if (first != 0) { - // memcpy(&state_last, &state, sizeof(state)); - // first = 0; - // return 0; - // } +secbool touch_ready(void) { + touch_driver_t *driver = &g_touch_driver; + return driver->initialized; +} - if ((state.TouchDetected == 0 && state_last.TouchDetected == 0) || - memcmp(&state, &state_last, sizeof(state)) == 0) { - // no change detected - return 0; - } - - xy = touch_pack_xy(state.X, state.Y); - - if (state.TouchDetected && !state_last.TouchDetected) { - // touch start - memcpy(&state_last, &state, sizeof(state)); - touching = 1; - return TOUCH_START | xy; - } else if (!state.TouchDetected && state_last.TouchDetected) { - // touch end - memcpy(&state_last, &state, sizeof(state)); - touching = 0; - return TOUCH_END | xy; - } else { - // touch move - memcpy(&state_last, &state, sizeof(state)); - return TOUCH_MOVE | xy; - } +secbool touch_set_sensitivity(uint8_t value) { + // Not implemented for the discovery kit + return sectrue; +} +uint8_t touch_get_version(void) { + // Not implemented for the discovery kit return 0; } -void touch_wait_until_ready(void) {} +secbool touch_activity(void) { + uint8_t state = ((IOE_Read(TS_I2C_ADDRESS, STMPE811_REG_TSC_CTRL) & + (uint8_t)STMPE811_TS_CTRL_STATUS) == (uint8_t)0x80); + return state > 0 ? sectrue : secfalse; +} -uint8_t touch_get_version(void) { return 0; } +uint32_t touch_get_event(void) { + touch_driver_t *driver = &g_touch_driver; + + if (driver->initialized != sectrue) { + return 0; + } + + TS_StateTypeDef new_state = {0}; + BSP_TS_GetState(&new_state); + + uint32_t event = 0; + + if (new_state.TouchDetected && !driver->prev_state.TouchDetected) { + uint32_t xy = touch_pack_xy(new_state.X, new_state.Y); + event = TOUCH_START | xy; + } else if (!new_state.TouchDetected && driver->prev_state.TouchDetected) { + uint32_t xy = touch_pack_xy(driver->prev_state.X, driver->prev_state.Y); + event = TOUCH_END | xy; + } else if (new_state.TouchDetected) { + uint32_t xy = touch_pack_xy(new_state.X, new_state.Y); + event = TOUCH_MOVE | xy; + } + + driver->prev_state = new_state; + + return event; +} diff --git a/core/embed/trezorhal/stm32u5/touch/sitronix.c b/core/embed/trezorhal/stm32u5/touch/sitronix.c index b705ceba77..91301376bf 100644 --- a/core/embed/trezorhal/stm32u5/touch/sitronix.c +++ b/core/embed/trezorhal/stm32u5/touch/sitronix.c @@ -360,7 +360,6 @@ int32_t SITRONIX_GetState(SITRONIX_Object_t *pObj, SITRONIX_State_t *State) { return ret; } - /** * @brief Get the touch screen Xn and Yn positions values in multi-touch mode * @param pObj Component object pointer @@ -1135,86 +1134,100 @@ static int32_t SITRONIX_Probe(uint32_t Instance) { #include #include "touch.h" +// Touch driver +typedef struct { + // Set if driver is initialized + secbool initialized; + // Last lower-level driver state + TS_State_t prev_state; + +} touch_driver_t; + +// Touch driver instance +static touch_driver_t g_touch_driver = { + .initialized = secfalse, +}; + secbool touch_init(void) { - TS_Init_t TsInit; + touch_driver_t *driver = &g_touch_driver; - /* Initialize the TouchScreen */ - TsInit.Width = 480; - TsInit.Height = 480; - TsInit.Orientation = 0; - TsInit.Accuracy = 10; + if (sectrue != driver->initialized) { + TS_Init_t TsInit; - BSP_TS_Init(0, &TsInit); + /* Initialize the TouchScreen */ + TsInit.Width = 480; + TsInit.Height = 480; + TsInit.Orientation = 0; + TsInit.Accuracy = 10; + BSP_TS_Init(0, &TsInit); + + driver->initialized = sectrue; + } + + return driver->initialized; +} + +void touch_deinit(void) { + touch_driver_t *driver = &g_touch_driver; + + if (sectrue == driver->initialized) { + BSP_TS_DeInit(0); + memset(driver, 0, sizeof(touch_driver_t)); + } +} + +secbool touch_ready(void) { + touch_driver_t *driver = &g_touch_driver; + return driver->initialized; +} + +secbool touch_set_sensitivity(uint8_t value) { + // Not implemented for the discovery kit return sectrue; } -void touch_power_on(void) {} -void touch_power_off(void) {} -secbool touch_sensitivity(uint8_t value) { return sectrue; } - -uint32_t touch_is_detected(void) { return sitronix_touching != 0; } - -uint32_t touch_read(void) { - TS_State_t state = {0}; - static uint32_t xy = 0; - static TS_State_t state_last = {0}; - // static uint16_t first = 1; - static uint16_t touching = 0; - - BSP_TS_GetState(0, &state); - - state.TouchDetected = touch_is_detected(); - state.TouchY = state.TouchY > 120 ? state.TouchY - 120 : 0; - state.TouchX = state.TouchX > 120 ? state.TouchX - 120 : 0; - - if (!touch_is_detected()) { - // if (state.TouchDetected == 0) { - if (touching) { - // touch end - memcpy(&state_last, &state, sizeof(state)); - touching = 0; - return TOUCH_END | xy; - } - return 0; - } - - if (state.TouchDetected == 0) { - return 0; - } - - // if (first != 0) { - // memcpy(&state_last, &state, sizeof(state)); - // first = 0; - // return 0; - // } - - if ((state.TouchDetected == 0 && state_last.TouchDetected == 0) || - memcmp(&state, &state_last, sizeof(state)) == 0) { - // no change detected - return 0; - } - - xy = touch_pack_xy(state.TouchX, state.TouchY); - - if (state.TouchDetected && !state_last.TouchDetected) { - // touch start - memcpy(&state_last, &state, sizeof(state)); - touching = 1; - return TOUCH_START | xy; - } else if (!state.TouchDetected && state_last.TouchDetected) { - // touch end - memcpy(&state_last, &state, sizeof(state)); - touching = 0; - return TOUCH_END | xy; - } else { - // touch move - memcpy(&state_last, &state, sizeof(state)); - return TOUCH_MOVE | xy; - } +uint8_t touch_get_version(void) { + // Not implemented for the discovery kit return 0; } -void touch_wait_until_ready(void) {} +secbool touch_activity(void) { + if (sitronix_touching) { + return sectrue; + } else { + return secfalse; + } +} -uint8_t touch_get_version(void) { return 0; } +uint32_t touch_get_event(void) { + touch_driver_t *driver = &g_touch_driver; + + if (sectrue != driver->initialized) { + return 0; + } + + TS_State_t new_state = {0}; + BSP_TS_GetState(0, &new_state); + + new_state.TouchX = new_state.TouchX > 120 ? new_state.TouchX - 120 : 0; + new_state.TouchY = new_state.TouchY > 120 ? new_state.TouchY - 120 : 0; + + uint32_t event = 0; + + if (new_state.TouchDetected && !driver->prev_state.TouchDetected) { + uint32_t xy = touch_pack_xy(new_state.TouchX, new_state.TouchY); + event = TOUCH_START | xy; + } else if (!new_state.TouchDetected && driver->prev_state.TouchDetected) { + uint32_t xy = + touch_pack_xy(driver->prev_state.TouchX, driver->prev_state.TouchY); + event = TOUCH_END | xy; + } else if (new_state.TouchDetected) { + uint32_t xy = touch_pack_xy(new_state.TouchX, new_state.TouchY); + event = TOUCH_MOVE | xy; + } + + driver->prev_state = new_state; + + return event; +} diff --git a/core/embed/trezorhal/touch.h b/core/embed/trezorhal/touch.h index decfa212d7..d0fe29a561 100644 --- a/core/embed/trezorhal/touch.h +++ b/core/embed/trezorhal/touch.h @@ -1,32 +1,90 @@ -#ifndef _TOUCH_H -#define _TOUCH_H +#ifndef TREZOR_HAL_TOUCH_H +#define TREZOR_HAL_TOUCH_H #include #include "secbool.h" +// Initializes the touch driver +// +// Powers on and initializes touch driver controller. +// The function has no effect if the driver was already initialized. +// +// Returns `sectrue` if the hardware was successfuly initialized. +secbool touch_init(void); + +// Deinitializes the touch driver +// +// The function deinitializes touch controller and powers it off. +void touch_deinit(); + +// Checks if the touch driver is ready to report touches +// +// Some drivers need time after power-up to stabilize. The app +// may use this function to wait until touch controller is +// fully functional. +secbool touch_ready(void); + +// Gets the touch controller firmware version +// +// Can be called only if the touch controller was initialized, +// othervise returns 0. +// +// We do not interpret the value of the version, we just print it +// during the production test. +uint8_t touch_get_version(void); + +// Sets touch controller sensitivity +// +// (Internally threadhsold for ????) +secbool touch_set_sensitivity(uint8_t value); + +// Checks if the touch is currently reporting any events +// +// The purpose of this function is very special. It is used +// in bootloader startup to detect if the user is touching the screen. +// On some hardware it's a bit more sensitive then `touch_get_event()` +// since it does not filter out any events. +// +// The function should not be used together with `touch_get_event()`. +secbool touch_activity(void); + +// Returns the last event in packed 32-bit format +// +// Returns `0` if there's no event or the driver is not initialized. +uint32_t touch_get_event(void); + +// Touch event is packed 32-bit value +// +// 31 24 23 12 11 0 +// |--------|------------|------------| +// | event | x-coord | y-coord | +// |--------|------------|------------| +// +// + +// Touch event bits #define TOUCH_START (1U << 24) #define TOUCH_MOVE (1U << 25) #define TOUCH_END (1U << 26) -secbool touch_init(void); -void touch_power_on(void); -void touch_power_off(void); -void touch_wait_until_ready(void); - -secbool touch_sensitivity(uint8_t value); -uint32_t touch_read(void); -uint32_t touch_click(void); -uint32_t touch_is_detected(void); -uint8_t touch_get_version(void); - +// Returns x-coordinates from a packed touch event static inline uint16_t touch_unpack_x(uint32_t evt) { return (evt >> 12) & 0xFFF; } + +// Returns y-coordinates from a packed touch event static inline uint16_t touch_unpack_y(uint32_t evt) { return (evt >> 0) & 0xFFF; } + +// Creates packed touch event from x and y coordinates static inline uint32_t touch_pack_xy(uint16_t x, uint16_t y) { return ((x & 0xFFF) << 12) | (y & 0xFFF); } +// ------------------------- +// legacy: + +uint32_t touch_is_detected(void); + #endif //_TOUCH_H diff --git a/core/embed/trezorhal/unix/button.c b/core/embed/trezorhal/unix/button.c new file mode 100644 index 0000000000..3ff9cd4b6f --- /dev/null +++ b/core/embed/trezorhal/unix/button.c @@ -0,0 +1,70 @@ +/* + * This file is part of the Trezor project, https://trezor.io/ + * + * Copyright (c) SatoshiLabs + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include TREZOR_BOARD + +#include + +#include "button.h" +#include "common.h" +#include "platform.h" + +static char last_left = 0, last_right = 0; + +char button_state_left(void) { return last_left; } + +char button_state_right(void) { return last_right; } + +uint32_t button_read(void) { + SDL_Event event; + SDL_PumpEvents(); + if (SDL_PollEvent(&event) > 0) { + switch (event.type) { + case SDL_KEYDOWN: + if (event.key.repeat) { + break; + } + switch (event.key.keysym.sym) { + case SDLK_LEFT: + last_left = 1; + return BTN_EVT_DOWN | BTN_LEFT; + case SDLK_RIGHT: + last_right = 1; + return BTN_EVT_DOWN | BTN_RIGHT; + } + break; + case SDL_KEYUP: + if (event.key.repeat) { + break; + } + switch (event.key.keysym.sym) { + case SDLK_LEFT: + last_left = 0; + return BTN_EVT_UP | BTN_LEFT; + case SDLK_RIGHT: + last_right = 0; + return BTN_EVT_UP | BTN_RIGHT; + } + break; + } + } + return 0; +} + +void button_init(void) {} diff --git a/core/embed/trezorhal/unix/touch/touch.c b/core/embed/trezorhal/unix/touch.c similarity index 56% rename from core/embed/trezorhal/unix/touch/touch.c rename to core/embed/trezorhal/unix/touch.c index b90b418d91..d2aa918e1f 100644 --- a/core/embed/trezorhal/unix/touch/touch.c +++ b/core/embed/trezorhal/unix/touch.c @@ -17,13 +17,12 @@ * along with this program. If not, see . */ +#include TREZOR_BOARD + #include #include #include -#include TREZOR_BOARD -#ifdef USE_TOUCH - #include "common.h" #include "platform.h" #include "touch.h" @@ -31,9 +30,6 @@ extern int sdl_display_res_x, sdl_display_res_y; extern int sdl_touch_offset_x, sdl_touch_offset_y; -static int _touch_x = 0; -static int _touch_y = 0; - // distance from the edge where arrow button swipe starts [px] static const int _btn_swipe_begin = 120; // length of the arrow button swipe [px] @@ -53,9 +49,23 @@ typedef enum { BUTTON_SWIPE_UP_INITIATED, BUTTON_SWIPE_DOWN_INITIATED, BUTTON_SWIPE_COMPLETED -} touch_input_state_t; +} touch_state_t; -static touch_input_state_t input_state = IDLE; +typedef struct { + // Set if driver is initialized + secbool initialized; + // Current state of the touch driver + touch_state_t state; + // Last valid coordinates + int last_x; + int last_y; + +} touch_driver_t; + +// Touch driver instance +static touch_driver_t g_touch_driver = { + .initialized = secfalse, +}; static bool is_inside_display(int x, int y) { return x >= sdl_touch_offset_x && y >= sdl_touch_offset_y && @@ -63,15 +73,15 @@ static bool is_inside_display(int x, int y) { y - sdl_touch_offset_y < sdl_display_res_y; } -static bool is_button_swipe_initiated() { - return input_state == BUTTON_SWIPE_LEFT_INITIATED || - input_state == BUTTON_SWIPE_RIGHT_INITIATED || - input_state == BUTTON_SWIPE_UP_INITIATED || - input_state == BUTTON_SWIPE_DOWN_INITIATED; +static bool is_button_swipe_initiated(const touch_driver_t* driver) { + return driver->state == BUTTON_SWIPE_LEFT_INITIATED || + driver->state == BUTTON_SWIPE_RIGHT_INITIATED || + driver->state == BUTTON_SWIPE_UP_INITIATED || + driver->state == BUTTON_SWIPE_DOWN_INITIATED; } -static void handle_mouse_events(SDL_Event event, int* ev_type, int* ev_x, - int* ev_y) { +static void handle_mouse_events(touch_driver_t* driver, SDL_Event event, + int* ev_type, int* ev_x, int* ev_y) { bool inside_display = is_inside_display(event.button.x, event.button.y); switch (event.type) { case SDL_MOUSEBUTTONDOWN: @@ -79,116 +89,167 @@ static void handle_mouse_events(SDL_Event event, int* ev_type, int* ev_x, *ev_x = event.button.x - sdl_touch_offset_x; *ev_y = event.button.y - sdl_touch_offset_y; *ev_type = TOUCH_START; - input_state = MOUSE_DOWN_INSIDE; + driver->state = MOUSE_DOWN_INSIDE; } break; case SDL_MOUSEBUTTONUP: - if (input_state != IDLE) { - *ev_x = inside_display ? event.button.x - sdl_touch_offset_x : _touch_x; - *ev_y = inside_display ? event.button.y - sdl_touch_offset_y : _touch_y; + if (driver->state != IDLE) { + *ev_x = inside_display ? event.button.x - sdl_touch_offset_x + : driver->last_x; + *ev_y = inside_display ? event.button.y - sdl_touch_offset_y + : driver->last_y; *ev_type = TOUCH_END; - input_state = IDLE; + driver->state = IDLE; } break; case SDL_MOUSEMOTION: - if (input_state != IDLE) { + if (driver->state != IDLE) { if (inside_display) { *ev_x = event.motion.x - sdl_touch_offset_x; *ev_y = event.motion.y - sdl_touch_offset_y; // simulate TOUCH_START if pressed in mouse returned on visible area *ev_type = - (input_state == MOUSE_DOWN_OUTSIDE) ? TOUCH_START : TOUCH_MOVE; - input_state = MOUSE_DOWN_INSIDE; + (driver->state == MOUSE_DOWN_OUTSIDE) ? TOUCH_START : TOUCH_MOVE; + driver->state = MOUSE_DOWN_INSIDE; } else { - if (input_state == MOUSE_DOWN_INSIDE) { + if (driver->state == MOUSE_DOWN_INSIDE) { // use last valid coordinates and simulate TOUCH_END - *ev_x = _touch_x; - *ev_y = _touch_y; + *ev_x = driver->last_x; + *ev_y = driver->last_y; *ev_type = TOUCH_END; } - input_state = MOUSE_DOWN_OUTSIDE; + driver->state = MOUSE_DOWN_OUTSIDE; } } break; } } -static void handle_button_events(SDL_Event event, int* ev_type, int* ev_x, - int* ev_y) { +static void handle_button_events(touch_driver_t* driver, SDL_Event event, + int* ev_type, int* ev_x, int* ev_y) { // Handle arrow buttons to trigger a scroll movement by set length in the // direction of the button if (event.type == SDL_KEYDOWN && !event.key.repeat && - !is_button_swipe_initiated()) { + !is_button_swipe_initiated(driver)) { switch (event.key.keysym.sym) { case SDLK_LEFT: *ev_x = _btn_swipe_begin; *ev_y = sdl_display_res_y / 2; *ev_type = TOUCH_START; - input_state = BUTTON_SWIPE_LEFT_INITIATED; + driver->state = BUTTON_SWIPE_LEFT_INITIATED; break; case SDLK_RIGHT: *ev_x = sdl_display_res_x - _btn_swipe_begin; *ev_y = sdl_display_res_y / 2; *ev_type = TOUCH_START; - input_state = BUTTON_SWIPE_RIGHT_INITIATED; + driver->state = BUTTON_SWIPE_RIGHT_INITIATED; break; case SDLK_UP: *ev_x = sdl_display_res_x / 2; *ev_y = _btn_swipe_begin; *ev_type = TOUCH_START; - input_state = BUTTON_SWIPE_UP_INITIATED; + driver->state = BUTTON_SWIPE_UP_INITIATED; break; case SDLK_DOWN: *ev_x = sdl_display_res_x / 2; *ev_y = sdl_display_res_y - _btn_swipe_begin; *ev_type = TOUCH_START; - input_state = BUTTON_SWIPE_DOWN_INITIATED; + driver->state = BUTTON_SWIPE_DOWN_INITIATED; break; } - } else if (event.type == SDL_KEYUP && input_state != IDLE) { + } else if (event.type == SDL_KEYUP && driver->state != IDLE) { switch (event.key.keysym.sym) { case SDLK_LEFT: - if (input_state == BUTTON_SWIPE_LEFT_INITIATED) { + if (driver->state == BUTTON_SWIPE_LEFT_INITIATED) { *ev_x = _btn_swipe_begin + _btn_swipe_length; *ev_y = sdl_display_res_y / 2; *ev_type = TOUCH_MOVE; - input_state = BUTTON_SWIPE_COMPLETED; + driver->state = BUTTON_SWIPE_COMPLETED; } break; case SDLK_RIGHT: - if (input_state == BUTTON_SWIPE_RIGHT_INITIATED) { + if (driver->state == BUTTON_SWIPE_RIGHT_INITIATED) { *ev_x = sdl_display_res_x - _btn_swipe_begin - _btn_swipe_length; *ev_y = sdl_display_res_y / 2; *ev_type = TOUCH_MOVE; - input_state = BUTTON_SWIPE_COMPLETED; + driver->state = BUTTON_SWIPE_COMPLETED; } break; case SDLK_UP: - if (input_state == BUTTON_SWIPE_UP_INITIATED) { + if (driver->state == BUTTON_SWIPE_UP_INITIATED) { *ev_x = sdl_display_res_x / 2; *ev_y = _btn_swipe_begin + _btn_swipe_length; *ev_type = TOUCH_MOVE; - input_state = BUTTON_SWIPE_COMPLETED; + driver->state = BUTTON_SWIPE_COMPLETED; } break; case SDLK_DOWN: - if (input_state == BUTTON_SWIPE_DOWN_INITIATED) { + if (driver->state == BUTTON_SWIPE_DOWN_INITIATED) { *ev_x = sdl_display_res_x / 2; *ev_y = sdl_display_res_y - _btn_swipe_begin - _btn_swipe_length; *ev_type = TOUCH_MOVE; - input_state = BUTTON_SWIPE_COMPLETED; + driver->state = BUTTON_SWIPE_COMPLETED; } break; } } } -uint32_t touch_read(void) { - if (input_state == BUTTON_SWIPE_COMPLETED) { - input_state = IDLE; - return TOUCH_END | touch_pack_xy(_touch_x, _touch_y); +secbool touch_init(void) { + touch_driver_t* driver = &g_touch_driver; + + if (driver->initialized != sectrue) { + memset(driver, 0, sizeof(touch_driver_t)); + driver->state = IDLE; + driver->initialized = sectrue; + } + + return driver->initialized; +} + +void touch_deinit(void) { + touch_driver_t* driver = &g_touch_driver; + + if (driver->initialized == sectrue) { + memset(driver, 0, sizeof(touch_driver_t)); + } +} + +secbool touch_ready(void) { + touch_driver_t* driver = &g_touch_driver; + return driver->initialized; +} + +secbool touch_set_sensitivity(uint8_t value) { + // Not implemented on the emulator + return sectrue; +} + +uint8_t touch_get_version(void) { + // Not implemented on the emulator + return 0; +} + +secbool touch_activity(void) { + if (touch_get_event() != 0) { + return sectrue; + } else { + return secfalse; + } +} + +uint32_t touch_get_event(void) { + touch_driver_t* driver = &g_touch_driver; + + if (driver->initialized != sectrue) { + return 0; + } + + if (driver->state == BUTTON_SWIPE_COMPLETED) { + driver->state = IDLE; + return TOUCH_END | touch_pack_xy(driver->last_x, driver->last_y); } emulator_poll_events(); @@ -200,80 +261,18 @@ uint32_t touch_read(void) { int ev_type = 0; while (SDL_PollEvent(&event) > 0) { - if (input_state == IDLE || input_state == MOUSE_DOWN_INSIDE || - input_state == MOUSE_DOWN_OUTSIDE) { - handle_mouse_events(event, &ev_type, &ev_x, &ev_y); + if (driver->state == IDLE || driver->state == MOUSE_DOWN_INSIDE || + driver->state == MOUSE_DOWN_OUTSIDE) { + handle_mouse_events(driver, event, &ev_type, &ev_x, &ev_y); } - if (input_state == IDLE || is_button_swipe_initiated()) { - handle_button_events(event, &ev_type, &ev_x, &ev_y); + if (driver->state == IDLE || is_button_swipe_initiated(driver)) { + handle_button_events(driver, event, &ev_type, &ev_x, &ev_y); } if (ev_type != 0) { - _touch_x = ev_x; - _touch_y = ev_y; + driver->last_x = ev_x; + driver->last_y = ev_y; } } return ev_type | touch_pack_xy(ev_x, ev_y); } - -secbool touch_init(void) { return sectrue; } -void touch_power_on(void) {} -void touch_wait_until_ready(void) {} - -uint32_t touch_is_detected(void) { - return input_state == MOUSE_DOWN_INSIDE || is_button_swipe_initiated(); -} - -uint8_t touch_get_version(void) { return 0; } - -#endif - -#ifdef USE_BUTTON - -#include "button.h" - -static char last_left = 0, last_right = 0; - -char button_state_left(void) { return last_left; } - -char button_state_right(void) { return last_right; } - -uint32_t button_read(void) { - SDL_Event event; - SDL_PumpEvents(); - if (SDL_PollEvent(&event) > 0) { - switch (event.type) { - case SDL_KEYDOWN: - if (event.key.repeat) { - break; - } - switch (event.key.keysym.sym) { - case SDLK_LEFT: - last_left = 1; - return BTN_EVT_DOWN | BTN_LEFT; - case SDLK_RIGHT: - last_right = 1; - return BTN_EVT_DOWN | BTN_RIGHT; - } - break; - case SDL_KEYUP: - if (event.key.repeat) { - break; - } - switch (event.key.keysym.sym) { - case SDLK_LEFT: - last_left = 0; - return BTN_EVT_UP | BTN_LEFT; - case SDLK_RIGHT: - last_right = 0; - return BTN_EVT_UP | BTN_RIGHT; - } - break; - } - } - return 0; -} - -void button_init(void) {} - -#endif diff --git a/core/embed/unix/main.c b/core/embed/unix/main.c index dac4b1141b..ffef7ca458 100644 --- a/core/embed/unix/main.c +++ b/core/embed/unix/main.c @@ -53,6 +53,7 @@ #include "py/repl.h" #include "py/runtime.h" #include "py/stackctrl.h" +#include "touch.h" #include "common.h" @@ -474,6 +475,10 @@ MP_NOINLINE int main_(int argc, char **argv) { display_init(); +#if USE_TOUCH + touch_init(); +#endif + // Map trezor.flash to memory. flash_init(); flash_otp_init(); diff --git a/core/site_scons/models/D001/discovery.py b/core/site_scons/models/D001/discovery.py index 68a6be7f62..327d6b9c58 100644 --- a/core/site_scons/models/D001/discovery.py +++ b/core/site_scons/models/D001/discovery.py @@ -71,7 +71,6 @@ def configure( if "input" in features_wanted: sources += ["embed/trezorhal/stm32f4/i2c.c"] sources += ["embed/trezorhal/stm32f4/touch/stmpe811.c"] - sources += ["embed/lib/touch.c"] features_available.append("touch") if "usb" in features_wanted: diff --git a/core/site_scons/models/D002/discovery2.py b/core/site_scons/models/D002/discovery2.py index 0fe7f4f9e2..94f16b7c65 100644 --- a/core/site_scons/models/D002/discovery2.py +++ b/core/site_scons/models/D002/discovery2.py @@ -60,9 +60,6 @@ def configure( sources += [ "embed/trezorhal/stm32u5/i2c.c", ] - sources += [ - "embed/lib/touch.c", - ] sources += [ "embed/trezorhal/stm32u5/touch/sitronix.c", ] diff --git a/core/site_scons/models/T1B1/emulator.py b/core/site_scons/models/T1B1/emulator.py index e44f8d7f58..7763364912 100644 --- a/core/site_scons/models/T1B1/emulator.py +++ b/core/site_scons/models/T1B1/emulator.py @@ -31,6 +31,7 @@ def configure( defines += ["FLASH_BLOCK_WORDS=1"] if "input" in features_wanted: + sources += ["embed/trezorhal/unix/button.c"] features_available.append("button") sources += ["embed/models/T1B1/model_T1B1_layout.c"] diff --git a/core/site_scons/models/T2B1/emulator.py b/core/site_scons/models/T2B1/emulator.py index f8e5da54f8..6936b34ded 100644 --- a/core/site_scons/models/T2B1/emulator.py +++ b/core/site_scons/models/T2B1/emulator.py @@ -41,6 +41,7 @@ def configure( features_available.append("optiga") if "input" in features_wanted: + sources += ["embed/trezorhal/unix/button.c"] features_available.append("button") sources += ["embed/models/T2B1/model_T2B1_layout.c"] diff --git a/core/site_scons/models/T2T1/emulator.py b/core/site_scons/models/T2T1/emulator.py index 02102b316b..a35db0370e 100644 --- a/core/site_scons/models/T2T1/emulator.py +++ b/core/site_scons/models/T2T1/emulator.py @@ -51,6 +51,7 @@ def configure( sources += ["embed/trezorhal/unix/sbu.c"] if "input" in features_wanted: + sources += ["embed/trezorhal/unix/touch.c"] features_available.append("touch") features_available.append("backlight") diff --git a/core/site_scons/models/T2T1/trezor_t.py b/core/site_scons/models/T2T1/trezor_t.py index 71b12d6216..11550162d7 100644 --- a/core/site_scons/models/T2T1/trezor_t.py +++ b/core/site_scons/models/T2T1/trezor_t.py @@ -82,7 +82,6 @@ def configure( if "input" in features_wanted: sources += ["embed/trezorhal/stm32f4/i2c.c"] sources += ["embed/trezorhal/stm32f4/touch/ft6x36.c"] - sources += ["embed/lib/touch.c"] features_available.append("touch") if "sd_card" in features_wanted: diff --git a/core/site_scons/models/T3T1/emulator.py b/core/site_scons/models/T3T1/emulator.py index 61ad1ad67a..fa5400b536 100644 --- a/core/site_scons/models/T3T1/emulator.py +++ b/core/site_scons/models/T3T1/emulator.py @@ -59,6 +59,7 @@ def configure( sources += ["embed/trezorhal/unix/optiga.c"] if "input" in features_wanted: + sources += ["embed/trezorhal/unix/touch.c"] features_available.append("touch") features_available.append("backlight") diff --git a/core/site_scons/models/T3T1/trezor_t3t1_revE.py b/core/site_scons/models/T3T1/trezor_t3t1_revE.py index 9d1cea76e7..64c5baab77 100644 --- a/core/site_scons/models/T3T1/trezor_t3t1_revE.py +++ b/core/site_scons/models/T3T1/trezor_t3t1_revE.py @@ -73,7 +73,6 @@ def configure( if "input" in features_wanted: sources += ["embed/trezorhal/stm32u5/i2c.c"] sources += ["embed/trezorhal/stm32u5/touch/ft6x36.c"] - sources += ["embed/lib/touch.c"] features_available.append("touch") if "haptic" in features_wanted: diff --git a/core/site_scons/models/T3T1/trezor_t3t1_v4.py b/core/site_scons/models/T3T1/trezor_t3t1_v4.py index f6ffda61df..6b32ca5e2d 100644 --- a/core/site_scons/models/T3T1/trezor_t3t1_v4.py +++ b/core/site_scons/models/T3T1/trezor_t3t1_v4.py @@ -75,7 +75,6 @@ def configure( if "input" in features_wanted: sources += ["embed/trezorhal/stm32u5/i2c.c"] sources += ["embed/trezorhal/stm32u5/touch/ft6x36.c"] - sources += ["embed/lib/touch.c"] features_available.append("touch") if "haptic" in features_wanted: