mirror of
https://github.com/trezor/trezor-firmware.git
synced 2024-11-22 23:48:12 +00:00
trezorhal: extract usb hid stuff into usb_hid-defs.h and usb_hid-impl.h
This commit is contained in:
parent
6b07961cf6
commit
2c03f97ca9
@ -8,14 +8,6 @@
|
|||||||
#define USB_MAX_CONFIG_DESC_SIZE 128
|
#define USB_MAX_CONFIG_DESC_SIZE 128
|
||||||
#define USB_MAX_STR_DESC_SIZE 256
|
#define USB_MAX_STR_DESC_SIZE 256
|
||||||
|
|
||||||
#define USB_DESC_TYPE_HID 0x21
|
|
||||||
#define USB_DESC_TYPE_REPORT 0x22
|
|
||||||
|
|
||||||
#define HID_REQ_SET_PROTOCOL 0x0b
|
|
||||||
#define HID_REQ_GET_PROTOCOL 0x03
|
|
||||||
#define HID_REQ_SET_IDLE 0x0a
|
|
||||||
#define HID_REQ_GET_IDLE 0x02
|
|
||||||
|
|
||||||
extern PCD_HandleTypeDef pcd_fs_handle;
|
extern PCD_HandleTypeDef pcd_fs_handle;
|
||||||
|
|
||||||
static USBD_HandleTypeDef usb_dev_handle;
|
static USBD_HandleTypeDef usb_dev_handle;
|
||||||
@ -166,254 +158,7 @@ static uint8_t usb_ep_clear_nak(USBD_HandleTypeDef *dev, uint8_t ep_num) {
|
|||||||
return USBD_OK;
|
return USBD_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* usb_hid_add adds and configures new USB HID interface according to
|
#include "usb_hid-impl.h"
|
||||||
* configuration options passed in `info`. */
|
|
||||||
int usb_hid_add(const usb_hid_info_t *info) {
|
|
||||||
usb_hid_descriptor_block_t *d = usb_desc_alloc_iface(sizeof(*d));
|
|
||||||
|
|
||||||
if (!d) {
|
|
||||||
return 1; // Not enough space in the configuration descriptor
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((info->iface_num < usb_config_desc->bNumInterfaces) ||
|
|
||||||
(info->iface_num >= USBD_MAX_NUM_INTERFACES) ||
|
|
||||||
((info->ep_in & 0x80) == 0) ||
|
|
||||||
((info->ep_out & 0x80) != 0)) {
|
|
||||||
|
|
||||||
return 1; // Invalid configuration values
|
|
||||||
}
|
|
||||||
|
|
||||||
// Interface descriptor
|
|
||||||
d->iface.bLength = USB_LEN_IF_DESC;
|
|
||||||
d->iface.bDescriptorType = USB_DESC_TYPE_INTERFACE;
|
|
||||||
d->iface.bInterfaceNumber = info->iface_num;
|
|
||||||
d->iface.bAlternateSetting = 0x00;
|
|
||||||
d->iface.bNumEndpoints = 0x02;
|
|
||||||
d->iface.bInterfaceClass = 0x03; // HID Class
|
|
||||||
d->iface.bInterfaceSubClass = info->subclass;
|
|
||||||
d->iface.bInterfaceProtocol = info->protocol;
|
|
||||||
d->iface.iInterface = 0x00; // Index of string descriptor describing the interface
|
|
||||||
|
|
||||||
// HID descriptor
|
|
||||||
d->hid.bLength = sizeof(usb_hid_descriptor_t);
|
|
||||||
d->hid.bDescriptorType = USB_DESC_TYPE_HID;
|
|
||||||
d->hid.bcdHID = 0x1101; // HID Class Spec release number
|
|
||||||
d->hid.bCountryCode = 0x00; // Hardware target country
|
|
||||||
d->hid.bNumDescriptors = 0x01; // Number of HID class descriptors to follow
|
|
||||||
d->hid.bReportDescriptorType = USB_DESC_TYPE_REPORT;
|
|
||||||
d->hid.wReportDescriptorLength = info->report_desc_len;
|
|
||||||
|
|
||||||
// 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_INTR;
|
|
||||||
d->ep_in.wMaxPacketSize = info->max_packet_len;
|
|
||||||
d->ep_in.bInterval = info->polling_interval;
|
|
||||||
|
|
||||||
// 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_INTR;
|
|
||||||
d->ep_out.wMaxPacketSize = info->max_packet_len;
|
|
||||||
d->ep_out.bInterval = info->polling_interval;
|
|
||||||
|
|
||||||
// Config descriptor
|
|
||||||
usb_desc_add_iface(sizeof(*d));
|
|
||||||
|
|
||||||
// Interface state
|
|
||||||
usb_iface_t *i = &usb_ifaces[info->iface_num];
|
|
||||||
i->type = USB_IFACE_TYPE_HID;
|
|
||||||
i->hid.ep_in = info->ep_in;
|
|
||||||
i->hid.ep_out = info->ep_out;
|
|
||||||
i->hid.rx_buffer = info->rx_buffer;
|
|
||||||
i->hid.max_packet_len = info->max_packet_len;
|
|
||||||
i->hid.report_desc_len = info->report_desc_len;
|
|
||||||
i->hid.report_desc = info->report_desc;
|
|
||||||
i->hid.desc_block = d;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int usb_hid_can_read(uint8_t iface_num) {
|
|
||||||
return ((iface_num < USBD_MAX_NUM_INTERFACES) &&
|
|
||||||
(usb_ifaces[iface_num].type == USB_IFACE_TYPE_HID) &&
|
|
||||||
(usb_ifaces[iface_num].hid.rx_buffer_len > 0) &&
|
|
||||||
(usb_dev_handle.dev_state == USBD_STATE_CONFIGURED));
|
|
||||||
}
|
|
||||||
|
|
||||||
int usb_hid_can_write(uint8_t iface_num) {
|
|
||||||
return ((iface_num < USBD_MAX_NUM_INTERFACES) &&
|
|
||||||
(usb_ifaces[iface_num].type == USB_IFACE_TYPE_HID) &&
|
|
||||||
(usb_ifaces[iface_num].hid.in_idle) &&
|
|
||||||
(usb_dev_handle.dev_state == USBD_STATE_CONFIGURED));
|
|
||||||
}
|
|
||||||
|
|
||||||
int usb_hid_read(uint8_t iface_num, uint8_t *buf, uint32_t len) {
|
|
||||||
if (iface_num >= USBD_MAX_NUM_INTERFACES) {
|
|
||||||
return -1; // Invalid interface number
|
|
||||||
}
|
|
||||||
if (usb_ifaces[iface_num].type != USB_IFACE_TYPE_HID) {
|
|
||||||
return -2; // Invalid interface type
|
|
||||||
}
|
|
||||||
usb_hid_state_t *state = &usb_ifaces[iface_num].hid;
|
|
||||||
if (len < state->rx_buffer_len) {
|
|
||||||
return 0; // Not enough data in the read buffer
|
|
||||||
}
|
|
||||||
|
|
||||||
memcpy(buf, state->rx_buffer, state->rx_buffer_len);
|
|
||||||
|
|
||||||
// Clear NAK to indicate we are ready to read more data
|
|
||||||
usb_ep_clear_nak(&usb_dev_handle, state->ep_out);
|
|
||||||
|
|
||||||
return state->rx_buffer_len;
|
|
||||||
}
|
|
||||||
|
|
||||||
int usb_hid_write(uint8_t iface_num, const uint8_t *buf, uint32_t len) {
|
|
||||||
if (iface_num >= USBD_MAX_NUM_INTERFACES) {
|
|
||||||
return -1; // Invalid interface number
|
|
||||||
}
|
|
||||||
if (usb_ifaces[iface_num].type != USB_IFACE_TYPE_HID) {
|
|
||||||
return -2; // Invalid interface type
|
|
||||||
}
|
|
||||||
usb_hid_state_t *state = &usb_ifaces[iface_num].hid;
|
|
||||||
|
|
||||||
state->in_idle = 0;
|
|
||||||
USBD_LL_Transmit(&usb_dev_handle, state->ep_in, (uint8_t *)buf, (uint16_t)len);
|
|
||||||
|
|
||||||
return len;
|
|
||||||
}
|
|
||||||
|
|
||||||
int usb_hid_read_blocking(uint8_t iface_num, uint8_t *buf, uint32_t len, uint32_t timeout) {
|
|
||||||
uint32_t start = HAL_GetTick();
|
|
||||||
while (!usb_hid_can_read(iface_num)) {
|
|
||||||
if (HAL_GetTick() - start >= timeout) {
|
|
||||||
return 0; // Timeout
|
|
||||||
}
|
|
||||||
__WFI(); // Enter sleep mode, waiting for interrupt
|
|
||||||
}
|
|
||||||
return usb_hid_read(iface_num, buf, len);
|
|
||||||
}
|
|
||||||
|
|
||||||
int usb_hid_write_blocking(uint8_t iface_num, const uint8_t *buf, uint32_t len, uint32_t timeout) {
|
|
||||||
uint32_t start = HAL_GetTick();
|
|
||||||
while (!usb_hid_can_write(iface_num)) {
|
|
||||||
if (HAL_GetTick() - start >= timeout) {
|
|
||||||
return 0; // Timeout
|
|
||||||
}
|
|
||||||
__WFI(); // Enter sleep mode, waiting for interrupt
|
|
||||||
}
|
|
||||||
return usb_hid_write(iface_num, buf, len);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int usb_hid_class_init(USBD_HandleTypeDef *dev, usb_hid_state_t *state, uint8_t cfg_idx) {
|
|
||||||
// Open endpoints
|
|
||||||
USBD_LL_OpenEP(dev, state->ep_in, USBD_EP_TYPE_INTR, state->max_packet_len);
|
|
||||||
USBD_LL_OpenEP(dev, state->ep_out, USBD_EP_TYPE_INTR, state->max_packet_len);
|
|
||||||
|
|
||||||
// Reset the state
|
|
||||||
state->in_idle = 1;
|
|
||||||
state->protocol = 0;
|
|
||||||
state->idle_rate = 0;
|
|
||||||
state->alt_setting = 0;
|
|
||||||
|
|
||||||
// Prepare Out endpoint to receive next packet
|
|
||||||
USBD_LL_PrepareReceive(dev, state->ep_out, state->rx_buffer, state->max_packet_len);
|
|
||||||
|
|
||||||
return USBD_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int usb_hid_class_deinit(USBD_HandleTypeDef *dev, usb_hid_state_t *state, uint8_t cfg_idx) {
|
|
||||||
// Close endpoints
|
|
||||||
USBD_LL_CloseEP(dev, state->ep_in);
|
|
||||||
USBD_LL_CloseEP(dev, state->ep_out);
|
|
||||||
|
|
||||||
return USBD_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int usb_hid_class_setup(USBD_HandleTypeDef *dev, usb_hid_state_t *state, USBD_SetupReqTypedef *req) {
|
|
||||||
switch (req->bmRequest & USB_REQ_TYPE_MASK) {
|
|
||||||
|
|
||||||
// Class request
|
|
||||||
case USB_REQ_TYPE_CLASS:
|
|
||||||
switch (req->bRequest) {
|
|
||||||
|
|
||||||
case HID_REQ_SET_PROTOCOL:
|
|
||||||
state->protocol = req->wValue;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case HID_REQ_GET_PROTOCOL:
|
|
||||||
USBD_CtlSendData(dev, &state->protocol, sizeof(state->protocol));
|
|
||||||
break;
|
|
||||||
|
|
||||||
case HID_REQ_SET_IDLE:
|
|
||||||
state->idle_rate = req->wValue >> 8;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case HID_REQ_GET_IDLE:
|
|
||||||
USBD_CtlSendData(dev, &state->idle_rate, sizeof(state->idle_rate));
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
USBD_CtlError(dev, req);
|
|
||||||
return USBD_FAIL;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
// Interface & Endpoint request
|
|
||||||
case USB_REQ_TYPE_STANDARD:
|
|
||||||
switch (req->bRequest) {
|
|
||||||
|
|
||||||
case USB_REQ_SET_INTERFACE:
|
|
||||||
state->alt_setting = req->wValue;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case USB_REQ_GET_INTERFACE:
|
|
||||||
USBD_CtlSendData(dev, &state->alt_setting, sizeof(state->alt_setting));
|
|
||||||
break;
|
|
||||||
|
|
||||||
case USB_REQ_GET_DESCRIPTOR:
|
|
||||||
switch (req->wValue >> 8) {
|
|
||||||
|
|
||||||
case USB_DESC_TYPE_HID:
|
|
||||||
USBD_CtlSendData(dev, (uint8_t*)&state->desc_block->hid, MIN(req->wLength, sizeof(state->desc_block->hid)));
|
|
||||||
break;
|
|
||||||
|
|
||||||
case USB_DESC_TYPE_REPORT:
|
|
||||||
USBD_CtlSendData(dev, (uint8_t*)state->report_desc, MIN(req->wLength, state->report_desc_len));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return USBD_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
static uint8_t usb_hid_class_data_in(USBD_HandleTypeDef *dev, usb_hid_state_t *state, uint8_t ep_num) {
|
|
||||||
if (ep_num == state->ep_in) {
|
|
||||||
// Ensure that the FIFO is empty before a new transfer,
|
|
||||||
// this condition could be caused by a new transfer
|
|
||||||
// before the end of the previous transfer.
|
|
||||||
state->in_idle = 1;
|
|
||||||
}
|
|
||||||
return USBD_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
static uint8_t usb_hid_class_data_out(USBD_HandleTypeDef *dev, usb_hid_state_t *state, uint8_t ep_num) {
|
|
||||||
if (ep_num == state->ep_out) {
|
|
||||||
// User should provide state->rx_buffer_len that is big
|
|
||||||
// enough for state->max_packet_len bytes.
|
|
||||||
state->rx_buffer_len = USBD_LL_GetRxDataSize(dev, ep_num);
|
|
||||||
|
|
||||||
if (state->rx_buffer_len > 0) {
|
|
||||||
// Block the OUT EP until we process received data
|
|
||||||
usb_ep_set_nak(dev, ep_num);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return USBD_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
static uint8_t usb_class_init(USBD_HandleTypeDef *dev, uint8_t cfg_idx) {
|
static uint8_t usb_class_init(USBD_HandleTypeDef *dev, uint8_t cfg_idx) {
|
||||||
for (int i = 0; i < USBD_MAX_NUM_INTERFACES; i++) {
|
for (int i = 0; i < USBD_MAX_NUM_INTERFACES; i++) {
|
||||||
|
@ -56,23 +56,6 @@ typedef struct __attribute__((packed)) {
|
|||||||
uint8_t bInterval;
|
uint8_t bInterval;
|
||||||
} usb_endpoint_descriptor_t;
|
} usb_endpoint_descriptor_t;
|
||||||
|
|
||||||
typedef struct __attribute__((packed)) {
|
|
||||||
uint8_t bLength;
|
|
||||||
uint8_t bDescriptorType;
|
|
||||||
uint16_t bcdHID;
|
|
||||||
uint8_t bCountryCode;
|
|
||||||
uint8_t bNumDescriptors;
|
|
||||||
uint8_t bReportDescriptorType;
|
|
||||||
uint16_t wReportDescriptorLength;
|
|
||||||
} usb_hid_descriptor_t;
|
|
||||||
|
|
||||||
typedef struct __attribute__((packed)) {
|
|
||||||
usb_interface_descriptor_t iface;
|
|
||||||
usb_hid_descriptor_t hid;
|
|
||||||
usb_endpoint_descriptor_t ep_in;
|
|
||||||
usb_endpoint_descriptor_t ep_out;
|
|
||||||
} usb_hid_descriptor_block_t;
|
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
USB_LANGID_ENGLISH_US = 0x409,
|
USB_LANGID_ENGLISH_US = 0x409,
|
||||||
} usb_language_id_t;
|
} usb_language_id_t;
|
||||||
@ -96,54 +79,6 @@ typedef struct {
|
|||||||
const uint8_t *interface_str;
|
const uint8_t *interface_str;
|
||||||
} usb_dev_info_t;
|
} usb_dev_info_t;
|
||||||
|
|
||||||
typedef enum {
|
|
||||||
USB_HID_SUBCLASS_NONE = 0,
|
|
||||||
USB_HID_SUBCLASS_BOOT = 1,
|
|
||||||
} usb_hid_subclass_t;
|
|
||||||
|
|
||||||
typedef enum {
|
|
||||||
USB_HID_PROTOCOL_NONE = 0,
|
|
||||||
USB_HID_PROTOCOL_KEYBOARD = 1,
|
|
||||||
USB_HID_PROTOCOL_MOUSE = 2,
|
|
||||||
} usb_hid_protocol_t;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
// Interface configuration
|
|
||||||
uint8_t iface_num; // Address of this HID interface
|
|
||||||
uint8_t ep_in; // Address of IN endpoint (with the highest bit set)
|
|
||||||
uint8_t ep_out; // Address of OUT endpoint
|
|
||||||
|
|
||||||
// HID configuration
|
|
||||||
uint8_t subclass; // usb_iface_subclass_t
|
|
||||||
uint8_t protocol; // usb_iface_protocol_t
|
|
||||||
uint8_t max_packet_len; // rx_buffer should be big enough
|
|
||||||
uint8_t polling_interval; // In units of 1ms
|
|
||||||
uint8_t report_desc_len;
|
|
||||||
const uint8_t *report_desc;
|
|
||||||
|
|
||||||
// HID read buffer
|
|
||||||
uint8_t *rx_buffer; // Big enough for max_packet_len
|
|
||||||
} usb_hid_info_t;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
|
|
||||||
// HID state
|
|
||||||
uint8_t in_idle; // Set to 1 after IN endpoint gets idle
|
|
||||||
uint8_t protocol; // For SET_PROTOCOL/GET_PROTOCOL setup reqs
|
|
||||||
uint8_t idle_rate; // For SET_IDLE/GET_IDLE setup reqs
|
|
||||||
uint8_t alt_setting; // For SET_INTERFACE/GET_INTERFACE setup reqs
|
|
||||||
uint8_t rx_buffer_len; // Length of data read into rx_buffer
|
|
||||||
|
|
||||||
// HID configuration (copied from usb_hid_info_t on init)
|
|
||||||
uint8_t ep_in;
|
|
||||||
uint8_t ep_out;
|
|
||||||
uint8_t max_packet_len;
|
|
||||||
uint8_t report_desc_len;
|
|
||||||
uint8_t *rx_buffer;
|
|
||||||
const uint8_t *report_desc;
|
|
||||||
const usb_hid_descriptor_block_t *desc_block;
|
|
||||||
} usb_hid_state_t;
|
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
USB_IFACE_TYPE_DISABLED = 0,
|
USB_IFACE_TYPE_DISABLED = 0,
|
||||||
// USB_IFACE_TYPE_CDC = 1,
|
// USB_IFACE_TYPE_CDC = 1,
|
||||||
@ -151,6 +86,8 @@ typedef enum {
|
|||||||
USB_IFACE_TYPE_HID = 3,
|
USB_IFACE_TYPE_HID = 3,
|
||||||
} usb_iface_type_t;
|
} usb_iface_type_t;
|
||||||
|
|
||||||
|
#include "usb_hid-defs.h"
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
union {
|
union {
|
||||||
usb_hid_state_t hid;
|
usb_hid_state_t hid;
|
||||||
@ -162,13 +99,4 @@ int usb_init(const usb_dev_info_t *dev_info);
|
|||||||
int usb_start(void);
|
int usb_start(void);
|
||||||
int usb_stop(void);
|
int usb_stop(void);
|
||||||
|
|
||||||
int usb_hid_add(const usb_hid_info_t *hid_info);
|
|
||||||
int usb_hid_can_read(uint8_t iface_num);
|
|
||||||
int usb_hid_can_write(uint8_t iface_num);
|
|
||||||
int usb_hid_read(uint8_t iface_num, uint8_t *buf, uint32_t len);
|
|
||||||
int usb_hid_write(uint8_t iface_num, const uint8_t *buf, uint32_t len);
|
|
||||||
|
|
||||||
int usb_hid_read_blocking(uint8_t iface_num, uint8_t *buf, uint32_t len, uint32_t timeout);
|
|
||||||
int usb_hid_write_blocking(uint8_t iface_num, const uint8_t *buf, uint32_t len, uint32_t timeout);
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
72
micropython/trezorhal/usb_hid-defs.h
Normal file
72
micropython/trezorhal/usb_hid-defs.h
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
typedef struct __attribute__((packed)) {
|
||||||
|
uint8_t bLength;
|
||||||
|
uint8_t bDescriptorType;
|
||||||
|
uint16_t bcdHID;
|
||||||
|
uint8_t bCountryCode;
|
||||||
|
uint8_t bNumDescriptors;
|
||||||
|
uint8_t bReportDescriptorType;
|
||||||
|
uint16_t wReportDescriptorLength;
|
||||||
|
} usb_hid_descriptor_t;
|
||||||
|
|
||||||
|
typedef struct __attribute__((packed)) {
|
||||||
|
usb_interface_descriptor_t iface;
|
||||||
|
usb_hid_descriptor_t hid;
|
||||||
|
usb_endpoint_descriptor_t ep_in;
|
||||||
|
usb_endpoint_descriptor_t ep_out;
|
||||||
|
} usb_hid_descriptor_block_t;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
USB_HID_SUBCLASS_NONE = 0,
|
||||||
|
USB_HID_SUBCLASS_BOOT = 1,
|
||||||
|
} usb_hid_subclass_t;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
USB_HID_PROTOCOL_NONE = 0,
|
||||||
|
USB_HID_PROTOCOL_KEYBOARD = 1,
|
||||||
|
USB_HID_PROTOCOL_MOUSE = 2,
|
||||||
|
} usb_hid_protocol_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
// Interface configuration
|
||||||
|
uint8_t iface_num; // Address of this HID interface
|
||||||
|
uint8_t ep_in; // Address of IN endpoint (with the highest bit set)
|
||||||
|
uint8_t ep_out; // Address of OUT endpoint
|
||||||
|
|
||||||
|
// HID configuration
|
||||||
|
uint8_t subclass; // usb_iface_subclass_t
|
||||||
|
uint8_t protocol; // usb_iface_protocol_t
|
||||||
|
uint8_t max_packet_len; // rx_buffer should be big enough
|
||||||
|
uint8_t polling_interval; // In units of 1ms
|
||||||
|
uint8_t report_desc_len;
|
||||||
|
const uint8_t *report_desc;
|
||||||
|
|
||||||
|
// HID read buffer
|
||||||
|
uint8_t *rx_buffer; // Big enough for max_packet_len
|
||||||
|
} usb_hid_info_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
// HID state
|
||||||
|
uint8_t in_idle; // Set to 1 after IN endpoint gets idle
|
||||||
|
uint8_t protocol; // For SET_PROTOCOL/GET_PROTOCOL setup reqs
|
||||||
|
uint8_t idle_rate; // For SET_IDLE/GET_IDLE setup reqs
|
||||||
|
uint8_t alt_setting; // For SET_INTERFACE/GET_INTERFACE setup reqs
|
||||||
|
uint8_t rx_buffer_len; // Length of data read into rx_buffer
|
||||||
|
|
||||||
|
// HID configuration (copied from usb_hid_info_t on init)
|
||||||
|
uint8_t ep_in;
|
||||||
|
uint8_t ep_out;
|
||||||
|
uint8_t max_packet_len;
|
||||||
|
uint8_t report_desc_len;
|
||||||
|
uint8_t *rx_buffer;
|
||||||
|
const uint8_t *report_desc;
|
||||||
|
const usb_hid_descriptor_block_t *desc_block;
|
||||||
|
} usb_hid_state_t;
|
||||||
|
|
||||||
|
int usb_hid_add(const usb_hid_info_t *hid_info);
|
||||||
|
int usb_hid_can_read(uint8_t iface_num);
|
||||||
|
int usb_hid_can_write(uint8_t iface_num);
|
||||||
|
int usb_hid_read(uint8_t iface_num, uint8_t *buf, uint32_t len);
|
||||||
|
int usb_hid_write(uint8_t iface_num, const uint8_t *buf, uint32_t len);
|
||||||
|
|
||||||
|
int usb_hid_read_blocking(uint8_t iface_num, uint8_t *buf, uint32_t len, uint32_t timeout);
|
||||||
|
int usb_hid_write_blocking(uint8_t iface_num, const uint8_t *buf, uint32_t len, uint32_t timeout);
|
256
micropython/trezorhal/usb_hid-impl.h
Normal file
256
micropython/trezorhal/usb_hid-impl.h
Normal file
@ -0,0 +1,256 @@
|
|||||||
|
#define USB_DESC_TYPE_HID 0x21
|
||||||
|
#define USB_DESC_TYPE_REPORT 0x22
|
||||||
|
|
||||||
|
#define HID_REQ_SET_PROTOCOL 0x0b
|
||||||
|
#define HID_REQ_GET_PROTOCOL 0x03
|
||||||
|
#define HID_REQ_SET_IDLE 0x0a
|
||||||
|
#define HID_REQ_GET_IDLE 0x02
|
||||||
|
|
||||||
|
/* usb_hid_add adds and configures new USB HID interface according to
|
||||||
|
* configuration options passed in `info`. */
|
||||||
|
int usb_hid_add(const usb_hid_info_t *info) {
|
||||||
|
usb_hid_descriptor_block_t *d = usb_desc_alloc_iface(sizeof(*d));
|
||||||
|
|
||||||
|
if (!d) {
|
||||||
|
return 1; // Not enough space in the configuration descriptor
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((info->iface_num < usb_config_desc->bNumInterfaces) ||
|
||||||
|
(info->iface_num >= USBD_MAX_NUM_INTERFACES) ||
|
||||||
|
((info->ep_in & 0x80) == 0) ||
|
||||||
|
((info->ep_out & 0x80) != 0)) {
|
||||||
|
|
||||||
|
return 1; // Invalid configuration values
|
||||||
|
}
|
||||||
|
|
||||||
|
// Interface descriptor
|
||||||
|
d->iface.bLength = USB_LEN_IF_DESC;
|
||||||
|
d->iface.bDescriptorType = USB_DESC_TYPE_INTERFACE;
|
||||||
|
d->iface.bInterfaceNumber = info->iface_num;
|
||||||
|
d->iface.bAlternateSetting = 0x00;
|
||||||
|
d->iface.bNumEndpoints = 0x02;
|
||||||
|
d->iface.bInterfaceClass = 0x03; // HID Class
|
||||||
|
d->iface.bInterfaceSubClass = info->subclass;
|
||||||
|
d->iface.bInterfaceProtocol = info->protocol;
|
||||||
|
d->iface.iInterface = 0x00; // Index of string descriptor describing the interface
|
||||||
|
|
||||||
|
// HID descriptor
|
||||||
|
d->hid.bLength = sizeof(usb_hid_descriptor_t);
|
||||||
|
d->hid.bDescriptorType = USB_DESC_TYPE_HID;
|
||||||
|
d->hid.bcdHID = 0x1101; // HID Class Spec release number
|
||||||
|
d->hid.bCountryCode = 0x00; // Hardware target country
|
||||||
|
d->hid.bNumDescriptors = 0x01; // Number of HID class descriptors to follow
|
||||||
|
d->hid.bReportDescriptorType = USB_DESC_TYPE_REPORT;
|
||||||
|
d->hid.wReportDescriptorLength = info->report_desc_len;
|
||||||
|
|
||||||
|
// 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_INTR;
|
||||||
|
d->ep_in.wMaxPacketSize = info->max_packet_len;
|
||||||
|
d->ep_in.bInterval = info->polling_interval;
|
||||||
|
|
||||||
|
// 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_INTR;
|
||||||
|
d->ep_out.wMaxPacketSize = info->max_packet_len;
|
||||||
|
d->ep_out.bInterval = info->polling_interval;
|
||||||
|
|
||||||
|
// Config descriptor
|
||||||
|
usb_desc_add_iface(sizeof(*d));
|
||||||
|
|
||||||
|
// Interface state
|
||||||
|
usb_iface_t *i = &usb_ifaces[info->iface_num];
|
||||||
|
i->type = USB_IFACE_TYPE_HID;
|
||||||
|
i->hid.ep_in = info->ep_in;
|
||||||
|
i->hid.ep_out = info->ep_out;
|
||||||
|
i->hid.rx_buffer = info->rx_buffer;
|
||||||
|
i->hid.max_packet_len = info->max_packet_len;
|
||||||
|
i->hid.report_desc_len = info->report_desc_len;
|
||||||
|
i->hid.report_desc = info->report_desc;
|
||||||
|
i->hid.desc_block = d;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int usb_hid_can_read(uint8_t iface_num) {
|
||||||
|
return ((iface_num < USBD_MAX_NUM_INTERFACES) &&
|
||||||
|
(usb_ifaces[iface_num].type == USB_IFACE_TYPE_HID) &&
|
||||||
|
(usb_ifaces[iface_num].hid.rx_buffer_len > 0) &&
|
||||||
|
(usb_dev_handle.dev_state == USBD_STATE_CONFIGURED));
|
||||||
|
}
|
||||||
|
|
||||||
|
int usb_hid_can_write(uint8_t iface_num) {
|
||||||
|
return ((iface_num < USBD_MAX_NUM_INTERFACES) &&
|
||||||
|
(usb_ifaces[iface_num].type == USB_IFACE_TYPE_HID) &&
|
||||||
|
(usb_ifaces[iface_num].hid.in_idle) &&
|
||||||
|
(usb_dev_handle.dev_state == USBD_STATE_CONFIGURED));
|
||||||
|
}
|
||||||
|
|
||||||
|
int usb_hid_read(uint8_t iface_num, uint8_t *buf, uint32_t len) {
|
||||||
|
if (iface_num >= USBD_MAX_NUM_INTERFACES) {
|
||||||
|
return -1; // Invalid interface number
|
||||||
|
}
|
||||||
|
if (usb_ifaces[iface_num].type != USB_IFACE_TYPE_HID) {
|
||||||
|
return -2; // Invalid interface type
|
||||||
|
}
|
||||||
|
usb_hid_state_t *state = &usb_ifaces[iface_num].hid;
|
||||||
|
if (len < state->rx_buffer_len) {
|
||||||
|
return 0; // Not enough data in the read buffer
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(buf, state->rx_buffer, state->rx_buffer_len);
|
||||||
|
|
||||||
|
// Clear NAK to indicate we are ready to read more data
|
||||||
|
usb_ep_clear_nak(&usb_dev_handle, state->ep_out);
|
||||||
|
|
||||||
|
return state->rx_buffer_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
int usb_hid_write(uint8_t iface_num, const uint8_t *buf, uint32_t len) {
|
||||||
|
if (iface_num >= USBD_MAX_NUM_INTERFACES) {
|
||||||
|
return -1; // Invalid interface number
|
||||||
|
}
|
||||||
|
if (usb_ifaces[iface_num].type != USB_IFACE_TYPE_HID) {
|
||||||
|
return -2; // Invalid interface type
|
||||||
|
}
|
||||||
|
usb_hid_state_t *state = &usb_ifaces[iface_num].hid;
|
||||||
|
|
||||||
|
state->in_idle = 0;
|
||||||
|
USBD_LL_Transmit(&usb_dev_handle, state->ep_in, (uint8_t *)buf, (uint16_t)len);
|
||||||
|
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
int usb_hid_read_blocking(uint8_t iface_num, uint8_t *buf, uint32_t len, uint32_t timeout) {
|
||||||
|
uint32_t start = HAL_GetTick();
|
||||||
|
while (!usb_hid_can_read(iface_num)) {
|
||||||
|
if (HAL_GetTick() - start >= timeout) {
|
||||||
|
return 0; // Timeout
|
||||||
|
}
|
||||||
|
__WFI(); // Enter sleep mode, waiting for interrupt
|
||||||
|
}
|
||||||
|
return usb_hid_read(iface_num, buf, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
int usb_hid_write_blocking(uint8_t iface_num, const uint8_t *buf, uint32_t len, uint32_t timeout) {
|
||||||
|
uint32_t start = HAL_GetTick();
|
||||||
|
while (!usb_hid_can_write(iface_num)) {
|
||||||
|
if (HAL_GetTick() - start >= timeout) {
|
||||||
|
return 0; // Timeout
|
||||||
|
}
|
||||||
|
__WFI(); // Enter sleep mode, waiting for interrupt
|
||||||
|
}
|
||||||
|
return usb_hid_write(iface_num, buf, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int usb_hid_class_init(USBD_HandleTypeDef *dev, usb_hid_state_t *state, uint8_t cfg_idx) {
|
||||||
|
// Open endpoints
|
||||||
|
USBD_LL_OpenEP(dev, state->ep_in, USBD_EP_TYPE_INTR, state->max_packet_len);
|
||||||
|
USBD_LL_OpenEP(dev, state->ep_out, USBD_EP_TYPE_INTR, state->max_packet_len);
|
||||||
|
|
||||||
|
// Reset the state
|
||||||
|
state->in_idle = 1;
|
||||||
|
state->protocol = 0;
|
||||||
|
state->idle_rate = 0;
|
||||||
|
state->alt_setting = 0;
|
||||||
|
|
||||||
|
// Prepare Out endpoint to receive next packet
|
||||||
|
USBD_LL_PrepareReceive(dev, state->ep_out, state->rx_buffer, state->max_packet_len);
|
||||||
|
|
||||||
|
return USBD_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int usb_hid_class_deinit(USBD_HandleTypeDef *dev, usb_hid_state_t *state, uint8_t cfg_idx) {
|
||||||
|
// Close endpoints
|
||||||
|
USBD_LL_CloseEP(dev, state->ep_in);
|
||||||
|
USBD_LL_CloseEP(dev, state->ep_out);
|
||||||
|
|
||||||
|
return USBD_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int usb_hid_class_setup(USBD_HandleTypeDef *dev, usb_hid_state_t *state, USBD_SetupReqTypedef *req) {
|
||||||
|
switch (req->bmRequest & USB_REQ_TYPE_MASK) {
|
||||||
|
|
||||||
|
// Class request
|
||||||
|
case USB_REQ_TYPE_CLASS:
|
||||||
|
switch (req->bRequest) {
|
||||||
|
|
||||||
|
case HID_REQ_SET_PROTOCOL:
|
||||||
|
state->protocol = req->wValue;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case HID_REQ_GET_PROTOCOL:
|
||||||
|
USBD_CtlSendData(dev, &state->protocol, sizeof(state->protocol));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case HID_REQ_SET_IDLE:
|
||||||
|
state->idle_rate = req->wValue >> 8;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case HID_REQ_GET_IDLE:
|
||||||
|
USBD_CtlSendData(dev, &state->idle_rate, sizeof(state->idle_rate));
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
USBD_CtlError(dev, req);
|
||||||
|
return USBD_FAIL;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Interface & Endpoint request
|
||||||
|
case USB_REQ_TYPE_STANDARD:
|
||||||
|
switch (req->bRequest) {
|
||||||
|
|
||||||
|
case USB_REQ_SET_INTERFACE:
|
||||||
|
state->alt_setting = req->wValue;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case USB_REQ_GET_INTERFACE:
|
||||||
|
USBD_CtlSendData(dev, &state->alt_setting, sizeof(state->alt_setting));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case USB_REQ_GET_DESCRIPTOR:
|
||||||
|
switch (req->wValue >> 8) {
|
||||||
|
|
||||||
|
case USB_DESC_TYPE_HID:
|
||||||
|
USBD_CtlSendData(dev, (uint8_t*)&state->desc_block->hid, MIN(req->wLength, sizeof(state->desc_block->hid)));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case USB_DESC_TYPE_REPORT:
|
||||||
|
USBD_CtlSendData(dev, (uint8_t*)state->report_desc, MIN(req->wLength, state->report_desc_len));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return USBD_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t usb_hid_class_data_in(USBD_HandleTypeDef *dev, usb_hid_state_t *state, uint8_t ep_num) {
|
||||||
|
if (ep_num == state->ep_in) {
|
||||||
|
// Ensure that the FIFO is empty before a new transfer,
|
||||||
|
// this condition could be caused by a new transfer
|
||||||
|
// before the end of the previous transfer.
|
||||||
|
state->in_idle = 1;
|
||||||
|
}
|
||||||
|
return USBD_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t usb_hid_class_data_out(USBD_HandleTypeDef *dev, usb_hid_state_t *state, uint8_t ep_num) {
|
||||||
|
if (ep_num == state->ep_out) {
|
||||||
|
// User should provide state->rx_buffer_len that is big
|
||||||
|
// enough for state->max_packet_len bytes.
|
||||||
|
state->rx_buffer_len = USBD_LL_GetRxDataSize(dev, ep_num);
|
||||||
|
|
||||||
|
if (state->rx_buffer_len > 0) {
|
||||||
|
// Block the OUT EP until we process received data
|
||||||
|
usb_ep_set_nak(dev, ep_num);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return USBD_OK;
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user