#include "service_guard.h" #include #include "arm/cache.h" #include "services/usbds.h" #include "runtime/hosversion.h" #include "runtime/util/utf.h" #include "switch/arm/counter.h" #define TOTAL_INTERFACES 4 #define TOTAL_ENDPOINTS_IN 16 #define TOTAL_ENDPOINTS_OUT 16 #define TOTAL_ENDPOINTS (TOTAL_ENDPOINTS_IN+TOTAL_ENDPOINTS_OUT) static Service g_usbDsRootSrv; static Service g_usbDsSrv; static Event 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, Handle procHandle); static Result _usbDsGetEvent(Service* srv, Event* out_event, bool autoclear, u32 cmd_id); NX_GENERATE_SERVICE_GUARD(usbDs); Result _usbDsInitialize(void) { Result rc = 0; rc = smGetService(&g_usbDsRootSrv, "usb:ds"); if (R_SUCCEEDED(rc)) rc = serviceConvertToDomain(&g_usbDsRootSrv); if (hosversionAtLeast(11,0,0)) { if (R_SUCCEEDED(rc)) { serviceAssumeDomain(&g_usbDsRootSrv); rc = serviceDispatch(&g_usbDsRootSrv, 0, .out_num_objects = 1, .out_objects = &g_usbDsSrv, ); } } else { g_usbDsSrv = g_usbDsRootSrv; } if (R_SUCCEEDED(rc)) rc = _usbDsBindDevice(UsbComplexId_Default, CUR_PROCESS_HANDLE); // GetStateChangeEvent if (R_SUCCEEDED(rc)) rc = _usbDsGetEvent(&g_usbDsSrv, &g_usbDsStateChangeEvent, false, hosversionAtLeast(11,0,0) ? 2 : 3); // Result code doesn't matter here, users can call themselves later, too. This prevents foot shooting. if (R_SUCCEEDED(rc) && hosversionAtLeast(5,0,0)) usbDsClearDeviceData(); return rc; } void _usbDsCleanup(void) { if (hosversionAtLeast(5,0,0) && serviceIsActive(&g_usbDsSrv)) { usbDsDisable(); } _usbDsFreeTables(); eventClose(&g_usbDsStateChangeEvent); if (hosversionAtLeast(11,0,0)) serviceClose(&g_usbDsSrv); serviceClose(&g_usbDsRootSrv); } Service* usbDsGetServiceSession(void) { return &g_usbDsSrv; } Event* usbDsGetStateChangeEvent(void) { return &g_usbDsStateChangeEvent; } static UsbDsInterface* _usbDsTryAllocateInterface(u8 num) { if (num >= TOTAL_INTERFACES) return NULL; UsbDsInterface* ptr = &g_usbDsInterfaceTable[num]; if (ptr->initialized) return NULL; memset(ptr, 0, sizeof(UsbDsInterface)); ptr->initialized = true; ptr->interface_index = num; return ptr; } static UsbDsEndpoint* _usbDsAllocateEndpoint(UsbDsInterface* interface) { u32 pos; UsbDsEndpoint* ptr = NULL; if (interface->interface_index>TOTAL_INTERFACES) return NULL; for (pos=0; posinterface_index][pos]; if (ptr->initialized) continue; memset(ptr, 0, sizeof(UsbDsEndpoint)); ptr->initialized = true; return ptr; } return NULL; } static void _usbDsFreeEndpoint(UsbDsEndpoint* endpoint) { if (!endpoint->initialized) return; /* Cancel any ongoing transactions. */ usbDsEndpoint_Cancel(endpoint); eventClose(&endpoint->CompletionEvent); serviceAssumeDomain(&endpoint->s); serviceClose(&endpoint->s); endpoint->initialized = false; } static void _usbDsFreeInterface(UsbDsInterface* interface) { if (!interface->initialized) return; /* Disable interface. */ usbDsInterface_DisableInterface(interface); /* Close endpoints. */ for (u32 ep = 0; ep < TOTAL_ENDPOINTS; ep++) { _usbDsFreeEndpoint(&g_usbDsEndpointTable[interface->interface_index][ep]); } eventClose(&interface->CtrlOutCompletionEvent); eventClose(&interface->CtrlInCompletionEvent); eventClose(&interface->SetupEvent); serviceAssumeDomain(&interface->s); serviceClose(&interface->s); interface->initialized = false; } static void _usbDsFreeTables(void) { for (u32 intf = 0; intf < TOTAL_INTERFACES; intf++) { _usbDsFreeInterface(&g_usbDsInterfaceTable[intf]); } } static Result _usbDsGetHandle(Service* srv, Handle* handle_out, u32 cmd_id) { serviceAssumeDomain(srv); return serviceDispatch(srv, cmd_id, .out_handle_attrs = { SfOutHandleAttr_HipcCopy }, .out_handles = handle_out, ); } static Result _usbDsGetEvent(Service* srv, Event* out_event, bool autoclear, u32 cmd_id) { Handle tmp_handle = INVALID_HANDLE; Result rc = 0; rc = _usbDsGetHandle(srv, &tmp_handle, cmd_id); if (R_SUCCEEDED(rc)) eventLoadRemote(out_event, tmp_handle, autoclear); return rc; } static Result _usbDsCmdNoIO(Service* srv, u32 cmd_id) { serviceAssumeDomain(srv); return serviceDispatch(srv, cmd_id); } static Result _usbDsCmdInU8NoOut(Service* srv, u8 inval, u32 cmd_id) { serviceAssumeDomain(srv); return serviceDispatchIn(srv, cmd_id, inval); } static Result _usbDsCmdInBoolNoOut(Service* srv, bool inval, u32 cmd_id) { serviceAssumeDomain(srv); return _usbDsCmdInU8NoOut(srv, inval!=0, cmd_id); } static Result _usbDsCmdInU32NoOut(Service* srv, u32 inval, u32 cmd_id) { serviceAssumeDomain(srv); return serviceDispatchIn(srv, cmd_id, inval); } static Result _usbDsCmdNoInOutU32(Service* srv, u32 *out, u32 cmd_id) { serviceAssumeDomain(srv); return serviceDispatchOut(srv, cmd_id, *out); } static Result _usbDsCmdSendBufNoOut(Service* srv, const void* buffer, size_t size, u32 cmd_id) { serviceAssumeDomain(srv); return serviceDispatch(srv, cmd_id, .buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_In }, .buffers = { { buffer, size } }, ); } static Result _usbDsBindDevice(UsbComplexId complexId, Handle prochandle) { Result rc; if (hosversionAtLeast(11,0,0)) { serviceAssumeDomain(&g_usbDsSrv); const u32 in = complexId; rc = serviceDispatchIn(&g_usbDsSrv, 0, in, .in_num_handles = 1, .in_handles = { prochandle }, ); } else { // BindDevice rc = _usbDsCmdInU32NoOut(&g_usbDsSrv, complexId, 0); if (R_SUCCEEDED(rc)) { serviceAssumeDomain(&g_usbDsSrv); rc = serviceDispatch(&g_usbDsSrv, 1, .in_num_handles = 1, .in_handles = { prochandle }, ); } } return rc; } Result usbDsGetState(UsbState *out) { _Static_assert(sizeof(*out) == sizeof(u32)); return _usbDsCmdNoInOutU32(&g_usbDsSrv, (u32 *)out, hosversionAtLeast(11,0,0) ? 3 : 4); } Result usbDsWaitReady(u64 timeout) { Result rc; UsbState state = UsbState_Detached; rc = usbDsGetState(&state); if (R_FAILED(rc)) return rc; if (state == UsbState_Configured) return 0; bool has_timeout = timeout != UINT64_MAX; u64 deadline = 0; if (has_timeout) deadline = armGetSystemTick() + armNsToTicks(timeout); do { if (has_timeout) { s64 remaining = deadline - armGetSystemTick(); timeout = remaining > 0 ? armTicksToNs(remaining) : 0; } eventWait(&g_usbDsStateChangeEvent, timeout); eventClear(&g_usbDsStateChangeEvent); rc = usbDsGetState(&state); } while (R_SUCCEEDED(rc) && state != UsbState_Configured && timeout > 0); if (R_SUCCEEDED(rc) && state != UsbState_Configured && timeout == 0) return KERNELRESULT(TimedOut); return rc; } Result usbDsParseReportData(UsbDsReportData *reportdata, u32 urbId, u32 *requestedSize, u32 *transferredSize) { Result rc = 0; u32 pos; u32 count = reportdata->report_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, LibnxError_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; } Result usbDsSetVidPidBcd(const UsbDsDeviceInfo* deviceinfo) { if (hosversionAtLeast(5,0,0)) return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer); return _usbDsCmdSendBufNoOut(&g_usbDsSrv, deviceinfo, sizeof(UsbDsDeviceInfo), 5); } static Result _usbDsPostBuffer(Service* srv, void* buffer, size_t size, u32 *urbId, u32 cmd_id) { armDCacheFlush(buffer, size); const struct { u32 size; u32 padding; u64 buffer; } in = { (u32)size, 0, (u64)buffer }; serviceAssumeDomain(srv); return serviceDispatchInOut(srv, cmd_id, in, *urbId); } static Result _usbDsGetReport(Service* srv, UsbDsReportData *out, u32 cmd_id) { serviceAssumeDomain(srv); return serviceDispatchOut(srv, cmd_id, *out); } static Result _usbDsGetDsInterface(Service* srv, Service* srv_out, const void* buf0, size_t buf0size, const void* buf1, size_t buf1size, u8 *out) { serviceAssumeDomain(srv); return serviceDispatchOut(srv, hosversionAtLeast(11,0,0) ? 1 : 2, *out, .buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_In, SfBufferAttr_HipcMapAlias | SfBufferAttr_In, }, .buffers = { { buf0, buf0size }, { buf1, buf1size }, }, .out_num_objects = 1, .out_objects = srv_out, ); } Result usbDsGetDsInterface(UsbDsInterface** interface, struct usb_interface_descriptor* _descriptor, const char *interface_name) { if (hosversionAtLeast(5,0,0)) return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer); struct usb_interface_descriptor send_desc = *_descriptor; Service srv; Result rc; for (u32 i = 0; i < TOTAL_INTERFACES; i++) { send_desc.bInterfaceNumber = i; rc = _usbDsGetDsInterface(&g_usbDsSrv, &srv, &send_desc, USB_DT_INTERFACE_SIZE, interface_name, strlen(interface_name)+1, NULL); if (R_SUCCEEDED(rc)) { break; } } if (R_FAILED(rc)) { return rc; } UsbDsInterface* ptr = _usbDsTryAllocateInterface(send_desc.bInterfaceNumber); if(ptr == NULL) { serviceAssumeDomain(&srv); serviceClose(&srv); return MAKERESULT(Module_Libnx, LibnxError_OutOfMemory); } ptr->s = srv; // GetSetupEvent if (R_SUCCEEDED(rc)) rc = _usbDsGetEvent(&ptr->s, &ptr->SetupEvent, false, 1); // GetCtrlInCompletionEvent if (R_SUCCEEDED(rc)) rc = _usbDsGetEvent(&ptr->s, &ptr->CtrlInCompletionEvent, false, 7); // GetCtrlOutCompletionEvent if (R_SUCCEEDED(rc)) rc = _usbDsGetEvent(&ptr->s, &ptr->CtrlOutCompletionEvent, false, 9); if (R_FAILED(rc)) _usbDsFreeInterface(ptr); if (R_SUCCEEDED(rc)) *interface = ptr; return rc; } static Result _usbDsRegisterInterface(Service* srv_out, u8 intf_num) { if (hosversionBefore(5,0,0)) return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer); serviceAssumeDomain(&g_usbDsSrv); return serviceDispatchIn(&g_usbDsSrv, hosversionAtLeast(11,0,0) ? 1 : 2, intf_num, .out_num_objects = 1, .out_objects = srv_out, ); } Result usbDsRegisterInterface(UsbDsInterface** interface) { Service srv; Result rc; u8 intf_num; for (intf_num = 0; intf_num < TOTAL_INTERFACES; intf_num++) { rc = _usbDsRegisterInterface(&srv, intf_num); if (R_SUCCEEDED(rc)) break; } if (R_FAILED(rc)) { return rc; } UsbDsInterface* ptr = _usbDsTryAllocateInterface(intf_num); if (ptr == NULL) { serviceAssumeDomain(&srv); serviceClose(&srv); return MAKERESULT(Module_Libnx, LibnxError_OutOfMemory); } ptr->s = srv; // GetSetupEvent if (R_SUCCEEDED(rc)) rc = _usbDsGetEvent(&ptr->s, &ptr->SetupEvent, false, 1); // GetCtrlInCompletionEvent if (R_SUCCEEDED(rc)) rc = _usbDsGetEvent(&ptr->s, &ptr->CtrlInCompletionEvent, false, hosversionAtLeast(11,0,0) ? 5 : 7); // GetCtrlOutCompletionEvent if (R_SUCCEEDED(rc)) rc = _usbDsGetEvent(&ptr->s, &ptr->CtrlOutCompletionEvent, false, hosversionAtLeast(11,0,0) ? 7 : 9); if (R_FAILED(rc)) _usbDsFreeInterface(ptr); if (R_SUCCEEDED(rc)) *interface = ptr; return rc; } Result usbDsRegisterInterfaceEx(UsbDsInterface** interface, u8 intf_num) { UsbDsInterface* ptr = _usbDsTryAllocateInterface(intf_num); if (ptr == NULL) return MAKERESULT(Module_Libnx, LibnxError_OutOfMemory); Result rc=0; rc = _usbDsRegisterInterface(&ptr->s, intf_num); // GetSetupEvent if (R_SUCCEEDED(rc)) rc = _usbDsGetEvent(&ptr->s, &ptr->SetupEvent, false, 1); // GetCtrlInCompletionEvent if (R_SUCCEEDED(rc)) rc = _usbDsGetEvent(&ptr->s, &ptr->CtrlInCompletionEvent, false, hosversionAtLeast(11,0,0) ? 5 : 7); // GetCtrlOutCompletionEvent if (R_SUCCEEDED(rc)) rc = _usbDsGetEvent(&ptr->s, &ptr->CtrlOutCompletionEvent, false, hosversionAtLeast(11,0,0) ? 7 : 9); if (R_FAILED(rc)) _usbDsFreeInterface(ptr); if (R_SUCCEEDED(rc)) *interface = ptr; return rc; } Result usbDsClearDeviceData(void) { if (hosversionBefore(5,0,0)) return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer); return _usbDsCmdNoIO(&g_usbDsSrv, hosversionAtLeast(11,0,0) ? 4 : 5); } static Result _usbDsAddUsbStringDescriptorRaw(u8 *out_index, struct usb_string_descriptor *descriptor) { if (hosversionBefore(5,0,0)) return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer); serviceAssumeDomain(&g_usbDsSrv); return serviceDispatchOut(&g_usbDsSrv, hosversionAtLeast(11,0,0) ? 5 : 6, *out_index, .buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_In }, .buffers = { { descriptor, sizeof(*descriptor) } }, ); } Result usbDsAddUsbStringDescriptor(u8* out_index, const char* string) { struct usb_string_descriptor descriptor = { .bDescriptorType = USB_DT_STRING, .wData = {0}, }; // Convert u32 len = (u32)utf8_to_utf16(descriptor.wData, (const uint8_t *)string, sizeof(descriptor.wData)/sizeof(u16) - 1); if (len > sizeof(descriptor.wData)/sizeof(u16)) len = sizeof(descriptor.wData)/sizeof(u16); // Set length descriptor.bLength = 2 + 2 * len; return _usbDsAddUsbStringDescriptorRaw(out_index, &descriptor); } Result usbDsAddUsbLanguageStringDescriptor(u8* out_index, const u16* lang_ids, u16 num_langs) { if (num_langs > 0x40) num_langs = 0x40; struct usb_string_descriptor descriptor = { .bLength = 2 + 2 * num_langs, .bDescriptorType = USB_DT_STRING, .wData = {0}, }; for (u32 i = 0; i < num_langs; i++) { descriptor.wData[i] = lang_ids[i]; } return _usbDsAddUsbStringDescriptorRaw(out_index, &descriptor); } Result usbDsDeleteUsbStringDescriptor(u8 index) { if (hosversionBefore(5,0,0)) return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer); return _usbDsCmdInU8NoOut(&g_usbDsSrv, index, hosversionAtLeast(11,0,0) ? 6 : 7); } Result usbDsSetUsbDeviceDescriptor(UsbDeviceSpeed speed, struct usb_device_descriptor* descriptor) { if (hosversionBefore(5,0,0)) return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer); u32 tmp=speed; serviceAssumeDomain(&g_usbDsSrv); return serviceDispatchIn(&g_usbDsSrv, hosversionAtLeast(11,0,0) ? 7 : 8, tmp, .buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_In }, .buffers = { { descriptor, USB_DT_DEVICE_SIZE } }, ); } Result usbDsSetBinaryObjectStore(const void* bos, size_t bos_size) { if (hosversionBefore(5,0,0)) return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer); return _usbDsCmdSendBufNoOut(&g_usbDsSrv, bos, bos_size, hosversionAtLeast(11,0,0) ? 8 : 9); } Result usbDsEnable(void) { if (hosversionBefore(5,0,0)) return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer); return _usbDsCmdNoIO(&g_usbDsSrv, hosversionAtLeast(11,0,0) ? 9 : 10); } Result usbDsDisable(void) { if (hosversionBefore(5,0,0)) return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer); return _usbDsCmdNoIO(&g_usbDsSrv, hosversionAtLeast(11,0,0) ? 10 : 11); } Result usbDsGetSpeed(UsbDeviceSpeed* out) { if (hosversionBefore(8,0,0)) return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer); _Static_assert(sizeof(*out) == sizeof(u32)); return _usbDsCmdNoInOutU32(&g_usbDsSrv, (u32 *)out, hosversionAtLeast(11,0,0) ? 11 : 12); } //IDsInterface void usbDsInterface_Close(UsbDsInterface* interface) { _usbDsFreeInterface(interface); } Result usbDsInterface_GetSetupPacket(UsbDsInterface* interface, void* buffer, size_t size) { if (!interface->initialized) return MAKERESULT(Module_Libnx, LibnxError_NotInitialized); serviceAssumeDomain(&interface->s); return serviceDispatch(&interface->s, 2, .buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_Out }, .buffers = { { buffer, size } }, ); } static Result _usbDsInterface_GetDsEndpoint(Service* srv, Service* srv_out, const void* buf0, size_t buf0size, u8 *out) { serviceAssumeDomain(srv); return serviceDispatchOut(srv, 0, *out, .buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_In }, .buffers = { { buf0, buf0size } }, .out_num_objects = 1, .out_objects = srv_out, ); } Result usbDsInterface_GetDsEndpoint(UsbDsInterface* interface, UsbDsEndpoint** endpoint, struct usb_endpoint_descriptor* descriptor) { if (!interface->initialized) return MAKERESULT(Module_Libnx, LibnxError_NotInitialized); if (hosversionAtLeast(5,0,0)) return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer); UsbDsEndpoint* ptr = _usbDsAllocateEndpoint(interface); if (ptr==NULL) return MAKERESULT(Module_Libnx, LibnxError_OutOfMemory); Result rc = _usbDsInterface_GetDsEndpoint(&interface->s, &ptr->s, descriptor, USB_DT_ENDPOINT_SIZE, NULL); if (R_SUCCEEDED(rc)) rc = _usbDsGetEvent(&ptr->s, &ptr->CompletionEvent, false, 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, LibnxError_NotInitialized); if (hosversionAtLeast(11,0,0)) return 0; return _usbDsCmdNoIO(&interface->s, 3); } Result usbDsInterface_DisableInterface(UsbDsInterface* interface) { if (!interface->initialized) return MAKERESULT(Module_Libnx, LibnxError_NotInitialized); if (hosversionAtLeast(11,0,0)) return 0; return _usbDsCmdNoIO(&interface->s, 4); } Result usbDsInterface_CtrlInPostBufferAsync(UsbDsInterface* interface, void* buffer, size_t size, u32 *urbId) { if (!interface->initialized) return MAKERESULT(Module_Libnx, LibnxError_NotInitialized); return _usbDsPostBuffer(&interface->s, buffer, size, urbId, hosversionAtLeast(11,0,0) ? 3 : 5); } Result usbDsInterface_CtrlOutPostBufferAsync(UsbDsInterface* interface, void* buffer, size_t size, u32 *urbId) { if (!interface->initialized) return MAKERESULT(Module_Libnx, LibnxError_NotInitialized); return _usbDsPostBuffer(&interface->s, buffer, size, urbId, hosversionAtLeast(11,0,0) ? 4 : 6); } Result usbDsInterface_GetCtrlInReportData(UsbDsInterface* interface, UsbDsReportData *out) { if (!interface->initialized) return MAKERESULT(Module_Libnx, LibnxError_NotInitialized); return _usbDsGetReport(&interface->s, out, hosversionAtLeast(11,0,0) ? 6 : 8); } Result usbDsInterface_GetCtrlOutReportData(UsbDsInterface* interface, UsbDsReportData *out) { if (!interface->initialized) return MAKERESULT(Module_Libnx, LibnxError_NotInitialized); return _usbDsGetReport(&interface->s, out, hosversionAtLeast(11,0,0) ? 8 : 10); } Result usbDsInterface_StallCtrl(UsbDsInterface* interface) { if (!interface->initialized) return MAKERESULT(Module_Libnx, LibnxError_NotInitialized); return _usbDsCmdNoIO(&interface->s, hosversionAtLeast(11,0,0) ? 9 : 11); } Result usbDsInterface_RegisterEndpoint(UsbDsInterface* interface, UsbDsEndpoint** endpoint, u8 endpoint_address) { if (!interface->initialized) return MAKERESULT(Module_Libnx, LibnxError_NotInitialized); if (hosversionBefore(5,0,0)) return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer); UsbDsEndpoint* ptr = _usbDsAllocateEndpoint(interface); if(ptr==NULL) return MAKERESULT(Module_Libnx, LibnxError_OutOfMemory); serviceAssumeDomain(&interface->s); Result rc = serviceDispatchIn(&interface->s, 0, endpoint_address, .out_num_objects = 1, .out_objects = &ptr->s, ); if (R_SUCCEEDED(rc)) rc = _usbDsGetEvent(&ptr->s, &ptr->CompletionEvent, false, 2);//GetCompletionEvent if (R_FAILED(rc)) _usbDsFreeEndpoint(ptr); if (R_SUCCEEDED(rc)) *endpoint = ptr; return rc; } Result usbDsInterface_AppendConfigurationData(UsbDsInterface* interface, UsbDeviceSpeed speed, const void* buffer, size_t size) { if (!interface->initialized) return MAKERESULT(Module_Libnx, LibnxError_NotInitialized); if (hosversionBefore(5,0,0)) return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer); if (hosversionBefore(11,0,0)) { const struct { u8 intf_num; u8 pad[3]; u32 speed; } in = { interface->interface_index, {0}, speed }; return serviceDispatchIn(&interface->s, 12, in, .buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_In }, .buffers = { { buffer, size } }, ); } u32 in = speed; return serviceDispatchIn(&interface->s, 10, in, .buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_In }, .buffers = { { buffer, size } }, ); } //IDsEndpoint void usbDsEndpoint_Close(UsbDsEndpoint* endpoint) { _usbDsFreeEndpoint(endpoint); } Result usbDsEndpoint_PostBufferAsync(UsbDsEndpoint* endpoint, void* buffer, size_t size, u32 *urbId) { if (!endpoint->initialized) return MAKERESULT(Module_Libnx, LibnxError_NotInitialized); return _usbDsPostBuffer(&endpoint->s, buffer, size, urbId, 0); } Result usbDsEndpoint_Cancel(UsbDsEndpoint* endpoint) { if (!endpoint->initialized) return MAKERESULT(Module_Libnx, LibnxError_NotInitialized); return _usbDsCmdNoIO(&endpoint->s, 1); } Result usbDsEndpoint_GetReportData(UsbDsEndpoint* endpoint, UsbDsReportData *out) { if (!endpoint->initialized) return MAKERESULT(Module_Libnx, LibnxError_NotInitialized); return _usbDsGetReport(&endpoint->s, out, 3); } Result usbDsEndpoint_Stall(UsbDsEndpoint* endpoint) { if (!endpoint->initialized) return MAKERESULT(Module_Libnx, LibnxError_NotInitialized); return _usbDsCmdNoIO(&endpoint->s, 4); } Result usbDsEndpoint_SetZlt(UsbDsEndpoint* endpoint, bool zlt) { if (!endpoint->initialized) return MAKERESULT(Module_Libnx, LibnxError_NotInitialized); return _usbDsCmdInBoolNoOut(&endpoint->s, zlt, 5); }