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.
This commit is contained in:
Lephe 2022-11-13 08:39:52 +01:00
parent 5f9553f3b8
commit 5e35871c98
Signed by: Lephenixnoir
GPG Key ID: 1BBA026E13FC0495
3 changed files with 93 additions and 15 deletions

View File

@ -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 <time.h>
#include <stdbool.h>
/* 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 */

View File

@ -10,9 +10,10 @@ extern "C" {
#endif
#include <gint/defs/types.h>
#include <gint/std/endian.h>
#include <gint/defs/timeout.h>
#include <gint/gint.h>
#include <stdarg.h>
#include <endian.h>
/* 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

View File

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