diff --git a/core/.changelog.d/2366.added b/core/.changelog.d/2366.added new file mode 100644 index 0000000000..4a57faa8b4 --- /dev/null +++ b/core/.changelog.d/2366.added @@ -0,0 +1 @@ +Show blue dot when USB data pins are not connected diff --git a/core/embed/extmod/modtrezorio/modtrezorio-poll.h b/core/embed/extmod/modtrezorio/modtrezorio-poll.h index e67717fa06..59fb7b7377 100644 --- a/core/embed/extmod/modtrezorio/modtrezorio-poll.h +++ b/core/embed/extmod/modtrezorio/modtrezorio-poll.h @@ -24,11 +24,14 @@ #include "display.h" #include "embed/extmod/trezorobj.h" +#define USB_DATA_IFACE (253) #define BUTTON_IFACE (254) #define TOUCH_IFACE (255) #define POLL_READ (0x0000) #define POLL_WRITE (0x0100) +extern bool usb_connected_previously; + /// package: trezorio.__init__ /// def poll(ifaces: Iterable[int], list_ref: list, timeout_ms: int) -> bool: @@ -114,6 +117,14 @@ STATIC mp_obj_t mod_trezorio_poll(mp_obj_t ifaces, mp_obj_t list_ref, ret->items[1] = MP_OBJ_FROM_PTR(tuple); return mp_const_true; } + } else if (iface == USB_DATA_IFACE) { + bool usb_connected = usb_configured() == sectrue ? true : false; + if (usb_connected != usb_connected_previously) { + usb_connected_previously = usb_connected; + ret->items[0] = MP_OBJ_NEW_SMALL_INT(i); + ret->items[1] = usb_connected ? mp_const_true : mp_const_false; + return mp_const_true; + } } #elif defined TREZOR_MODEL_1 || defined TREZOR_MODEL_R else if (iface == BUTTON_IFACE) { diff --git a/core/embed/extmod/modtrezorio/modtrezorio.c b/core/embed/extmod/modtrezorio/modtrezorio.c index d28dc5d05e..20c2f748d7 100644 --- a/core/embed/extmod/modtrezorio/modtrezorio.c +++ b/core/embed/extmod/modtrezorio/modtrezorio.c @@ -31,6 +31,9 @@ #include "touch.h" #include "usb.h" +// Whether USB data pins were connected on last check (USB configured) +__attribute__((unused)) static bool usb_connected_previously = false; + #define CHECK_PARAM_RANGE(value, minimum, maximum) \ if (value < minimum || value > maximum) { \ mp_raise_ValueError(#value " is out of range"); \ @@ -67,6 +70,8 @@ /// BUTTON_LEFT: int # button number of left button /// BUTTON_RIGHT: int # button number of right button +/// USB_CHECK: int # interface id for check of USB data connection + /// WireInterface = Union[HID, WebUSB] STATIC const mp_rom_map_elem_t mp_module_trezorio_globals_table[] = { @@ -81,6 +86,8 @@ STATIC const mp_rom_map_elem_t mp_module_trezorio_globals_table[] = { {MP_ROM_QSTR(MP_QSTR_TOUCH_START), MP_ROM_INT((TOUCH_START >> 24) & 0xFFU)}, {MP_ROM_QSTR(MP_QSTR_TOUCH_MOVE), MP_ROM_INT((TOUCH_MOVE >> 24) & 0xFFU)}, {MP_ROM_QSTR(MP_QSTR_TOUCH_END), MP_ROM_INT((TOUCH_END >> 24) & 0xFFU)}, + + {MP_ROM_QSTR(MP_QSTR_USB_CHECK), MP_ROM_INT(USB_DATA_IFACE)}, #elif defined TREZOR_MODEL_1 || defined TREZOR_MODEL_R {MP_ROM_QSTR(MP_QSTR_BUTTON), MP_ROM_INT(BUTTON_IFACE)}, {MP_ROM_QSTR(MP_QSTR_BUTTON_PRESSED), diff --git a/core/embed/extmod/modtrezorutils/modtrezorutils.c b/core/embed/extmod/modtrezorutils/modtrezorutils.c index 13e4ea4ea6..4021255779 100644 --- a/core/embed/extmod/modtrezorutils/modtrezorutils.c +++ b/core/embed/extmod/modtrezorutils/modtrezorutils.c @@ -34,6 +34,7 @@ #include "blake2s.h" #include "common.h" #include "flash.h" +#include "usb.h" #ifndef TREZOR_EMULATOR #include "image.h" @@ -221,6 +222,17 @@ STATIC mp_obj_t mod_trezorutils_reboot_to_bootloader() { STATIC MP_DEFINE_CONST_FUN_OBJ_0(mod_trezorutils_reboot_to_bootloader_obj, mod_trezorutils_reboot_to_bootloader); +/// def usb_data_connected() -> bool: +/// """ +/// Returns whether USB has been enumerated/configured +/// (and is not just connected by cable without data pins) +/// """ +STATIC mp_obj_t mod_trezorutils_usb_data_connected() { + return usb_configured() == sectrue ? mp_const_true : mp_const_false; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_0(mod_trezorutils_usb_data_connected_obj, + mod_trezorutils_usb_data_connected); + STATIC mp_obj_str_t mod_trezorutils_revision_obj = { {&mp_type_bytes}, 0, sizeof(SCM_REVISION) - 1, (const byte *)SCM_REVISION}; @@ -243,6 +255,8 @@ STATIC const mp_rom_map_elem_t mp_module_trezorutils_globals_table[] = { MP_ROM_PTR(&mod_trezorutils_firmware_vendor_obj)}, {MP_ROM_QSTR(MP_QSTR_reboot_to_bootloader), MP_ROM_PTR(&mod_trezorutils_reboot_to_bootloader_obj)}, + {MP_ROM_QSTR(MP_QSTR_usb_data_connected), + MP_ROM_PTR(&mod_trezorutils_usb_data_connected_obj)}, // various built-in constants {MP_ROM_QSTR(MP_QSTR_SCM_REVISION), MP_ROM_PTR(&mod_trezorutils_revision_obj)}, diff --git a/core/embed/trezorhal/usb.c b/core/embed/trezorhal/usb.c index a2038cdbf9..bae24dd88d 100644 --- a/core/embed/trezorhal/usb.c +++ b/core/embed/trezorhal/usb.c @@ -152,6 +152,15 @@ void usb_start(void) { USBD_Start(&usb_dev_handle); } void usb_stop(void) { USBD_Stop(&usb_dev_handle); } +secbool usb_configured(void) { + USBD_HandleTypeDef *pdev = &usb_dev_handle; + if (pdev->dev_state == USBD_STATE_CONFIGURED) { + return sectrue; + } + + return secfalse; +} + /* * Utility functions for USB interfaces */ diff --git a/core/embed/trezorhal/usb.h b/core/embed/trezorhal/usb.h index fcd682a3c3..b9b27a4de8 100644 --- a/core/embed/trezorhal/usb.h +++ b/core/embed/trezorhal/usb.h @@ -143,5 +143,6 @@ void usb_init(const usb_dev_info_t *dev_info); void usb_deinit(void); void usb_start(void); void usb_stop(void); +secbool usb_configured(void); #endif diff --git a/core/embed/unix/usb.c b/core/embed/unix/usb.c index 1854bd9d1b..fb3c6f7cd4 100644 --- a/core/embed/unix/usb.c +++ b/core/embed/unix/usb.c @@ -25,6 +25,7 @@ #include #include #include +#include #include "touch.h" #include "usb.h" @@ -249,3 +250,11 @@ int usb_webusb_write(uint8_t iface_num, const uint8_t *buf, uint32_t len) { void pendsv_kbd_intr(void) {} void mp_hal_set_vcp_iface(int iface_num) {} + +secbool usb_configured(void) { + if (access("/var/tmp/trezor.usb_data_disconnected", F_OK) == 0) { + return secfalse; + } + + return sectrue; +} diff --git a/core/mocks/generated/trezorio/__init__.pyi b/core/mocks/generated/trezorio/__init__.pyi index 4435c2c955..e84de507c0 100644 --- a/core/mocks/generated/trezorio/__init__.pyi +++ b/core/mocks/generated/trezorio/__init__.pyi @@ -202,4 +202,5 @@ BUTTON_PRESSED: int # button down event BUTTON_RELEASED: int # button up event BUTTON_LEFT: int # button number of left button BUTTON_RIGHT: int # button number of right button +USB_CHECK: int # interface id for check of USB data connection WireInterface = Union[HID, WebUSB] diff --git a/core/mocks/generated/trezorutils.pyi b/core/mocks/generated/trezorutils.pyi index 671eced571..177a42288a 100644 --- a/core/mocks/generated/trezorutils.pyi +++ b/core/mocks/generated/trezorutils.pyi @@ -65,6 +65,14 @@ def reboot_to_bootloader() -> None: """ Reboots to bootloader. """ + + +# extmod/modtrezorutils/modtrezorutils.c +def usb_data_connected() -> bool: + """ + Returns whether USB has been enumerated/configured + (and is not just connected by cable without data pins) + """ SCM_REVISION: bytes VERSION_MAJOR: int VERSION_MINOR: int diff --git a/core/src/apps/homescreen/homescreen.py b/core/src/apps/homescreen/homescreen.py index 891aa13903..c2a00a6b6d 100644 --- a/core/src/apps/homescreen/homescreen.py +++ b/core/src/apps/homescreen/homescreen.py @@ -1,10 +1,11 @@ import utime from micropython import const +from typing import Tuple import storage import storage.cache import storage.device -from trezor import config, ui, utils +from trezor import config, io, loop, ui, utils from trezor.ui.loader import Loader, LoaderNeutral from apps.base import lock_device @@ -25,6 +26,7 @@ class Homescreen(HomescreenBase): def __init__(self) -> None: super().__init__() + self.is_connected = False if not storage.device.is_initialized(): self.label = "Go to trezor.io/start" @@ -36,6 +38,17 @@ class Homescreen(HomescreenBase): ) self.touch_ms: int | None = None + def create_tasks(self) -> Tuple[loop.AwaitableTask, ...]: + return super().create_tasks() + (self.usb_checker_task(),) + + async def usb_checker_task(self) -> None: + usbcheck = loop.wait(io.USB_CHECK) + while True: + is_connected = await usbcheck + if is_connected != self.is_connected: + self.is_connected = is_connected + self.set_repaint(True) + def do_render(self) -> None: # warning bar on top if storage.device.is_initialized() and storage.device.no_backup(): @@ -54,6 +67,9 @@ class Homescreen(HomescreenBase): # homescreen with shifted avatar and text on bottom # Differs for each model + if not utils.usb_data_connected(): + ui.display.bar(0, 0, 8, 8, ui.BLUE) + # TODO: support homescreen avatar change for R and 1 if utils.MODEL in ("T",): ui.display.avatar(48, 48 - 10, self.get_image(), ui.WHITE, ui.BLACK) @@ -69,6 +85,8 @@ class Homescreen(HomescreenBase): ui.WIDTH // 2, label_heights[utils.MODEL], self.label, ui.BOLD, ui.FG, ui.BG ) + ui.refresh() + def on_touch_start(self, _x: int, _y: int) -> None: if self.loader.start_ms is not None: self.loader.start() diff --git a/core/src/trezor/utils.py b/core/src/trezor/utils.py index f96d792f42..1a73310796 100644 --- a/core/src/trezor/utils.py +++ b/core/src/trezor/utils.py @@ -14,6 +14,7 @@ from trezorutils import ( # noqa: F401 halt, memcpy, reboot_to_bootloader, + usb_data_connected, ) from typing import TYPE_CHECKING