usb: drivers: Fix ZLP handling for Nordic driver.
ZLP - zero length packet is used to indicate that the device has no more data to send. If the Host asks for more data that the device can provide and the data size is mutliplication of Endpoint wMaxPacketSize then the device must terminate the data transfer with ZLP. Until this patch Nordic device driver controller was not aware of the requested data length and could not determine when the ZLP was required. This patch introduces a fix that prevents the driver from starting setup stage before the ZLP is being send. For consistance with the Zephyr USB stack sending ZLP must be issued from the stack level. Making trans_zlp flag true results in blocking the driver from starting setup stage without required ZLP. After the data transfer finishes the driver will be prepared for ZLP and will call back the stack to start writing ZLP. After the ZLP is being send the driver will automatically start status stage and end the Control Transfer. This patch also removes CONFIG_USB_DEVICE_DISABLE_ZLP_EPIN_HANDLING and aligns Nordic driver with others. Without this patch the issue could occur when handling get requests. Typical case is string descriptor of length equal to wMaxPacketSize. Hosts usually asks for wLength = 255 Bytes when string descriptors are being requested. In that case to successfully finish the data stage of the Control transfer the device must send wMacPacketSize Bytes of actual string descriptor and then ZLP to indicate that no more data are present. After ZLP the status stage may start and the request is finished successfully. Without this patch the driver will not send ZLP making it unable to end the Control Request successful - this may lead to failing 'Device Descriptor Test' from USB3CV test tool. Signed-off-by: Emil Obalski <emil.obalski@nordicsemi.no>
This commit is contained in:
parent
2eb2bc325e
commit
773f02e6e9
4 changed files with 32 additions and 10 deletions
|
@ -54,7 +54,6 @@ menuconfig USB_NRFX
|
|||
select USB_DEVICE_DRIVER
|
||||
select NRFX_USBD
|
||||
select NRFX_POWER
|
||||
select USB_DEVICE_DISABLE_ZLP_EPIN_HANDLING
|
||||
help
|
||||
nRF USB Device Controller Driver
|
||||
|
||||
|
|
|
@ -111,6 +111,9 @@ struct nrf_usbd_ep_buf {
|
|||
* a data transfer.
|
||||
* @param write_in_progress A flag indicating that write operation has
|
||||
* been scheduled.
|
||||
* @param trans_zlp Flag required for Control IN Endpoint. It
|
||||
* indicates that ZLP is required to end data
|
||||
* stage of the control request.
|
||||
*/
|
||||
struct nrf_usbd_ep_ctx {
|
||||
struct nrf_usbd_ep_cfg cfg;
|
||||
|
@ -118,6 +121,7 @@ struct nrf_usbd_ep_ctx {
|
|||
volatile bool read_complete;
|
||||
volatile bool read_pending;
|
||||
volatile bool write_in_progress;
|
||||
bool trans_zlp;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -612,6 +616,7 @@ static void ep_ctx_reset(struct nrf_usbd_ep_ctx *ep_ctx)
|
|||
ep_ctx->read_complete = true;
|
||||
ep_ctx->read_pending = false;
|
||||
ep_ctx->write_in_progress = false;
|
||||
ep_ctx->trans_zlp = false;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -871,7 +876,12 @@ static inline void usbd_work_process_ep_events(struct usbd_ep_event *ep_evt)
|
|||
break;
|
||||
|
||||
case EP_EVT_WRITE_COMPLETE:
|
||||
if (ep_ctx->cfg.type == USB_DC_EP_CONTROL) {
|
||||
if (ep_ctx->cfg.type == USB_DC_EP_CONTROL &&
|
||||
!ep_ctx->trans_zlp) {
|
||||
/* Trigger the hardware to perform
|
||||
* status stage, but only if there is
|
||||
* no ZLP required.
|
||||
*/
|
||||
k_mutex_lock(&ctx->drv_lock, K_FOREVER);
|
||||
nrfx_usbd_setup_clear();
|
||||
k_mutex_unlock(&ctx->drv_lock);
|
||||
|
@ -1695,6 +1705,27 @@ int usb_dc_ep_write(const uint8_t ep, const uint8_t *const data,
|
|||
return -EAGAIN;
|
||||
}
|
||||
|
||||
/** Clear the ZLP flag if current write is ZLP. After the ZLP will be
|
||||
* send the driver will perform status stage.
|
||||
*/
|
||||
if (!data_len && ep_ctx->trans_zlp) {
|
||||
ep_ctx->trans_zlp = false;
|
||||
}
|
||||
|
||||
/** If writing to a Control Endpoint there might be a need to transfer
|
||||
* ZLP. If the Hosts asks for more data that the device may return and
|
||||
* the last packet is wMaxPacketSize long. The driver must send ZLP.
|
||||
* For consistance with the Zephyr USB stack sending ZLP must be issued
|
||||
* from the stack level. Making trans_zlp flag true results in blocking
|
||||
* the driver from starting setup stage without required ZLP.
|
||||
*/
|
||||
if (ep_ctx->cfg.type == USB_DC_EP_CONTROL) {
|
||||
if (data_len && usbd_ctx.setup.wLength > data_len &&
|
||||
!(data_len % ep_ctx->cfg.max_sz)) {
|
||||
ep_ctx->trans_zlp = true;
|
||||
}
|
||||
}
|
||||
|
||||
/* Setup stage is handled by hardware.
|
||||
* Detect the setup stage initiated by the stack
|
||||
* and perform appropriate action.
|
||||
|
|
|
@ -88,12 +88,6 @@ config USB_DEVICE_REMOTE_WAKEUP
|
|||
help
|
||||
This option requires USBD peripheral driver to also support remote wakeup.
|
||||
|
||||
config USB_DEVICE_DISABLE_ZLP_EPIN_HANDLING
|
||||
bool
|
||||
help
|
||||
Stack should not handle ZLP for Variable-length Data Stage
|
||||
because it is taken over by the hardware.
|
||||
|
||||
config USB_DEVICE_BOS
|
||||
bool "Enable USB Binary Device Object Store (BOS)"
|
||||
|
||||
|
|
|
@ -227,7 +227,6 @@ static void usb_data_to_host(uint16_t len)
|
|||
usb_dev.data_buf += chunk;
|
||||
usb_dev.data_buf_residue -= chunk;
|
||||
|
||||
#ifndef CONFIG_USB_DEVICE_DISABLE_ZLP_EPIN_HANDLING
|
||||
/*
|
||||
* Set ZLP flag when host asks for a bigger length and the
|
||||
* last chunk is wMaxPacketSize long, to indicate the last
|
||||
|
@ -242,7 +241,6 @@ static void usb_data_to_host(uint16_t len)
|
|||
usb_dev.zlp_flag = true;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
} else {
|
||||
usb_dev.zlp_flag = false;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue