diff --git a/nx/include/switch/runtime/devices/usb_comms.h b/nx/include/switch/runtime/devices/usb_comms.h index 7c2c3efb..5c11d8d3 100644 --- a/nx/include/switch/runtime/devices/usb_comms.h +++ b/nx/include/switch/runtime/devices/usb_comms.h @@ -11,5 +11,21 @@ Result usbCommsInitialize(void); void usbCommsExit(void); +/// Same as usbCommsInitialize, except this can be used after usbCommsInitialize (or instead of usbCommsInitialize), for creating new interface(s). +/// bInterface* are the values for the same fields in usb.h \ref usb_interface_descriptor. \ref usbCommsInitialize uses USB_CLASS_VENDOR_SPEC for all of these internally. +Result usbCommsInitializeEx(u32 *interface, u8 bInterfaceClass, u8 bInterfaceSubClass, u8 bInterfaceProtocol); + +/// Shutdown the specified interface. If no interfaces are remaining, this then uses \ref usbCommsExit internally. +void usbCommsExitEx(u32 interface); + +/// Read data with the default interface. size_t usbCommsRead(void* buffer, size_t size); + +/// Write data with the default interface. size_t usbCommsWrite(const void* buffer, size_t size); + +/// Same as usbCommsRead except with the specified interface. +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); diff --git a/nx/source/runtime/devices/usb_comms.c b/nx/source/runtime/devices/usb_comms.c index 409ee04b..a0ec7d36 100644 --- a/nx/source/runtime/devices/usb_comms.c +++ b/nx/source/runtime/devices/usb_comms.c @@ -2,97 +2,173 @@ #include #include "types.h" #include "result.h" +#include "kernel/rwlock.h" #include "services/fatal.h" #include "services/usb.h" #include "runtime/devices/usb_comms.h" +#define TOTAL_INTERFACES 4 + +typedef struct { + RwLock lock, lock_in, lock_out; + bool initialized; + + UsbDsInterface* interface; + UsbDsEndpoint *endpoint_in, *endpoint_out; + + u8 *endpoint_in_buffer, *endpoint_out_buffer; +} usbCommsInterface; + static bool g_usbCommsInitialized = false; -static UsbDsInterface* interface = NULL; -static UsbDsEndpoint *g_usbComms_endpoint_in = NULL, *g_usbComms_endpoint_out = NULL; +static usbCommsInterface g_usbCommsInterfaces[TOTAL_INTERFACES]; -static u8 *g_usbComms_endpoint_in_buffer = NULL, *g_usbComms_endpoint_out_buffer = NULL; +static RwLock g_usbCommsLock; -static Result _usbCommsInit(void); +static Result _usbCommsInterfaceInit(usbCommsInterface *interface, u8 bInterfaceClass, u8 bInterfaceSubClass, u8 bInterfaceProtocol); -static Result _usbCommsWrite(const void* buffer, size_t size, size_t *transferredSize); +static Result _usbCommsWrite(usbCommsInterface *interface, const void* buffer, size_t size, size_t *transferredSize); + +Result usbCommsInitializeEx(u32 *interface, u8 bInterfaceClass, u8 bInterfaceSubClass, u8 bInterfaceProtocol) +{ + bool found=0; + usbCommsInterface *inter = NULL; + + rwlockWriteLock(&g_usbCommsLock); + + if (g_usbCommsInitialized && interface==NULL) { + rwlockWriteUnlock(&g_usbCommsLock); + return 0; + } + + Result rc=0; + u32 i = 0; + + if (!g_usbCommsInitialized) rc = usbDsInitialize(UsbComplexId_Default, NULL); + + if (R_SUCCEEDED(rc)) { + for(i=0; ilock); + if (!inter->initialized) found=1; + rwlockReadUnlock(&inter->lock); + + if (found) break; + } + + if (!found) rc = MAKERESULT(Module_Libnx, LibnxError_OutOfMemory); + } + + if (R_SUCCEEDED(rc)) { + rwlockWriteLock(&inter->lock); + rwlockWriteLock(&inter->lock_in); + rwlockWriteLock(&inter->lock_out); + rc = _usbCommsInterfaceInit(inter, bInterfaceClass, bInterfaceSubClass, bInterfaceProtocol); + rwlockWriteUnlock(&inter->lock_out); + rwlockWriteUnlock(&inter->lock_in); + rwlockWriteUnlock(&inter->lock); + } + + if (R_FAILED(rc)) { + usbCommsExit(); + } + + if (R_SUCCEEDED(rc)) g_usbCommsInitialized=true; + + if (R_SUCCEEDED(rc) && interface) *interface = i; + + rwlockWriteUnlock(&g_usbCommsLock); + + return rc; +} Result usbCommsInitialize(void) { - if (g_usbCommsInitialized) return 0; + return usbCommsInitializeEx(NULL, USB_CLASS_VENDOR_SPEC, USB_CLASS_VENDOR_SPEC, USB_CLASS_VENDOR_SPEC); +} - Result ret=0; - - ret = usbDsInitialize(UsbComplexId_Default, NULL); - - if (R_SUCCEEDED(ret)) { - //The buffer for PostBufferAsync commands must be 0x1000-byte aligned. - g_usbComms_endpoint_in_buffer = memalign(0x1000, 0x1000); - if (g_usbComms_endpoint_in_buffer==NULL) ret = MAKERESULT(Module_Libnx, LibnxError_OutOfMemory); - - if (R_SUCCEEDED(ret)) { - g_usbComms_endpoint_out_buffer = memalign(0x1000, 0x1000); - if (g_usbComms_endpoint_out_buffer==NULL) ret = MAKERESULT(Module_Libnx, LibnxError_OutOfMemory); - } - - if (R_SUCCEEDED(ret)) { - memset(g_usbComms_endpoint_in_buffer, 0, 0x1000); - memset(g_usbComms_endpoint_out_buffer, 0, 0x1000); - ret = _usbCommsInit(); - - if (ret != 0) { - ret += 2000<<9; - } - } - - if (R_FAILED(ret)) { - usbDsExit(); - - free(g_usbComms_endpoint_in_buffer); - g_usbComms_endpoint_in_buffer = NULL; - - free(g_usbComms_endpoint_out_buffer); - g_usbComms_endpoint_out_buffer = NULL; - } - } - else { - ret += 1000<<9; +static void _usbCommsInterfaceExit(usbCommsInterface *interface) +{ + rwlockWriteLock(&interface->lock); + if (!interface->initialized) { + rwlockWriteUnlock(&interface->lock); + return; } - if (R_SUCCEEDED(ret)) g_usbCommsInitialized=true; + rwlockWriteLock(&interface->lock_in); + rwlockWriteLock(&interface->lock_out); - return ret; + interface->initialized = 0; + + usbDsInterface_DisableInterface(interface->interface); + usbDsEndpoint_Close(interface->endpoint_in); + usbDsEndpoint_Close(interface->endpoint_out); + usbDsInterface_Close(interface->interface); + + interface->endpoint_in = NULL; + interface->endpoint_out = NULL; + interface->interface = NULL; + + free(interface->endpoint_in_buffer); + free(interface->endpoint_out_buffer); + interface->endpoint_in_buffer = NULL; + interface->endpoint_out_buffer = NULL; + + rwlockWriteUnlock(&interface->lock_out); + rwlockWriteUnlock(&interface->lock_in); + + rwlockWriteUnlock(&interface->lock); +} + +void usbCommsExitEx(u32 interface) +{ + u32 i; + bool found=0; + if (interface>=TOTAL_INTERFACES) return; + + _usbCommsInterfaceExit(&g_usbCommsInterfaces[interface]); + + for (i=0; iinitialized = 1; + + //The buffer for PostBufferAsync commands must be 0x1000-byte aligned. + interface->endpoint_in_buffer = memalign(0x1000, 0x1000); + if (interface->endpoint_in_buffer==NULL) rc = MAKERESULT(Module_Libnx, LibnxError_OutOfMemory); + + if (R_SUCCEEDED(rc)) { + interface->endpoint_out_buffer = memalign(0x1000, 0x1000); + if (interface->endpoint_out_buffer==NULL) rc = MAKERESULT(Module_Libnx, LibnxError_OutOfMemory); + } + + if (R_SUCCEEDED(rc)) { + memset(interface->endpoint_in_buffer, 0, 0x1000); + memset(interface->endpoint_out_buffer, 0, 0x1000); + } + + if (R_FAILED(rc)) return rc; + //Setup interface. - ret = usbDsGetDsInterface(&interface, &interface_descriptor, "usb"); - if (R_FAILED(ret)) return ret; + rc = usbDsGetDsInterface(&interface->interface, &interface_descriptor, "usb"); + if (R_FAILED(rc)) return rc; //Setup endpoints. - ret = usbDsInterface_GetDsEndpoint(interface, &g_usbComms_endpoint_in, &endpoint_descriptor_in);//device->host - if (R_FAILED(ret)) return ret; + rc = usbDsInterface_GetDsEndpoint(interface->interface, &interface->endpoint_in, &endpoint_descriptor_in);//device->host + if (R_FAILED(rc)) return rc; - ret = usbDsInterface_GetDsEndpoint(interface, &g_usbComms_endpoint_out, &endpoint_descriptor_out);//host->device - if (R_FAILED(ret)) return ret; + rc = usbDsInterface_GetDsEndpoint(interface->interface, &interface->endpoint_out, &endpoint_descriptor_out);//host->device + if (R_FAILED(rc)) return rc; - ret = usbDsInterface_EnableInterface(interface); - if (R_FAILED(ret)) return ret; + rc = usbDsInterface_EnableInterface(interface->interface); + if (R_FAILED(rc)) return rc; - return ret; + return rc; } -static Result _usbCommsRead(void* buffer, size_t size, size_t *transferredSize) +static Result _usbCommsRead(usbCommsInterface *interface, void* buffer, size_t size, size_t *transferredSize) { - Result ret=0; + Result rc=0; u32 urbId=0; u8 *bufptr = (u8*)buffer; u8 *transfer_buffer = NULL; @@ -141,15 +235,15 @@ static Result _usbCommsRead(void* buffer, size_t size, size_t *transferredSize) usbDsReportData reportdata; //Makes sure endpoints are ready for data-transfer / wait for init if needed. - ret = usbDsWaitReady(); - if (R_FAILED(ret)) return ret; + rc = usbDsWaitReady(); + if (R_FAILED(rc)) return rc; while(size) { if(((u64)bufptr) & 0xfff)//When bufptr isn't page-aligned copy the data into g_usbComms_endpoint_in_buffer and transfer that, otherwise use the bufptr directly. { - transfer_buffer = g_usbComms_endpoint_out_buffer; - memset(g_usbComms_endpoint_out_buffer, 0, 0x1000); + transfer_buffer = interface->endpoint_out_buffer; + memset(interface->endpoint_out_buffer, 0, 0x1000); chunksize = 0x1000; chunksize-= ((u64)bufptr) & 0xfff;//After this transfer, bufptr will be page-aligned(if size is large enough for another transfer). @@ -166,18 +260,18 @@ static Result _usbCommsRead(void* buffer, size_t size, size_t *transferredSize) } //Start a host->device transfer. - ret = usbDsEndpoint_PostBufferAsync(g_usbComms_endpoint_out, transfer_buffer, chunksize, &urbId); - if (R_FAILED(ret)) return ret; + rc = usbDsEndpoint_PostBufferAsync(interface->endpoint_out, transfer_buffer, chunksize, &urbId); + if (R_FAILED(rc)) return rc; //Wait for the transfer to finish. - svcWaitSynchronizationSingle(g_usbComms_endpoint_out->CompletionEvent, U64_MAX); - svcClearEvent(g_usbComms_endpoint_out->CompletionEvent); + svcWaitSynchronizationSingle(interface->endpoint_out->CompletionEvent, U64_MAX); + svcClearEvent(interface->endpoint_out->CompletionEvent); - ret = usbDsEndpoint_GetReportData(g_usbComms_endpoint_out, &reportdata); - if (R_FAILED(ret)) return ret; + rc = usbDsEndpoint_GetReportData(interface->endpoint_out, &reportdata); + if (R_FAILED(rc)) return rc; - ret = usbDsParseReportData(&reportdata, urbId, NULL, &tmp_transferredSize); - if (R_FAILED(ret)) return ret; + rc = usbDsParseReportData(&reportdata, urbId, NULL, &tmp_transferredSize); + if (R_FAILED(rc)) return rc; if (tmp_transferredSize > chunksize) tmp_transferredSize = chunksize; total_transferredSize+= (size_t)tmp_transferredSize; @@ -191,12 +285,12 @@ static Result _usbCommsRead(void* buffer, size_t size, size_t *transferredSize) if (transferredSize) *transferredSize = total_transferredSize; - return ret; + return rc; } -static Result _usbCommsWrite(const void* buffer, size_t size, size_t *transferredSize) +static Result _usbCommsWrite(usbCommsInterface *interface, const void* buffer, size_t size, size_t *transferredSize) { - Result ret=0; + Result rc=0; u32 urbId=0; u32 chunksize=0; u8 *bufptr = (u8*)buffer; @@ -206,21 +300,21 @@ static Result _usbCommsWrite(const void* buffer, size_t size, size_t *transferre usbDsReportData reportdata; //Makes sure endpoints are ready for data-transfer / wait for init if needed. - ret = usbDsWaitReady(); - if (R_FAILED(ret)) return ret; + rc = usbDsWaitReady(); + if (R_FAILED(rc)) return rc; while(size) { if(((u64)bufptr) & 0xfff)//When bufptr isn't page-aligned copy the data into g_usbComms_endpoint_in_buffer and transfer that, otherwise use the bufptr directly. { - transfer_buffer = g_usbComms_endpoint_in_buffer; - memset(g_usbComms_endpoint_in_buffer, 0, 0x1000); + transfer_buffer = interface->endpoint_in_buffer; + memset(interface->endpoint_in_buffer, 0, 0x1000); chunksize = 0x1000; chunksize-= ((u64)bufptr) & 0xfff;//After this transfer, bufptr will be page-aligned(if size is large enough for another transfer). if (sizeendpoint_in_buffer, bufptr, chunksize); } else { @@ -229,18 +323,18 @@ static Result _usbCommsWrite(const void* buffer, size_t size, size_t *transferre } //Start a device->host transfer. - ret = usbDsEndpoint_PostBufferAsync(g_usbComms_endpoint_in, transfer_buffer, chunksize, &urbId); - if(R_FAILED(ret))return ret; + rc = usbDsEndpoint_PostBufferAsync(interface->endpoint_in, transfer_buffer, chunksize, &urbId); + if(R_FAILED(rc))return rc; //Wait for the transfer to finish. - svcWaitSynchronizationSingle(g_usbComms_endpoint_in->CompletionEvent, U64_MAX); - svcClearEvent(g_usbComms_endpoint_in->CompletionEvent); + svcWaitSynchronizationSingle(interface->endpoint_in->CompletionEvent, U64_MAX); + svcClearEvent(interface->endpoint_in->CompletionEvent); - ret = usbDsEndpoint_GetReportData(g_usbComms_endpoint_in, &reportdata); - if (R_FAILED(ret)) return ret; + rc = usbDsEndpoint_GetReportData(interface->endpoint_in, &reportdata); + if (R_FAILED(rc)) return rc; - ret = usbDsParseReportData(&reportdata, urbId, NULL, &tmp_transferredSize); - if (R_FAILED(ret)) return ret; + rc = usbDsParseReportData(&reportdata, urbId, NULL, &tmp_transferredSize); + if (R_FAILED(rc)) return rc; if (tmp_transferredSize > chunksize) tmp_transferredSize = chunksize; @@ -254,38 +348,80 @@ static Result _usbCommsWrite(const void* buffer, size_t size, size_t *transferre if (transferredSize) *transferredSize = total_transferredSize; - return ret; + return rc; +} + +size_t usbCommsReadEx(void* buffer, size_t size, u32 interface) +{ + size_t transferredSize=0; + u32 state=0; + Result rc, rc2; + usbCommsInterface *inter = &g_usbCommsInterfaces[interface]; + bool initialized; + + if (interface>=TOTAL_INTERFACES) return 0; + + rwlockReadLock(&inter->lock); + initialized = inter->initialized; + rwlockReadUnlock(&inter->lock); + if (!initialized) return 0; + + rwlockWriteLock(&inter->lock_out); + rc = _usbCommsRead(inter, buffer, size, &transferredSize); + rwlockWriteUnlock(&inter->lock_out); + if (R_FAILED(rc)) { + rc2 = usbDsGetState(&state); + if (R_SUCCEEDED(rc2)) { + if (state!=5) { + rwlockWriteLock(&inter->lock_out); + rc = _usbCommsRead(&g_usbCommsInterfaces[interface], buffer, size, &transferredSize); //If state changed during transfer, try again. usbDsWaitReady() will be called from this. + rwlockWriteUnlock(&inter->lock_out); + } + } + if (R_FAILED(rc)) fatalSimple(MAKERESULT(Module_Libnx, LibnxError_BadUsbCommsRead)); + } + return transferredSize; } size_t usbCommsRead(void* buffer, size_t size) +{ + return usbCommsReadEx(buffer, size, 0); +} + +size_t usbCommsWriteEx(const void* buffer, size_t size, u32 interface) { size_t transferredSize=0; u32 state=0; - Result ret, ret2; - ret = _usbCommsRead(buffer, size, &transferredSize); - if (R_FAILED(ret)) { - ret2 = usbDsGetState(&state); - if (R_SUCCEEDED(ret2)) { - if (state!=5) ret = _usbCommsRead(buffer, size, &transferredSize); //If state changed during transfer, try again. usbDsWaitReady() will be called from this. + Result rc, rc2; + usbCommsInterface *inter = &g_usbCommsInterfaces[interface]; + bool initialized; + + if (interface>=TOTAL_INTERFACES) return 0; + + rwlockReadLock(&inter->lock); + initialized = inter->initialized; + rwlockReadUnlock(&inter->lock); + if (!initialized) return 0; + + rwlockWriteLock(&inter->lock_in); + rc = _usbCommsWrite(&g_usbCommsInterfaces[interface], buffer, size, &transferredSize); + rwlockWriteUnlock(&inter->lock_in); + if (R_FAILED(rc)) { + rc2 = usbDsGetState(&state); + if (R_SUCCEEDED(rc2)) { + if (state!=5) { + rwlockWriteLock(&inter->lock_in); + rc = _usbCommsWrite(&g_usbCommsInterfaces[interface], buffer, size, &transferredSize); //If state changed during transfer, try again. usbDsWaitReady() will be called from this. + rwlockWriteUnlock(&inter->lock_in); + } } - if (R_FAILED(ret))fatalSimple(MAKERESULT(Module_Libnx, LibnxError_BadUsbCommsRead)); + if (R_FAILED(rc)) fatalSimple(MAKERESULT(Module_Libnx, LibnxError_BadUsbCommsWrite)); } return transferredSize; } size_t usbCommsWrite(const void* buffer, size_t size) { - size_t transferredSize=0; - u32 state=0; - Result ret, ret2; - ret = _usbCommsWrite(buffer, size, &transferredSize); - if (R_FAILED(ret)) { - ret2 = usbDsGetState(&state); - if (R_SUCCEEDED(ret2)) { - if (state!=5) ret = _usbCommsWrite(buffer, size, &transferredSize); //If state changed during transfer, try again. usbDsWaitReady() will be called from this. - } - if (R_FAILED(ret))fatalSimple(MAKERESULT(Module_Libnx, LibnxError_BadUsbCommsWrite)); - } - return transferredSize; + return usbCommsWriteEx(buffer, size, 0); }