summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNicolas Boichat <drinkcat@google.com>2016-11-09 18:45:51 +0800
committerchrome-bot <chrome-bot@chromium.org>2016-11-22 18:36:36 -0800
commit67473cb5b4834d57c2544229913dd06331df7530 (patch)
treef411d34156ec3d755220b4d1a95e2317c723e9d0
parent0e4776ec287ce92ce61ef9abae179330f4120aad (diff)
downloadchrome-ec-67473cb5b4834d57c2544229913dd06331df7530.tar.gz
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 <drinkcat@chromium.org> Tested-by: Nicolas Boichat <drinkcat@chromium.org> Reviewed-by: Vincent Palatin <vpalatin@chromium.org>
-rw-r--r--chip/stm32/usb.c30
-rw-r--r--chip/stm32/usb_hid.c49
-rw-r--r--chip/stm32/usb_hw.h4
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, \