diff --git a/micropython/firmware/main.c b/micropython/firmware/main.c index ed7031b6e3..7554e9cfe3 100644 --- a/micropython/firmware/main.c +++ b/micropython/firmware/main.c @@ -64,14 +64,21 @@ int usb_init_all(void) { .report_desc_len = sizeof(hid_report_desc), .report_desc = hid_report_desc, }; + static uint8_t vcp_rx_buffer[1024]; + static uint8_t vcp_rx_packet[64]; static const usb_vcp_info_t vcp_info = { .iface_num = 0x01, .data_iface_num = 0x02, .ep_cmd = USB_EP_DIR_IN | 0x02, .ep_in = USB_EP_DIR_IN | 0x03, .ep_out = USB_EP_DIR_OUT | 0x03, - .polling_interval = 1, - .max_data_packet_len = 64, + .polling_interval = 10, + .max_data_packet_len = sizeof(vcp_rx_packet), + .rx_packet = vcp_rx_packet, + .rx_buffer_len = sizeof(vcp_rx_buffer), + .rx_buffer = vcp_rx_buffer, + .rx_intr_val = 3, // Ctrl-C + .rx_intr_fn = pendsv_kbd_intr, }; if (0 != usb_init(&dev_info)) { diff --git a/micropython/firmware/mphalport.c b/micropython/firmware/mphalport.c index 32446fbfd6..92a19c16ec 100644 --- a/micropython/firmware/mphalport.c +++ b/micropython/firmware/mphalport.c @@ -2,12 +2,13 @@ #include "usb.h" #define VCP_IFACE 0x01 -#define VCP_WRITE_TIMEOUT 25 +#define VCP_READ_TIMEOUT 25 +#define VCP_WRITE_TIMEOUT 5 int mp_hal_stdin_rx_chr(void) { - for (;;) { - - } + uint8_t c = 0; + while (usb_vcp_read_blocking(VCP_IFACE, &c, 1, VCP_READ_TIMEOUT) < 1); + return c; } void mp_hal_stdout_tx_strn(const char *str, size_t len) { diff --git a/micropython/trezorhal/usb.c b/micropython/trezorhal/usb.c index 8dd6fc4fe4..a504e2f7d9 100644 --- a/micropython/trezorhal/usb.c +++ b/micropython/trezorhal/usb.c @@ -266,19 +266,6 @@ static uint8_t usb_class_setup(USBD_HandleTypeDef *dev, USBD_SetupReqTypedef *re } } -static uint8_t usb_class_ep0_rx_ready(USBD_HandleTypeDef *dev) { - for (int i = 0; i < USBD_MAX_NUM_INTERFACES; i++) { - switch (usb_ifaces[i].type) { - case USB_IFACE_TYPE_VCP: - usb_vcp_class_ep0_rx_ready(dev, &usb_ifaces[i].vcp); - break; - default: - break; - } - } - return USBD_OK; -} - static uint8_t usb_class_data_in(USBD_HandleTypeDef *dev, uint8_t ep_num) { for (int i = 0; i < USBD_MAX_NUM_INTERFACES; i++) { switch (usb_ifaces[i].type) { @@ -321,7 +308,7 @@ static const USBD_ClassTypeDef usb_class = { .DeInit = usb_class_deinit, .Setup = usb_class_setup, .EP0_TxSent = NULL, - .EP0_RxReady = usb_class_ep0_rx_ready, + .EP0_RxReady = NULL, .DataIn = usb_class_data_in, .DataOut = usb_class_data_out, .SOF = NULL, diff --git a/micropython/trezorhal/usb_vcp-defs.h b/micropython/trezorhal/usb_vcp-defs.h index 237f9edbdb..379bc0c7d7 100644 --- a/micropython/trezorhal/usb_vcp-defs.h +++ b/micropython/trezorhal/usb_vcp-defs.h @@ -5,6 +5,8 @@ * see LICENSE file for details */ +#include + typedef struct __attribute__((packed)) { uint8_t bFunctionLength; uint8_t bDescriptorType; @@ -69,6 +71,8 @@ typedef enum { USB_CDC_SPACE_PARITY = 4, } usb_cdc_line_coding_bParityType_t; +typedef void (*usb_vcp_intr_fn_t)(void); + typedef struct { uint8_t iface_num; // Address of this VCP interface uint8_t data_iface_num; // Address of data interface of the VCP interface association @@ -77,15 +81,27 @@ typedef struct { uint8_t ep_out; // Address of OUT endpoint uint8_t polling_interval; // In units of 1ms uint8_t max_data_packet_len; + + size_t rx_buffer_len; // Length of rx_buffer, power of 2 + uint8_t *rx_buffer; // With length of rx_buffer_len bytes + uint8_t *rx_packet; // With length of at least max_data_packet_len + + uint8_t rx_intr_val; // Value matched against every received byte + usb_vcp_intr_fn_t rx_intr_fn; // Callback called if rx_intr_val matches + } usb_vcp_info_t; +typedef struct { + size_t cap; + volatile size_t read; + volatile size_t write; + uint8_t *buf; +} ring_buffer_t; + typedef struct { uint8_t is_connected; uint8_t in_idle; - // uint8_t cmd_op_code; - // uint8_t cmd_length; - // Configuration (copied from usb_vcp_info_t on init) uint8_t data_iface_num; uint8_t ep_cmd; uint8_t ep_in; @@ -93,6 +109,12 @@ typedef struct { uint8_t polling_interval; uint8_t max_data_packet_len; + uint8_t *rx_packet; + ring_buffer_t rx_ring; + + uint8_t rx_intr_val; + usb_vcp_intr_fn_t rx_intr_fn; + const usb_vcp_descriptor_block_t *desc_block; } usb_vcp_state_t; diff --git a/micropython/trezorhal/usb_vcp-impl.h b/micropython/trezorhal/usb_vcp-impl.h index 7c0ea2b184..f089d287eb 100644 --- a/micropython/trezorhal/usb_vcp-impl.h +++ b/micropython/trezorhal/usb_vcp-impl.h @@ -27,6 +27,11 @@ #define USB_DESC_TYPE_ACM 0x02 #define USB_DESC_TYPE_UNION 0x06 +// Data Phase Transfer Direction (bmRequest) +#define USB_REQ_DIR_MASK 0x80 +#define USB_H2D 0x00 +#define USB_D2H 0x80 + // Class-Specific Request Codes for PSTN subclasses #define USB_CDC_GET_LINE_CODING 0x21 #define USB_CDC_SET_CONTROL_LINE_STATE 0x22 @@ -34,6 +39,18 @@ // Maximal length of packets on IN CMD EP #define USB_CDC_MAX_CMD_PACKET_LEN 0x08 +static inline size_t ring_length(ring_buffer_t *b) { + return (b->write - b->read); +} + +static inline int ring_empty(ring_buffer_t *b) { + return ring_length(b) == 0; +} + +static inline int ring_full(ring_buffer_t *b) { + return ring_length(b) == b->cap; +} + /* usb_vcp_add adds and configures new USB VCP interface according to * configuration options passed in `info`. */ int usb_vcp_add(const usb_vcp_info_t *info) { @@ -62,6 +79,15 @@ int usb_vcp_add(const usb_vcp_info_t *info) { if ((info->ep_out & USB_EP_DIR_MSK) != USB_EP_DIR_OUT) { return 1; // OUT EP is invalid } + if ((info->rx_buffer_len == 0) || (info->rx_buffer_len & (info->rx_buffer_len - 1)) != 0) { + return 1; // Capacity needs to be a power of 2 + } + if (info->rx_buffer == NULL) { + return 1; + } + if (info->rx_packet == NULL) { + return 1; + } // Interface association descriptor d->assoc.bLength = sizeof(usb_interface_assoc_descriptor_t); @@ -151,18 +177,38 @@ int usb_vcp_add(const usb_vcp_info_t *info) { // Interface state iface->type = USB_IFACE_TYPE_VCP; + iface->vcp.is_connected = 0; + iface->vcp.in_idle = 1; iface->vcp.data_iface_num = info->data_iface_num; iface->vcp.ep_cmd = info->ep_cmd; iface->vcp.ep_in = info->ep_in; iface->vcp.ep_out = info->ep_out; + iface->vcp.polling_interval = info->polling_interval; iface->vcp.max_data_packet_len = info->max_data_packet_len; + iface->vcp.rx_packet = info->rx_packet; + iface->vcp.rx_ring.cap = info->rx_buffer_len; + iface->vcp.rx_ring.buf = info->rx_buffer; + iface->vcp.rx_ring.read = 0; + iface->vcp.rx_ring.write = 0; + iface->vcp.rx_intr_val = info->rx_intr_val; + iface->vcp.rx_intr_fn = info->rx_intr_fn; iface->vcp.desc_block = d; return 0; } int usb_vcp_can_read(uint8_t iface_num) { - return 0; + usb_iface_t *iface = usb_get_iface(iface_num); + if (iface == NULL) { + return 0; // Invalid interface number + } + if (iface->type != USB_IFACE_TYPE_VCP) { + return 0; // Invalid interface type + } + if (ring_empty(&iface->vcp.rx_ring)) { + return 0; // Nothing in the rx buffer + } + return 1; } int usb_vcp_can_write(uint8_t iface_num) { @@ -176,6 +222,9 @@ int usb_vcp_can_write(uint8_t iface_num) { if (iface->vcp.in_idle == 0) { return 0; // Last transmission is not over yet } + // if (iface->vcp.is_connected == 0) { + // return 0; // Receiving end is not connected + // } if (usb_dev_handle.dev_state != USBD_STATE_CONFIGURED) { return 0; // Device is not configured } @@ -190,10 +239,17 @@ int usb_vcp_read(uint8_t iface_num, uint8_t *buf, uint32_t len) { if (iface->type != USB_IFACE_TYPE_VCP) { return -2; // Interface interface type } - // usb_vcp_state_t *state = &iface->vcp; - // TODO + usb_vcp_state_t *state = &iface->vcp; - return 0; + // Read from the rx ring buffer + ring_buffer_t *b = &state->rx_ring; + size_t mask = b->cap - 1; + size_t i; + for (i = 0; (i < len) && !ring_empty(b); i++) { + buf[i] = b->buf[b->read & mask]; + b->read++; + } + return i; } int usb_vcp_write(uint8_t iface_num, const uint8_t *buf, uint32_t len) { @@ -206,9 +262,9 @@ int usb_vcp_write(uint8_t iface_num, const uint8_t *buf, uint32_t len) { } usb_vcp_state_t *state = &iface->vcp; - if (!state->is_connected) { - return 0; - } + // if (!state->is_connected) { + // return 0; + // } state->in_idle = 0; USBD_LL_Transmit(&usb_dev_handle, state->ep_in, UNCONST(buf), (uint16_t)len); @@ -245,11 +301,13 @@ static int usb_vcp_class_init(USBD_HandleTypeDef *dev, usb_vcp_state_t *state, u USBD_LL_OpenEP(dev, state->ep_cmd, USBD_EP_TYPE_INTR, USB_CDC_MAX_CMD_PACKET_LEN); // Reset the state + state->rx_ring.read = 0; + state->rx_ring.write = 0; + state->is_connected = 0; state->in_idle = 1; - // TODO // Prepare the OUT EP to receive next packet - // USBD_LL_PrepareReceive(dev, state->ep_out, state->rx_buffer, state->max_data_packet_len); + USBD_LL_PrepareReceive(dev, state->ep_out, state->rx_packet, state->max_data_packet_len); return USBD_OK; } @@ -273,16 +331,20 @@ static int usb_vcp_class_setup(USBD_HandleTypeDef *dev, usb_vcp_state_t *state, switch (req->bmRequest & USB_REQ_TYPE_MASK) { - // Class request - case USB_REQ_TYPE_CLASS : - switch (req->bRequest) { + case USB_REQ_TYPE_CLASS: + switch (req->bmRequest & USB_REQ_DIR_MASK) { - case USB_CDC_GET_LINE_CODING: - USBD_CtlSendData(dev, (uint8_t *)(&line_coding), sizeof(line_coding)); - break; + case USB_D2H: + switch (req->bRequest) { - case USB_CDC_SET_CONTROL_LINE_STATE: - state->is_connected = req->wLength & 1; + case USB_CDC_GET_LINE_CODING: + USBD_CtlSendData(dev, (uint8_t *)(&line_coding), MIN(req->wLength, sizeof(line_coding))); + break; + + case USB_CDC_SET_CONTROL_LINE_STATE: + state->is_connected = req->wLength & 1; + break; + } break; } break; @@ -299,6 +361,25 @@ static uint8_t usb_vcp_class_data_in(USBD_HandleTypeDef *dev, usb_vcp_state_t *s } static uint8_t usb_vcp_class_data_out(USBD_HandleTypeDef *dev, usb_vcp_state_t *state, uint8_t ep_num) { - // TODO: process received data + if (ep_num == state->ep_out) { + uint32_t len = USBD_LL_GetRxDataSize(dev, ep_num); + + // Write into the rx ring buffer + ring_buffer_t *b = &state->rx_ring; + size_t mask = b->cap - 1; + size_t i; + for (i = 0; i < len; i++) { + if ((state->rx_intr_fn != NULL) && (state->rx_packet[i] == state->rx_intr_val)) { + state->rx_intr_fn(); + } + if (!ring_full(b)) { + b->buf[b->write & mask] = state->rx_packet[i]; + b->write++; + } + } + + // Prepare the OUT EP to receive next packet + USBD_LL_PrepareReceive(dev, state->ep_out, state->rx_packet, state->max_data_packet_len); + } return USBD_OK; }