From 67473cb5b4834d57c2544229913dd06331df7530 Mon Sep 17 00:00:00 2001 From: Nicolas Boichat Date: Wed, 9 Nov 2016 18:45:51 +0800 Subject: chip/stm32/usb: Allow interface handler to reply with more than 64 bytes For example, when a HID descriptor is longer than 64 bytes, we need to split it in multiple packets. BRANCH=none BUG=chrome-os-partner:59083 TEST=make buildall -j TEST=make BOARD=hammer -j && util/flash_ec --board=hammer Change-Id: I25a05eabaf9413e332fe3cd70695a0d53639713d Reviewed-on: https://chromium-review.googlesource.com/409316 Commit-Ready: Nicolas Boichat Tested-by: Nicolas Boichat Reviewed-by: Vincent Palatin --- chip/stm32/usb.c | 30 +++++++++++++++++++++++++----- chip/stm32/usb_hid.c | 49 ++++++++++++++++++++++++++++++++++++++++--------- chip/stm32/usb_hw.h | 4 ++++ 3 files changed, 69 insertions(+), 14 deletions(-) diff --git a/chip/stm32/usb.c b/chip/stm32/usb.c index dbbc800ffe..2afab3f415 100644 --- a/chip/stm32/usb.c +++ b/chip/stm32/usb.c @@ -99,7 +99,8 @@ static int set_addr; static int desc_left; /* pointer to descriptor data if any */ static const uint8_t *desc_ptr; - +/* interface that should handle the next tx transaction */ +static uint8_t iface_next = USB_IFACE_COUNT; void usb_read_setup_packet(usb_uint *buffer, struct usb_setup_packet *packet) @@ -118,14 +119,21 @@ static void ep0_rx(void) /* reset any incomplete descriptor transfer */ desc_ptr = NULL; + iface_next = USB_IFACE_COUNT; /* interface specific requests */ if ((req & USB_RECIP_MASK) == USB_RECIP_INTERFACE) { uint8_t iface = ep0_buf_rx[2] & 0xff; - if (iface < USB_IFACE_COUNT && - usb_iface_request[iface](ep0_buf_rx, ep0_buf_tx)) - goto unknown_req; - return; + if (iface < USB_IFACE_COUNT) { + int ret; + + ret = usb_iface_request[iface](ep0_buf_rx, ep0_buf_tx); + if (ret < 0) + goto unknown_req; + if (ret == 1) + iface_next = iface; + return; + } } /* TODO check setup bit ? */ @@ -241,6 +249,18 @@ static void ep0_tx(void) /* send the null OUT transaction if the transfer is complete */ return; } + if (iface_next < USB_IFACE_COUNT) { + int ret; + + ret = usb_iface_request[iface_next](NULL, ep0_buf_tx); + if (ret < 0) + goto error; + if (ret == 0) + iface_next = USB_IFACE_COUNT; + return; + } + +error: STM32_TOGGLE_EP(0, EP_TX_MASK, EP_TX_VALID, 0); } diff --git a/chip/stm32/usb_hid.c b/chip/stm32/usb_hid.c index b316585e77..79668ad0de 100644 --- a/chip/stm32/usb_hid.c +++ b/chip/stm32/usb_hid.c @@ -40,23 +40,54 @@ void hid_reset(int ep, usb_uint *hid_ep_buf, int len) (0 << 12) /* RX Disabled */; } +/* + * Keep track of state in case we need to be called multiple times, + * if the report length is bigger than 64 bytes. + */ +static int report_left; +static const uint8_t *report_ptr; + int hid_iface_request(usb_uint *ep0_buf_rx, usb_uint *ep0_buf_tx, const uint8_t *report_desc, int report_size) { - if ((ep0_buf_rx[0] == (USB_DIR_IN | USB_RECIP_INTERFACE | + if (!ep0_buf_rx) { + /* + * Continue previous transfer. We ignore report_desc/size here, + * which is fine as only one GET_DESCRIPTOR command comes at a + * time. + */ + if (report_left == 0) + return -1; + report_size = MIN(USB_MAX_PACKET_SIZE, report_left); + memcpy_to_usbram((void *) usb_sram_addr(ep0_buf_tx), + report_ptr, report_size); + btable_ep[0].tx_count = report_size; + report_left -= report_size; + report_ptr += report_size; + STM32_TOGGLE_EP(0, EP_TX_MASK, EP_TX_VALID, + report_left ? 0 : EP_STATUS_OUT); + return report_left ? 1 : 0; + } else if ((ep0_buf_rx[0] == (USB_DIR_IN | USB_RECIP_INTERFACE | (USB_REQ_GET_DESCRIPTOR << 8))) && (ep0_buf_rx[1] == (USB_HID_DT_REPORT << 8))) { /* Setup : HID specific : Get Report descriptor */ + report_size = MIN(ep0_buf_rx[3], report_size); + + if (report_size > USB_MAX_PACKET_SIZE) { + /* Report descriptor does not fit in one packet. */ + report_left = report_size - USB_MAX_PACKET_SIZE; + report_ptr = report_desc + USB_MAX_PACKET_SIZE; + report_size = USB_MAX_PACKET_SIZE; + } else { + report_left = 0; + } memcpy_to_usbram((void *) usb_sram_addr(ep0_buf_tx), - report_desc, - report_size); - btable_ep[0].tx_count = MIN(ep0_buf_rx[3], report_size); + report_desc, report_size); + btable_ep[0].tx_count = report_size; STM32_TOGGLE_EP(0, EP_TX_RX_MASK, EP_TX_RX_VALID, - EP_STATUS_OUT); - CPRINTF("RPT %04x[l %04x]\n", STM32_USB_EP(0), - ep0_buf_rx[3]); - return 0; + report_left ? 0 : EP_STATUS_OUT); + return report_left ? 1 : 0; } - return 1; + return -1; } diff --git a/chip/stm32/usb_hw.h b/chip/stm32/usb_hw.h index 29bb347e3c..ef231c4567 100644 --- a/chip/stm32/usb_hw.h +++ b/chip/stm32/usb_hw.h @@ -81,6 +81,10 @@ extern void (*usb_ep_reset[]) (void); /* array with interface-specific control request callbacks */ extern int (*usb_iface_request[]) (usb_uint *ep0_buf_rx, usb_uint *ep0_buf_tx); +/* + * Interface handler returns -1 on error, 0 if it wrote the last chunk of data, + * or 1 if more data needs to be transferred on the next control request. + */ #define _IFACE_HANDLER(num) CONCAT3(iface_, num, _request) #define USB_DECLARE_IFACE(num, handler) \ int _IFACE_HANDLER(num)(usb_uint *ep0_buf_rx, \ -- cgit v1.2.1