From 96dc3a4ff0623029bf84f7183c908b5c8ebe48b5 Mon Sep 17 00:00:00 2001 From: yellows8 Date: Thu, 12 Oct 2017 18:20:31 -0400 Subject: [PATCH] Added usbdev under new directory 'devices'. Added more libnx errors to result.h. Added usbDsParseReportData() and the structs for it. Changed the post-buffer funcs 'out' field to 'urbId'. Updated the get-report funcs for using the report-data structure. --- nx/Makefile | 2 +- nx/include/switch.h | 2 + nx/include/switch/devices/usb_dev.h | 12 ++ nx/include/switch/result.h | 2 + nx/include/switch/services/usb.h | 27 ++- nx/source/devices/usb_dev.c | 296 ++++++++++++++++++++++++++++ nx/source/services/usb.c | 70 +++++-- 7 files changed, 389 insertions(+), 22 deletions(-) create mode 100644 nx/include/switch/devices/usb_dev.h create mode 100644 nx/source/devices/usb_dev.c diff --git a/nx/Makefile b/nx/Makefile index 27fcee88..2b8159db 100644 --- a/nx/Makefile +++ b/nx/Makefile @@ -24,7 +24,7 @@ VERSION := $(LIBNX_MAJOR).$(LIBNX_MINOR).$(LIBNX_PATCH) #--------------------------------------------------------------------------------- TARGET := nx #BUILD := build -SOURCES := source/system source/kernel source/services +SOURCES := source/system source/kernel source/services source/devices DATA := data INCLUDES := include diff --git a/nx/include/switch.h b/nx/include/switch.h index 5773eacd..62374e46 100644 --- a/nx/include/switch.h +++ b/nx/include/switch.h @@ -26,6 +26,8 @@ extern "C" { #include #include +#include + #ifdef __cplusplus } #endif diff --git a/nx/include/switch/devices/usb_dev.h b/nx/include/switch/devices/usb_dev.h new file mode 100644 index 00000000..bb51b244 --- /dev/null +++ b/nx/include/switch/devices/usb_dev.h @@ -0,0 +1,12 @@ +/// Switch-as-device<>host USB comms for serial, uses usbDs. Do not directly use usbDs when using this. + +/// usbDevInitialize will not return until the newline data transfer to the host finishes, aka when the host reads that data. +Result usbDevInitialize(void); +void usbDevExit(void); + +/// These will throw fatal-error when any errors occur. These return the actual transfer size. +/// Note that if you use usbDevRead() where size is <0x200(wMaxPacketSize), any data after that in an USB packet will be discarded. That remaining data in a packet won't be readable by calling usbDevRead again. +/// These will not return until the data transfer finishes. +size_t usbDevRead(void* buffer, size_t size); +size_t usbDevWrite(const void* buffer, size_t size); + diff --git a/nx/include/switch/result.h b/nx/include/switch/result.h index 3c5d1e40..3dec642e 100644 --- a/nx/include/switch/result.h +++ b/nx/include/switch/result.h @@ -26,3 +26,5 @@ #define LIBNX_BADQUERYMEMORY 5 #define LIBNX_ALREADYINITIALIZED 6 #define LIBNX_NOTINITIALIZED 7 +#define LIBNX_NOTFOUND 8 +#define LIBNX_IOERROR 9 diff --git a/nx/include/switch/services/usb.h b/nx/include/switch/services/usb.h index 63ceed47..3bde52c2 100644 --- a/nx/include/switch/services/usb.h +++ b/nx/include/switch/services/usb.h @@ -54,6 +54,18 @@ typedef struct { char SerialNumber[0x20]; } usbDsDeviceInfo; +typedef struct { + u32 id; /// urbId from post-buffer cmds + u32 requestedSize; + u32 transferredSize; + u32 urb_status; +} usbDsReportEntry; + +typedef struct { + usbDsReportEntry report[8]; + u32 report_count; +} usbDsReportData; + typedef struct { bool initialized; u32 interface_index; @@ -199,21 +211,24 @@ Result usbDsGetDsInterface(UsbDsInterface** interface, struct usb_interface_desc /// Wait for initialization to finish where data-transfer is usable. Result usbDsWaitReady(void); +/// Parse usbDsReportData from the Get*ReportData commands, where urbId is from the post-buffer commands. Will return the converted urb_status result-value. +Result usbDsParseReportData(usbDsReportData *reportdata, u32 urbId, u32 *requestedSize, u32 *transferredSize); + /// IDsInterface void usbDsInterface_Close(UsbDsInterface* interface); Result usbDsInterface_GetDsEndpoint(UsbDsInterface* interface, UsbDsEndpoint** endpoint, struct usb_endpoint_descriptor* descriptor); Result usbDsInterface_EnableInterface(UsbDsInterface* interface); Result usbDsInterface_DisableInterface(UsbDsInterface* interface); -Result usbDsInterface_CtrlInPostBufferAsync(UsbDsInterface* interface, void* buffer, size_t size, u32 *out); -Result usbDsInterface_CtrlOutPostBufferAsync(UsbDsInterface* interface, void* buffer, size_t size, u32 *out); -Result usbDsInterface_GetCtrlInReportData(UsbDsInterface* interface, u8 out[0x84]); -Result usbDsInterface_GetCtrlOutReportData(UsbDsInterface* interface, u8 out[0x84]); +Result usbDsInterface_CtrlInPostBufferAsync(UsbDsInterface* interface, void* buffer, size_t size, u32 *urbId); +Result usbDsInterface_CtrlOutPostBufferAsync(UsbDsInterface* interface, void* buffer, size_t size, u32 *urbId); +Result usbDsInterface_GetCtrlInReportData(UsbDsInterface* interface, usbDsReportData *out); +Result usbDsInterface_GetCtrlOutReportData(UsbDsInterface* interface, usbDsReportData *out); Result usbDsInterface_StallCtrl(UsbDsInterface* interface); /// IDsEndpoint void usbDsEndpoint_Close(UsbDsEndpoint* endpoint); -Result usbDsEndpoint_PostBufferAsync(UsbDsEndpoint* endpoint, void* buffer, size_t size, u32 *out); -Result usbDsEndpoint_GetReportData(UsbDsEndpoint* endpoint, u8 out[0x84]); +Result usbDsEndpoint_PostBufferAsync(UsbDsEndpoint* endpoint, void* buffer, size_t size, u32 *urbId); +Result usbDsEndpoint_GetReportData(UsbDsEndpoint* endpoint, usbDsReportData *out); Result usbDsEndpoint_StallCtrl(UsbDsEndpoint* endpoint); diff --git a/nx/source/devices/usb_dev.c b/nx/source/devices/usb_dev.c new file mode 100644 index 00000000..4d9351a2 --- /dev/null +++ b/nx/source/devices/usb_dev.c @@ -0,0 +1,296 @@ +#include +#include +#include + +//TODO: devoptab support + +static bool g_usbDevInitialized = false; + +static UsbDsInterface* interface = NULL; +static UsbDsEndpoint *g_usbDev_endpoint_in = NULL, *g_usbDev_endpoint_out = NULL; + +static u8 *g_usbDev_endpoint_in_buffer = NULL, *g_usbDev_endpoint_out_buffer = NULL; + +static Result _usbDevInit(void); + +static Result _usbDevWrite(const void* buffer, size_t size, size_t *transferredSize); + +Result usbDevInitialize(void) +{ + if (g_usbDevInitialized) return 0; + + Result ret=0; + + usbDsDeviceInfo deviceinfo = { + .idVendor = 0x0403, // "Future Technology Devices International, Ltd" + .idProduct = 0x6001, // "FT232 USB-Serial (UART) IC" + .bcdDevice = 0x0200, + .Manufacturer = "libnx", + .Product = "usbDev", + .SerialNumber = "1337", + }; + + ret = usbDsInitialize(USBCOMPLEXID_Default, &deviceinfo); + + if (R_SUCCEEDED(ret)) { + //The buffer for PostBufferAsync commands must be 0x1000-byte aligned. + g_usbDev_endpoint_in_buffer = memalign(0x1000, 0x1000); + if (g_usbDev_endpoint_in_buffer==NULL) ret = MAKERESULT(MODULE_LIBNX, LIBNX_OUTOFMEM); + + if (R_SUCCEEDED(ret)) { + g_usbDev_endpoint_out_buffer = memalign(0x1000, 0x1000); + if (g_usbDev_endpoint_out_buffer==NULL) ret = MAKERESULT(MODULE_LIBNX, LIBNX_OUTOFMEM); + } + + if (R_SUCCEEDED(ret)) { + memset(g_usbDev_endpoint_in_buffer, 0, 0x1000); + memset(g_usbDev_endpoint_out_buffer, 0, 0x1000); + ret = _usbDevInit(); + } + + if (R_FAILED(ret)) { + usbDsExit(); + + if (g_usbDev_endpoint_in_buffer) { + free(g_usbDev_endpoint_in_buffer); + g_usbDev_endpoint_in_buffer = NULL; + } + + if (g_usbDev_endpoint_out) { + free(g_usbDev_endpoint_out_buffer); + g_usbDev_endpoint_out_buffer = NULL; + } + } + } + + if (R_SUCCEEDED(ret)) g_usbDevInitialized=true; + + return ret; +} + +void usbDevExit(void) +{ + if (!g_usbDevInitialized) return; + + usbDsExit(); + + g_usbDevInitialized = false; + + g_usbDev_endpoint_in = NULL; + g_usbDev_endpoint_out = NULL; + + if (g_usbDev_endpoint_in_buffer) { + free(g_usbDev_endpoint_in_buffer); + g_usbDev_endpoint_in_buffer = NULL; + } + + if (g_usbDev_endpoint_out) { + free(g_usbDev_endpoint_out_buffer); + g_usbDev_endpoint_out_buffer = NULL; + } +} + +static Result _usbDevInit(void) +{ + Result ret=0; + size_t transferredSize=0; + + struct usb_interface_descriptor interface_descriptor = { + .bLength = USB_DT_INTERFACE_SIZE, + .bDescriptorType = USB_DT_INTERFACE, + .bInterfaceNumber = USBDS_DEFAULT_InterfaceNumber, + .bInterfaceClass = USB_CLASS_VENDOR_SPEC, + .bInterfaceSubClass = USB_CLASS_VENDOR_SPEC, + .bInterfaceProtocol = USB_CLASS_VENDOR_SPEC, + }; + + struct usb_endpoint_descriptor endpoint_descriptor_in = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_ENDPOINT_IN, + .bmAttributes = USB_TRANSFER_TYPE_BULK, + .wMaxPacketSize = 0x200, + }; + + struct usb_endpoint_descriptor endpoint_descriptor_out = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_ENDPOINT_OUT, + .bmAttributes = USB_TRANSFER_TYPE_BULK, + .wMaxPacketSize = 0x200, + }; + + //Setup interface. + ret = usbDsGetDsInterface(&interface, &interface_descriptor, "usb"); + if (R_FAILED(ret)) return ret; + + //Setup endpoints. + ret = usbDsInterface_GetDsEndpoint(interface, &g_usbDev_endpoint_in, &endpoint_descriptor_in);//device->host + if (R_FAILED(ret)) return ret; + + ret = usbDsInterface_GetDsEndpoint(interface, &g_usbDev_endpoint_out, &endpoint_descriptor_out);//host->device + if (R_FAILED(ret)) return ret; + + ret = usbDsInterface_EnableInterface(interface); + if (R_FAILED(ret)) return ret; + + //Host-side serial handling breaks with binary data without this. + ret = _usbDevWrite("\n", 1, &transferredSize); + if (R_SUCCEEDED(ret) && transferredSize!=1) ret = MAKERESULT(MODULE_LIBNX, LIBNX_IOERROR); + + return ret; +} + +static Result _usbDevRead(void* buffer, size_t size, size_t *transferredSize) +{ + Result ret=0; + u32 urbId=0; + u8 *bufptr = (u8*)buffer; + s32 tmpindex=0; + u32 chunksize=0; + u32 tmp_transferredSize = 0; + size_t total_transferredSize=0; + usbDsReportData reportdata; + + //Makes sure endpoints are ready for data-transfer / wait for init if needed. + ret = usbDsWaitReady(); + if (R_FAILED(ret)) return ret; + + while(size) + { + chunksize = 0x1000; + if (sizedevice transfer. + ret = usbDsEndpoint_PostBufferAsync(g_usbDev_endpoint_out, g_usbDev_endpoint_out_buffer, chunksize, &urbId); + if (R_FAILED(ret)) return ret; + + //Wait for the transfer to finish. + svcWaitSynchronization(&tmpindex, &g_usbDev_endpoint_out->CompletionEvent, 1, U64_MAX); + svcClearEvent(g_usbDev_endpoint_out->CompletionEvent); + + ret = usbDsEndpoint_GetReportData(g_usbDev_endpoint_out, &reportdata); + if (R_FAILED(ret)) return ret; + + ret = usbDsParseReportData(&reportdata, urbId, NULL, &tmp_transferredSize); + if (R_FAILED(ret)) return ret; + + if (tmp_transferredSize > chunksize) tmp_transferredSize = chunksize; + total_transferredSize+= (size_t)tmp_transferredSize; + + memcpy(bufptr, g_usbDev_endpoint_out_buffer, chunksize); + bufptr+= chunksize; + size-= chunksize; + + if(tmp_transferredSize < chunksize)break; + } + + if (transferredSize) *transferredSize = total_transferredSize; + + return ret; +} + +static Result _usbDevWrite(const void* buffer, size_t size, size_t *transferredSize) +{ + Result ret=0; + u32 urbId=0; + u32 chunksize=0; + u32 bufpos=0; + u32 transfer_size=0; + u32 total_chunks=0; + u32 last_chunksize=0; + u8 *bufptr = (u8*)buffer; + s32 tmpindex=0; + u32 tmp_transferredSize = 0; + u32 partial_transfer=0; + size_t total_transferredSize=0; + usbDsReportData reportdata; + + //Makes sure endpoints are ready for data-transfer / wait for init if needed. + ret = usbDsWaitReady(); + if (R_FAILED(ret)) return ret; + + while(size) + { + memset(g_usbDev_endpoint_in_buffer, 0, 0x1000); + transfer_size = 0; + total_chunks = 0; + + for(bufpos=0; bufpos<0x1000; bufpos+=0x200) + { + chunksize = 0x200; + if(sizehost transfer. + ret = usbDsEndpoint_PostBufferAsync(g_usbDev_endpoint_in, g_usbDev_endpoint_in_buffer, transfer_size, &urbId); + if(R_FAILED(ret))return ret; + + //Wait for the transfer to finish. + svcWaitSynchronization(&tmpindex, &g_usbDev_endpoint_in->CompletionEvent, 1, U64_MAX); + svcClearEvent(g_usbDev_endpoint_in->CompletionEvent); + + ret = usbDsEndpoint_GetReportData(g_usbDev_endpoint_in, &reportdata); + if (R_FAILED(ret)) return ret; + + ret = usbDsParseReportData(&reportdata, urbId, NULL, &tmp_transferredSize); + if (R_FAILED(ret)) return ret; + + if (tmp_transferredSize > transfer_size) tmp_transferredSize = transfer_size; + + partial_transfer = 0; + for(bufpos=0; bufposreport_count; + usbDsReportEntry *entry = NULL; + if(count>8)count = 8; + + for(pos=0; posreport[pos]; + if (entry->id == urbId) break; + } + + if (pos == count) return MAKERESULT(MODULE_LIBNX, LIBNX_NOTFOUND); + + switch(entry->urb_status) { + case 0x3: + rc = 0; + break; + + case 0x4: + rc = 0x828c; + break; + + case 0x5: + rc = 0x748c; + break; + + default: + rc = 0x108c; + break; + } + + if (R_SUCCEEDED(rc)) { + if (requestedSize) *requestedSize = entry->requestedSize; + if (transferredSize) *transferredSize = entry->transferredSize; + } + + return rc; +} + static Result _usbDsSetVidPidBcd(const usbDsDeviceInfo* deviceinfo) { if(g_usbDsServiceSession==0)return MAKERESULT(MODULE_LIBNX, LIBNX_NOTINITIALIZED); @@ -432,7 +472,7 @@ static Result _usbDsCmdNoParams(Handle sessionhandle, u64 cmd_id) { return rc; } -static Result _usbDsPostBuffer(Handle sessionhandle, u64 cmd_id, void* buffer, size_t size, u32 *out) { +static Result _usbDsPostBuffer(Handle sessionhandle, u64 cmd_id, void* buffer, size_t size, u32 *urbId) { if(sessionhandle==0)return MAKERESULT(MODULE_LIBNX, LIBNX_NOTINITIALIZED); osDCacheFlush(buffer, size); @@ -465,17 +505,17 @@ static Result _usbDsPostBuffer(Handle sessionhandle, u64 cmd_id, void* buffer, s struct { u64 magic; u64 result; - u32 out; + u32 urbId; } *resp = r.Raw; rc = resp->result; - if (R_SUCCEEDED(rc) && out)*out = resp->out; + if (R_SUCCEEDED(rc) && urbId)*urbId = resp->urbId; } return rc; } -static Result _usbDsGetReport(Handle sessionhandle, u64 cmd_id, u8 out[0x84]) { +static Result _usbDsGetReport(Handle sessionhandle, u64 cmd_id, usbDsReportData *out) { if(sessionhandle==0)return MAKERESULT(MODULE_LIBNX, LIBNX_NOTINITIALIZED); IpcCommand c; @@ -500,11 +540,11 @@ static Result _usbDsGetReport(Handle sessionhandle, u64 cmd_id, u8 out[0x84]) { struct { u64 magic; u64 result; - u8 out[0x84]; + usbDsReportData out; } *resp = r.Raw; rc = resp->result; - if (R_SUCCEEDED(rc) && out)memcpy(out, resp->out, sizeof(resp->out)); + if (R_SUCCEEDED(rc) && out)memcpy(out, &resp->out, sizeof(resp->out)); } return rc; @@ -565,28 +605,28 @@ Result usbDsInterface_DisableInterface(UsbDsInterface* interface) return _usbDsCmdNoParams(interface->h, 4); } -Result usbDsInterface_CtrlInPostBufferAsync(UsbDsInterface* interface, void* buffer, size_t size, u32 *out) +Result usbDsInterface_CtrlInPostBufferAsync(UsbDsInterface* interface, void* buffer, size_t size, u32 *urbId) { if(!interface->initialized)return MAKERESULT(MODULE_LIBNX, LIBNX_NOTINITIALIZED); - return _usbDsPostBuffer(interface->h, 5, buffer, size, out); + return _usbDsPostBuffer(interface->h, 5, buffer, size, urbId); } -Result usbDsInterface_CtrlOutPostBufferAsync(UsbDsInterface* interface, void* buffer, size_t size, u32 *out) +Result usbDsInterface_CtrlOutPostBufferAsync(UsbDsInterface* interface, void* buffer, size_t size, u32 *urbId) { if(!interface->initialized)return MAKERESULT(MODULE_LIBNX, LIBNX_NOTINITIALIZED); - return _usbDsPostBuffer(interface->h, 6, buffer, size, out); + return _usbDsPostBuffer(interface->h, 6, buffer, size, urbId); } -Result usbDsInterface_GetCtrlInReportData(UsbDsInterface* interface, u8 out[0x84]) +Result usbDsInterface_GetCtrlInReportData(UsbDsInterface* interface, usbDsReportData *out) { if(!interface->initialized)return MAKERESULT(MODULE_LIBNX, LIBNX_NOTINITIALIZED); return _usbDsGetReport(interface->h, 8, out); } -Result usbDsInterface_GetCtrlOutReportData(UsbDsInterface* interface, u8 out[0x84]) +Result usbDsInterface_GetCtrlOutReportData(UsbDsInterface* interface, usbDsReportData *out) { if(!interface->initialized)return MAKERESULT(MODULE_LIBNX, LIBNX_NOTINITIALIZED); @@ -607,14 +647,14 @@ void usbDsEndpoint_Close(UsbDsEndpoint* endpoint) _usbDsFreeEndpoint(endpoint); } -Result usbDsEndpoint_PostBufferAsync(UsbDsEndpoint* endpoint, void* buffer, size_t size, u32 *out) +Result usbDsEndpoint_PostBufferAsync(UsbDsEndpoint* endpoint, void* buffer, size_t size, u32 *urbId) { if(!endpoint->initialized)return MAKERESULT(MODULE_LIBNX, LIBNX_NOTINITIALIZED); - return _usbDsPostBuffer(endpoint->h, 0, buffer, size, out); + return _usbDsPostBuffer(endpoint->h, 0, buffer, size, urbId); } -Result usbDsEndpoint_GetReportData(UsbDsEndpoint* endpoint, u8 out[0x84]) +Result usbDsEndpoint_GetReportData(UsbDsEndpoint* endpoint, usbDsReportData *out) { if(!endpoint->initialized)return MAKERESULT(MODULE_LIBNX, LIBNX_NOTINITIALIZED);