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 */