diff --git a/nx/include/switch.h b/nx/include/switch.h index 1d7706dd..63d6662b 100644 --- a/nx/include/switch.h +++ b/nx/include/switch.h @@ -22,6 +22,7 @@ extern "C" { #include #include #include +#include #include #ifdef __cplusplus diff --git a/nx/include/switch/services/usb.h b/nx/include/switch/services/usb.h new file mode 100644 index 00000000..f6170017 --- /dev/null +++ b/nx/include/switch/services/usb.h @@ -0,0 +1,215 @@ +/// usb:ds Switch-as-device<>host USB comms, see also here: http://switchbrew.org/index.php?title=USB_services + +/// Names starting with "libusb" were changed to "usb" to avoid collision with actual libusb if it's ever used. + +#define USBDS_DEFAULT_InterfaceNumber 0x4 ///Value for usb_interface_descriptor bInterfaceNumber for automatically allocating the actual bInterfaceNumber. + +/// Imported from libusb with changed names. +/* Descriptor sizes per descriptor type */ +#define USB_DT_INTERFACE_SIZE 9 +#define USB_DT_ENDPOINT_SIZE 7 + +/// Imported from libusb, with some adjustments. +struct usb_endpoint_descriptor { + uint8_t bLength; + + uint8_t bDescriptorType; /// Must match USB_DT_ENDPOINT. + + uint8_t bEndpointAddress; /// Should be one of the usb_endpoint_direction values, the endpoint-number is automatically allocated. + + uint8_t bmAttributes; + + uint16_t wMaxPacketSize; + + uint8_t bInterval; +}; + +/// Imported from libusb, with some adjustments. +struct usb_interface_descriptor { + uint8_t bLength; + + uint8_t bDescriptorType; /// Must match USB_DT_INTERFACE. + + uint8_t bInterfaceNumber; /// See also USBDS_DEFAULT_InterfaceNumber. + + uint8_t bAlternateSetting; /// Must match 0. + + uint8_t bNumEndpoints; /// Ignored. + + uint8_t bInterfaceClass; + + uint8_t bInterfaceSubClass; + + uint8_t bInterfaceProtocol; + + uint8_t iInterface; /// Ignored. +}; + +typedef struct { + u16 idVendor; /// VID + u16 idProduct; /// PID + u16 bcdDevice; + char Manufacturer[0x20]; + char Product[0x20]; + char SerialNumber[0x20]; +} usbDsDeviceInfo; + +typedef struct { + bool initialized; + u32 interface_index; + Handle h; + + Handle SetupEvent; + Handle CtrlInCompletionEvent; + Handle CtrlOutCompletionEvent; +} UsbDsInterface; + +typedef struct { + bool initialized; + Handle h; + + Handle CompletionEvent; +} UsbDsEndpoint; + +typedef enum +{ + USBCOMPLEXID_Default = 0x2 +} usbComplexId; + +/// Imported from libusb, with changed names. +enum usb_class_code { + USB_CLASS_PER_INTERFACE = 0, + + USB_CLASS_AUDIO = 1, + + USB_CLASS_COMM = 2, + + USB_CLASS_HID = 3, + + USB_CLASS_PHYSICAL = 5, + + USB_CLASS_PRINTER = 7, + + USB_CLASS_PTP = 6, /* legacy name from libusb-0.1 usb.h */ + USB_CLASS_IMAGE = 6, + + USB_CLASS_MASS_STORAGE = 8, + + USB_CLASS_HUB = 9, + + USB_CLASS_DATA = 10, + + USB_CLASS_SMART_CARD = 0x0b, + + USB_CLASS_CONTENT_SECURITY = 0x0d, + + USB_CLASS_VIDEO = 0x0e, + + USB_CLASS_PERSONAL_HEALTHCARE = 0x0f, + + USB_CLASS_DIAGNOSTIC_DEVICE = 0xdc, + + USB_CLASS_WIRELESS = 0xe0, + + USB_CLASS_APPLICATION = 0xfe, + + USB_CLASS_VENDOR_SPEC = 0xff +}; + +/// Imported from libusb, with changed names. +enum usb_descriptor_type { + USB_DT_DEVICE = 0x01, + + USB_DT_CONFIG = 0x02, + + USB_DT_STRING = 0x03, + + USB_DT_INTERFACE = 0x04, + + USB_DT_ENDPOINT = 0x05, + + USB_DT_BOS = 0x0f, + + USB_DT_DEVICE_CAPABILITY = 0x10, + + USB_DT_HID = 0x21, + + USB_DT_REPORT = 0x22, + + USB_DT_PHYSICAL = 0x23, + + USB_DT_HUB = 0x29, + + USB_DT_SUPERSPEED_HUB = 0x2a, + + USB_DT_SS_ENDPOINT_COMPANION = 0x30 +}; + +/// Imported from libusb, with changed names. +enum usb_endpoint_direction { + USB_ENDPOINT_IN = 0x80, + + USB_ENDPOINT_OUT = 0x00 +}; + +/// Imported from libusb, with changed names. +enum usb_transfer_type { + USB_TRANSFER_TYPE_CONTROL = 0, + + USB_TRANSFER_TYPE_ISOCHRONOUS = 1, + + USB_TRANSFER_TYPE_BULK = 2, + + USB_TRANSFER_TYPE_INTERRUPT = 3, + + USB_TRANSFER_TYPE_BULK_STREAM = 4, +}; + +/// Imported from libusb, with changed names. +enum usb_iso_sync_type { + USB_ISO_SYNC_TYPE_NONE = 0, + + USB_ISO_SYNC_TYPE_ASYNC = 1, + + USB_ISO_SYNC_TYPE_ADAPTIVE = 2, + + USB_ISO_SYNC_TYPE_SYNC = 3 +}; + +/// Imported from libusb, with changed names. +enum usb_iso_usage_type { + USB_ISO_USAGE_TYPE_DATA = 0, + + USB_ISO_USAGE_TYPE_FEEDBACK = 1, + + USB_ISO_USAGE_TYPE_IMPLICIT = 2, +}; + +Result usbDsInitialize(usbComplexId complexId, const usbDsDeviceInfo* deviceinfo); + +/// Exit usbDs. Any interfaces/endpoints which are left open are automatically closed, since otherwise usb-sysmodule won't fully reset usbds to defaults. +void usbDsExit(void); + +Handle usbDsGetServiceSession(void); +Handle usbDsGetStateChangeEvent(void); + +Result usbDsGetDsInterface(UsbDsInterface** interface, struct usb_interface_descriptor* descriptor, const char *interface_name); + +/// 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_StallCtrl(UsbDsInterface* interface); + +/// IDsEndpoint + +void usbDsEndpoint_Close(UsbDsEndpoint* endpoint); +Result usbDsEndpoint_PostBufferAsync(UsbDsEndpoint* endpoint, void* buffer, size_t size, u32 *out); +Result usbDsEndpoint_GetCtrlOutReportData(UsbDsEndpoint* endpoint, u8 out[0x84]); +Result usbDsEndpoint_StallCtrl(UsbDsEndpoint* endpoint); + diff --git a/nx/source/services/usb.c b/nx/source/services/usb.c new file mode 100644 index 00000000..5008b45a --- /dev/null +++ b/nx/source/services/usb.c @@ -0,0 +1,577 @@ +#include +#include + +#define TOTAL_INTERFACES 4 +#define TOTAL_ENDPOINTS 15*2 + +static Handle g_usbDsServiceSession = 0; +static Handle g_usbDsStateChangeEvent = 0; + +static UsbDsInterface g_usbDsInterfaceTable[TOTAL_INTERFACES]; +static UsbDsEndpoint g_usbDsEndpointTable[TOTAL_INTERFACES*TOTAL_ENDPOINTS]; + +static void _usbDsFreeTables(void); + +static Result _usbDsBindDevice(usbComplexId complexId); +static Result _usbDsBindClientProcess(Handle prochandle); +static Result _usbDsGetEvent(Handle sessionhandle, Handle* handle_out, u64 cmd_id); +static Result _usbDsSetVidPidBcd(const usbDsDeviceInfo* deviceinfo); + +static Result _usbDsGetSession(Handle sessionhandle, Handle* handle_out, u64 cmd_id, const void* buf0, size_t buf0size, const void* buf1, size_t buf1size); + +Result usbDsInitialize(usbComplexId complexId, const usbDsDeviceInfo* deviceinfo) { + if(g_usbDsServiceSession!=0)return MAKERESULT(MODULE_LIBNX, LIBNX_ALREADYINITIALIZED); + + Result rc = 0; + + rc = smGetService(&g_usbDsServiceSession, "usb:ds"); + + if (R_SUCCEEDED(rc))rc = _usbDsBindDevice(complexId); + if (R_SUCCEEDED(rc))rc = _usbDsBindClientProcess(CUR_PROCESS_HANDLE); + if (R_SUCCEEDED(rc))rc = _usbDsGetEvent(g_usbDsServiceSession, &g_usbDsStateChangeEvent, 3);//GetStateChangeEvent + if (R_SUCCEEDED(rc))rc = _usbDsSetVidPidBcd(deviceinfo); + + if (R_FAILED(rc)) { + if(g_usbDsStateChangeEvent) { + svcCloseHandle(g_usbDsStateChangeEvent); + g_usbDsStateChangeEvent = 0; + } + + if(g_usbDsServiceSession) { + svcCloseHandle(g_usbDsServiceSession); + g_usbDsServiceSession = 0; + } + } + + return rc; +} + +void usbDsExit(void) +{ + if(g_usbDsServiceSession==0)return; + + _usbDsFreeTables(); + + if(g_usbDsStateChangeEvent) { + svcCloseHandle(g_usbDsStateChangeEvent); + g_usbDsStateChangeEvent = 0; + } + + if(g_usbDsServiceSession) { + svcCloseHandle(g_usbDsServiceSession); + g_usbDsServiceSession = 0; + } +} + +Handle usbDsGetServiceSession(void) +{ + return g_usbDsServiceSession; +} + +Handle usbDsGetStateChangeEvent(void) +{ + return g_usbDsStateChangeEvent; +} + +static UsbDsInterface* _usbDsAllocateInterface(void) +{ + u32 pos; + UsbDsInterface* ptr = NULL; + + for(pos=0; posinitialized)continue; + memset(ptr, 0, sizeof(UsbDsInterface)); + ptr->initialized = true; + ptr->interface_index = pos; + return ptr; + } + + return NULL; +} + +static UsbDsEndpoint* _usbDsAllocateEndpoint(UsbDsInterface* interface) +{ + u32 pos; + UsbDsEndpoint* ptr = NULL; + + if(interface->interface_index>TOTAL_INTERFACES)return NULL; + + for(pos=0; posinterface_index*TOTAL_ENDPOINTS) + pos]; + if(ptr->initialized)continue; + memset(ptr, 0, sizeof(UsbDsEndpoint)); + ptr->initialized = true; + return ptr; + } + + return NULL; +} + +static void _usbDsFreeInterface(UsbDsInterface* interface) +{ + if(!interface->initialized)return; + + if(interface->CtrlOutCompletionEvent) { + svcCloseHandle(interface->CtrlOutCompletionEvent); + interface->CtrlOutCompletionEvent = 0; + } + + if(interface->CtrlInCompletionEvent) { + svcCloseHandle(interface->CtrlInCompletionEvent); + interface->CtrlInCompletionEvent = 0; + } + + if(interface->SetupEvent) { + svcCloseHandle(interface->SetupEvent); + interface->SetupEvent = 0; + } + + if(interface->h) { + svcCloseHandle(interface->h); + interface->h = 0; + } + + interface->initialized = false; +} + +static void _usbDsFreeEndpoint(UsbDsEndpoint* endpoint) +{ + if(!endpoint->initialized)return; + + if(endpoint->CompletionEvent) { + svcCloseHandle(endpoint->CompletionEvent); + endpoint->CompletionEvent = 0; + } + + if(endpoint->h) { + svcCloseHandle(endpoint->h); + endpoint->h = 0; + } + + endpoint->initialized = false; +} + +static void _usbDsFreeTables(void) +{ + u32 pos, pos2; + for(pos=0; posmagic = SFCI_MAGIC; + raw->cmd_id = 0; + raw->complexId = complexId; + + Result rc = ipcDispatch(g_usbDsServiceSession); + + if (R_SUCCEEDED(rc)) { + IpcCommandResponse r; + ipcParseResponse(&r); + + struct { + u64 magic; + u64 result; + } *resp = r.Raw; + + rc = resp->result; + } + + return rc; +} + +static Result _usbDsBindClientProcess(Handle prochandle) { + if(g_usbDsServiceSession==0)return MAKERESULT(MODULE_LIBNX, LIBNX_NOTINITIALIZED); + + IpcCommand c; + ipcInitialize(&c); + + struct { + u64 magic; + u64 cmd_id; + } *raw; + + ipcSendHandleCopy(&c, prochandle); + + raw = ipcPrepareHeader(&c, sizeof(*raw)); + + raw->magic = SFCI_MAGIC; + raw->cmd_id = 1; + + Result rc = ipcDispatch(g_usbDsServiceSession); + + if (R_SUCCEEDED(rc)) { + IpcCommandResponse r; + ipcParseResponse(&r); + + struct { + u64 magic; + u64 result; + } *resp = r.Raw; + + rc = resp->result; + } + + return rc; +} + +static Result _usbDsGetEvent(Handle sessionhandle, Handle* handle_out, u64 cmd_id) { + if(sessionhandle==0)return MAKERESULT(MODULE_LIBNX, LIBNX_NOTINITIALIZED); + + IpcCommand c; + ipcInitialize(&c); + + struct { + u64 magic; + u64 cmd_id; + } *raw; + + raw = ipcPrepareHeader(&c, sizeof(*raw)); + + raw->magic = SFCI_MAGIC; + raw->cmd_id = cmd_id; + + Result rc = ipcDispatch(sessionhandle); + + if (R_SUCCEEDED(rc)) { + IpcCommandResponse r; + ipcParseResponse(&r); + + struct { + u64 magic; + u64 result; + } *resp = r.Raw; + + rc = resp->result; + + if (R_SUCCEEDED(rc)) { + *handle_out = r.Handles[0]; + } + } + + return rc; +} + +static Result _usbDsSetVidPidBcd(const usbDsDeviceInfo* deviceinfo) { + if(g_usbDsServiceSession==0)return MAKERESULT(MODULE_LIBNX, LIBNX_NOTINITIALIZED); + + IpcCommand c; + ipcInitialize(&c); + + struct { + u64 magic; + u64 cmd_id; + } *raw; + + ipcAddSendBuffer(&c, deviceinfo, sizeof(usbDsDeviceInfo), 0); + + raw = ipcPrepareHeader(&c, sizeof(*raw)); + + raw->magic = SFCI_MAGIC; + raw->cmd_id = 5; + + Result rc = ipcDispatch(g_usbDsServiceSession); + + if (R_SUCCEEDED(rc)) { + IpcCommandResponse r; + ipcParseResponse(&r); + + struct { + u64 magic; + u64 result; + } *resp = r.Raw; + + rc = resp->result; + } + + return rc; +} + +static Result _usbDsGetSession(Handle sessionhandle, Handle* handle_out, u64 cmd_id, const void* buf0, size_t buf0size, const void* buf1, size_t buf1size) { + if(sessionhandle==0)return MAKERESULT(MODULE_LIBNX, LIBNX_NOTINITIALIZED); + + IpcCommand c; + ipcInitialize(&c); + + struct { + u64 magic; + u64 cmd_id; + } *raw; + + if(buf0 && buf0size)ipcAddSendBuffer(&c, buf0, buf0size, 0); + if(buf1 && buf1size)ipcAddSendBuffer(&c, buf1, buf1size, 0); + + raw = ipcPrepareHeader(&c, sizeof(*raw)); + + raw->magic = SFCI_MAGIC; + raw->cmd_id = cmd_id; + + Result rc = ipcDispatch(sessionhandle); + + if (R_SUCCEEDED(rc)) { + IpcCommandResponse r; + ipcParseResponse(&r); + + struct { + u64 magic; + u64 result; + } *resp = r.Raw; + + rc = resp->result; + + if (R_SUCCEEDED(rc)) { + *handle_out = r.Handles[0]; + } + } + + return rc; +} + +static Result _usbDsCmdNoParams(Handle sessionhandle, u64 cmd_id) { + if(sessionhandle==0)return MAKERESULT(MODULE_LIBNX, LIBNX_NOTINITIALIZED); + + IpcCommand c; + ipcInitialize(&c); + + struct { + u64 magic; + u64 cmd_id; + } *raw; + + raw = ipcPrepareHeader(&c, sizeof(*raw)); + + raw->magic = SFCI_MAGIC; + raw->cmd_id = cmd_id; + + Result rc = ipcDispatch(sessionhandle); + + if (R_SUCCEEDED(rc)) { + IpcCommandResponse r; + ipcParseResponse(&r); + + struct { + u64 magic; + u64 result; + } *resp = r.Raw; + + rc = resp->result; + } + + return rc; +} + +static Result _usbDsPostBuffer(Handle sessionhandle, u64 cmd_id, void* buffer, size_t size, u32 *out) { + if(sessionhandle==0)return MAKERESULT(MODULE_LIBNX, LIBNX_NOTINITIALIZED); + + //TODO: Add dache-flush code here, breaks otherwise. + + IpcCommand c; + ipcInitialize(&c); + + struct { + u64 magic; + u64 cmd_id; + u32 size; + u32 padding; + u64 buffer; + } *raw; + + raw = ipcPrepareHeader(&c, sizeof(*raw)); + + raw->magic = SFCI_MAGIC; + raw->cmd_id = cmd_id; + raw->size = (u32)size; + raw->padding = 0; + raw->buffer = (u64)buffer; + + Result rc = ipcDispatch(sessionhandle); + + if (R_SUCCEEDED(rc)) { + IpcCommandResponse r; + ipcParseResponse(&r); + + struct { + u64 magic; + u64 result; + u32 out; + } *resp = r.Raw; + + rc = resp->result; + if (R_SUCCEEDED(rc) && out)*out = resp->out; + } + + return rc; +} + +static Result _usbDsGetReport(Handle sessionhandle, u64 cmd_id, u8 out[0x84]) { + if(sessionhandle==0)return MAKERESULT(MODULE_LIBNX, LIBNX_NOTINITIALIZED); + + IpcCommand c; + ipcInitialize(&c); + + struct { + u64 magic; + u64 cmd_id; + } *raw; + + raw = ipcPrepareHeader(&c, sizeof(*raw)); + + raw->magic = SFCI_MAGIC; + raw->cmd_id = cmd_id; + + Result rc = ipcDispatch(sessionhandle); + + if (R_SUCCEEDED(rc)) { + IpcCommandResponse r; + ipcParseResponse(&r); + + struct { + u64 magic; + u64 result; + u8 out[0x84]; + } *resp = r.Raw; + + rc = resp->result; + if (R_SUCCEEDED(rc) && out)memcpy(out, resp->out, sizeof(resp->out)); + } + + return rc; +} + +Result usbDsGetDsInterface(UsbDsInterface** interface, struct usb_interface_descriptor* descriptor, const char *interface_name) +{ + UsbDsInterface* ptr = _usbDsAllocateInterface(); + if(ptr==NULL)return MAKERESULT(MODULE_LIBNX, LIBNX_OUTOFMEM); + + Result rc = _usbDsGetSession(g_usbDsServiceSession, &ptr->h, 2, descriptor, sizeof(struct usb_interface_descriptor), interface_name, strlen(interface_name)+1); + + if (R_SUCCEEDED(rc)) rc = _usbDsGetEvent(ptr->h, &ptr->SetupEvent, 1);//GetSetupEvent + if (R_SUCCEEDED(rc)) rc = _usbDsGetEvent(ptr->h, &ptr->CtrlInCompletionEvent, 7);//GetCtrlInCompletionEvent + if (R_SUCCEEDED(rc)) rc = _usbDsGetEvent(ptr->h, &ptr->CtrlOutCompletionEvent, 9);//GetCtrlOutCompletionEvent + + if (R_FAILED(rc)) _usbDsFreeInterface(ptr); + + if (R_SUCCEEDED(rc)) *interface = ptr; + return rc; +} + +//IDsInterface + +void usbDsInterface_Close(UsbDsInterface* interface) +{ + _usbDsFreeInterface(interface); +} + +Result usbDsInterface_GetDsEndpoint(UsbDsInterface* interface, UsbDsEndpoint** endpoint, struct usb_endpoint_descriptor* descriptor) +{ + if(!interface->initialized)return MAKERESULT(MODULE_LIBNX, LIBNX_NOTINITIALIZED); + + UsbDsEndpoint* ptr = _usbDsAllocateEndpoint(interface); + if(ptr==NULL)return MAKERESULT(MODULE_LIBNX, LIBNX_OUTOFMEM); + + Result rc = _usbDsGetSession(interface->h, &ptr->h, 0, descriptor, sizeof(struct usb_endpoint_descriptor), NULL, 0); + + if (R_SUCCEEDED(rc)) rc = _usbDsGetEvent(ptr->h, &ptr->CompletionEvent, 2);//GetCompletionEvent + + if (R_FAILED(rc)) _usbDsFreeEndpoint(ptr); + + if (R_SUCCEEDED(rc)) *endpoint = ptr; + return rc; +} + +Result usbDsInterface_EnableInterface(UsbDsInterface* interface) +{ + if(!interface->initialized)return MAKERESULT(MODULE_LIBNX, LIBNX_NOTINITIALIZED); + + return _usbDsCmdNoParams(interface->h, 3); +} + +Result usbDsInterface_DisableInterface(UsbDsInterface* interface) +{ + if(!interface->initialized)return MAKERESULT(MODULE_LIBNX, LIBNX_NOTINITIALIZED); + + return _usbDsCmdNoParams(interface->h, 4); +} + +Result usbDsInterface_CtrlInPostBufferAsync(UsbDsInterface* interface, void* buffer, size_t size, u32 *out) +{ + if(!interface->initialized)return MAKERESULT(MODULE_LIBNX, LIBNX_NOTINITIALIZED); + + return _usbDsPostBuffer(interface->h, 5, buffer, size, out); +} + +Result usbDsInterface_CtrlOutPostBufferAsync(UsbDsInterface* interface, void* buffer, size_t size, u32 *out) +{ + if(!interface->initialized)return MAKERESULT(MODULE_LIBNX, LIBNX_NOTINITIALIZED); + + return _usbDsPostBuffer(interface->h, 6, buffer, size, out); +} + +Result usbDsInterface_GetCtrlInReportData(UsbDsInterface* interface, u8 out[0x84]) +{ + if(!interface->initialized)return MAKERESULT(MODULE_LIBNX, LIBNX_NOTINITIALIZED); + + return _usbDsGetReport(interface->h, 8, out); +} + +Result usbDsInterface_GetCtrlOutReportData(UsbDsInterface* interface, u8 out[0x84]) +{ + if(!interface->initialized)return MAKERESULT(MODULE_LIBNX, LIBNX_NOTINITIALIZED); + + return _usbDsGetReport(interface->h, 10, out); +} + +Result usbDsInterface_StallCtrl(UsbDsInterface* interface) +{ + if(!interface->initialized)return MAKERESULT(MODULE_LIBNX, LIBNX_NOTINITIALIZED); + + return _usbDsCmdNoParams(interface->h, 11); +} + +//IDsEndpoint + +void usbDsEndpoint_Close(UsbDsEndpoint* endpoint) +{ + _usbDsFreeEndpoint(endpoint); +} + +Result usbDsEndpoint_PostBufferAsync(UsbDsEndpoint* endpoint, void* buffer, size_t size, u32 *out) +{ + if(!endpoint->initialized)return MAKERESULT(MODULE_LIBNX, LIBNX_NOTINITIALIZED); + + return _usbDsPostBuffer(endpoint->h, 0, buffer, size, out); +} + +Result usbDsEndpoint_GetCtrlOutReportData(UsbDsEndpoint* endpoint, u8 out[0x84]) +{ + if(!endpoint->initialized)return MAKERESULT(MODULE_LIBNX, LIBNX_NOTINITIALIZED); + + return _usbDsGetReport(endpoint->h, 3, out); +} + +Result usbDsEndpoint_StallCtrl(UsbDsEndpoint* endpoint) +{ + if(!endpoint->initialized)return MAKERESULT(MODULE_LIBNX, LIBNX_NOTINITIALIZED); + + return _usbDsCmdNoParams(endpoint->h, 4); +} +