diff --git a/core/embed/io/usb/stm32/usb_class_vcp.c b/core/embed/io/usb/stm32/usb_class_vcp.c index 7e2be20ecd..288303fec2 100644 --- a/core/embed/io/usb/stm32/usb_class_vcp.c +++ b/core/embed/io/usb/stm32/usb_class_vcp.c @@ -22,6 +22,7 @@ #include #include +#include #include "usb_internal.h" @@ -162,6 +163,8 @@ _Static_assert(sizeof(usb_vcp_state_t) <= USBD_CLASS_STATE_MAX_SIZE); static const USBD_ClassTypeDef usb_vcp_class; static const USBD_ClassTypeDef usb_vcp_data_class; +static const syshandle_vmt_t usb_vcp_handle_vmt; + #define usb_get_vcp_state(iface_num) \ ((usb_vcp_state_t *)usb_get_iface_state(iface_num, &usb_vcp_class)) @@ -449,12 +452,22 @@ static uint8_t usb_vcp_class_init(USBD_HandleTypeDef *dev, uint8_t cfg_idx) { USBD_LL_PrepareReceive(dev, state->ep_out, state->rx_packet, state->max_packet_len); + uint8_t iface_num = state->desc_block->iface_data.bInterfaceNumber; + syshandle_t handle = SYSHANDLE_USB_IFACE_0 + iface_num; + if (!syshandle_register(handle, &usb_vcp_handle_vmt, state)) { + return USBD_FAIL; + } + return USBD_OK; } static uint8_t usb_vcp_class_deinit(USBD_HandleTypeDef *dev, uint8_t cfg_idx) { usb_vcp_state_t *state = (usb_vcp_state_t *)dev->pUserData; + uint8_t iface_num = state->desc_block->iface_data.bInterfaceNumber; + syshandle_t handle = SYSHANDLE_USB_IFACE_0 + iface_num; + syshandle_unregister(handle); + // Flush endpoints USBD_LL_FlushEP(dev, state->ep_in); USBD_LL_FlushEP(dev, state->ep_out); @@ -590,4 +603,54 @@ static const USBD_ClassTypeDef usb_vcp_class = { static const USBD_ClassTypeDef usb_vcp_data_class = {}; +static void on_event_poll(void *context, bool read_awaited, + bool write_awaited) { + usb_vcp_state_t *state = (usb_vcp_state_t *)context; + + uint8_t iface_num = state->desc_block->iface_data.bInterfaceNumber; + syshandle_t handle = SYSHANDLE_USB_IFACE_0 + iface_num; + + // Only one task can read or write at a time. Therefore, we can + // assume that only one task is waiting for events and keep the + // logic simple. + + if (read_awaited && usb_vcp_can_read(iface_num)) { + syshandle_signal_read_ready(handle, NULL); + } + + if (write_awaited && usb_vcp_can_write(iface_num)) { + syshandle_signal_write_ready(handle, NULL); + } +} + +static bool on_check_read_ready(void *context, systask_id_t task_id, + void *param) { + usb_vcp_state_t *state = (usb_vcp_state_t *)context; + uint8_t iface_num = state->desc_block->iface_data.bInterfaceNumber; + + UNUSED(task_id); + UNUSED(param); + + return usb_vcp_can_read(iface_num); +} + +static bool on_check_write_ready(void *context, systask_id_t task_id, + void *param) { + usb_vcp_state_t *state = (usb_vcp_state_t *)context; + uint8_t iface_num = state->desc_block->iface_data.bInterfaceNumber; + + UNUSED(task_id); + UNUSED(param); + + return usb_vcp_can_write(iface_num); +} + +static const syshandle_vmt_t usb_vcp_handle_vmt = { + .task_created = NULL, + .task_killed = NULL, + .check_read_ready = on_check_read_ready, + .check_write_ready = on_check_write_ready, + .poll = on_event_poll, +}; + #endif // KERNEL_MODE