usb: Rework timeouts

- Rework all timeouts to be more relaxed when doing big data transfers.
- Fix a bug where async transfer would timeout sooner instead of infinite tries.

Both showed up in Arch Linux, because of it's huge latency USB stack latency that can reach 1-2s.

The rework will let every OS work without adding additional wait time in the gadget loops.
This commit is contained in:
CTCaer 2020-12-30 13:37:36 +02:00
parent 2c695e9a96
commit 4949331f4c
5 changed files with 50 additions and 46 deletions

View file

@ -309,7 +309,7 @@ static bool _fts_touch_read(touchpad_report_t *rpt)
static u8 _hid_transfer_start(usb_ctxt_t *usbs, u32 len)
{
u8 status = usb_ops.usb_device_ep1_in_write((u8 *)USB_EP_BULK_IN_BUF_ADDR, len, NULL, USB_XFER_SYNCED);
u8 status = usb_ops.usb_device_ep1_in_write((u8 *)USB_EP_BULK_IN_BUF_ADDR, len, NULL, USB_XFER_SYNCED_CMD);
if (status == USB_ERROR_XFER_ERROR)
{
usbs->set_text(usbs->label, "#FFDD00 Error:# EP IN transfer!");

View file

@ -315,13 +315,13 @@ static void ums_flush_endpoint(u32 ep)
usb_ops.usbd_flush_endpoint(ep);
}
static void _ums_transfer_start(usbd_gadget_ums_t *ums, bulk_ctxt_t *bulk_ctxt, u32 ep, bool sync)
static void _ums_transfer_start(usbd_gadget_ums_t *ums, bulk_ctxt_t *bulk_ctxt, u32 ep, u32 sync_timeout)
{
if (ep == bulk_ctxt->bulk_in)
{
bulk_ctxt->bulk_in_status = usb_ops.usb_device_ep1_in_write(
bulk_ctxt->bulk_in_buf, bulk_ctxt->bulk_in_length,
&bulk_ctxt->bulk_in_length_actual, sync);
&bulk_ctxt->bulk_in_length_actual, sync_timeout);
if (bulk_ctxt->bulk_in_status == USB_ERROR_XFER_ERROR)
{
@ -331,14 +331,14 @@ static void _ums_transfer_start(usbd_gadget_ums_t *ums, bulk_ctxt_t *bulk_ctxt,
else if (bulk_ctxt->bulk_in_status == USB2_ERROR_XFER_NOT_ALIGNED)
ums->set_text(ums->label, "#FFDD00 Error:# EP IN Buffer not aligned!");
if (sync)
if (sync_timeout)
bulk_ctxt->bulk_in_buf_state = BUF_STATE_EMPTY;
}
else
{
bulk_ctxt->bulk_out_status = usb_ops.usb_device_ep1_out_read(
bulk_ctxt->bulk_out_buf, bulk_ctxt->bulk_out_length,
&bulk_ctxt->bulk_out_length_actual, sync);
&bulk_ctxt->bulk_out_length_actual, sync_timeout);
if (bulk_ctxt->bulk_out_status == USB_ERROR_XFER_ERROR)
{
@ -348,7 +348,7 @@ static void _ums_transfer_start(usbd_gadget_ums_t *ums, bulk_ctxt_t *bulk_ctxt,
else if (bulk_ctxt->bulk_out_status == USB2_ERROR_XFER_NOT_ALIGNED)
ums->set_text(ums->label, "#FFDD00 Error:# EP OUT Buffer not aligned!");
if (sync)
if (sync_timeout)
bulk_ctxt->bulk_out_buf_state = BUF_STATE_FULL;
}
}
@ -386,7 +386,7 @@ static void _ums_transfer_finish(usbd_gadget_ums_t *ums, bulk_ctxt_t *bulk_ctxt,
else
{
bulk_ctxt->bulk_out_status = usb_ops.usb_device_ep1_out_reading_finish(
&bulk_ctxt->bulk_out_length_actual, 1000000);
&bulk_ctxt->bulk_out_length_actual);
if (bulk_ctxt->bulk_out_status == USB_ERROR_XFER_ERROR)
{
@ -1407,7 +1407,7 @@ static int pad_with_zeros(usbd_gadget_ums_t *ums, bulk_ctxt_t *bulk_ctxt)
u32 nsend = MIN(ums->usb_amount_left, USB_EP_BUFFER_MAX_SIZE);
memset(bulk_ctxt->bulk_in_buf + current_len_to_keep, 0, nsend - current_len_to_keep);
bulk_ctxt->bulk_in_length = nsend;
_ums_transfer_start(ums, bulk_ctxt, bulk_ctxt->bulk_in, USB_XFER_SYNCED);
_ums_transfer_start(ums, bulk_ctxt, bulk_ctxt->bulk_in, USB_XFER_SYNCED_DATA);
ums->usb_amount_left -= nsend;
current_len_to_keep = 0;
}
@ -1425,7 +1425,7 @@ static int throw_away_data(usbd_gadget_ums_t *ums, bulk_ctxt_t *bulk_ctxt)
u32 amount = MIN(ums->usb_amount_left, USB_EP_BUFFER_MAX_SIZE);
bulk_ctxt->bulk_out_length = amount;
_ums_transfer_start(ums, bulk_ctxt, bulk_ctxt->bulk_out, USB_XFER_SYNCED);
_ums_transfer_start(ums, bulk_ctxt, bulk_ctxt->bulk_out, USB_XFER_SYNCED_DATA);
ums->usb_amount_left -= amount;
return UMS_RES_OK;
@ -1472,7 +1472,7 @@ static int finish_reply(usbd_gadget_ums_t *ums, bulk_ctxt_t *bulk_ctxt)
// If there's no residue, simply send the last buffer.
if (!ums->residue)
{
_ums_transfer_start(ums, bulk_ctxt, bulk_ctxt->bulk_in, USB_XFER_SYNCED);
_ums_transfer_start(ums, bulk_ctxt, bulk_ctxt->bulk_in, USB_XFER_SYNCED_DATA);
/* For Bulk-only, if we're allowed to stall then send the
* short packet and halt the bulk-in endpoint. If we can't
@ -1480,7 +1480,7 @@ static int finish_reply(usbd_gadget_ums_t *ums, bulk_ctxt_t *bulk_ctxt)
}
else if (ums->can_stall)
{
_ums_transfer_start(ums, bulk_ctxt, bulk_ctxt->bulk_in, USB_XFER_SYNCED);
_ums_transfer_start(ums, bulk_ctxt, bulk_ctxt->bulk_in, USB_XFER_SYNCED_DATA);
rc = ums_set_stall(bulk_ctxt->bulk_in);
ums->set_text(ums->label, "#FFDD00 Error:# Residue. Stalled EP IN!");
}
@ -1661,7 +1661,7 @@ static int get_next_command(usbd_gadget_ums_t *ums, bulk_ctxt_t *bulk_ctxt)
bulk_ctxt->bulk_out_length = USB_BULK_CB_WRAP_LEN;
/* Queue a request to read a Bulk-only CBW */
_ums_transfer_start(ums, bulk_ctxt, bulk_ctxt->bulk_out, USB_XFER_SYNCED);
_ums_transfer_start(ums, bulk_ctxt, bulk_ctxt->bulk_out, USB_XFER_SYNCED_CMD);
/* We will drain the buffer in software, which means we
* can reuse it for the next filling. No need to advance
@ -1707,7 +1707,7 @@ static void send_status(usbd_gadget_ums_t *ums, bulk_ctxt_t *bulk_ctxt)
csw->Status = status;
bulk_ctxt->bulk_in_length = USB_BULK_CS_WRAP_LEN;
_ums_transfer_start(ums, bulk_ctxt, bulk_ctxt->bulk_in, USB_XFER_SYNCED);
_ums_transfer_start(ums, bulk_ctxt, bulk_ctxt->bulk_in, USB_XFER_SYNCED_CMD);
}
static void handle_exception(usbd_gadget_ums_t *ums, bulk_ctxt_t *bulk_ctxt)

View file

@ -724,7 +724,7 @@ static usb_ep_status_t _usbd_get_ep_status(usb_ep_t endpoint)
return USB_EP_STATUS_IDLE;
}
static int _usbd_ep_operation(usb_ep_t endpoint, u8 *buf, u32 len, bool sync)
static int _usbd_ep_operation(usb_ep_t endpoint, u8 *buf, u32 len, u32 sync_timeout)
{
if (!buf)
len = 0;
@ -797,12 +797,12 @@ static int _usbd_ep_operation(usb_ep_t endpoint, u8 *buf, u32 len, bool sync)
int res = USB_RES_OK;
usb_ep_status_t ep_status;
if (sync)
if (sync_timeout)
{
ep_status = _usbd_get_ep_status(endpoint);
if (ep_status == USB_EP_STATUS_ACTIVE)
{
u32 retries = 1000000; // Timeout 2s.
u32 retries = sync_timeout;
while (retries)
{
ep_status = _usbd_get_ep_status(endpoint);
@ -834,7 +834,7 @@ out:
static int _usbd_ep_ack(usb_ep_t ep)
{
return _usbd_ep_operation(ep, NULL, 0, true);
return _usbd_ep_operation(ep, NULL, 0, USB_XFER_SYNCED_ENUM);
}
static void _usbd_set_ep0_stall()
@ -1275,7 +1275,7 @@ static int _usbd_handle_ep0_control_transfer()
if (_wLength < size)
size = _wLength;
res = _usbd_ep_operation(USB_EP_CTRL_IN, usb_ep0_ctrl_buf, size, true);
res = _usbd_ep_operation(USB_EP_CTRL_IN, usb_ep0_ctrl_buf, size, USB_XFER_SYNCED_ENUM);
if (!res)
res = _usbd_ep_ack(USB_EP_CTRL_OUT);
}
@ -1402,7 +1402,7 @@ static usb_ep_status_t _usbd_get_ep1_status(usb_dir_t dir)
return _usbd_get_ep_status(ep);
}
int usb_device_ep1_out_read(u8 *buf, u32 len, u32 *bytes_read, bool sync)
int usb_device_ep1_out_read(u8 *buf, u32 len, u32 *bytes_read, u32 sync_timeout)
{
if ((u32)buf % USB_EP_BUFFER_ALIGN)
return USB2_ERROR_XFER_NOT_ALIGNED;
@ -1410,9 +1410,9 @@ int usb_device_ep1_out_read(u8 *buf, u32 len, u32 *bytes_read, bool sync)
if (len > USB_EP_BUFFER_MAX_SIZE)
len = USB_EP_BUFFER_MAX_SIZE;
int res = _usbd_ep_operation(USB_EP_BULK_OUT, buf, len, sync);
int res = _usbd_ep_operation(USB_EP_BULK_OUT, buf, len, sync_timeout);
if (sync && bytes_read)
if (sync_timeout && bytes_read)
*bytes_read = res ? 0 : len;
return res;
@ -1435,7 +1435,7 @@ int usb_device_ep1_out_read_big(u8 *buf, u32 len, u32 *bytes_read)
{
u32 len_ep = MIN(len, USB_EP_BUFFER_MAX_SIZE);
res = usb_device_ep1_out_read(buf_curr, len_ep, &bytes, USB_XFER_SYNCED);
res = usb_device_ep1_out_read(buf_curr, len_ep, &bytes, USB_XFER_SYNCED_DATA);
if (res)
return res;
@ -1455,7 +1455,7 @@ static int _usbd_get_ep1_out_bytes_read()
return (usbdaemon->ep_bytes_requested[USB_EP_BULK_OUT] - (usbdaemon->qhs[USB_EP_BULK_OUT].token >> 16));
}
int usb_device_ep1_out_reading_finish(u32 *pending_bytes, int tries)
int usb_device_ep1_out_reading_finish(u32 *pending_bytes)
{
usb_ep_status_t ep_status;
do
@ -1480,7 +1480,7 @@ int usb_device_ep1_out_reading_finish(u32 *pending_bytes, int tries)
return USB_ERROR_XFER_ERROR;
}
int usb_device_ep1_in_write(u8 *buf, u32 len, u32 *bytes_written, bool sync)
int usb_device_ep1_in_write(u8 *buf, u32 len, u32 *bytes_written, u32 sync_timeout)
{
if ((u32)buf % USB_EP_BUFFER_ALIGN)
return USB2_ERROR_XFER_NOT_ALIGNED;
@ -1488,9 +1488,9 @@ int usb_device_ep1_in_write(u8 *buf, u32 len, u32 *bytes_written, bool sync)
if (len > USB_EP_BUFFER_MAX_SIZE)
len = USB_EP_BUFFER_MAX_SIZE;
int res = _usbd_ep_operation(USB_EP_BULK_IN, buf, len, sync);
int res = _usbd_ep_operation(USB_EP_BULK_IN, buf, len, sync_timeout);
if (sync && bytes_written)
if (sync_timeout && bytes_written)
*bytes_written = res ? 0 : len;
return res;

View file

@ -30,8 +30,12 @@
#define USB_EP_BUFFER_MAX_SIZE (USB_EP_BUFFER_4_TD)
#define USB_EP_BUFFER_ALIGN (USB_TD_BUFFER_PAGE_SIZE)
#define USB_XFER_START false
#define USB_XFER_SYNCED true
#define USB_XFER_START 0
#define USB_XFER_SYNCED_ENUM 1000000
#define USB_XFER_SYNCED_CMD 1000000
#define USB_XFER_SYNCED_DATA 2000000
#define USB_XFER_SYNCED_CLASS 5000000
#define USB_XFER_SYNCED -1
typedef enum _usb_hid_type
{
@ -169,10 +173,10 @@ typedef struct _usb_ops_t
int (*usb_device_class_send_max_lun)(u8);
int (*usb_device_class_send_hid_report)();
int (*usb_device_ep1_out_read)(u8 *, u32, u32 *, bool);
int (*usb_device_ep1_out_read)(u8 *, u32, u32 *, u32);
int (*usb_device_ep1_out_read_big)(u8 *, u32, u32 *);
int (*usb_device_ep1_out_reading_finish)(u32 *, int);
int (*usb_device_ep1_in_write)(u8 *, u32, u32 *, bool);
int (*usb_device_ep1_out_reading_finish)(u32 *);
int (*usb_device_ep1_in_write)(u8 *, u32, u32 *, u32);
int (*usb_device_ep1_in_writing_finish)(u32 *);
bool (*usb_device_get_suspended)();
bool (*usb_device_get_port_in_sleep)();

View file

@ -881,7 +881,7 @@ int xusb_device_init()
_xusbd_init_device_clocks();
// Enable AHB redirect for access to IRAM for Event/EP ring buffers.
mc_enable_ahb_redirect(); // can be skipped if IRAM is not used/////////////////
mc_enable_ahb_redirect(); // Can be skipped if IRAM is not used.
// Enable XUSB device IPFS.
XUSB_DEV_DEV(XUSB_DEV_CONFIGURATION) |= DEV_CONFIGURATION_EN_FPCI;
@ -1803,7 +1803,7 @@ int xusb_device_enumerate(usb_gadget_type gadget)
u32 timer = get_tmr_ms() + 90000;
while (true)
{
int res = _xusb_ep_operation(1000000); // 2s timeout.
int res = _xusb_ep_operation(USB_XFER_SYNCED_ENUM); // 2s timeout.
if (res && res != USB_ERROR_TIMEOUT)
return res;
@ -1826,7 +1826,7 @@ void xusb_end(bool reset_ep, bool only_controller)
CLOCK(CLK_RST_CONTROLLER_RST_DEV_W_SET) = BIT(CLK_W_XUSB_PADCTL);
CLOCK(CLK_RST_CONTROLLER_CLK_ENB_W_CLR) = BIT(CLK_W_XUSB);
CLOCK(CLK_RST_CONTROLLER_RST_DEV_W_SET) = BIT(CLK_W_XUSB);
mc_disable_ahb_redirect();///////////////////
mc_disable_ahb_redirect(); // Can be skipped if IRAM is not used.
}
int xusb_handle_ep0_ctrl_setup()
@ -1844,7 +1844,7 @@ int xusb_handle_ep0_ctrl_setup()
return USB_RES_OK;
}
int xusb_device_ep1_out_read(u8 *buf, u32 len, u32 *bytes_read, bool sync)
int xusb_device_ep1_out_read(u8 *buf, u32 len, u32 *bytes_read, u32 sync_tries)
{
if (len > USB_EP_BUFFER_MAX_SIZE)
len = USB_EP_BUFFER_MAX_SIZE;
@ -1855,10 +1855,10 @@ int xusb_device_ep1_out_read(u8 *buf, u32 len, u32 *bytes_read, bool sync)
_xusb_issue_normal_trb(buf, len, USB_DIR_OUT);
usbd_xotg->tx_count[USB_DIR_OUT]++;
if (sync)
if (sync_tries)
{
while (!res && usbd_xotg->tx_count[USB_DIR_OUT])
res = _xusb_ep_operation(1000000); // 2s timeout.
res = _xusb_ep_operation(sync_tries);
if (bytes_read)
*bytes_read = res ? 0 : usbd_xotg->bytes_remaining[USB_DIR_OUT];
@ -1882,7 +1882,7 @@ int xusb_device_ep1_out_read_big(u8 *buf, u32 len, u32 *bytes_read)
{
u32 len_ep = MIN(len, USB_EP_BUFFER_MAX_SIZE);
int res = xusb_device_ep1_out_read(buf_curr, len_ep, &bytes, USB_XFER_SYNCED);
int res = xusb_device_ep1_out_read(buf_curr, len_ep, &bytes, USB_XFER_SYNCED_DATA);
if (res)
return res;
@ -1894,11 +1894,11 @@ int xusb_device_ep1_out_read_big(u8 *buf, u32 len, u32 *bytes_read)
return USB_RES_OK;
}
int xusb_device_ep1_out_reading_finish(u32 *pending_bytes, int tries)
int xusb_device_ep1_out_reading_finish(u32 *pending_bytes)
{
int res = USB_RES_OK;
while (!res && usbd_xotg->tx_count[USB_DIR_OUT])
res = _xusb_ep_operation(tries);
res = _xusb_ep_operation(USB_XFER_SYNCED); // Infinite retries.
if (pending_bytes)
*pending_bytes = res ? 0 : usbd_xotg->bytes_remaining[USB_DIR_OUT];
@ -1908,7 +1908,7 @@ int xusb_device_ep1_out_reading_finish(u32 *pending_bytes, int tries)
return res;
}
int xusb_device_ep1_in_write(u8 *buf, u32 len, u32 *bytes_written, bool sync)
int xusb_device_ep1_in_write(u8 *buf, u32 len, u32 *bytes_written, u32 sync_tries)
{
if (len > USB_EP_BUFFER_MAX_SIZE)
len = USB_EP_BUFFER_MAX_SIZE;
@ -1921,10 +1921,10 @@ int xusb_device_ep1_in_write(u8 *buf, u32 len, u32 *bytes_written, bool sync)
_xusb_issue_normal_trb(buf, len, USB_DIR_IN);
usbd_xotg->tx_count[USB_DIR_IN]++;
if (sync)
if (sync_tries)
{
while (!res && usbd_xotg->tx_count[USB_DIR_IN])
res = _xusb_ep_operation(1000000); // 2s timeout.
res = _xusb_ep_operation(sync_tries);
if (bytes_written)
*bytes_written = res ? 0 : usbd_xotg->bytes_remaining[USB_DIR_IN];
@ -1947,7 +1947,7 @@ int xusb_device_ep1_in_writing_finish(u32 *pending_bytes)
{
int res = USB_RES_OK;
while (!res && usbd_xotg->tx_count[USB_DIR_IN])
res = _xusb_ep_operation(1000000); // 2s timeout.
res = _xusb_ep_operation(USB_XFER_SYNCED); // Infinite retries.
if (pending_bytes)
*pending_bytes = res ? 0 : usbd_xotg->bytes_remaining[USB_DIR_IN];
@ -1973,7 +1973,7 @@ bool xusb_device_class_send_max_lun(u8 max_lun)
// Wait for request and transfer start.
while (usbd_xotg->device_state != XUSB_LUN_CONFIGURED)
{
_xusb_ep_operation(500000);
_xusb_ep_operation(USB_XFER_SYNCED_CLASS);
if (timer < get_tmr_ms() || btn_read_vol() == (BTN_VOL_UP | BTN_VOL_DOWN))
return true;
}
@ -1991,7 +1991,7 @@ bool xusb_device_class_send_hid_report()
// Wait for request and transfer start.
while (usbd_xotg->device_state != XUSB_HID_CONFIGURED)
{
_xusb_ep_operation(500000);
_xusb_ep_operation(USB_XFER_SYNCED_CLASS);
if (timer < get_tmr_ms() || btn_read_vol() == (BTN_VOL_UP | BTN_VOL_DOWN))
return true;
}