diff --git a/nx/include/switch/services/usbhs.h b/nx/include/switch/services/usbhs.h index a0d81814..e4112ae3 100644 --- a/nx/include/switch/services/usbhs.h +++ b/nx/include/switch/services/usbhs.h @@ -87,6 +87,7 @@ typedef struct { Service s; Event event0; ///< Unknown. Event eventCtrlXfer; ///< [2.0.0+] Signaled when CtrlXferAsync finishes. + s32 ID; UsbHsInterface inf; ///< Initialized with the input interface from \ref usbHsAcquireUsbIf, then the first 0x1B8-bytes are overwritten with the cmd output (data before pathstr). } UsbHsClientIfSession; @@ -105,7 +106,7 @@ void usbHsExit(void); Event* usbHsGetInterfaceStateChangeEvent(void); /** - * @brief Returns an array of all \ref UsbHsInterface. Internally this loads the same interfaces as \ref usbHsQueryAvailableInterfaces, followed by \ref usbHsQueryAcquiredInterfaces. + * @brief Returns an array of all \ref UsbHsInterface. Internally this loads the same interfaces as \ref usbHsQueryAvailableInterfaces, followed by \ref usbHsQueryAcquiredInterfaces. However, ID in \ref UsbHsInterface is set to -1, hence the output from this should not be used with \ref usbHsAcquireUsbIf. * @param[in] filter \ref UsbHsInterfaceFilter. * @param[out] interfaces Array of output interfaces. * @param[in] interfaces_maxsize Max byte-size of the interfaces buffer. @@ -147,7 +148,7 @@ Result usbHsCreateInterfaceAvailableEvent(Event* event, bool autoclear, u8 index Result usbHsDestroyInterfaceAvailableEvent(Event* event, u8 index); /** - * @brief Acquires/opens the specified interface. + * @brief Acquires/opens the specified interface. This returns an error if the interface was already acquired by another process. * @param[in] s The service object. * @param[in] interface Interface to use. */ @@ -158,3 +159,32 @@ Result usbHsAcquireUsbIf(UsbHsClientIfSession* s, UsbHsInterface *interface); /// Closes the specified interface session. void usbHsIfClose(UsbHsClientIfSession* s); +/** + * @brief Selects an interface. + * @param[in] s The service object. + * @param[out] inf The output interface info. If NULL, the output is stored within s instead. + * @param[in] id ID + */ +Result usbHsIfSetInterface(UsbHsClientIfSession* s, UsbHsInterfaceInfo* inf, u8 id); + +/** + * @brief Gets an interface. + * @param[in] s The service object. + * @param[out] inf The output interface info. If NULL, the output is stored within s instead. + */ +Result usbHsIfGetInterface(UsbHsClientIfSession* s, UsbHsInterfaceInfo* inf); + +/** + * @brief Gets an alternate interface. + * @param[in] s The service object. + * @param[out] inf The output interface info. If NULL, the output is stored within s instead. + * @param[in] id ID + */ +Result usbHsIfGetAlternateInterface(UsbHsClientIfSession* s, UsbHsInterfaceInfo* inf, u8 id); + +/// On 1.0.0 this is stubbed, just returns 0 with out=0. +Result usbHsIfGetCurrentFrame(UsbHsClientIfSession* s, u32* out); + +/// 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 1d020642..2b529bb6 100644 --- a/nx/source/services/usbhs.c +++ b/nx/source/services/usbhs.c @@ -297,6 +297,7 @@ Result usbHsAcquireUsbIf(UsbHsClientIfSession* s, UsbHsInterface *interface) { memset(s, 0, sizeof(UsbHsClientIfSession)); memcpy(&s->inf, interface, sizeof(UsbHsInterface)); + s->ID = interface->inf.ID; struct { u64 magic; @@ -310,7 +311,7 @@ Result usbHsAcquireUsbIf(UsbHsClientIfSession* s, UsbHsInterface *interface) { raw->magic = SFCI_MAGIC; raw->cmd_id = kernelAbove200() ? 7 : 6; - raw->ID = interface->inf.ID; + raw->ID = s->ID; Result rc = serviceIpcDispatch(&g_usbHsSrv); @@ -352,3 +353,152 @@ void usbHsIfClose(UsbHsClientIfSession* s) { memset(s, 0, sizeof(UsbHsClientIfSession)); } +static Result _usbHsIfGetInf(UsbHsClientIfSession* s, UsbHsInterfaceInfo* inf, u8 id, u64 cmd_id) { + IpcCommand c; + ipcInitialize(&c); + + if (inf==NULL) inf = &s->inf.inf; + + struct { + u64 magic; + u64 cmd_id; + u8 id; + } *raw; + + ipcAddRecvBuffer(&c, inf, sizeof(UsbHsInterfaceInfo), BufferType_Normal); + + raw = serviceIpcPrepareHeader(&s->s, &c, sizeof(*raw)); + + raw->magic = SFCI_MAGIC; + raw->cmd_id = cmd_id; + raw->id = id; + + 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 usbHsIfSetInterface(UsbHsClientIfSession* s, UsbHsInterfaceInfo* inf, u8 id) { + return _usbHsIfGetInf(s, inf, id, 1); +} + +Result usbHsIfGetAlternateInterface(UsbHsClientIfSession* s, UsbHsInterfaceInfo* inf, u8 id) { + return _usbHsIfGetInf(s, inf, id, 3); +} + +Result usbHsIfGetInterface(UsbHsClientIfSession* s, UsbHsInterfaceInfo* inf) { + IpcCommand c; + ipcInitialize(&c); + + if (inf==NULL) inf = &s->inf.inf; + + struct { + u64 magic; + u64 cmd_id; + } *raw; + + ipcAddRecvBuffer(&c, inf, sizeof(UsbHsInterfaceInfo), BufferType_Normal); + + raw = serviceIpcPrepareHeader(&s->s, &c, sizeof(*raw)); + + raw->magic = SFCI_MAGIC; + raw->cmd_id = 2; + + 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 usbHsIfGetCurrentFrame(UsbHsClientIfSession* s, u32* out) { + IpcCommand c; + ipcInitialize(&c); + + struct { + u64 magic; + u64 cmd_id; + } *raw; + + raw = serviceIpcPrepareHeader(&s->s, &c, sizeof(*raw)); + + raw->magic = SFCI_MAGIC; + raw->cmd_id = kernelAbove200() ? 4 : 5; + + Result rc = serviceIpcDispatch(&s->s); + + if (R_SUCCEEDED(rc)) { + IpcParsedCommand r; + struct { + u64 magic; + u64 result; + u32 out; + } *resp; + + serviceIpcParse(&s->s, &r, sizeof(*resp)); + resp = r.Raw; + + rc = resp->result; + + if (R_SUCCEEDED(rc) && out) *out = resp->out; + } + + return rc; +} + +Result usbHsIfResetDevice(UsbHsClientIfSession* s) { + IpcCommand c; + ipcInitialize(&c); + + struct { + u64 magic; + u64 cmd_id; + } *raw; + + raw = serviceIpcPrepareHeader(&s->s, &c, sizeof(*raw)); + + raw->magic = SFCI_MAGIC; + raw->cmd_id = 8; + + 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; +} +