1
0
mirror of https://github.com/trezor/trezor-firmware.git synced 2024-12-22 22:38:08 +00:00

trezorhal: add cdc vcp tx

TODO: rx support
TODO: buffering
TODO: cleanup
This commit is contained in:
Jan Pochyla 2017-03-30 16:06:01 +02:00 committed by Pavol Rusnak
parent e87d0d3905
commit 38b99b306a
6 changed files with 477 additions and 138 deletions

View File

@ -33,19 +33,29 @@ int usb_init_all(void) {
.configuration_str = (uint8_t *)"configuration_str", .configuration_str = (uint8_t *)"configuration_str",
.interface_str = (uint8_t *)"interface_str", .interface_str = (uint8_t *)"interface_str",
}; };
static uint8_t rx_buffer[64]; static uint8_t hid_rx_buffer[64];
static const usb_hid_info_t hid_info = { static const usb_hid_info_t hid_info = {
.iface_num = 0, .iface_num = 0x00,
.ep_in = 0x81, .ep_in = 0x81,
.ep_out = 0x01, .ep_out = 0x01,
.subclass = 0, .subclass = 0,
.protocol = 0, .protocol = 0,
.rx_buffer = rx_buffer, .rx_buffer = hid_rx_buffer,
.max_packet_len = sizeof(rx_buffer), .max_packet_len = sizeof(hid_rx_buffer),
.polling_interval = 1, .polling_interval = 1,
.report_desc_len = 34, .report_desc_len = 34,
.report_desc = (uint8_t*)"\x06\x00\xff\x09\x01\xa1\x01\x09\x20\x15\x00\x26\xff\x00\x75\x08\x95\x40\x81\x02\x09\x21\x15\x00\x26\xff\x00\x75\x08\x95\x40\x91\x02\xc0", .report_desc = (uint8_t*)"\x06\x00\xff\x09\x01\xa1\x01\x09\x20\x15\x00\x26\xff\x00\x75\x08\x95\x40\x81\x02\x09\x21\x15\x00\x26\xff\x00\x75\x08\x95\x40\x91\x02\xc0",
}; };
static const usb_vcp_info_t vcp_info = {
.iface_num = 0x01,
.data_iface_num = 0x02,
.ep_cmd = 0x82,
.ep_in = 0x83,
.ep_out = 0x03,
.polling_interval = 1,
.max_cmd_packet_len = 8,
.max_data_packet_len = 64,
};
if (0 != usb_init(&dev_info)) { if (0 != usb_init(&dev_info)) {
__fatal_error("usb_init failed"); __fatal_error("usb_init failed");
@ -53,6 +63,9 @@ int usb_init_all(void) {
if (0 != usb_hid_add(&hid_info)) { if (0 != usb_hid_add(&hid_info)) {
__fatal_error("usb_hid_add failed"); __fatal_error("usb_hid_add failed");
} }
if (0 != usb_vcp_add(&vcp_info)) {
__fatal_error("usb_vcp_add failed");
}
if (0 != usb_start()) { if (0 != usb_start()) {
__fatal_error("usb_start failed"); __fatal_error("usb_start failed");
} }

View File

@ -1,17 +1,15 @@
#include "py/mphal.h" #include "py/mphal.h"
#include "usb.h"
// void USBD_CDC_TxAlways(const uint8_t *buf, uint32_t len); #define VCP_IFACE 0x01
// int USBD_CDC_Rx(uint8_t *buf, uint32_t len, uint32_t timeout); #define VCP_WRITE_TIMEOUT 25
int mp_hal_stdin_rx_chr(void) { int mp_hal_stdin_rx_chr(void) {
for (;;) { for (;;) {
// byte c;
// if (USBD_CDC_Rx(&c, 1, 0) != 0) {
// return c;
// }
} }
} }
void mp_hal_stdout_tx_strn(const char *str, size_t len) { void mp_hal_stdout_tx_strn(const char *str, size_t len) {
// USBD_CDC_TxAlways((const uint8_t*)str, len); usb_vcp_write_blocking(VCP_IFACE, (const uint8_t *)str, len, VCP_WRITE_TIMEOUT);
} }

View File

@ -47,6 +47,17 @@ typedef struct __attribute__((packed)) {
uint8_t iInterface; uint8_t iInterface;
} usb_interface_descriptor_t; } usb_interface_descriptor_t;
typedef struct __attribute__((packed)) {
uint8_t bLength;
uint8_t bDescriptorType;
uint8_t bFirstInterface;
uint8_t bInterfaceCount;
uint8_t bFunctionClass;
uint8_t bFunctionSubClass;
uint8_t bFunctionProtocol;
uint8_t iFunction;
} usb_interface_assoc_descriptor_t;
typedef struct __attribute__((packed)) { typedef struct __attribute__((packed)) {
uint8_t bLength; uint8_t bLength;
uint8_t bDescriptorType; uint8_t bDescriptorType;

View File

@ -1,23 +1,79 @@
typedef struct __attribute__((packed)) { typedef struct __attribute__((packed)) {
} usb_vcp_descriptor_t; uint8_t bFunctionLength;
uint8_t bDescriptorType;
uint8_t bDescriptorSubtype;
uint16_t bcdCDC;
} usb_vcp_header_descriptor_t;
typedef struct __attribute__((packed)) { typedef struct __attribute__((packed)) {
usb_interface_descriptor_t iface; uint8_t bFunctionLength;
usb_vcp_descriptor_t vcp; uint8_t bDescriptorType;
uint8_t bDescriptorSubtype;
uint8_t bmCapabilities;
uint8_t bDataInterface;
} usb_vcp_cm_descriptor_t;
typedef struct __attribute__((packed)) {
uint8_t bFunctionLength;
uint8_t bDescriptorType;
uint8_t bDescriptorSubtype;
uint8_t bmCapabilities;
} usb_vcp_acm_descriptor_t;
typedef struct __attribute__((packed)) {
uint8_t bFunctionLength;
uint8_t bDescriptorType;
uint8_t bDescriptorSubtype;
uint8_t bControlInterface;
uint8_t bSubordinateInterface0;
} usb_vcp_union_descriptor_t;
typedef struct __attribute__((packed)) {
usb_interface_assoc_descriptor_t assoc;
usb_interface_descriptor_t iface_cdc;
usb_vcp_header_descriptor_t fheader; // Class-Specific Descriptor Header Format
usb_vcp_cm_descriptor_t fcm; // Call Management Functional Descriptor
usb_vcp_acm_descriptor_t facm; // Abstract Control Management Functional Descriptor
usb_vcp_union_descriptor_t funion; // Union Interface Functional Descriptor
usb_endpoint_descriptor_t ep_cmd;
usb_interface_descriptor_t iface_data;
usb_endpoint_descriptor_t ep_in; usb_endpoint_descriptor_t ep_in;
usb_endpoint_descriptor_t ep_out; usb_endpoint_descriptor_t ep_out;
usb_endpoint_descriptor_t ep_cmd;
} usb_vcp_descriptor_block_t; } usb_vcp_descriptor_block_t;
typedef struct { typedef struct {
// Interface configuration
uint8_t iface_num; // Address of this VCP interface uint8_t iface_num; // Address of this VCP interface
uint8_t data_iface_num; // Address of data interface of the VCP interface association
uint8_t ep_cmd; // Address of IN CMD endpoint (with the highest bit set)
uint8_t ep_in; // Address of IN endpoint (with the highest bit set) uint8_t ep_in; // Address of IN endpoint (with the highest bit set)
uint8_t ep_out; // Address of OUT endpoint uint8_t ep_out; // Address of OUT endpoint
uint8_t ep_cmd; // Address of CMD endpoint uint8_t polling_interval; // In units of 1ms
uint8_t max_cmd_packet_len;
uint8_t max_data_packet_len;
} usb_vcp_info_t; } usb_vcp_info_t;
// typedef struct {
// uint32_t cap;
// uint32_t read;
// uint32_t write;
// uint8_t *buf;
// } ring_buffer_t;
typedef struct { 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;
uint8_t ep_out;
uint8_t polling_interval;
uint8_t max_cmd_packet_len;
uint8_t max_data_packet_len;
const usb_vcp_descriptor_block_t *desc_block; const usb_vcp_descriptor_block_t *desc_block;
} usb_vcp_state_t; } usb_vcp_state_t;

View File

@ -1,6 +1,183 @@
#define USB_LEN_ASSOC_DESC (0x08)
#define USB_DESC_TYPE_ASSOCIATION (0x0b)
#define USB_DESC_TYPE_HEADER (0x00)
#define USB_DESC_TYPE_CALL_MANAGEMENT (0x01)
#define USB_DESC_TYPE_ACM (0x02)
#define USB_DESC_TYPE_UNION (0x06)
#define USB_CDC_GET_LINE_CODING (0x21)
#define USB_CDC_SET_CONTROL_LINE_STATE (0x22)
// static int ring_init(ring_buffer_t *b, uint8_t *buf, size_t cap) {
// if (cap == 0 || (cap & (cap - 1)) != 0) {
// return 1; // Capacity needs to be a power of 2
// }
// b->buf = buf;
// b->cap = cap;
// b->read = 0;
// b->write = 0;
// return 0;
// }
// 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;
// }
// uint32_t ring_read(ring_buffer_t *b, uint8_t *buf, uint32_t len) {
// const uint32_t mask = b->cap - 1;
// uint32_t i;
// for (i = 0; (i < len) && !ring_empty(b); i++) {
// buf[i] = b->buf[b->read & mask];
// b->read++;
// }
// return i;
// }
// uint32_t ring_write(ring_buffer_t *b, const uint8_t *buf, uint32_t len) {
// const uint32_t mask = b->cap - 1;
// uint32_t i;
// for (i = 0; (i < len) && !ring_full(b); i++) {
// b->buf[b->write & mask] = buf[i];
// b->write++;
// }
// return i;
// }
/* usb_vcp_add adds and configures new USB VCP interface according to /* usb_vcp_add adds and configures new USB VCP interface according to
* configuration options passed in `info`. */ * configuration options passed in `info`. */
int usb_vcp_add(const usb_vcp_info_t *info) { int usb_vcp_add(const usb_vcp_info_t *info) {
usb_iface_t *iface = usb_get_iface(info->iface_num);
if (iface == NULL) {
return 1; // Invalid interface number
}
if (iface->type != USB_IFACE_TYPE_DISABLED) {
return 1; // Interface is already enabled
}
usb_vcp_descriptor_block_t *d = usb_desc_alloc_iface(sizeof(usb_vcp_descriptor_block_t));
if (d == NULL) {
return 1; // Not enough space in the configuration descriptor
}
if ((info->ep_cmd & USB_EP_DIR_MSK) != USB_EP_DIR_IN) {
return 1; // CMD EP is invalid
}
if ((info->ep_in & USB_EP_DIR_MSK) != USB_EP_DIR_IN) {
return 1; // IN EP is invalid
}
if ((info->ep_out & USB_EP_DIR_MSK) != USB_EP_DIR_OUT) {
return 1; // OUT EP is invalid
}
// Interface association descriptor
d->assoc.bLength = USB_LEN_ASSOC_DESC;
d->assoc.bDescriptorType = USB_DESC_TYPE_ASSOCIATION;
d->assoc.bFirstInterface = info->iface_num;
d->assoc.bInterfaceCount = 2;
d->assoc.bFunctionClass = 0x02; // Communication Interface Class
d->assoc.bFunctionSubClass = 0x02; // Abstract Control Model
d->assoc.bFunctionProtocol = 0x01; // Common AT commands
d->assoc.iFunction = 0x00; // Index of string descriptor describing the function
// Interface descriptor
d->iface_cdc.bLength = USB_LEN_IF_DESC;
d->iface_cdc.bDescriptorType = USB_DESC_TYPE_INTERFACE;
d->iface_cdc.bInterfaceNumber = info->iface_num;
d->iface_cdc.bAlternateSetting = 0x00;
d->iface_cdc.bNumEndpoints = 1;
d->iface_cdc.bInterfaceClass = 0x02; // Communication Interface Class
d->iface_cdc.bInterfaceSubClass = 0x02; // Abstract Control Model
d->iface_cdc.bInterfaceProtocol = 0x01; // Common AT commands
d->iface_cdc.iInterface = 0x00; // Index of string descriptor describing the interface
// Header Functional Descriptor
d->fheader.bFunctionLength = sizeof(usb_vcp_header_descriptor_t);
d->fheader.bDescriptorType = 0x24; // CS_INTERFACE
d->fheader.bDescriptorSubtype = 0x00; // Header Func desc
d->fheader.bcdCDC = 0x1001; // Spec release number
// Call Management Functional Descriptor
d->fcm.bFunctionLength = sizeof(usb_vcp_cm_descriptor_t);
d->fcm.bDescriptorType = 0x24; // CS_INTERFACE
d->fcm.bDescriptorSubtype = 0x01; // Call Management Func desc
d->fcm.bmCapabilities = 0x00; // D0+D1
d->fcm.bDataInterface = info->data_iface_num;
// ACM Functional Descriptor
d->facm.bFunctionLength = sizeof(usb_vcp_acm_descriptor_t);
d->facm.bDescriptorType = 0x24; // CS_INTERFACE
d->facm.bDescriptorSubtype = 0x02; // Abstract Control Management desc
d->facm.bmCapabilities = 0x02;
// Union Functional Descriptor
d->funion.bFunctionLength = sizeof(usb_vcp_union_descriptor_t);
d->funion.bDescriptorType = 0x24; // CS_INTERFACE
d->funion.bDescriptorSubtype = 0x06; // Union Func desc
d->funion.bControlInterface = info->iface_num;
d->funion.bSubordinateInterface0 = info->data_iface_num;
// IN CMD endpoint (control)
d->ep_cmd.bLength = USB_LEN_EP_DESC;
d->ep_cmd.bDescriptorType = USB_DESC_TYPE_ENDPOINT;
d->ep_cmd.bEndpointAddress = info->ep_cmd;
d->ep_cmd.bmAttributes = USBD_EP_TYPE_INTR;
d->ep_cmd.wMaxPacketSize = info->max_cmd_packet_len;
d->ep_cmd.bInterval = info->polling_interval;
// Interface descriptor
d->iface_data.bLength = USB_LEN_IF_DESC;
d->iface_data.bDescriptorType = USB_DESC_TYPE_INTERFACE;
d->iface_data.bInterfaceNumber = info->data_iface_num;
d->iface_data.bAlternateSetting = 0x00;
d->iface_data.bNumEndpoints = 2;
d->iface_data.bInterfaceClass = 0x0A; // CDC
d->iface_data.bInterfaceSubClass = 0x00;
d->iface_data.bInterfaceProtocol = 0x00;
d->iface_data.iInterface = 0x00; // Index of string descriptor describing the interface
// OUT endpoint (receiving)
d->ep_out.bLength = USB_LEN_EP_DESC;
d->ep_out.bDescriptorType = USB_DESC_TYPE_ENDPOINT;
d->ep_out.bEndpointAddress = info->ep_out;
d->ep_out.bmAttributes = USBD_EP_TYPE_BULK;
d->ep_out.wMaxPacketSize = info->max_data_packet_len;
d->ep_out.bInterval = 0x00; // Ignored for bulk endpoints
// IN endpoint (sending)
d->ep_in.bLength = USB_LEN_EP_DESC;
d->ep_in.bDescriptorType = USB_DESC_TYPE_ENDPOINT;
d->ep_in.bEndpointAddress = info->ep_in;
d->ep_in.bmAttributes = USBD_EP_TYPE_BULK;
d->ep_in.wMaxPacketSize = info->max_data_packet_len;
d->ep_in.bInterval = 0x00; // Ignored for bulk endpoints
// Config descriptor
// TODO: do this in a clean way
usb_desc_add_iface(sizeof(usb_vcp_descriptor_block_t));
usb_config_desc->bNumInterfaces++;
// Interface state
iface->type = USB_IFACE_TYPE_VCP;
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.max_cmd_packet_len = info->max_cmd_packet_len;
iface->vcp.max_data_packet_len = info->max_data_packet_len;
iface->vcp.desc_block = d;
return 0; return 0;
} }
@ -9,17 +186,56 @@ int usb_vcp_can_read(uint8_t iface_num) {
} }
int usb_vcp_can_write(uint8_t iface_num) { int usb_vcp_can_write(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 (iface->vcp.in_idle == 0) {
return 0; // Last transmission is not over yet
}
if (usb_dev_handle.dev_state != USBD_STATE_CONFIGURED) {
return 0; // Device is not configured
}
return 1;
} }
int usb_vcp_read(uint8_t iface_num, uint8_t *buf, uint32_t len) { int usb_vcp_read(uint8_t iface_num, uint8_t *buf, uint32_t len) {
usb_iface_t *iface = usb_get_iface(iface_num);
if (iface == NULL) {
return -1; // Invalid interface number
}
if (iface->type != USB_IFACE_TYPE_VCP) {
return -2; // Interface interface type
}
// usb_vcp_state_t *state = &iface->vcp;
// TODO
return 0; return 0;
} }
int usb_vcp_write(uint8_t iface_num, const uint8_t *buf, uint32_t len) { int usb_vcp_write(uint8_t iface_num, const uint8_t *buf, uint32_t len) {
usb_iface_t *iface = usb_get_iface(iface_num);
if (iface == NULL) {
return -1; // Invalid interface number
}
if (iface->type != USB_IFACE_TYPE_VCP) {
return -2; // Interface interface type
}
usb_vcp_state_t *state = &iface->vcp;
if (!state->is_connected) {
return 0; return 0;
} }
state->in_idle = 0;
USBD_LL_Transmit(&usb_dev_handle, state->ep_in, UNCONST(buf), (uint16_t)len);
return len;
}
int usb_vcp_read_blocking(uint8_t iface_num, uint8_t *buf, uint32_t len, uint32_t timeout) { int usb_vcp_read_blocking(uint8_t iface_num, uint8_t *buf, uint32_t len, uint32_t timeout) {
uint32_t start = HAL_GetTick(); uint32_t start = HAL_GetTick();
while (!usb_vcp_can_read(iface_num)) { while (!usb_vcp_can_read(iface_num)) {
@ -43,21 +259,69 @@ int usb_vcp_write_blocking(uint8_t iface_num, const uint8_t *buf, uint32_t len,
} }
static int usb_vcp_class_init(USBD_HandleTypeDef *dev, usb_vcp_state_t *state, uint8_t cfg_idx) { static int usb_vcp_class_init(USBD_HandleTypeDef *dev, usb_vcp_state_t *state, uint8_t cfg_idx) {
// Open endpoints
USBD_LL_OpenEP(dev, state->ep_in, USBD_EP_TYPE_BULK, state->max_data_packet_len);
USBD_LL_OpenEP(dev, state->ep_out, USBD_EP_TYPE_BULK, state->max_data_packet_len);
USBD_LL_OpenEP(dev, state->ep_cmd, USBD_EP_TYPE_INTR, state->max_cmd_packet_len);
// Reset the state
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);
return USBD_OK; return USBD_OK;
} }
static int usb_vcp_class_deinit(USBD_HandleTypeDef *dev, usb_vcp_state_t *state, uint8_t cfg_idx) { static int usb_vcp_class_deinit(USBD_HandleTypeDef *dev, usb_vcp_state_t *state, uint8_t cfg_idx) {
// Close endpoints
USBD_LL_CloseEP(dev, state->ep_in);
USBD_LL_CloseEP(dev, state->ep_out);
USBD_LL_CloseEP(dev, state->ep_cmd);
return USBD_OK; return USBD_OK;
} }
static int usb_vcp_class_setup(USBD_HandleTypeDef *dev, usb_vcp_state_t *state, USBD_SetupReqTypedef *req) { static int usb_vcp_class_setup(USBD_HandleTypeDef *dev, usb_vcp_state_t *state, USBD_SetupReqTypedef *req) {
static const uint8_t line_coding[] = {
(uint8_t)(115200 >> 0),
(uint8_t)(115200 >> 8),
(uint8_t)(115200 >> 16),
(uint8_t)(115200 >> 24),
0, // Stop bits
0, // Parity
8, // Number of bits
};
switch (req->bmRequest & USB_REQ_TYPE_MASK) {
// Class request
case USB_REQ_TYPE_CLASS :
switch (req->bRequest) {
case USB_CDC_GET_LINE_CODING:
USBD_CtlSendData(dev, UNCONST(line_coding), sizeof(line_coding));
break;
case USB_CDC_SET_CONTROL_LINE_STATE:
state->is_connected = req->wLength & 1;
break;
}
break;
}
return USBD_OK; return USBD_OK;
} }
static uint8_t usb_vcp_class_data_in(USBD_HandleTypeDef *dev, usb_vcp_state_t *state, uint8_t ep_num) { static uint8_t usb_vcp_class_data_in(USBD_HandleTypeDef *dev, usb_vcp_state_t *state, uint8_t ep_num) {
if ((ep_num | USB_EP_DIR_IN) == state->ep_in) {
state->in_idle = 1;
}
return USBD_OK; return USBD_OK;
} }
static uint8_t usb_vcp_class_data_out(USBD_HandleTypeDef *dev, usb_vcp_state_t *state, uint8_t ep_num) { static uint8_t usb_vcp_class_data_out(USBD_HandleTypeDef *dev, usb_vcp_state_t *state, uint8_t ep_num) {
// TODO: process received data
return USBD_OK; return USBD_OK;
} }

View File

@ -290,13 +290,10 @@ void HAL_PCD_DataInStageCallback(PCD_HandleTypeDef *hpcd, uint8_t epnum)
* @param hpcd: PCD handle * @param hpcd: PCD handle
* @retval None * @retval None
*/ */
/*
This is now handled by the USB CDC interface.
void HAL_PCD_SOFCallback(PCD_HandleTypeDef *hpcd) void HAL_PCD_SOFCallback(PCD_HandleTypeDef *hpcd)
{ {
USBD_LL_SOF(hpcd->pData); USBD_LL_SOF(hpcd->pData);
} }
*/
/** /**
* @brief Reset callback. * @brief Reset callback.