From 5e35871c9801dfc34d2b44e6932f4c1b46c164e1 Mon Sep 17 00:00:00 2001 From: Lephe Date: Sun, 13 Nov 2022 08:39:52 +0100 Subject: [PATCH] usb: add timeout variants for functions We're not using them yet (specifically in fxlink) because timeouts leave the pipes in undesirable states that currently end up crashing. Some reset mechanism is needed, plus support from the protocol for canceling messages, etc. --- include/gint/defs/timeout.h | 36 ++++++++++++++++++++++++ include/gint/usb.h | 16 +++++++++-- src/usb/pipes.c | 56 ++++++++++++++++++++++++++++--------- 3 files changed, 93 insertions(+), 15 deletions(-) create mode 100644 include/gint/defs/timeout.h diff --git a/include/gint/defs/timeout.h b/include/gint/defs/timeout.h new file mode 100644 index 0000000..b4ca63c --- /dev/null +++ b/include/gint/defs/timeout.h @@ -0,0 +1,36 @@ +//--- +// gint:defs:timeout - RTC-based timeouts +// +// This header provides an interface for simplistic timers used for timeout +// waiting. Currently they are based on the RTC with a resolution of 1/128 s. +//--- + +#ifndef GINT_DEFS_TIMEOUT +#define GINT_DEFS_TIMEOUT + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +/* Object holding information about a timeout (specifically, when it expires). + TODO: timeout: consider using a struct timespec with clock_gettime()? */ +typedef clock_t timeout_t; + +static inline timeout_t timeout_make_ms(int ms) +{ + return clock() + (int64_t)ms * CLOCKS_PER_SEC / 1000; +} + +static inline bool timeout_elapsed(timeout_t const *t) +{ + return t && clock() >= *t; +} + +#ifdef __cplusplus +} +#endif + +#endif /* GINT_DEFS_TIMEOUT */ diff --git a/include/gint/usb.h b/include/gint/usb.h index a9fa12d..07ddba5 100644 --- a/include/gint/usb.h +++ b/include/gint/usb.h @@ -10,9 +10,10 @@ extern "C" { #endif #include -#include +#include #include #include +#include /* See "Interfaces to communicate with USB transfers" below for details */ typedef struct usb_interface usb_interface_t; @@ -42,11 +43,15 @@ enum { USB_WRITE_BUSY, /* Both FIFO controllers are busy, none is available to transfer */ USB_WRITE_NOFIFO, + /* Timeout */ + USB_WRITE_TIMEOUT, /* This pipe is busy (returned by usb_commit_async()) */ USB_COMMIT_BUSY, /* This pipe has no ongoing transfer to commit */ USB_COMMIT_INACTIVE, + /* Timeout */ + USB_COMMIT_TIMEOUT, }; /* usb_open(): Open the USB link @@ -204,7 +209,11 @@ int usb_interface_pipe(usb_interface_t const *interface, int endpoint); @dma Whether to use the DMA to perform the write -> Returns an error code (0 on success). */ int usb_write_sync(int pipe, void const *data, int size, int unit_size, - bool use_dma); + bool use_dma); + +/* usb_write_sync_timeout(): Synchronously write, with a timeout */ +int usb_write_sync_timeout(int pipe, void const *data, int size, + int unit_size, bool use_dma, timeout_t const *timeout); /* usb_write_async(): Asynchronously write to a USB pipe @@ -241,6 +250,9 @@ int usb_write_async(int pipe, void const *data, int size, int unit_size, transfers whatever data is left, and returns when the transfer completes. */ void usb_commit_sync(int pipe); +/* usb_commit_sync_timeout(): Synchronously commit a write, with timeout */ +int usb_commit_sync_timeout(int pipe, timeout_t const *timeout); + /* usb_commit_async(): Asynchronously commit a write This function commits the specified pipe, causing the pipe to transfer diff --git a/src/usb/pipes.c b/src/usb/pipes.c index b796770..1128752 100644 --- a/src/usb/pipes.c +++ b/src/usb/pipes.c @@ -337,26 +337,40 @@ int usb_write_async(int pipe, void const *data, int size, int unit_size, return 0; } -int usb_write_sync(int pipe, void const *data, int size, int unit_size, - bool use_dma) +int usb_write_sync_timeout(int pipe, void const *data, int size, int unit_size, + bool use_dma, timeout_t const *timeout) { volatile int flag = 0; - int rc; while(1) { - rc = usb_write_async(pipe, data, size, unit_size, use_dma, + int rc = usb_write_async(pipe, data, size, unit_size, use_dma, GINT_CALL_SET(&flag)); - if(rc == 0) break; + if(rc == 0) + break; if(rc == USB_WRITE_NOFIFO) usb_log("USB_WRITE_NOFIFO\n"); + if(rc != USB_WRITE_BUSY) + return rc; + if(timeout_elapsed(timeout)) + return USB_WRITE_TIMEOUT; sleep(); } - while(!flag) sleep(); + while(!flag) + { + if(timeout_elapsed(timeout)) + return USB_WRITE_TIMEOUT; + sleep(); + } return 0; } +int usb_write_sync(int pipe, void const *data, int size, int unit, bool dma) +{ + return usb_write_sync_timeout(pipe, data, size, unit, dma, NULL); +} + int usb_commit_async(int pipe, gint_call_t callback) { struct transfer volatile *t = &pipe_transfers[pipe]; @@ -392,20 +406,36 @@ int usb_commit_async(int pipe, gint_call_t callback) return 0; } -void usb_commit_sync(int pipe) +int usb_commit_sync_timeout(int pipe, timeout_t const *timeout) { - volatile int flag = 0; - int rc = 0; + int volatile flag = 0; /* Wait until the pipe is free, then commit */ - do rc = usb_commit_async(pipe, GINT_CALL_SET(&flag)); - while(rc == USB_COMMIT_BUSY); + while(1) + { + int rc = usb_commit_async(pipe, GINT_CALL_SET(&flag)); + if(rc == 0) + break; + if(rc != USB_COMMIT_BUSY) + return rc; + if(timeout_elapsed(timeout)) + return USB_COMMIT_TIMEOUT; + sleep(); + } /* Wait until the commit completes */ - if(rc == 0) + while(!flag) { - while(!flag) sleep(); + if(timeout_elapsed(timeout)) + return USB_COMMIT_TIMEOUT; + sleep(); } + return 0; +} + +void usb_commit_sync(int pipe) +{ + usb_commit_sync_timeout(pipe, NULL); } /* usb_pipe_write_bemp(): Callback for the BEMP interrupt on a pipe */