trezorhal: implement vcp read and ctrl-c soft reset, fix write

This commit is contained in:
Jan Pochyla 2017-04-10 18:29:47 +02:00
parent 137d27d69e
commit 28d6b38f6f
5 changed files with 139 additions and 41 deletions

View File

@ -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)) {

View File

@ -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) {

View File

@ -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,

View File

@ -5,6 +5,8 @@
* see LICENSE file for details
*/
#include <stddef.h>
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;

View File

@ -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;
}