usb: short control IN might need a ZLP

Control transfers can transfer less than was requested by the host in the
wLength field.  if this short transfer is a multiple of the endpoint's packet
size, a zero length packet must be sent.

Adds tests for a range of control transfer IN requests, and properly supports
this in the core.  Based heavily on work by Kuldeep Dhaka.

See https://github.com/libopencm3/libopencm3/pull/505
and https://github.com/libopencm3/libopencm3/pull/194 for original discussion.

Tested with stm32f4, stm32f103 and stm32l053.
This commit is contained in:
Karl Palsson
2015-10-11 01:17:24 +00:00
parent 5270c11a09
commit 3ed12b6fd9
4 changed files with 146 additions and 3 deletions

View File

@@ -50,6 +50,24 @@ static void stall_transaction(usbd_device *usbd_dev)
usbd_dev->control_state.state = IDLE;
}
/**
* If we're replying with _some_ data, but less than the host is expecting,
* then we normally just do a short transfer. But if it's short, but a
* multiple of the endpoint max packet size, we need an explicit ZLP.
* @param len how much data we want to transfer
* @param wLength how much the host asked for
* @param ep_size
* @return
*/
static bool needs_zlp(uint16_t len, uint16_t wLength, uint8_t ep_size) {
if (len < wLength) {
if (len && (len % ep_size == 0)) {
return true;
}
}
return false;
}
/* Register application callback function for handling USB control requests. */
int usbd_register_control_callback(usbd_device *usbd_dev, uint8_t type,
uint8_t type_mask,
@@ -89,7 +107,10 @@ static void usb_control_send_chunk(usbd_device *usbd_dev)
usbd_ep_write_packet(usbd_dev, 0,
usbd_dev->control_state.ctrl_buf,
usbd_dev->control_state.ctrl_len);
usbd_dev->control_state.state = LAST_DATA_IN;
usbd_dev->control_state.state =
usbd_dev->control_state.needs_zlp ? DATA_IN : LAST_DATA_IN;
usbd_dev->control_state.needs_zlp = false;
usbd_dev->control_state.ctrl_len = 0;
usbd_dev->control_state.ctrl_buf = NULL;
}
@@ -153,7 +174,11 @@ static void usb_control_setup_read(usbd_device *usbd_dev,
usbd_dev->control_state.ctrl_len = req->wLength;
if (usb_control_request_dispatch(usbd_dev, req)) {
if (usbd_dev->control_state.ctrl_len) {
if (req->wLength) {
usbd_dev->control_state.needs_zlp =
needs_zlp(usbd_dev->control_state.ctrl_len,
req->wLength,
usbd_dev->desc->bMaxPacketSize0);
/* Go to data out stage if handled. */
usb_control_send_chunk(usbd_dev);
} else {

View File

@@ -74,6 +74,7 @@ struct _usbd_device {
uint8_t *ctrl_buf;
uint16_t ctrl_len;
usbd_control_complete_callback complete;
bool needs_zlp;
} control_state;
struct user_control_callback {