diff --git a/core/embed/io/touch/ft6x36/ft6x36.c b/core/embed/io/touch/ft6x36/ft6x36.c index 1b6b539480..3d271f38f0 100644 --- a/core/embed/io/touch/ft6x36/ft6x36.c +++ b/core/embed/io/touch/ft6x36/ft6x36.c @@ -24,6 +24,7 @@ #include #include +#include #include #include "ft6x36.h" @@ -33,6 +34,8 @@ #include "panels/lhs200kb-if21.h" #endif +#include "../touch_fsm.h" + // #define TOUCH_TRACE_REGS // #define TOUCH_TRACE_EVENT @@ -46,17 +49,12 @@ typedef struct { 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; // Time (in ticks) when the touch registers were read last time uint32_t read_ticks; - // Set if the touch controller is currently touched - // (respectively, that we detected a touch event) - bool pressed; - // Previously reported x-coordinate - uint16_t last_x; - // Previously reported y-coordinate - uint16_t last_y; + // Last reported touch state + uint32_t state; + // Touch state machine for each task + touch_fsm_t tls[SYSTASK_MAX_TASKS]; } touch_driver_t; @@ -65,6 +63,9 @@ static touch_driver_t g_touch_driver = { .initialized = secfalse, }; +// Forward declarations +static const syshandle_vmt_t g_touch_handle_vmt; + // Reads a subsequent registers from the FT6X36. // // Returns: `sectrue` if the register was read @@ -192,7 +193,7 @@ static void ft6x36_power_down(void) { if (state == GPIO_PIN_SET) { // 90 ms for circuitry to stabilize (being conservative) - hal_delay(90); + systick_delay_ms(90); } #endif } @@ -214,7 +215,7 @@ static void ft6x36_power_up(void) { // Wait until the circuit fully kicks-in // (5ms is the minimum time required for the reset signal to be effective) - hal_delay(10); + systick_delay_ms(10); // Enable intterrupt input GPIO_InitTypeDef GPIO_InitStructure = {0}; @@ -230,7 +231,7 @@ static void ft6x36_power_up(void) { #endif // Wait for the touch controller to boot up - hal_delay(5); + systick_delay_ms(5); // Clear the flag indicating rising edge on INT_PIN __HAL_GPIO_EXTI_CLEAR_FLAG(TOUCH_INT_PIN); @@ -316,28 +317,29 @@ secbool touch_init(void) { goto cleanup; } - driver->init_ticks = hal_ticks_ms(); - driver->poll_ticks = driver->init_ticks; + if (!syshandle_register(SYSHANDLE_TOUCH, &g_touch_handle_vmt, driver)) { + goto cleanup; + } + + driver->init_ticks = systick_ms(); driver->read_ticks = driver->init_ticks; driver->initialized = sectrue; return sectrue; cleanup: - i2c_bus_close(driver->i2c_bus); - ft6x36_power_down(); - memset(driver, 0, sizeof(touch_driver_t)); + touch_deinit(); return secfalse; } void touch_deinit(void) { touch_driver_t* driver = &g_touch_driver; - + syshandle_unregister(SYSHANDLE_TOUCH); + i2c_bus_close(driver->i2c_bus); if (sectrue == driver->initialized) { - i2c_bus_close(driver->i2c_bus); ft6x36_power_down(); - memset(driver, 0, sizeof(touch_driver_t)); } + memset(driver, 0, sizeof(touch_driver_t)); } void touch_power_set(bool on) { @@ -355,7 +357,7 @@ secbool touch_ready(void) { 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) { + if ((int)(systick_ms() - driver->init_ticks) >= 310) { driver->ready = sectrue; } } @@ -386,7 +388,7 @@ uint8_t touch_get_version(void) { // 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); + systick_delay_ms(1); } ft6x36_wake_up(driver->i2c_bus); @@ -442,7 +444,7 @@ void trace_regs(uint8_t* regs) { event = '-'; } - uint32_t time = hal_ticks_ms() % 10000; + uint32_t time = systicks_ms() % 10000; printf("%04ld [gesture=%02X, nb_touches=%d, flags=%c, x=%3d, y=%3d]\r\n", time, gesture, nb_touches, event, x, y); @@ -461,17 +463,16 @@ void trace_event(uint32_t event) { uint32_t time = hal_ticks_ms() % 10000; - printf("%04ld [event=%c, x=%3d, y=%3d]\r\n", time, event_type, x, y); + systask_id_t task_id = systask_id(systask_active()); + + printf("%04ld [task=%d, event=%c, x=%3d, y=%3d]\r\n", time, task_id, + event_type, x, y); } #endif -uint32_t touch_get_event(void) { - touch_driver_t* driver = &g_touch_driver; - - if (sectrue != driver->initialized) { - return 0; - } - +// Reads touch registers and returns the last touch event +// (state of touch registers) the controller is reporting. +static uint32_t touch_get_state(touch_driver_t* driver) { // Content of registers 0x00 - 0x06 read from the touch controller uint8_t regs[7]; @@ -485,18 +486,16 @@ uint32_t touch_get_event(void) { 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; - // Test if the touch controller is polled too frequently // (less than 20ms since the last read) bool toofast = (int32_t)(ticks - driver->read_ticks) < 20 /* ms */; // 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 || toofast)) { - return 0; + bool pressed = (driver->state & TOUCH_START) || (driver->state & TOUCH_MOVE); + + if (!ft6x36_test_and_clear_interrupt() && (!pressed || toofast)) { + return driver->state; } driver->read_ticks = ticks; @@ -537,71 +536,35 @@ uint32_t touch_get_event(void) { ft6x36_panel_correction(x_raw, y_raw, &x, &y); - uint32_t event = 0; + uint32_t state = 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 { - if ((x != driver->last_x) || (y != driver->last_y)) { - // 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 { - // We have received the same coordinates as before, - // probably this is the same start event, or a quick bounce, - // we should ignore it. - } - } + state = TOUCH_START | xy; } 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; - } - } + state = TOUCH_MOVE | xy; } else if ((nb_touches == 0) && (flags == FT6X63_EVENT_LIFT_UP)) { - if (driver->pressed) { - // Finger was just lifted up - event = TOUCH_END | xy; - } else { - if (!starving && ((x != driver->last_x) || (y != driver->last_y))) { - // We have missed the PRESS_DOWN event. - // Report the start event only if the coordinates - // have changed and driver is not starving. - // This suggests that the previous touch was very short, - // or/and the driver is not called very frequently. - event = TOUCH_START | xy; - } else { - // Either the driver is starving or the coordinates - // have not changed, which would suggest that the TOUCH_END - // is repeated, so no event is needed -this should not happen - // since two consecutive LIFT_UPs are not possible due to - // testing the interrupt line before reading the registers. - } - } + state = TOUCH_END | xy; } - // remember the last state - if ((event & TOUCH_START) || (event & TOUCH_MOVE)) { - driver->pressed = true; - } else if (event & TOUCH_END) { - driver->pressed = false; + driver->state = state; + + return state; +} + +uint32_t touch_get_event(void) { + touch_driver_t* driver = &g_touch_driver; + + if (sectrue != driver->initialized) { + return 0; } - driver->last_x = x; - driver->last_y = y; + touch_fsm_t* fsm = &driver->tls[systask_id(systask_active())]; + + uint32_t touch_state = touch_get_state(driver); + + uint32_t event = touch_fsm_get_event(fsm, touch_state); #ifdef TOUCH_TRACE_EVENT trace_event(event); @@ -610,4 +573,41 @@ uint32_t touch_get_event(void) { return event; } +static void on_task_created(void* context, systask_id_t task_id) { + touch_driver_t* driver = (touch_driver_t*)context; + touch_fsm_t* fsm = &driver->tls[task_id]; + touch_fsm_init(fsm); +} + +static void on_event_poll(void* context, bool read_awaited, + bool write_awaited) { + touch_driver_t* driver = (touch_driver_t*)context; + UNUSED(write_awaited); + + if (read_awaited) { + uint32_t touch_state = touch_get_state(driver); + if (touch_state != 0) { + syshandle_signal_read_ready(SYSHANDLE_TOUCH, &touch_state); + } + } +} + +static bool on_check_read_ready(void* context, systask_id_t task_id, + void* param) { + touch_driver_t* driver = (touch_driver_t*)context; + touch_fsm_t* fsm = &driver->tls[task_id]; + + uint32_t touch_state = *(uint32_t*)param; + + return touch_fsm_event_ready(fsm, touch_state); +} + +static const syshandle_vmt_t g_touch_handle_vmt = { + .task_created = on_task_created, + .task_killed = NULL, + .check_read_ready = on_check_read_ready, + .check_write_ready = NULL, + .poll = on_event_poll, +}; + #endif // KERNEL_MODE diff --git a/core/embed/io/touch/ft6x36/ft6x36.h b/core/embed/io/touch/ft6x36/ft6x36.h index 852b8fea39..7548e75608 100644 --- a/core/embed/io/touch/ft6x36/ft6x36.h +++ b/core/embed/io/touch/ft6x36/ft6x36.h @@ -17,8 +17,7 @@ * along with this program. If not, see . */ -#ifndef _TOUCH_FT6X36_H -#define _TOUCH_FT6X36_H +#pragma once // I2C address of the FT6X36 on the I2C bus. #define FT6X36_I2C_ADDR 0x38 @@ -71,5 +70,3 @@ // ------------------------------------------------------------ #define FT6X36_GESTURE_NONE 0x00 - -#endif diff --git a/core/embed/io/touch/sitronix/sitronix.c b/core/embed/io/touch/sitronix/sitronix.c index dbda4fb393..22de533d92 100644 --- a/core/embed/io/touch/sitronix/sitronix.c +++ b/core/embed/io/touch/sitronix/sitronix.c @@ -658,6 +658,9 @@ __attribute__((optimize("-O0"))) int32_t SITRONIX_DetectTouch( * ****************************************************************************** */ + +#include "sitronix.h" + /* TS instances */ #define TS_INSTANCES_NBR 1U #define TS_TOUCH_NBR 10U @@ -671,13 +674,6 @@ __attribute__((optimize("-O0"))) int32_t SITRONIX_DetectTouch( /** @defgroup STM32U5x9J_DISCOVERY_TS_Exported_Types TS Exported Types * @{ */ -typedef struct { - uint32_t Width; /* Screen width */ - uint32_t Height; /* Screen height */ - uint32_t Orientation; /* Touch screen orientation */ - uint32_t Accuracy; /* Expressed in pixel and means the x or y difference vs - old position to consider the new values valid */ -} TS_Init_t; typedef struct { uint8_t MultiTouch; @@ -687,12 +683,6 @@ typedef struct { uint32_t MaxYl; } TS_Capabilities_t; -typedef struct { - uint32_t TouchDetected; - uint32_t TouchX; - uint32_t TouchY; -} TS_State_t; - typedef struct { uint32_t TouchDetected; uint32_t TouchX[2]; @@ -1206,119 +1196,4 @@ static int32_t SITRONIX_Probe(uint32_t Instance) { * @} */ -#include - -#include - -// 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) { - touch_driver_t *driver = &g_touch_driver; - - if (sectrue != driver->initialized) { - TS_Init_t 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)); - } -} - -void touch_power_set(bool on) { - // Not implemented for the discovery kit -} - -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; -} - -uint8_t touch_get_version(void) { - // Not implemented for the discovery kit - return 0; -} - -secbool touch_activity(void) { - touch_driver_t *driver = &g_touch_driver; - - if (sectrue != driver->initialized) { - return secfalse; - } - - TS_State_t new_state = {0}; - BSP_TS_GetState(0, &new_state); - - return sitronix_touching ? sectrue : secfalse; -} - -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.TouchDetected = (sitronix_touching != 0); - 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) { - if ((new_state.TouchX != driver->prev_state.TouchX) || - (new_state.TouchY != driver->prev_state.TouchY)) { - uint32_t xy = touch_pack_xy(new_state.TouchX, new_state.TouchY); - event = TOUCH_MOVE | xy; - } - } - - driver->prev_state = new_state; - - return event; -} - #endif diff --git a/core/embed/io/touch/sitronix/sitronix.h b/core/embed/io/touch/sitronix/sitronix.h new file mode 100644 index 0000000000..fa63dca282 --- /dev/null +++ b/core/embed/io/touch/sitronix/sitronix.h @@ -0,0 +1,34 @@ +#pragma once + +// TS orientations +#define TS_ORIENTATION_PORTRAIT 0U +#define TS_ORIENTATION_LANDSCAPE 1U +#define TS_ORIENTATION_PORTRAIT_ROT180 2U +#define TS_ORIENTATION_LANDSCAPE_ROT180 3U + +typedef struct { + // Screen width + uint32_t Width; + // Screen width + uint32_t Height; + // Touch screen orientation + uint32_t Orientation; + // Expressed in pixel and means the x or y difference vs + // old position to consider the new values valid + uint32_t Accuracy; + +} TS_Init_t; + +typedef struct { + uint32_t TouchDetected; + uint32_t TouchX; + uint32_t TouchY; +} TS_State_t; + +extern uint8_t sitronix_touching; + +int32_t BSP_TS_Init(uint32_t Instance, TS_Init_t *TS_Init); + +int32_t BSP_TS_DeInit(uint32_t Instance); + +int32_t BSP_TS_GetState(uint32_t Instance, TS_State_t *TS_State); diff --git a/core/embed/io/touch/sitronix/touch.c b/core/embed/io/touch/sitronix/touch.c new file mode 100644 index 0000000000..728f32fdb7 --- /dev/null +++ b/core/embed/io/touch/sitronix/touch.c @@ -0,0 +1,210 @@ +/* + * 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 . + */ + +#ifdef KERNEL_MODE + +#include + +#include +#include + +#include "../touch_fsm.h" +#include "sitronix.h" + +// Touch driver +typedef struct { + // Set if driver is initialized + secbool initialized; + // Last reported touch state + uint32_t state; + // Touch state machine for each task + touch_fsm_t tls[SYSTASK_MAX_TASKS]; + +} touch_driver_t; + +// Touch driver instance +static touch_driver_t g_touch_driver = { + .initialized = secfalse, +}; + +// Forward declarations +static const syshandle_vmt_t g_touch_handle_vmt; + +secbool touch_init(void) { + touch_driver_t* drv = &g_touch_driver; + + if (sectrue == drv->initialized) { + // The driver is already initialized + return sectrue; + } + + TS_Init_t TsInit; + + // Initialize the TouchScreen + TsInit.Width = 480; + TsInit.Height = 480; + TsInit.Orientation = 0; + TsInit.Accuracy = 2; + + if (0 != BSP_TS_Init(0, &TsInit)) { + goto cleanup; + } + + if (!syshandle_register(SYSHANDLE_TOUCH, &g_touch_handle_vmt, drv)) { + goto cleanup; + } + + drv->initialized = sectrue; + return sectrue; + +cleanup: + touch_deinit(); + return secfalse; +} + +void touch_deinit(void) { + touch_driver_t* drv = &g_touch_driver; + + BSP_TS_DeInit(0); + syshandle_unregister(SYSHANDLE_TOUCH); + + memset(drv, 0, sizeof(touch_driver_t)); +} + +void touch_power_set(bool on) { + // Not implemented for the discovery kit +} + +secbool touch_ready(void) { + touch_driver_t* drv = &g_touch_driver; + return drv->initialized; +} + +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; +} + +secbool touch_activity(void) { + touch_driver_t* drv = &g_touch_driver; + + if (sectrue != drv->initialized) { + return secfalse; + } + + TS_State_t new_state = {0}; + BSP_TS_GetState(0, &new_state); + + return sitronix_touching ? sectrue : secfalse; +} + +static uint32_t touch_get_state(touch_driver_t* drv) { + TS_State_t ts = {0}; + BSP_TS_GetState(0, &ts); + + uint32_t state = drv->state; + + ts.TouchDetected = sitronix_touching ? 1 : 0; + ts.TouchX = ts.TouchX > 120 ? ts.TouchX - 120 : 0; + ts.TouchY = ts.TouchY > 120 ? ts.TouchY - 120 : 0; + + uint32_t xy = touch_pack_xy(ts.TouchX, ts.TouchY); + + if (ts.TouchDetected) { + if ((drv->state & TOUCH_END) || (drv->state == 0)) { + state = TOUCH_START | xy; + } else if (drv->state & TOUCH_MOVE) { + state = TOUCH_MOVE | xy; + } else { + state = TOUCH_START | xy; + if (state != drv->state) { + state = TOUCH_MOVE | xy; + } + } + } else { + if (drv->state & (TOUCH_START | TOUCH_MOVE)) { + state = drv->state & ~(TOUCH_START | TOUCH_MOVE); + state |= TOUCH_END; + } + } + + drv->state = state; + return state; +} + +uint32_t touch_get_event(void) { + touch_driver_t* drv = &g_touch_driver; + + if (sectrue != drv->initialized) { + return 0; + } + + touch_fsm_t* fsm = &drv->tls[systask_id(systask_active())]; + + uint32_t touch_state = touch_get_state(drv); + + uint32_t event = touch_fsm_get_event(fsm, touch_state); + + return event; +} + +static void on_task_created(void* context, systask_id_t task_id) { + touch_driver_t* drv = (touch_driver_t*)context; + touch_fsm_t* fsm = &drv->tls[task_id]; + touch_fsm_init(fsm); +} + +static void on_event_poll(void* context, bool read_awaited, + bool write_awaited) { + touch_driver_t* drv = (touch_driver_t*)context; + + UNUSED(write_awaited); + + if (read_awaited) { + uint32_t touch_state = touch_get_state(drv); + if (touch_state != 0) { + syshandle_signal_read_ready(SYSHANDLE_TOUCH, &touch_state); + } + } +} + +static bool on_check_read_ready(void* context, systask_id_t task_id, + void* param) { + touch_driver_t* drv = (touch_driver_t*)context; + touch_fsm_t* fsm = &drv->tls[task_id]; + + uint32_t touch_state = *(uint32_t*)param; + + return touch_fsm_event_ready(fsm, touch_state); +} + +static const syshandle_vmt_t g_touch_handle_vmt = { + .task_created = on_task_created, + .task_killed = NULL, + .check_read_ready = on_check_read_ready, + .check_write_ready = NULL, + .poll = on_event_poll, +}; + +#endif // KERNEL_MODE diff --git a/core/embed/io/touch/stmpe811/stmpe811.c b/core/embed/io/touch/stmpe811/stmpe811.c index 6f92f3be45..a77c3f05e1 100644 --- a/core/embed/io/touch/stmpe811/stmpe811.c +++ b/core/embed/io/touch/stmpe811/stmpe811.c @@ -17,13 +17,13 @@ * along with this program. If not, see . */ -#include #include #ifdef KERNEL_MODE #include -#include +#include + #include "stmpe811.h" /* Chip IDs */ @@ -175,7 +175,7 @@ uint32_t I2cxTimeout = I2Cx_TIMEOUT_MAX; /*TouchDetected = touch_active(); + TsState->TouchDetected = _detected; + TsState->X = _x; + TsState->Y = _y; - if (TsState->TouchDetected) { - stmpe811_TS_GetXY(&x, &y); + bool detected = (IOE_Read(TS_I2C_ADDRESS, STMPE811_REG_TSC_CTRL) & + STMPE811_TS_CTRL_STATUS) != 0; - /* Y value first correction */ - y -= 360; + if (!detected) { + TsState->TouchDetected = _detected = false; + return; + } else { + if (IOE_Read(TS_I2C_ADDRESS, STMPE811_REG_FIFO_SIZE) > 0) { + stmpe811_TS_GetXY(&x, &y); - /* Y value second correction */ - yr = y / 11; + /* Y value first correction */ + y -= 360; - /* Return y position value */ - if (yr <= 0) { - yr = 0; - } else if (yr > 320) { - yr = 320 - 1; - } else { - yr = 320 - yr; - } - y = yr; + /* Y value second correction */ + yr = y / 11; - /* X value first correction */ - if (x <= 3000) { - x = 3870 - x; - } else { - x = 3800 - x; - } + /* Return y position value */ + if (yr <= 0) { + yr = 0; + } else if (yr > 320) { + yr = 320 - 1; + } else { + yr = 320 - yr; + } + y = yr; - /* X value second correction */ - xr = x / 15; + /* X value first correction */ + if (x <= 3000) { + x = 3870 - x; + } else { + x = 3800 - x; + } - /* Return X position value */ - if (xr <= 0) { - xr = 0; - } else if (xr > 240) { - xr = 240 - 1; - } else { - } + /* X value second correction */ + xr = x / 15; - x = xr; - xDiff = x > _x ? (x - _x) : (_x - x); - yDiff = y > _y ? (y - _y) : (_y - y); + /* Return X position value */ + if (xr <= 0) { + xr = 0; + } else if (xr > 240) { + xr = 240 - 1; + } else { + } - if (xDiff + yDiff > 5) { - _x = x; - _y = y; - } + x = xr; + xDiff = x > _x ? (x - _x) : (_x - x); + yDiff = y > _y ? (y - _y) : (_y - y); - /* Update the X position */ - TsState->X = _x; + if (xDiff + yDiff > 5) { + _x = x; + _y = y; + } - /* Update the Y position */ - TsState->Y = _y; - } -} + _detected = true; -typedef struct { - // Set if driver is initialized - secbool initialized; - // Last lower-level driver state - TS_StateTypeDef prev_state; + /* Update the X position */ + TsState->X = _x; -} 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) { - i2c_bus = i2c_bus_open(TOUCH_I2C_INSTANCE); - if (i2c_bus == NULL) { - return secfalse; - } - - stmpe811_Reset(); - touch_set_mode(); - - driver->initialized = sectrue; - } - - return driver->initialized; -} - -void touch_deinit(void) { - touch_driver_t *driver = &g_touch_driver; - - if (driver->initialized == sectrue) { - // Not implemented properly - - i2c_bus_close(i2c_bus); - memset(driver, 0, sizeof(touch_driver_t)); - } -} - -void touch_power_set(bool on) { - // Not implemented for the discovery kit -} - -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; -} - -uint8_t touch_get_version(void) { - // Not implemented for the discovery kit - return 0; -} - -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; -} - -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) { - if ((new_state.X != driver->prev_state.X) || - (new_state.Y != driver->prev_state.Y)) { - uint32_t xy = touch_pack_xy(new_state.X, new_state.Y); - event = TOUCH_MOVE | xy; + /* Update the Y position */ + TsState->Y = _y; } } - - driver->prev_state = new_state; - - return event; } #endif diff --git a/core/embed/io/touch/stmpe811/stmpe811.h b/core/embed/io/touch/stmpe811/stmpe811.h index bc35390458..29b900b3a4 100644 --- a/core/embed/io/touch/stmpe811/stmpe811.h +++ b/core/embed/io/touch/stmpe811/stmpe811.h @@ -1,6 +1,39 @@ +/* + * 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 . + */ +#pragma once -#ifndef _STMPE811_H -#define _STMPE811_H +#include -#endif //_STMPE811_H +#include + +typedef struct { + uint16_t TouchDetected; + uint16_t X; + uint16_t Y; + uint16_t Z; +} TS_StateTypeDef; + +void BSP_TS_GetState(TS_StateTypeDef *TsState); + +void stmpe811_Reset(i2c_bus_t *i2c_bus); + +void touch_set_mode(void); + +uint32_t touch_active(void); diff --git a/core/embed/io/touch/stmpe811/touch.c b/core/embed/io/touch/stmpe811/touch.c new file mode 100644 index 0000000000..78b647cd19 --- /dev/null +++ b/core/embed/io/touch/stmpe811/touch.c @@ -0,0 +1,199 @@ +/* + * 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 +#include + +#ifdef KERNEL_MODE + +#include +#include + +#include "../touch_fsm.h" +#include "stmpe811.h" + +typedef struct { + // Set if driver is initialized + secbool initialized; + // I2C Bus driver + i2c_bus_t* i2c_bus; + // Last reported touch state + uint32_t state; + // Touch state machine for each task + touch_fsm_t tls[SYSTASK_MAX_TASKS]; + +} touch_driver_t; + +// Touch driver instance +static touch_driver_t g_touch_driver = { + .initialized = secfalse, +}; + +// Forward declarations +static const syshandle_vmt_t g_touch_handle_vmt; + +secbool touch_init(void) { + touch_driver_t* drv = &g_touch_driver; + + if (drv->initialized == sectrue) { + return sectrue; + } + + memset(drv, 0, sizeof(drv)); + + drv->i2c_bus = i2c_bus_open(TOUCH_I2C_INSTANCE); + + if (drv->i2c_bus == NULL) { + goto cleanup; + } + + if (!syshandle_register(SYSHANDLE_TOUCH, &g_touch_handle_vmt, drv)) { + goto cleanup; + } + + stmpe811_Reset(drv->i2c_bus); + touch_set_mode(); + + drv->initialized = sectrue; + return sectrue; + +cleanup: + touch_deinit(); + return secfalse; +} + +void touch_deinit(void) { + touch_driver_t* drv = &g_touch_driver; + + syshandle_unregister(SYSHANDLE_TOUCH); + i2c_bus_close(drv->i2c_bus); + memset(drv, 0, sizeof(touch_driver_t)); +} + +void touch_power_set(bool on) { + // Not implemented for the discovery kit +} + +secbool touch_ready(void) { + touch_driver_t* drv = &g_touch_driver; + return drv->initialized; +} + +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; +} + +secbool touch_activity(void) { + return touch_active() ? sectrue : secfalse; + /* 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;*/ +} + +static uint32_t touch_get_state(touch_driver_t* drv) { + TS_StateTypeDef ts = {0}; + BSP_TS_GetState(&ts); + + uint32_t state = drv->state; + + uint32_t xy = touch_pack_xy(ts.X, ts.Y); + + if (ts.TouchDetected) { + if ((drv->state & TOUCH_END) || (drv->state == 0)) { + state = TOUCH_START | xy; + } else if (drv->state & TOUCH_MOVE) { + state = TOUCH_MOVE | xy; + } else { + state = TOUCH_START | xy; + if (state != drv->state) { + state = TOUCH_MOVE | xy; + } + } + } else { + if (drv->state & (TOUCH_START | TOUCH_MOVE)) { + state = drv->state & ~(TOUCH_START | TOUCH_MOVE); + state |= TOUCH_END; + } + } + + drv->state = state; + return state; +} + +uint32_t touch_get_event(void) { + touch_driver_t* drv = &g_touch_driver; + + if (sectrue != drv->initialized) { + return 0; + } + + touch_fsm_t* fsm = &drv->tls[systask_id(systask_active())]; + + uint32_t touch_state = touch_get_state(drv); + + uint32_t event = touch_fsm_get_event(fsm, touch_state); + + return event; +} + +static void on_task_created(void* context, systask_id_t task_id) { + touch_driver_t* drv = (touch_driver_t*)context; + touch_fsm_t* fsm = &drv->tls[task_id]; + touch_fsm_init(fsm); +} + +static void on_event_poll(void* context, bool read_awaited, + bool write_awaited) { + touch_driver_t* drv = (touch_driver_t*)context; + + UNUSED(write_awaited); + + if (read_awaited) { + uint32_t touch_state = touch_get_state(drv); + if (touch_state != 0) { + syshandle_signal_read_ready(SYSHANDLE_TOUCH, &touch_state); + } + } +} + +static bool on_check_read_ready(void* context, systask_id_t task_id, + void* param) { + touch_driver_t* drv = (touch_driver_t*)context; + touch_fsm_t* fsm = &drv->tls[task_id]; + + uint32_t touch_state = *(uint32_t*)param; + + return touch_fsm_event_ready(fsm, touch_state); +} + +static const syshandle_vmt_t g_touch_handle_vmt = { + .task_created = on_task_created, + .task_killed = NULL, + .check_read_ready = on_check_read_ready, + .check_write_ready = NULL, + .poll = on_event_poll, +}; + +#endif // KERNEL_MODE diff --git a/core/embed/io/touch/touch_fsm.c b/core/embed/io/touch/touch_fsm.c new file mode 100644 index 0000000000..b3d7f487c5 --- /dev/null +++ b/core/embed/io/touch/touch_fsm.c @@ -0,0 +1,118 @@ +/* + * 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 . + */ + +#ifdef KERNEL_MODE + +#include + +#include +#include + +#include "touch_fsm.h" + +void touch_fsm_init(touch_fsm_t* fsm) { + memset(fsm, 0, sizeof(touch_fsm_t)); + fsm->update_ticks = systick_ms(); +} + +bool touch_fsm_event_ready(touch_fsm_t* fsm, uint32_t touch_state) { + return fsm->state != touch_state; +} + +uint32_t touch_fsm_get_event(touch_fsm_t* fsm, uint32_t touch_state) { + fsm->state = touch_state; + + uint32_t ticks = hal_ticks_ms(); + + // Test if the touch_get_event() is starving (not called frequently enough) + bool starving = (int32_t)(ticks - fsm->update_ticks) > 300 /* ms */; + fsm->update_ticks = ticks; + + uint16_t x = touch_unpack_x(touch_state); + uint16_t y = touch_unpack_y(touch_state); + + uint32_t event = 0; + uint32_t xy = touch_pack_xy(x, y); + + if (touch_state & TOUCH_START) { + if (!fsm->pressed) { + // Finger was just pressed down + event = TOUCH_START | xy; + } else { + if ((x != fsm->last_x) || (y != fsm->last_y)) { + // 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(fsm->last_x, fsm->last_y); + } else { + // We have received the same coordinates as before, + // probably this is the same start event, or a quick bounce, + // we should ignore it. + } + } + } else if (touch_state & TOUCH_MOVE) { + if (fsm->pressed) { + if ((x != fsm->last_x) || (y != fsm->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 (touch_state & TOUCH_END) { + if (fsm->pressed) { + // Finger was just lifted up + event = TOUCH_END | xy; + } else { + if (!starving && ((x != fsm->last_x) || (y != fsm->last_y))) { + // We have missed the PRESS_DOWN event. + // Report the start event only if the coordinates + // have changed and driver is not starving. + // This suggests that the previous touch was very short, + // or/and the driver is not called very frequently. + event = TOUCH_START | xy; + } else { + // Either the driver is starving or the coordinates + // have not changed, which would suggest that the TOUCH_END + // is repeated, so no event is 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)) { + fsm->pressed = true; + } else if (event & TOUCH_END) { + fsm->pressed = false; + } + + fsm->last_x = x; + fsm->last_y = y; + + return event; +} + +#endif // KERNEL_MODE diff --git a/core/embed/io/touch/touch_fsm.h b/core/embed/io/touch/touch_fsm.h new file mode 100644 index 0000000000..642b37a98b --- /dev/null +++ b/core/embed/io/touch/touch_fsm.h @@ -0,0 +1,63 @@ +/* + * 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 . + */ + +#pragma once + +// This module is a simple finite state machine for touch events. +// +// It is designed to be used in a polling loop, where the state of the touch +// is read periodically. The module keeps track of the state changes and +// provides a simple interface to get the events that happened since the last +// call to touch_fsm_get_event(). +// +// The benefit of using this module is that it can properly handle situations +// when the touch panel is not read frequently enough or when some +// touch events are missed. +// +// The structure is designed to be used in a multi-threaded environment, where +// each thread has its own state machine. The state machines are stored in an +// array indexed by the task ID. + +typedef struct { + // Time (in ticks) when the tls was last updated + uint32_t update_ticks; + // Last reported touch state + uint32_t state; + // Set if the touch controller is currently touched + // (respectively, that we detected a touch event) + bool pressed; + // Previously reported x-coordinate + uint16_t last_x; + // Previously reported y-coordinate + uint16_t last_y; +} touch_fsm_t; + +// Initializes button finite state machine +void touch_fsm_init(touch_fsm_t* fsm); + +// Checks if touch_fsm_get_event() would return `true` on the next call +bool touch_fsm_event_ready(touch_fsm_t* fsm, uint32_t touch_state); + +// Processes the new state of thetouch panel and fills the event structure. +// +// `touch_state` is the current state of the touch panel. The state has +// the same format as the return value of `touch_get_state()`. +// +// Returns `true` if the event structure was filled. +uint32_t touch_fsm_get_event(touch_fsm_t* fsm, uint32_t touch_state); diff --git a/core/embed/io/touch/unix/touch.c b/core/embed/io/touch/unix/touch.c index 8391823a14..fa127ea1cb 100644 --- a/core/embed/io/touch/unix/touch.c +++ b/core/embed/io/touch/unix/touch.c @@ -20,9 +20,12 @@ #include #include -#include - #include +#include +#include +#include + +#include "../touch_fsm.h" extern int sdl_display_res_x, sdl_display_res_y; extern int sdl_touch_offset_x, sdl_touch_offset_y; @@ -41,11 +44,7 @@ typedef enum { IDLE, MOUSE_DOWN_INSIDE, MOUSE_DOWN_OUTSIDE, - BUTTON_SWIPE_LEFT_INITIATED, - BUTTON_SWIPE_RIGHT_INITIATED, - BUTTON_SWIPE_UP_INITIATED, - BUTTON_SWIPE_DOWN_INITIATED, - BUTTON_SWIPE_COMPLETED + BUTTON_SWIPE_INITIATED, } touch_state_t; typedef struct { @@ -53,9 +52,18 @@ typedef struct { secbool initialized; // Current state of the touch driver touch_state_t state; - // Last valid coordinates - int last_x; - int last_y; + + uint32_t swipe_time; + int swipe_start_x; + int swipe_start_y; + int swipe_end_x; + int swipe_end_y; + int swipe_key; + + // Last event not yet read + uint32_t last_event; + // Touch state machine for each task + touch_fsm_t tls[SYSTASK_MAX_TASKS]; } touch_driver_t; @@ -64,153 +72,164 @@ static touch_driver_t g_touch_driver = { .initialized = secfalse, }; +// Forward declarations +static const syshandle_vmt_t g_touch_handle_vmt; + static bool is_inside_display(int x, int y) { return x >= sdl_touch_offset_x && y >= sdl_touch_offset_y && x - sdl_touch_offset_x < sdl_display_res_x && y - sdl_touch_offset_y < sdl_display_res_y; } -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(touch_driver_t* drv, SDL_Event* event) { + bool inside_display = is_inside_display(event->button.x, event->button.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) { + switch (event->type) { case SDL_MOUSEBUTTONDOWN: if (inside_display) { - *ev_x = event.button.x - sdl_touch_offset_x; - *ev_y = event.button.y - sdl_touch_offset_y; - *ev_type = TOUCH_START; - driver->state = MOUSE_DOWN_INSIDE; + int x = event->button.x - sdl_touch_offset_x; + int y = event->button.y - sdl_touch_offset_y; + drv->last_event = TOUCH_START | touch_pack_xy(x, y); + drv->state = MOUSE_DOWN_INSIDE; } break; case SDL_MOUSEBUTTONUP: - 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; - driver->state = IDLE; + if (drv->state != IDLE) { + int x = inside_display ? event->button.x - sdl_touch_offset_x + : touch_unpack_x(drv->last_event); + int y = inside_display ? event->button.y - sdl_touch_offset_y + : touch_unpack_y(drv->last_event); + ; + drv->last_event = TOUCH_END | touch_pack_xy(x, y); + drv->state = IDLE; } break; case SDL_MOUSEMOTION: - if (driver->state != IDLE) { + if (drv->state != IDLE) { if (inside_display) { - *ev_x = event.motion.x - sdl_touch_offset_x; - *ev_y = event.motion.y - sdl_touch_offset_y; + int x = event->motion.x - sdl_touch_offset_x; + int y = event->motion.y - sdl_touch_offset_y; // simulate TOUCH_START if pressed in mouse returned on visible area - *ev_type = - (driver->state == MOUSE_DOWN_OUTSIDE) ? TOUCH_START : TOUCH_MOVE; - driver->state = MOUSE_DOWN_INSIDE; - } else { - if (driver->state == MOUSE_DOWN_INSIDE) { - // use last valid coordinates and simulate TOUCH_END - *ev_x = driver->last_x; - *ev_y = driver->last_y; - *ev_type = TOUCH_END; + if (drv->state == MOUSE_DOWN_OUTSIDE) { + drv->last_event = TOUCH_START | touch_pack_xy(x, y); + } else { + drv->last_event = TOUCH_MOVE | touch_pack_xy(x, y); } - driver->state = MOUSE_DOWN_OUTSIDE; + drv->state = MOUSE_DOWN_INSIDE; + } else { + if (drv->state == MOUSE_DOWN_INSIDE) { + // use last valid coordinates and simulate TOUCH_END + int x = touch_unpack_x(drv->last_event); + int y = touch_unpack_y(drv->last_event); + drv->last_event = TOUCH_END | touch_pack_xy(x, y); + } + drv->state = MOUSE_DOWN_OUTSIDE; } } break; } } -static void handle_button_events(touch_driver_t* driver, SDL_Event event, - int* ev_type, int* ev_x, int* ev_y) { +static void handle_button_events(touch_driver_t* drv, SDL_Event* event) { // 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(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; - 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; - 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; - 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; - driver->state = BUTTON_SWIPE_DOWN_INITIATED; - break; + if (event->type == SDL_KEYDOWN && !event->key.repeat) { + if (drv->state != BUTTON_SWIPE_INITIATED) { + switch (event->key.keysym.sym) { + case SDLK_LEFT: + drv->swipe_start_x = _btn_swipe_begin; + drv->swipe_start_y = sdl_display_res_y / 2; + drv->swipe_end_x = drv->swipe_start_x + _btn_swipe_length; + drv->swipe_end_y = drv->swipe_start_y; + drv->state = BUTTON_SWIPE_INITIATED; + break; + case SDLK_RIGHT: + drv->swipe_start_x = sdl_display_res_x - _btn_swipe_begin; + drv->swipe_start_y = sdl_display_res_y / 2; + drv->swipe_end_x = drv->swipe_start_x - _btn_swipe_length; + drv->swipe_end_y = drv->swipe_start_y; + drv->state = BUTTON_SWIPE_INITIATED; + break; + case SDLK_UP: + drv->swipe_start_x = sdl_display_res_x / 2; + drv->swipe_start_y = _btn_swipe_begin; + drv->swipe_end_x = drv->swipe_start_x; + drv->swipe_end_y = drv->swipe_start_y + _btn_swipe_length; + drv->state = BUTTON_SWIPE_INITIATED; + break; + case SDLK_DOWN: + drv->swipe_start_x = sdl_display_res_x / 2; + drv->swipe_start_y = sdl_display_res_y - _btn_swipe_begin; + drv->swipe_end_x = drv->swipe_start_x; + drv->swipe_end_y = drv->swipe_start_y - _btn_swipe_length; + drv->state = BUTTON_SWIPE_INITIATED; + break; + } + + if (drv->state == BUTTON_SWIPE_INITIATED) { + drv->swipe_key = event->key.keysym.sym; + drv->swipe_time = systick_ms(); + drv->last_event = + TOUCH_START | touch_pack_xy(drv->swipe_start_x, drv->swipe_start_y); + } } - } else if (event.type == SDL_KEYUP && driver->state != IDLE) { - switch (event.key.keysym.sym) { - case SDLK_LEFT: - 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; - driver->state = BUTTON_SWIPE_COMPLETED; - } - break; - case SDLK_RIGHT: - 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; - driver->state = BUTTON_SWIPE_COMPLETED; - } - break; - case SDLK_UP: - 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; - driver->state = BUTTON_SWIPE_COMPLETED; - } - break; - case SDLK_DOWN: - 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; - driver->state = BUTTON_SWIPE_COMPLETED; - } - break; + } else if (event->type == SDL_KEYUP && + event->key.keysym.sym == drv->swipe_key) { + if (drv->state == BUTTON_SWIPE_INITIATED) { + drv->last_event = + TOUCH_END | touch_pack_xy(drv->swipe_end_x, drv->swipe_end_y); + drv->state = IDLE; } } } +// Called from global event loop to filter and process SDL events +static void touch_sdl_event_filter(void* context, SDL_Event* sdl_event) { + touch_driver_t* drv = (touch_driver_t*)context; + + if (drv->state == IDLE || drv->state == MOUSE_DOWN_INSIDE || + drv->state == MOUSE_DOWN_OUTSIDE) { + handle_mouse_events(drv, sdl_event); + } + + if (drv->state == IDLE || drv->state == BUTTON_SWIPE_INITIATED) { + handle_button_events(drv, sdl_event); + } +} + secbool touch_init(void) { - touch_driver_t* driver = &g_touch_driver; + touch_driver_t* drv = &g_touch_driver; - if (driver->initialized != sectrue) { - memset(driver, 0, sizeof(touch_driver_t)); - driver->state = IDLE; - driver->initialized = sectrue; + if (drv->initialized) { + return sectrue; } - return driver->initialized; + memset(drv, 0, sizeof(touch_driver_t)); + drv->state = IDLE; + + if (!syshandle_register(SYSHANDLE_TOUCH, &g_touch_handle_vmt, drv)) { + goto cleanup; + } + + if (!sdl_events_register(touch_sdl_event_filter, drv)) { + goto cleanup; + } + + drv->initialized = sectrue; + return drv->initialized; + +cleanup: + return secfalse; } void touch_deinit(void) { - touch_driver_t* driver = &g_touch_driver; + touch_driver_t* drv = &g_touch_driver; - if (driver->initialized == sectrue) { - memset(driver, 0, sizeof(touch_driver_t)); + if (drv->initialized == sectrue) { + syshandle_unregister(SYSHANDLE_TOUCH); + memset(drv, 0, sizeof(touch_driver_t)); } } @@ -219,8 +238,8 @@ void touch_power_set(bool on) { } secbool touch_ready(void) { - touch_driver_t* driver = &g_touch_driver; - return driver->initialized; + touch_driver_t* drv = &g_touch_driver; + return drv->initialized; } secbool touch_set_sensitivity(uint8_t value) { @@ -241,37 +260,74 @@ secbool touch_activity(void) { } } +uint32_t touch_get_state(touch_driver_t* drv) { + sdl_events_poll(); + + if (drv->state == BUTTON_SWIPE_INITIATED) { + if (drv->last_event & TOUCH_START) { + // Emulate swipe by sending MOVE event after 100ms + uint32_t time_delta = systick_ms() - drv->swipe_time; + if (time_delta > 100) { + int x = (drv->swipe_start_x + drv->swipe_end_x) / 2; + int y = (drv->swipe_start_y + drv->swipe_end_y) / 2; + drv->last_event = TOUCH_MOVE | touch_pack_xy(x, y); + } + } + } + + return drv->last_event; +} + uint32_t touch_get_event(void) { touch_driver_t* driver = &g_touch_driver; - if (driver->initialized != sectrue) { + if (sectrue != driver->initialized) { return 0; } - if (driver->state == BUTTON_SWIPE_COMPLETED) { - driver->state = IDLE; - return TOUCH_END | touch_pack_xy(driver->last_x, driver->last_y); - } + touch_fsm_t* fsm = &driver->tls[systask_id(systask_active())]; - SDL_Event event; + uint32_t touch_state = touch_get_state(driver); - int ev_x = 0; - int ev_y = 0; - int ev_type = 0; + uint32_t event = touch_fsm_get_event(fsm, touch_state); - while (SDL_PollEvent(&event) > 0) { - 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 (driver->state == IDLE || is_button_swipe_initiated(driver)) { - handle_button_events(driver, event, &ev_type, &ev_x, &ev_y); - } - - if (ev_type != 0) { - driver->last_x = ev_x; - driver->last_y = ev_y; - } - } - return ev_type | touch_pack_xy(ev_x, ev_y); + return event; } + +static void on_task_created(void* context, systask_id_t task_id) { + touch_driver_t* dr = (touch_driver_t*)context; + touch_fsm_t* fsm = &dr->tls[task_id]; + touch_fsm_init(fsm); +} + +static void on_event_poll(void* context, bool read_awaited, + bool write_awaited) { + touch_driver_t* drv = (touch_driver_t*)context; + + UNUSED(write_awaited); + + if (read_awaited) { + uint32_t touch_state = touch_get_state(drv); + if (touch_state != 0) { + syshandle_signal_read_ready(SYSHANDLE_TOUCH, &touch_state); + } + } +} + +static bool on_check_read_ready(void* context, systask_id_t task_id, + void* param) { + touch_driver_t* drv = (touch_driver_t*)context; + touch_fsm_t* fsm = &drv->tls[task_id]; + + uint32_t touch_state = *(uint32_t*)param; + + return touch_fsm_event_ready(fsm, touch_state); +} + +static const syshandle_vmt_t g_touch_handle_vmt = { + .task_created = on_task_created, + .task_killed = NULL, + .check_read_ready = on_check_read_ready, + .check_write_ready = NULL, + .poll = on_event_poll, +}; diff --git a/core/site_scons/models/D001/discovery.py b/core/site_scons/models/D001/discovery.py index 644495b2fc..4991e0e8ed 100644 --- a/core/site_scons/models/D001/discovery.py +++ b/core/site_scons/models/D001/discovery.py @@ -72,6 +72,8 @@ def configure( if "input" in features_wanted: sources += ["embed/io/i2c_bus/stm32f4/i2c_bus.c"] sources += ["embed/io/touch/stmpe811/stmpe811.c"] + sources += ["embed/io/touch/stmpe811/touch.c"] + sources += ["embed/io/touch/touch_fsm.c"] paths += ["embed/io/i2c_bus/inc"] paths += ["embed/io/touch/inc"] features_available.append("touch") diff --git a/core/site_scons/models/D002/discovery2.py b/core/site_scons/models/D002/discovery2.py index f3499e8df8..c1abee7354 100644 --- a/core/site_scons/models/D002/discovery2.py +++ b/core/site_scons/models/D002/discovery2.py @@ -55,7 +55,9 @@ def configure( if "input" in features_wanted: sources += ["embed/io/i2c_bus/stm32u5/i2c_bus.c"] + sources += ["embed/io/touch/sitronix/touch.c"] sources += ["embed/io/touch/sitronix/sitronix.c"] + sources += ["embed/io/touch/touch_fsm.c"] paths += ["embed/io/i2c_bus/inc"] paths += ["embed/io/touch/inc"] features_available.append("touch") diff --git a/core/site_scons/models/T2T1/emulator.py b/core/site_scons/models/T2T1/emulator.py index a4ad3f97d9..0f88116cf8 100644 --- a/core/site_scons/models/T2T1/emulator.py +++ b/core/site_scons/models/T2T1/emulator.py @@ -52,6 +52,7 @@ def configure( if "input" in features_wanted: sources += ["embed/io/touch/unix/touch.c"] + sources += ["embed/io/touch/touch_fsm.c"] paths += ["embed/io/touch/inc"] features_available.append("touch") defines += [("USE_TOUCH", "1")] diff --git a/core/site_scons/models/T2T1/trezor_t.py b/core/site_scons/models/T2T1/trezor_t.py index 901ecb0b44..adc0b6767b 100644 --- a/core/site_scons/models/T2T1/trezor_t.py +++ b/core/site_scons/models/T2T1/trezor_t.py @@ -63,6 +63,7 @@ def configure( if "input" in features_wanted: sources += ["embed/io/i2c_bus/stm32f4/i2c_bus.c"] sources += ["embed/io/touch/ft6x36/ft6x36.c"] + sources += ["embed/io/touch/touch_fsm.c"] paths += ["embed/io/i2c_bus/inc"] paths += ["embed/io/touch/inc"] features_available.append("touch") diff --git a/core/site_scons/models/T3T1/emulator.py b/core/site_scons/models/T3T1/emulator.py index ad6af823e0..cae604fa76 100644 --- a/core/site_scons/models/T3T1/emulator.py +++ b/core/site_scons/models/T3T1/emulator.py @@ -62,6 +62,7 @@ def configure( if "input" in features_wanted: sources += ["embed/io/touch/unix/touch.c"] + sources += ["embed/io/touch/touch_fsm.c"] paths += ["embed/io/touch/inc"] features_available.append("touch") defines += [("USE_TOUCH", "1")] diff --git a/core/site_scons/models/T3T1/trezor_t3t1_revE.py b/core/site_scons/models/T3T1/trezor_t3t1_revE.py index a2042f34d0..c679672932 100644 --- a/core/site_scons/models/T3T1/trezor_t3t1_revE.py +++ b/core/site_scons/models/T3T1/trezor_t3t1_revE.py @@ -66,6 +66,7 @@ def configure( if "input" in features_wanted: sources += ["embed/io/i2c_bus/stm32u5/i2c_bus.c"] sources += ["embed/io/touch/ft6x36/ft6x36.c"] + sources += ["embed/io/touch/touch_fsm.c"] sources += ["embed/io/touch/ft6x36/panels/lx154a2422cpt23.c"] paths += ["embed/io/i2c_bus/inc"] paths += ["embed/io/touch/inc"] diff --git a/core/site_scons/models/T3W1/emulator.py b/core/site_scons/models/T3W1/emulator.py index cc5cf28d3c..816dc0aa77 100644 --- a/core/site_scons/models/T3W1/emulator.py +++ b/core/site_scons/models/T3W1/emulator.py @@ -81,6 +81,7 @@ def configure( if "input" in features_wanted: sources += ["embed/io/touch/unix/touch.c"] + sources += ["embed/io/touch/touch_fsm.c"] paths += ["embed/io/touch/inc"] features_available.append("touch") defines += [("USE_TOUCH", "1")] diff --git a/core/site_scons/models/T3W1/trezor_t3w1_revA.py b/core/site_scons/models/T3W1/trezor_t3w1_revA.py index 04b7e47fa7..5805fba6e6 100644 --- a/core/site_scons/models/T3W1/trezor_t3w1_revA.py +++ b/core/site_scons/models/T3W1/trezor_t3w1_revA.py @@ -60,6 +60,7 @@ def configure( if "input" in features_wanted: sources += ["embed/io/touch/ft6x36/ft6x36.c"] + sources += ["embed/io/touch/touch_fsm.c"] paths += ["embed/io/touch/inc"] features_available.append("touch") sources += ["embed/io/button/stm32/button.c"] diff --git a/core/site_scons/models/T3W1/trezor_t3w1_revB.py b/core/site_scons/models/T3W1/trezor_t3w1_revB.py index e36da80dab..9fe96e36ee 100644 --- a/core/site_scons/models/T3W1/trezor_t3w1_revB.py +++ b/core/site_scons/models/T3W1/trezor_t3w1_revB.py @@ -60,6 +60,7 @@ def configure( if "input" in features_wanted: sources += ["embed/io/touch/ft6x36/ft6x36.c"] + sources += ["embed/io/touch/touch_fsm.c"] paths += ["embed/io/touch/inc"] features_available.append("touch") sources += ["embed/io/button/stm32/button.c"] diff --git a/core/site_scons/models/T3W1/trezor_t3w1_revC.py b/core/site_scons/models/T3W1/trezor_t3w1_revC.py index 611e1d5eb0..09087a18c2 100644 --- a/core/site_scons/models/T3W1/trezor_t3w1_revC.py +++ b/core/site_scons/models/T3W1/trezor_t3w1_revC.py @@ -60,6 +60,7 @@ def configure( if "input" in features_wanted: sources += ["embed/io/touch/ft6x36/ft6x36.c"] + sources += ["embed/io/touch/touch_fsm.c"] paths += ["embed/io/touch/inc"] features_available.append("touch") sources += ["embed/io/button/stm32/button.c"]