diff --git a/nx/include/switch/services/usbhs.h b/nx/include/switch/services/usbhs.h index e4112ae3..1969d5a9 100644 --- a/nx/include/switch/services/usbhs.h +++ b/nx/include/switch/services/usbhs.h @@ -77,7 +77,7 @@ typedef struct { typedef struct { u32 unk_x0; Result res; - u32 unk_x8; + u32 requestedSize; u32 transferredSize; u64 unk_x10; } UsbHsXferReport; @@ -185,6 +185,9 @@ Result usbHsIfGetAlternateInterface(UsbHsClientIfSession* s, UsbHsInterfaceInfo* /// On 1.0.0 this is stubbed, just returns 0 with out=0. Result usbHsIfGetCurrentFrame(UsbHsClientIfSession* s, u32* out); +/// Uses a control transfer, this will block until the transfer finishes. The buffer address and size should be aligned to 0x1000-bytes, where wLength is the original size. +Result usbHsIfCtrlXfer(UsbHsClientIfSession* s, u8 bmRequestType, u8 bRequest, u16 wValue, u16 wIndex, u16 wLength, void* buffer, u32* transferredSize); + /// Resets the device: has the same affect as unplugging the device and plugging it back in. Result usbHsIfResetDevice(UsbHsClientIfSession* s); diff --git a/nx/source/services/usbhs.c b/nx/source/services/usbhs.c index 2b529bb6..346c9d5e 100644 --- a/nx/source/services/usbhs.c +++ b/nx/source/services/usbhs.c @@ -334,7 +334,7 @@ Result usbHsAcquireUsbIf(UsbHsClientIfSession* s, UsbHsInterface *interface) { if (R_SUCCEEDED(rc)) { rc = _usbHsGetEvent(&s->s, &s->event0, 0); - if (kernelAbove200()) rc = _usbHsGetEvent(&s->s, &s->event0, 6); + if (kernelAbove200()) rc = _usbHsGetEvent(&s->s, &s->eventCtrlXfer, 6); if (R_FAILED(rc)) { serviceClose(&s->s); @@ -470,6 +470,164 @@ Result usbHsIfGetCurrentFrame(UsbHsClientIfSession* s, u32* out) { return rc; } +static Result _usbHsIfSubmitControlRequest(UsbHsClientIfSession* s, u8 bRequest, u8 bmRequestType, u16 wValue, u16 wIndex, u16 wLength, void* buffer, u32 timeoutInMs, u32* transferredSize) { + bool dir = (bmRequestType & USB_ENDPOINT_IN) != 0; + size_t bufsize = (wLength + 0xFFF) & ~0xFFF; + + armDCacheFlush(buffer, wLength); + + IpcCommand c; + ipcInitialize(&c); + + struct { + u64 magic; + u64 cmd_id; + u8 bRequest; + u8 bmRequestType; + u16 wValue; + u16 wIndex; + u16 wLength; + u32 timeoutInMs; + } PACKED *raw; + + if (dir) ipcAddRecvBuffer(&c, buffer, bufsize, BufferType_Normal); + if (!dir) ipcAddSendBuffer(&c, buffer, bufsize, BufferType_Normal); + + raw = serviceIpcPrepareHeader(&s->s, &c, sizeof(*raw)); + + raw->magic = SFCI_MAGIC; + raw->cmd_id = dir ? 6 : 7; + raw->bRequest = bRequest; + raw->bmRequestType = bmRequestType; + raw->wValue = wValue; + raw->wIndex = wIndex; + raw->wLength = wLength; + raw->timeoutInMs = timeoutInMs; + + Result rc = serviceIpcDispatch(&s->s); + + if (R_SUCCEEDED(rc)) { + IpcParsedCommand r; + struct { + u64 magic; + u64 result; + u32 transferredSize; + } *resp; + + serviceIpcParse(&s->s, &r, sizeof(*resp)); + resp = r.Raw; + + rc = resp->result; + + if (R_SUCCEEDED(rc) && transferredSize) *transferredSize = resp->transferredSize; + } + + if (dir) armDCacheFlush(buffer, wLength); + + return rc; +} + +static Result _usbHsIfCtrlXferAsync(UsbHsClientIfSession* s, u8 bmRequestType, u8 bRequest, u16 wValue, u16 wIndex, u16 wLength, void* buffer) { + IpcCommand c; + ipcInitialize(&c); + + struct { + u64 magic; + u64 cmd_id; + u8 bmRequestType; + u8 bRequest; + u16 wValue; + u16 wIndex; + u16 wLength; + u64 buffer; + } PACKED *raw; + + raw = serviceIpcPrepareHeader(&s->s, &c, sizeof(*raw)); + + raw->magic = SFCI_MAGIC; + raw->cmd_id = 5; + raw->bmRequestType = bmRequestType; + raw->bRequest = bRequest; + raw->wValue = wValue; + raw->wIndex = wIndex; + raw->wLength = wLength; + raw->buffer = (u64)buffer; + + Result rc = serviceIpcDispatch(&s->s); + + if (R_SUCCEEDED(rc)) { + IpcParsedCommand r; + struct { + u64 magic; + u64 result; + } *resp; + + serviceIpcParse(&s->s, &r, sizeof(*resp)); + resp = r.Raw; + + rc = resp->result; + } + + return rc; +} + +static Result _usbHsIfGetCtrlXferReport(UsbHsClientIfSession* s, UsbHsXferReport* report) { + IpcCommand c; + ipcInitialize(&c); + + struct { + u64 magic; + u64 cmd_id; + } *raw; + + ipcAddRecvBuffer(&c, report, sizeof(UsbHsXferReport), BufferType_Normal); + + raw = serviceIpcPrepareHeader(&s->s, &c, sizeof(*raw)); + + raw->magic = SFCI_MAGIC; + raw->cmd_id = 7; + + Result rc = serviceIpcDispatch(&s->s); + + if (R_SUCCEEDED(rc)) { + IpcParsedCommand r; + struct { + u64 magic; + u64 result; + } *resp; + + serviceIpcParse(&s->s, &r, sizeof(*resp)); + resp = r.Raw; + + rc = resp->result; + } + + return rc; +} + +Result usbHsIfCtrlXfer(UsbHsClientIfSession* s, u8 bmRequestType, u8 bRequest, u16 wValue, u16 wIndex, u16 wLength, void* buffer, u32* transferredSize) { + Result rc=0; + UsbHsXferReport report; + + if (!kernelAbove200()) return _usbHsIfSubmitControlRequest(s, bRequest, bmRequestType, wValue, wIndex, wLength, buffer, 0, transferredSize); + + rc = _usbHsIfCtrlXferAsync(s, bmRequestType, bRequest, wValue, wIndex, wLength, buffer); + if (R_FAILED(rc)) return rc; + + rc = eventWait(&s->eventCtrlXfer, U64_MAX); + if (R_FAILED(rc)) return rc; + eventClear(&s->eventCtrlXfer); + + memset(&report, 0, sizeof(report)); + rc = _usbHsIfGetCtrlXferReport(s, &report); + if (R_FAILED(rc)) return rc; + + *transferredSize = report.transferredSize; + rc = report.res; + + return rc; +} + Result usbHsIfResetDevice(UsbHsClientIfSession* s) { IpcCommand c; ipcInitialize(&c);