From 51fce6e4c364c8f0f0c74d2fd14fce13918be55e Mon Sep 17 00:00:00 2001 From: Liam Date: Tue, 4 Apr 2023 19:05:34 -0400 Subject: [PATCH] usbcomms: add async API --- nx/include/switch/runtime/devices/usb_comms.h | 31 ++++ nx/source/runtime/devices/usb_comms.c | 157 ++++++++++++++++++ 2 files changed, 188 insertions(+) diff --git a/nx/include/switch/runtime/devices/usb_comms.h b/nx/include/switch/runtime/devices/usb_comms.h index 9da622d3..268ae895 100644 --- a/nx/include/switch/runtime/devices/usb_comms.h +++ b/nx/include/switch/runtime/devices/usb_comms.h @@ -26,6 +26,9 @@ void usbCommsExit(void); /// Sets whether to throw a fatal error in usbComms{Read/Write}* on failure, or just return the transferred size. By default (false) the latter is used. void usbCommsSetErrorHandling(bool flag); +///@name Synchronous API +///@{ + /// Read data with the default interface. size_t usbCommsRead(void* buffer, size_t size); @@ -37,3 +40,31 @@ size_t usbCommsReadEx(void* buffer, size_t size, u32 interface); /// Same as usbCommsWrite except with the specified interface. size_t usbCommsWriteEx(const void* buffer, size_t size, u32 interface); + +///@} + +///@name Asynchronous API +///@{ + +/// Retrieve event used for read completion with the given interface. +Event *usbCommsGetReadCompletionEvent(u32 interface); + +/// Start an asynchronous read. The completion event will be signaled when the read completes. +/// The buffer must be page-aligned and no larger than one page. +Result usbCommsReadAsync(void *buffer, size_t size, u32 *urbId, u32 interface); + +/// Complete an asynchronous read, clearing the completion event, and return the amount of data which was read. +Result usbCommsGetReadResult(u32 urbId, u32 *transferredSize, u32 interface); + + +/// Retrieve event used for write completion with the given interface. +Event *usbCommsGetWriteCompletionEvent(u32 interface); + +/// Start an asynchronous write. The completion event will be signaled when the write completes. +/// The buffer must be page-aligned and no larger than one page. +Result usbCommsWriteAsync(void *buffer, size_t size, u32 *urbId, u32 interface); + +/// Complete an asynchronous write, clearing the completion event, and return the amount of data which was written. +Result usbCommsGetWriteResult(u32 urbId, u32 *transferredSize, u32 interface); + +///@} diff --git a/nx/source/runtime/devices/usb_comms.c b/nx/source/runtime/devices/usb_comms.c index 18edd882..5a7c830e 100644 --- a/nx/source/runtime/devices/usb_comms.c +++ b/nx/source/runtime/devices/usb_comms.c @@ -462,6 +462,28 @@ static Result _usbCommsRead(usbCommsInterface *interface, void* buffer, size_t s return rc; } +static Result _usbCommsReadAsync(usbCommsInterface *interface, void *buffer, size_t size, u32 *urbId) +{ + if (((uintptr_t)buffer) & 0xfff) + return MAKERESULT(Module_Libnx, LibnxError_BadInput); + + // Start a host->device transfer. + return usbDsEndpoint_PostBufferAsync(interface->endpoint_out, buffer, size, urbId); +} + +static Result _usbCommsGetReadResult(usbCommsInterface *interface, u32 urbId, u32 *transferredSize) +{ + Result rc=0; + UsbDsReportData reportdata; + + eventClear(&interface->endpoint_out->CompletionEvent); + + rc = usbDsEndpoint_GetReportData(interface->endpoint_out, &reportdata); + if (R_FAILED(rc)) return rc; + + return usbDsParseReportData(&reportdata, urbId, NULL, transferredSize); +} + static Result _usbCommsWrite(usbCommsInterface *interface, const void* buffer, size_t size, size_t *transferredSize) { Result rc=0; @@ -525,6 +547,28 @@ static Result _usbCommsWrite(usbCommsInterface *interface, const void* buffer, s return rc; } +static Result _usbCommsWriteAsync(usbCommsInterface *interface, void *buffer, size_t size, u32 *urbId) +{ + if (((uintptr_t)buffer) & 0xfff) + return MAKERESULT(Module_Libnx, LibnxError_BadInput); + + // Start a device->host transfer. + return usbDsEndpoint_PostBufferAsync(interface->endpoint_in, buffer, size, urbId); +} + +static Result _usbCommsGetWriteResult(usbCommsInterface *interface, u32 urbId, u32 *transferredSize) +{ + Result rc=0; + UsbDsReportData reportdata; + + eventClear(&interface->endpoint_in->CompletionEvent); + + rc = usbDsEndpoint_GetReportData(interface->endpoint_in, &reportdata); + if (R_FAILED(rc)) return rc; + + return usbDsParseReportData(&reportdata, urbId, NULL, transferredSize); +} + size_t usbCommsReadEx(void* buffer, size_t size, u32 interface) { size_t transferredSize=0; @@ -562,6 +606,63 @@ size_t usbCommsRead(void* buffer, size_t size) return usbCommsReadEx(buffer, size, 0); } +Event *usbCommsGetReadCompletionEvent(u32 interface) +{ + usbCommsInterface *inter = &g_usbCommsInterfaces[interface]; + bool initialized; + + if (interface >= TOTAL_INTERFACES) return NULL; + + rwlockReadLock(&inter->lock); + initialized = inter->initialized; + rwlockReadUnlock(&inter->lock); + + if (!initialized) return NULL; + return &inter->endpoint_out->CompletionEvent; +} + +Result usbCommsReadAsync(void *buffer, size_t size, u32 *urbId, u32 interface) +{ + Result rc; + usbCommsInterface *inter = &g_usbCommsInterfaces[interface]; + bool initialized; + + if (interface >= TOTAL_INTERFACES) return MAKERESULT(Module_Libnx, LibnxError_BadUsbCommsRead); + + rwlockReadLock(&inter->lock); + initialized = inter->initialized; + rwlockReadUnlock(&inter->lock); + + if (!initialized) return MAKERESULT(Module_Libnx, LibnxError_BadUsbCommsRead); + + rwlockWriteLock(&inter->lock_out); + rc = _usbCommsReadAsync(inter, buffer, size, urbId); + rwlockWriteUnlock(&inter->lock_out); + + return rc; +} + +Result usbCommsGetReadResult(u32 urbId, u32 *transferredSize, u32 interface) +{ + Result rc; + usbCommsInterface *inter = &g_usbCommsInterfaces[interface]; + bool initialized; + + if (interface >= TOTAL_INTERFACES) return MAKERESULT(Module_Libnx, LibnxError_BadUsbCommsRead); + + rwlockReadLock(&inter->lock); + initialized = inter->initialized; + rwlockReadUnlock(&inter->lock); + + if (!initialized) return MAKERESULT(Module_Libnx, LibnxError_BadUsbCommsRead); + + rwlockWriteLock(&inter->lock_out); + rc = _usbCommsGetReadResult(inter, urbId, transferredSize); + rwlockWriteUnlock(&inter->lock_out); + + return rc; +} + size_t usbCommsWriteEx(const void* buffer, size_t size, u32 interface) { size_t transferredSize=0; @@ -599,3 +700,59 @@ size_t usbCommsWrite(const void* buffer, size_t size) return usbCommsWriteEx(buffer, size, 0); } +Event *usbCommsGetWriteCompletionEvent(u32 interface) +{ + usbCommsInterface *inter = &g_usbCommsInterfaces[interface]; + bool initialized; + + if (interface >= TOTAL_INTERFACES) return NULL; + + rwlockWriteLock(&inter->lock); + initialized = inter->initialized; + rwlockWriteUnlock(&inter->lock); + + if (!initialized) return NULL; + return &inter->endpoint_in->CompletionEvent; +} + +Result usbCommsWriteAsync(void *buffer, size_t size, u32 *urbId, u32 interface) +{ + Result rc; + usbCommsInterface *inter = &g_usbCommsInterfaces[interface]; + bool initialized; + + if (interface >= TOTAL_INTERFACES) return MAKERESULT(Module_Libnx, LibnxError_BadUsbCommsWrite); + + rwlockWriteLock(&inter->lock); + initialized = inter->initialized; + rwlockWriteUnlock(&inter->lock); + + if (!initialized) return MAKERESULT(Module_Libnx, LibnxError_BadUsbCommsWrite); + + rwlockWriteLock(&inter->lock_in); + rc = _usbCommsWriteAsync(inter, buffer, size, urbId); + rwlockWriteUnlock(&inter->lock_in); + + return rc; +} + +Result usbCommsGetWriteResult(u32 urbId, u32 *transferredSize, u32 interface) +{ + Result rc; + usbCommsInterface *inter = &g_usbCommsInterfaces[interface]; + bool initialized; + + if (interface >= TOTAL_INTERFACES) return MAKERESULT(Module_Libnx, LibnxError_BadUsbCommsWrite); + + rwlockWriteLock(&inter->lock); + initialized = inter->initialized; + rwlockWriteUnlock(&inter->lock); + + if (!initialized) return MAKERESULT(Module_Libnx, LibnxError_BadUsbCommsWrite); + + rwlockWriteLock(&inter->lock_in); + rc = _usbCommsGetWriteResult(inter, urbId, transferredSize); + rwlockWriteUnlock(&inter->lock_in); + + return rc; +}