Refactor usb:ds, add support for 5.0.0+

This commit is contained in:
Michael Scire 2018-10-04 21:37:43 -07:00
parent 96dce1a8b6
commit d146430265
4 changed files with 813 additions and 190 deletions

View File

@ -8,16 +8,14 @@
#pragma once
#include "../../types.h"
/// Initializes usbComms with the default number of interfaces (1)
Result usbCommsInitialize(void);
/// Initializes usbComms with a specific number of interfaces.
Result usbCommsInitializeEx(u32 num_interfaces);
/// Exits usbComms.
void usbCommsExit(void);
/// Same as usbCommsInitialize, except this can be used after usbCommsInitialize (or instead of usbCommsInitialize), for creating new interface(s).
/// bInterface* are the values for the same fields in usb.h \ref usb_interface_descriptor. \ref usbCommsInitialize uses USB_CLASS_VENDOR_SPEC for all of these internally.
Result usbCommsInitializeEx(u32 *interface, u8 bInterfaceClass, u8 bInterfaceSubClass, u8 bInterfaceProtocol);
/// Shutdown the specified interface. If no interfaces are remaining, this then uses \ref usbCommsExit internally.
void usbCommsExitEx(u32 interface);
/// Read data with the default interface.
size_t usbCommsRead(void* buffer, size_t size);

View File

@ -1,12 +1,13 @@
/**
* @file usb.h
* @brief USB (usb:*) service IPC wrapper.
* @author yellows8
* @author SciresM, yellows8
* @copyright libnx Authors
*/
#pragma once
#include "../types.h"
#include "../services/sm.h"
#include "../kernel/event.h"
/// usb:ds Switch-as-device<>host USB comms, see also here: http://switchbrew.org/index.php?title=USB_services
@ -18,6 +19,8 @@
/* Descriptor sizes per descriptor type */
#define USB_DT_INTERFACE_SIZE 9
#define USB_DT_ENDPOINT_SIZE 7
#define USB_DT_DEVICE_SIZE 0x12
#define USB_DT_SS_ENDPOINT_COMPANION_SIZE 6
/// Imported from libusb, with some adjustments.
struct usb_endpoint_descriptor {
@ -35,13 +38,47 @@ struct usb_interface_descriptor {
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 bNumEndpoints;
uint8_t bInterfaceClass;
uint8_t bInterfaceSubClass;
uint8_t bInterfaceProtocol;
uint8_t iInterface; /// Ignored.
};
/// Imported from libusb, with some adjustments.
struct usb_device_descriptor {
uint8_t bLength;
uint8_t bDescriptorType; /// Must match USB_DT_Device.
uint16_t bcdUSB;
uint8_t bDeviceClass;
uint8_t bDeviceSubClass;
uint8_t bDeviceProtocol;
uint8_t bMaxPacketSize0;
uint16_t idVendor;
uint16_t idProduct;
uint16_t bcdDevice;
uint8_t iManufacturer;
uint8_t iProduct;
uint8_t iSerialNumber;
uint8_t bNumConfigurations;
};
/// Imported from libusb, with some adjustments.
struct usb_ss_endpoint_companion_descriptor {
uint8_t bLength;
uint8_t bDescriptorType; /// Must match USB_DT_SS_ENDPOINT_COMPANION.
uint8_t bMaxBurst;
uint8_t bmAttributes;
uint16_t wBytesPerInterval;
};
/// Imported from libusb, with some adjustments.
struct usb_string_descriptor {
uint8_t bLength;
uint8_t bDescriptorType; /// Must match USB_DT_STRING.
uint16_t wData[0x40];
};
typedef struct {
u16 idVendor; /// VID
u16 idProduct; /// PID
@ -68,21 +105,27 @@ typedef struct {
u32 interface_index;
Service h;
Handle SetupEvent;
Handle CtrlInCompletionEvent;
Handle CtrlOutCompletionEvent;
Event SetupEvent;
Event CtrlInCompletionEvent;
Event CtrlOutCompletionEvent;
} UsbDsInterface;
typedef struct {
bool initialized;
Service h;
Handle CompletionEvent;
Event CompletionEvent;
} UsbDsEndpoint;
typedef enum {
UsbComplexId_Default = 0x2
} UsbComplexId;
typedef enum {
UsbDeviceSpeed_Full = 0x2,
UsbDeviceSpeed_High = 0x3,
UsbDeviceSpeed_Super = 0x4,
} UsbDeviceSpeed;
/// Imported from libusb, with changed names.
enum usb_class_code {
USB_CLASS_PER_INTERFACE = 0,
@ -153,38 +196,63 @@ enum usb_iso_usage_type {
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.
/// Opens a session with usb:ds.
Result usbDsInitialize(void);
/// Closes the usb:ds session. Any interfaces/endpoints which are left open are automatically closed, since otherwise usb-sysmodule won't fully reset usb:ds to defaults.
void usbDsExit(void);
Service* usbDsGetServiceSession(void);
Handle usbDsGetStateChangeEvent(void);
Result usbDsGetState(u32 *out);
Result usbDsGetDsInterface(UsbDsInterface** interface, struct usb_interface_descriptor* descriptor, const char *interface_name);
/// 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.
/// Helpers
Result usbDsWaitReady(u64 timeout);
Result usbDsParseReportData(UsbDsReportData *reportdata, u32 urbId, u32 *requestedSize, u32 *transferredSize);
/// IDsService
// Do not provide API access to these functions, as they're handled by usbDsInitialize().
// Result usbDsBindDevice(UsbComplexId complexId);
// Result usbDsBindClientProcess(Handle prochandle);
Event* usbDsGetStateChangeEvent(void);
Result usbDsGetState(u32* out);
/// Removed in 5.0.0
Result usbDsGetDsInterface(UsbDsInterface** out, struct usb_interface_descriptor* descriptor, const char* interface_name);
Result usbDsSetVidPidBcd(const UsbDsDeviceInfo* deviceinfo);
/// Added in 5.0.0
Result usbDsRegisterInterface(UsbDsInterface** out, u32 intf_num);
Result usbDsClearDeviceData(void);
Result usbDsAddUsbStringDescriptor(u8* out_index, const char* string);
Result usbDsAddUsbLanguageStringDescriptor(u8* out_index, const u16* lang_ids, u16 num_langs);
Result usbDsDeleteUsbStringDescriptor(u8 index);
Result usbDsSetUsbDeviceDescriptor(UsbDeviceSpeed speed, struct usb_device_descriptor* descriptor);
Result usbDsSetBinaryObjectStore(void* bos, size_t bos_size);
Result usbDsEnable(void);
Result usbDsDisable(void);
/// IDsInterface
void usbDsInterface_Close(UsbDsInterface* interface);
Result usbDsInterface_GetDsEndpoint(UsbDsInterface* interface, UsbDsEndpoint** endpoint, struct usb_endpoint_descriptor* descriptor);
Result usbDsInterface_GetSetupPacket(UsbDsInterface* interface, void* buffer, size_t size);
Result usbDsInterface_EnableInterface(UsbDsInterface* interface);
Result usbDsInterface_DisableInterface(UsbDsInterface* interface);
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_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);
/// Removed in 5.0.0
Result usbDsInterface_GetDsEndpoint(UsbDsInterface* interface, UsbDsEndpoint** endpoint, struct usb_endpoint_descriptor* descriptor);
/// Added in 5.0.0
Result usbDsInterface_RegisterEndpoint(UsbDsInterface* interface, UsbDsEndpoint** endpoint, u8 endpoint_address);
Result usbDsInterface_AppendConfigurationData(UsbDsInterface* interface, UsbDeviceSpeed speed, void* buffer, size_t size);
/// IDsEndpoint
void usbDsEndpoint_Close(UsbDsEndpoint* endpoint);
Result usbDsEndpoint_PostBufferAsync(UsbDsEndpoint* endpoint, void* buffer, size_t size, u32 *urbId);
Result usbDsEndpoint_GetReportData(UsbDsEndpoint* endpoint, UsbDsReportData *out);
Result usbDsEndpoint_StallCtrl(UsbDsEndpoint* endpoint);
Result usbDsEndpoint_Cancel(UsbDsEndpoint* endpoint);
Result usbDsEndpoint_PostBufferAsync(UsbDsEndpoint* endpoint, void* buffer, size_t size, u32* urbId);
Result usbDsEndpoint_GetReportData(UsbDsEndpoint* endpoint, UsbDsReportData* out);
Result usbDsEndpoint_StallCtrl(UsbDsEndpoint* endpoint);
Result usbDsEndpoint_SetZlt(UsbDsEndpoint* endpoint, bool zlt);

View File

@ -2,6 +2,7 @@
#include <malloc.h>
#include "types.h"
#include "result.h"
#include "kernel/detect.h"
#include "kernel/rwlock.h"
#include "services/fatal.h"
#include "services/usb.h"
@ -25,69 +26,127 @@ static usbCommsInterface g_usbCommsInterfaces[TOTAL_INTERFACES];
static RwLock g_usbCommsLock;
static Result _usbCommsInterfaceInit(usbCommsInterface *interface, u8 bInterfaceClass, u8 bInterfaceSubClass, u8 bInterfaceProtocol);
static Result _usbCommsInterfaceInit1x(u32 intf_ind);
static Result _usbCommsInterfaceInit5x(u32 intf_ind);
static Result _usbCommsInterfaceInit(u32 intf_ind);
static Result _usbCommsWrite(usbCommsInterface *interface, const void* buffer, size_t size, size_t *transferredSize);
Result usbCommsInitializeEx(u32 *interface, u8 bInterfaceClass, u8 bInterfaceSubClass, u8 bInterfaceProtocol)
Result usbCommsInitializeEx(u32 num_interfaces)
{
bool found=0;
usbCommsInterface *inter = NULL;
Result rc = 0;
rwlockWriteLock(&g_usbCommsLock);
if (g_usbCommsInitialized && interface==NULL) {
rwlockWriteUnlock(&g_usbCommsLock);
return 0;
}
Result rc=0;
u32 i = 0;
if (!g_usbCommsInitialized) rc = usbDsInitialize(UsbComplexId_Default, NULL);
if (R_SUCCEEDED(rc)) {
for(i=0; i<TOTAL_INTERFACES; i++) {
inter = &g_usbCommsInterfaces[i];
rwlockReadLock(&inter->lock);
if (!inter->initialized) found=1;
rwlockReadUnlock(&inter->lock);
if (found) break;
if (g_usbCommsInitialized) {
rc = MAKERESULT(Module_Libnx, LibnxError_AlreadyInitialized);
} else if (num_interfaces > TOTAL_INTERFACES) {
rc = MAKERESULT(Module_Libnx, LibnxError_OutOfMemory);
} else {
rc = usbDsInitialize();
if (R_SUCCEEDED(rc)) {
if (kernelAbove500()) {
u8 iManufacturer, iProduct, iSerialNumber;
static const u16 supported_langs[1] = {0x0409};
// Send language descriptor
rc = usbDsAddUsbLanguageStringDescriptor(NULL, supported_langs, sizeof(supported_langs)/sizeof(u16));
// Send manufacturer
if (R_SUCCEEDED(rc)) rc = usbDsAddUsbStringDescriptor(&iManufacturer, "Switchbrew");
// Send product
if (R_SUCCEEDED(rc)) rc = usbDsAddUsbStringDescriptor(&iProduct, "libnx USB comms");
// Send serial number
if (R_SUCCEEDED(rc)) rc = usbDsAddUsbStringDescriptor(&iSerialNumber, "SerialNumber");
// Send device descriptors
struct usb_device_descriptor device_descriptor = {
.bLength = USB_DT_DEVICE_SIZE,
.bDescriptorType = USB_DT_DEVICE,
.bcdUSB = 0x0110,
.bDeviceClass = 0x00,
.bDeviceSubClass = 0x00,
.bDeviceProtocol = 0x00,
.bMaxPacketSize0 = 0x40,
.idVendor = 0x057e,
.idProduct = 0x3000,
.bcdDevice = 0x0100,
.iManufacturer = iManufacturer,
.iProduct = iProduct,
.iSerialNumber = iSerialNumber,
.bNumConfigurations = 0x01
};
if (R_SUCCEEDED(rc)) rc = usbDsSetUsbDeviceDescriptor(UsbDeviceSpeed_Full, &device_descriptor);
// High Speed is USB 2.0
device_descriptor.bcdUSB = 0x0200;
if (R_SUCCEEDED(rc)) rc = usbDsSetUsbDeviceDescriptor(UsbDeviceSpeed_High, &device_descriptor);
// Super Speed is USB 3.0
device_descriptor.bcdUSB = 0x0300;
// Upgrade packet size to 512
device_descriptor.bMaxPacketSize0 = 0x09;
if (R_SUCCEEDED(rc)) rc = usbDsSetUsbDeviceDescriptor(UsbDeviceSpeed_Super, &device_descriptor);
// Define Binary Object Store
u8 bos[0x16] = {
0x05, // .bLength
USB_DT_BOS, // .bDescriptorType
0x16, 0x00, // .wTotalLength
0x02, // .bNumDeviceCaps
// USB 2.0
0x07, // .bLength
USB_DT_DEVICE_CAPABILITY, // .bDescriptorType
0x02, // .bDevCapabilityType
0x02, 0x00, 0x00, 0x00, // dev_capability_data
// USB 3.0
0x0A, // .bLength
USB_DT_DEVICE_CAPABILITY, // .bDescriptorType
0x03, // .bDevCapabilityType
0x00, 0x0E, 0x00, 0x03, 0x00, 0x00, 0x00
};
if (R_SUCCEEDED(rc)) rc = usbDsSetBinaryObjectStore(bos, sizeof(bos));
}
if (R_SUCCEEDED(rc)) {
for (u32 i = 0; i < num_interfaces; i++) {
usbCommsInterface *intf = &g_usbCommsInterfaces[i];
rwlockWriteLock(&intf->lock);
rwlockWriteLock(&intf->lock_in);
rwlockWriteLock(&intf->lock_out);
rc = _usbCommsInterfaceInit(i);
rwlockWriteUnlock(&intf->lock_out);
rwlockWriteUnlock(&intf->lock_in);
rwlockWriteUnlock(&intf->lock);
if (R_FAILED(rc)) {
break;
}
}
}
}
if (R_SUCCEEDED(rc) && kernelAbove500()) {
rc = usbDsEnable();
}
if (R_FAILED(rc)) {
usbCommsExit();
}
if (!found) rc = MAKERESULT(Module_Libnx, LibnxError_OutOfMemory);
}
if (R_SUCCEEDED(rc)) {
rwlockWriteLock(&inter->lock);
rwlockWriteLock(&inter->lock_in);
rwlockWriteLock(&inter->lock_out);
rc = _usbCommsInterfaceInit(inter, bInterfaceClass, bInterfaceSubClass, bInterfaceProtocol);
rwlockWriteUnlock(&inter->lock_out);
rwlockWriteUnlock(&inter->lock_in);
rwlockWriteUnlock(&inter->lock);
}
if (R_FAILED(rc)) {
usbCommsExit();
}
if (R_SUCCEEDED(rc)) g_usbCommsInitialized=true;
if (R_SUCCEEDED(rc) && interface) *interface = i;
if (R_SUCCEEDED(rc)) g_usbCommsInitialized = true;
rwlockWriteUnlock(&g_usbCommsLock);
return rc;
}
Result usbCommsInitialize(void)
{
return usbCommsInitializeEx(NULL, USB_CLASS_VENDOR_SPEC, USB_CLASS_VENDOR_SPEC, USB_CLASS_VENDOR_SPEC);
return usbCommsInitializeEx(1);
}
static void _usbCommsInterfaceExit(usbCommsInterface *interface)
static void _usbCommsInterfaceFree(usbCommsInterface *interface)
{
rwlockWriteLock(&interface->lock);
if (!interface->initialized) {
@ -100,11 +159,6 @@ static void _usbCommsInterfaceExit(usbCommsInterface *interface)
interface->initialized = 0;
usbDsInterface_DisableInterface(interface->interface);
usbDsEndpoint_Close(interface->endpoint_in);
usbDsEndpoint_Close(interface->endpoint_out);
usbDsInterface_Close(interface->interface);
interface->endpoint_in = NULL;
interface->endpoint_out = NULL;
interface->interface = NULL;
@ -120,26 +174,6 @@ static void _usbCommsInterfaceExit(usbCommsInterface *interface)
rwlockWriteUnlock(&interface->lock);
}
void usbCommsExitEx(u32 interface)
{
u32 i;
bool found=0;
if (interface>=TOTAL_INTERFACES) return;
_usbCommsInterfaceExit(&g_usbCommsInterfaces[interface]);
for (i=0; i<TOTAL_INTERFACES; i++)
{
rwlockReadLock(&g_usbCommsInterfaces[i].lock);
if (g_usbCommsInterfaces[i].initialized) found = 1;
rwlockReadUnlock(&g_usbCommsInterfaces[i].lock);
if (found) break;
}
if (!found) usbCommsExit();
}
void usbCommsExit(void)
{
u32 i;
@ -154,27 +188,145 @@ void usbCommsExit(void)
for (i=0; i<TOTAL_INTERFACES; i++)
{
_usbCommsInterfaceExit(&g_usbCommsInterfaces[i]);
_usbCommsInterfaceFree(&g_usbCommsInterfaces[i]);
}
}
static Result _usbCommsInterfaceInit(usbCommsInterface *interface, u8 bInterfaceClass, u8 bInterfaceSubClass, u8 bInterfaceProtocol)
static Result _usbCommsInterfaceInit(u32 intf_ind)
{
Result rc=0;
if (kernelAbove500()) {
return _usbCommsInterfaceInit5x(intf_ind);
} else {
return _usbCommsInterfaceInit1x(intf_ind);
}
}
static Result _usbCommsInterfaceInit5x(u32 intf_ind)
{
Result rc = 0;
u32 ep_num = intf_ind + 1;
usbCommsInterface *interface = &g_usbCommsInterfaces[intf_ind];
struct usb_interface_descriptor interface_descriptor = {
.bLength = USB_DT_INTERFACE_SIZE,
.bDescriptorType = USB_DT_INTERFACE,
.bInterfaceNumber = USBDS_DEFAULT_InterfaceNumber,
.bInterfaceClass = bInterfaceClass,
.bInterfaceSubClass = bInterfaceSubClass,
.bInterfaceProtocol = bInterfaceProtocol,
.bInterfaceNumber = intf_ind,
.bNumEndpoints = 2,
.bInterfaceClass = 0xFF,
.bInterfaceSubClass = 0xFF,
.bInterfaceProtocol = 0xFF,
};
struct usb_endpoint_descriptor endpoint_descriptor_in = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = USB_ENDPOINT_IN,
.bEndpointAddress = USB_ENDPOINT_IN + ep_num,
.bmAttributes = USB_TRANSFER_TYPE_BULK,
.wMaxPacketSize = 0x40,
};
struct usb_endpoint_descriptor endpoint_descriptor_out = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = USB_ENDPOINT_OUT + ep_num,
.bmAttributes = USB_TRANSFER_TYPE_BULK,
.wMaxPacketSize = 0x40,
};
struct usb_ss_endpoint_companion_descriptor endpoint_companion = {
.bLength = sizeof(struct usb_ss_endpoint_companion_descriptor),
.bDescriptorType = USB_DT_SS_ENDPOINT_COMPANION,
.bMaxBurst = 0x0F,
.bmAttributes = 0x00,
.wBytesPerInterval = 0x00,
};
interface->initialized = 1;
//The buffer for PostBufferAsync commands must be 0x1000-byte aligned.
interface->endpoint_in_buffer = memalign(0x1000, 0x1000);
if (interface->endpoint_in_buffer==NULL) rc = MAKERESULT(Module_Libnx, LibnxError_OutOfMemory);
if (R_SUCCEEDED(rc)) {
interface->endpoint_out_buffer = memalign(0x1000, 0x1000);
if (interface->endpoint_out_buffer==NULL) rc = MAKERESULT(Module_Libnx, LibnxError_OutOfMemory);
}
if (R_SUCCEEDED(rc)) {
memset(interface->endpoint_in_buffer, 0, 0x1000);
memset(interface->endpoint_out_buffer, 0, 0x1000);
}
if (R_FAILED(rc)) return rc;
rc = usbDsRegisterInterface(&interface->interface, interface_descriptor.bInterfaceNumber);
if (R_FAILED(rc)) return rc;
// Full Speed Config
rc = usbDsInterface_AppendConfigurationData(interface->interface, UsbDeviceSpeed_Full, &interface_descriptor, USB_DT_INTERFACE_SIZE);
if (R_FAILED(rc)) return rc;
rc = usbDsInterface_AppendConfigurationData(interface->interface, UsbDeviceSpeed_Full, &endpoint_descriptor_in, USB_DT_ENDPOINT_SIZE);
if (R_FAILED(rc)) return rc;
rc = usbDsInterface_AppendConfigurationData(interface->interface, UsbDeviceSpeed_Full, &endpoint_descriptor_out, USB_DT_ENDPOINT_SIZE);
if (R_FAILED(rc)) return rc;
// High Speed Config
endpoint_descriptor_in.wMaxPacketSize = 0x200;
endpoint_descriptor_out.wMaxPacketSize = 0x200;
rc = usbDsInterface_AppendConfigurationData(interface->interface, UsbDeviceSpeed_High, &interface_descriptor, USB_DT_INTERFACE_SIZE);
if (R_FAILED(rc)) return rc;
rc = usbDsInterface_AppendConfigurationData(interface->interface, UsbDeviceSpeed_High, &endpoint_descriptor_in, USB_DT_ENDPOINT_SIZE);
if (R_FAILED(rc)) return rc;
rc = usbDsInterface_AppendConfigurationData(interface->interface, UsbDeviceSpeed_High, &endpoint_descriptor_out, USB_DT_ENDPOINT_SIZE);
if (R_FAILED(rc)) return rc;
// Super Speed Config
endpoint_descriptor_in.wMaxPacketSize = 0x400;
endpoint_descriptor_out.wMaxPacketSize = 0x400;
rc = usbDsInterface_AppendConfigurationData(interface->interface, UsbDeviceSpeed_Super, &interface_descriptor, USB_DT_INTERFACE_SIZE);
if (R_FAILED(rc)) return rc;
rc = usbDsInterface_AppendConfigurationData(interface->interface, UsbDeviceSpeed_Super, &endpoint_descriptor_in, USB_DT_ENDPOINT_SIZE);
if (R_FAILED(rc)) return rc;
rc = usbDsInterface_AppendConfigurationData(interface->interface, UsbDeviceSpeed_Super, &endpoint_companion, USB_DT_SS_ENDPOINT_COMPANION_SIZE);
if (R_FAILED(rc)) return rc;
rc = usbDsInterface_AppendConfigurationData(interface->interface, UsbDeviceSpeed_Super, &endpoint_descriptor_out, USB_DT_ENDPOINT_SIZE);
if (R_FAILED(rc)) return rc;
rc = usbDsInterface_AppendConfigurationData(interface->interface, UsbDeviceSpeed_Super, &endpoint_companion, USB_DT_SS_ENDPOINT_COMPANION_SIZE);
if (R_FAILED(rc)) return rc;
//Setup endpoints.
rc = usbDsInterface_RegisterEndpoint(interface->interface, &interface->endpoint_in, endpoint_descriptor_in.bEndpointAddress);
if (R_FAILED(rc)) return rc;
rc = usbDsInterface_RegisterEndpoint(interface->interface, &interface->endpoint_out, endpoint_descriptor_out.bEndpointAddress);
if (R_FAILED(rc)) return rc;
rc = usbDsInterface_EnableInterface(interface->interface);
if (R_FAILED(rc)) return rc;
return rc;
}
static Result _usbCommsInterfaceInit1x(u32 intf_ind)
{
Result rc = 0;
u32 ep_num = intf_ind + 1;
usbCommsInterface *interface = &g_usbCommsInterfaces[intf_ind];
struct usb_interface_descriptor interface_descriptor = {
.bLength = USB_DT_INTERFACE_SIZE,
.bDescriptorType = USB_DT_INTERFACE,
.bInterfaceNumber = intf_ind,
.bInterfaceClass = 0xFF,
.bInterfaceSubClass = 0xFF,
.bInterfaceProtocol = 0xFF,
};
struct usb_endpoint_descriptor endpoint_descriptor_in = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = USB_ENDPOINT_IN + ep_num,
.bmAttributes = USB_TRANSFER_TYPE_BULK,
.wMaxPacketSize = 0x200,
};
@ -182,7 +334,7 @@ static Result _usbCommsInterfaceInit(usbCommsInterface *interface, u8 bInterface
struct usb_endpoint_descriptor endpoint_descriptor_out = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = USB_ENDPOINT_OUT,
.bEndpointAddress = USB_ENDPOINT_OUT + ep_num,
.bmAttributes = USB_TRANSFER_TYPE_BULK,
.wMaxPacketSize = 0x200,
};
@ -235,7 +387,7 @@ static Result _usbCommsRead(usbCommsInterface *interface, void* buffer, size_t s
UsbDsReportData reportdata;
//Makes sure endpoints are ready for data-transfer / wait for init if needed.
rc = usbDsWaitReady();
rc = usbDsWaitReady(U64_MAX);
if (R_FAILED(rc)) return rc;
while(size)
@ -264,8 +416,8 @@ static Result _usbCommsRead(usbCommsInterface *interface, void* buffer, size_t s
if (R_FAILED(rc)) return rc;
//Wait for the transfer to finish.
svcWaitSynchronizationSingle(interface->endpoint_out->CompletionEvent, U64_MAX);
svcClearEvent(interface->endpoint_out->CompletionEvent);
eventWait(&interface->endpoint_out->CompletionEvent, U64_MAX);
eventClear(&interface->endpoint_out->CompletionEvent);
rc = usbDsEndpoint_GetReportData(interface->endpoint_out, &reportdata);
if (R_FAILED(rc)) return rc;
@ -300,7 +452,7 @@ static Result _usbCommsWrite(usbCommsInterface *interface, const void* buffer, s
UsbDsReportData reportdata;
//Makes sure endpoints are ready for data-transfer / wait for init if needed.
rc = usbDsWaitReady();
rc = usbDsWaitReady(U64_MAX);
if (R_FAILED(rc)) return rc;
while(size)
@ -327,8 +479,8 @@ static Result _usbCommsWrite(usbCommsInterface *interface, const void* buffer, s
if(R_FAILED(rc))return rc;
//Wait for the transfer to finish.
svcWaitSynchronizationSingle(interface->endpoint_in->CompletionEvent, U64_MAX);
svcClearEvent(interface->endpoint_in->CompletionEvent);
eventWait(&interface->endpoint_in->CompletionEvent, U64_MAX);
eventClear(&interface->endpoint_in->CompletionEvent);
rc = usbDsEndpoint_GetReportData(interface->endpoint_in, &reportdata);
if (R_FAILED(rc)) return rc;

View File

@ -6,26 +6,26 @@
#include "kernel/detect.h"
#include "services/usb.h"
#include "services/sm.h"
#include "runtime/util/utf.h"
#define TOTAL_INTERFACES 4
#define TOTAL_ENDPOINTS 15*2
#define TOTAL_ENDPOINTS_IN 16
#define TOTAL_ENDPOINTS_OUT 16
#define TOTAL_ENDPOINTS (TOTAL_ENDPOINTS_IN+TOTAL_ENDPOINTS_OUT)
static Service g_usbDsSrv;
static Handle g_usbDsStateChangeEvent = INVALID_HANDLE;
static Event g_usbDsStateChangeEvent = {0};
static UsbDsInterface g_usbDsInterfaceTable[TOTAL_INTERFACES];
static UsbDsEndpoint g_usbDsEndpointTable[TOTAL_INTERFACES*TOTAL_ENDPOINTS];
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(Service* srv, Handle* handle_out, u64 cmd_id);
static Result _usbDsSetVidPidBcd(const UsbDsDeviceInfo* deviceinfo);
static Result _usbDsGetEvent(Service* srv, Event* event_out, u64 cmd_id);
static Result _usbDsGetSession(Service* srv, Service* srv_out, u64 cmd_id, const void* buf0, size_t buf0size, const void* buf1, size_t buf1size);
Result usbDsInitialize(UsbComplexId complexId, const UsbDsDeviceInfo* deviceinfo)
Result usbDsInitialize(void)
{
if (serviceIsActive(&g_usbDsSrv))
return MAKERESULT(Module_Libnx, LibnxError_AlreadyInitialized);
@ -35,24 +35,21 @@ Result usbDsInitialize(UsbComplexId complexId, const UsbDsDeviceInfo* deviceinfo
rc = smGetService(&g_usbDsSrv, "usb:ds");
if (R_SUCCEEDED(rc))
rc = _usbDsBindDevice(complexId);
rc = _usbDsBindDevice(UsbComplexId_Default);
if (R_SUCCEEDED(rc))
rc = _usbDsBindClientProcess(CUR_PROCESS_HANDLE);
// GetStateChangeEvent
if (R_SUCCEEDED(rc))
rc = _usbDsGetEvent(&g_usbDsSrv, &g_usbDsStateChangeEvent, 3);
if (R_SUCCEEDED(rc) && deviceinfo && kernelAbove200()) {
rc = _usbDsSetVidPidBcd(deviceinfo);
}
// Result code doesn't matter here, users can call themselves later, too. This prevents foot shooting.
if (R_SUCCEEDED(rc))
usbDsClearDeviceData();
if (R_FAILED(rc))
{
if(g_usbDsStateChangeEvent) {
svcCloseHandle(g_usbDsStateChangeEvent);
g_usbDsStateChangeEvent = INVALID_HANDLE;
}
eventClose(&g_usbDsStateChangeEvent);
serviceClose(&g_usbDsSrv);
}
@ -64,24 +61,31 @@ void usbDsExit(void)
{
if (!serviceIsActive(&g_usbDsSrv))
return;
if (kernelAbove500()) {
usbDsDisable();
}
_usbDsFreeTables();
if (g_usbDsStateChangeEvent) {
svcCloseHandle(g_usbDsStateChangeEvent);
g_usbDsStateChangeEvent = 0;
}
eventClose(&g_usbDsStateChangeEvent);
serviceClose(&g_usbDsSrv);
}
Service* usbDsGetServiceSession(void) {
return &g_usbDsSrv;
Event* usbDsGetStateChangeEvent(void)
{
return &g_usbDsStateChangeEvent;
}
Handle usbDsGetStateChangeEvent(void)
{
return g_usbDsStateChangeEvent;
static UsbDsInterface* _usbDsTryAllocateInterface(u32 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 UsbDsInterface* _usbDsAllocateInterface(void)
@ -111,7 +115,7 @@ static UsbDsEndpoint* _usbDsAllocateEndpoint(UsbDsInterface* interface)
for(pos=0; pos<TOTAL_ENDPOINTS; pos++)
{
ptr = &g_usbDsEndpointTable[(interface->interface_index*TOTAL_ENDPOINTS) + pos];
ptr = &g_usbDsEndpointTable[interface->interface_index][pos];
if(ptr->initialized)continue;
memset(ptr, 0, sizeof(UsbDsEndpoint));
ptr->initialized = true;
@ -121,53 +125,47 @@ static UsbDsEndpoint* _usbDsAllocateEndpoint(UsbDsInterface* interface)
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;
}
serviceClose(&interface->h);
interface->initialized = false;
}
static void _usbDsFreeEndpoint(UsbDsEndpoint* endpoint)
{
if (!endpoint->initialized)
return;
/* Cancel any ongoing transactions. */
usbDsEndpoint_Cancel(endpoint);
if (endpoint->CompletionEvent) {
svcCloseHandle(endpoint->CompletionEvent);
endpoint->CompletionEvent = 0;
}
eventClose(&endpoint->CompletionEvent);
serviceClose(&endpoint->h);
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);
serviceClose(&interface->h);
interface->initialized = false;
}
static void _usbDsFreeTables(void)
{
u32 pos, pos2;
for(pos=0; pos<TOTAL_INTERFACES; pos++)
{
for(pos2=0; pos2<TOTAL_ENDPOINTS; pos2++)_usbDsFreeEndpoint(&g_usbDsEndpointTable[(pos*TOTAL_ENDPOINTS) + pos2]);
_usbDsFreeInterface(&g_usbDsInterfaceTable[pos]);
for (u32 intf = 0; intf < TOTAL_INTERFACES; intf++) {
_usbDsFreeInterface(&g_usbDsInterfaceTable[intf]);
}
}
@ -237,7 +235,7 @@ static Result _usbDsBindClientProcess(Handle prochandle) {
return rc;
}
static Result _usbDsGetEvent(Service* srv, Handle* handle_out, u64 cmd_id) {
static Result _usbDsGetEvent(Service* srv, Event* event_out, u64 cmd_id) {
IpcCommand c;
ipcInitialize(&c);
@ -265,7 +263,7 @@ static Result _usbDsGetEvent(Service* srv, Handle* handle_out, u64 cmd_id) {
rc = resp->result;
if (R_SUCCEEDED(rc)) {
*handle_out = r.Handles[0];
eventLoadRemote(event_out, r.Handles[0], false);
}
}
@ -305,7 +303,7 @@ Result usbDsGetState(u32 *out) {
return rc;
}
Result usbDsWaitReady(void) {
Result usbDsWaitReady(u64 timeout) {
Result rc;
u32 state = 0;
@ -314,8 +312,8 @@ Result usbDsWaitReady(void) {
while (R_SUCCEEDED(rc) && state != 5)
{
svcWaitSynchronizationSingle(g_usbDsStateChangeEvent, U64_MAX);
svcClearEvent(g_usbDsStateChangeEvent);
eventWait(&g_usbDsStateChangeEvent, timeout);
eventClear(&g_usbDsStateChangeEvent);
rc = usbDsGetState(&state);
}
@ -362,7 +360,7 @@ Result usbDsParseReportData(UsbDsReportData *reportdata, u32 urbId, u32 *request
return rc;
}
static Result _usbDsSetVidPidBcd(const UsbDsDeviceInfo* deviceinfo) {
Result usbDsSetVidPidBcd(const UsbDsDeviceInfo* deviceinfo) {
IpcCommand c;
ipcInitialize(&c);
@ -569,6 +567,248 @@ Result usbDsGetDsInterface(UsbDsInterface** interface, struct usb_interface_desc
return rc;
}
Result usbDsRegisterInterface(UsbDsInterface** interface, u32 intf_num)
{
UsbDsInterface* ptr = _usbDsTryAllocateInterface(intf_num);
if(ptr == NULL)
return MAKERESULT(Module_Libnx, LibnxError_OutOfMemory);
IpcCommand c;
ipcInitialize(&c);
struct {
u64 magic;
u64 cmd_id;
u32 intf_num;
} *raw;
raw = ipcPrepareHeader(&c, sizeof(*raw));
raw->magic = SFCI_MAGIC;
raw->cmd_id = 2;
raw->intf_num = intf_num;
Result rc = serviceIpcDispatch(&g_usbDsSrv);
if (R_SUCCEEDED(rc)) {
IpcParsedCommand r;
ipcParse(&r);
struct {
u64 magic;
u64 result;
} *resp = r.Raw;
rc = resp->result;
if (R_SUCCEEDED(rc)) {
serviceCreate(&ptr->h, r.Handles[0]);
}
}
// GetSetupEvent
if (R_SUCCEEDED(rc))
rc = _usbDsGetEvent(&ptr->h, &ptr->SetupEvent, 1);
// GetCtrlInCompletionEvent
if (R_SUCCEEDED(rc))
rc = _usbDsGetEvent(&ptr->h, &ptr->CtrlInCompletionEvent, 7);
// GetCtrlOutCompletionEvent
if (R_SUCCEEDED(rc))
rc = _usbDsGetEvent(&ptr->h, &ptr->CtrlOutCompletionEvent, 9);
if (R_FAILED(rc))
_usbDsFreeInterface(ptr);
if (R_SUCCEEDED(rc))
*interface = ptr;
return rc;
}
Result usbDsClearDeviceData(void) {
return _usbDsCmdNoParams(&g_usbDsSrv, 5);
}
static Result _usbDsAddUsbStringDescriptorRaw(u8 *out_index, struct usb_string_descriptor *descriptor) {
IpcCommand c;
ipcInitialize(&c);
ipcAddSendBuffer(&c, descriptor, sizeof(*descriptor), 0);
struct {
u64 magic;
u64 cmd_id;
} *raw;
raw = ipcPrepareHeader(&c, sizeof(*raw));
raw->magic = SFCI_MAGIC;
raw->cmd_id = 6;
Result rc = serviceIpcDispatch(&g_usbDsSrv);
if (R_SUCCEEDED(rc)) {
IpcParsedCommand r;
ipcParse(&r);
struct {
u64 magic;
u64 result;
u8 index;
} *resp = r.Raw;
rc = resp->result;
if (R_SUCCEEDED(rc) && out_index) {
*out_index = resp->index;
}
}
return rc;
}
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) {
IpcCommand c;
ipcInitialize(&c);
struct {
u64 magic;
u64 cmd_id;
u32 index;
} *raw;
raw = ipcPrepareHeader(&c, sizeof(*raw));
raw->magic = SFCI_MAGIC;
raw->cmd_id = 7;
raw->index = index;
Result rc = serviceIpcDispatch(&g_usbDsSrv);
if (R_SUCCEEDED(rc)) {
IpcParsedCommand r;
ipcParse(&r);
struct {
u64 magic;
u64 result;
} *resp = r.Raw;
rc = resp->result;
}
return rc;
}
Result usbDsSetUsbDeviceDescriptor(UsbDeviceSpeed speed, struct usb_device_descriptor* descriptor) {
IpcCommand c;
ipcInitialize(&c);
ipcAddSendBuffer(&c, descriptor, USB_DT_DEVICE_SIZE, 0);
struct {
u64 magic;
u64 cmd_id;
u32 speed;
} *raw;
raw = ipcPrepareHeader(&c, sizeof(*raw));
raw->magic = SFCI_MAGIC;
raw->cmd_id = 8;
raw->speed = speed;
Result rc = serviceIpcDispatch(&g_usbDsSrv);
if (R_SUCCEEDED(rc)) {
IpcParsedCommand r;
ipcParse(&r);
struct {
u64 magic;
u64 result;
} *resp = r.Raw;
rc = resp->result;
}
return rc;
}
Result usbDsSetBinaryObjectStore(void* bos, size_t bos_size) {
IpcCommand c;
ipcInitialize(&c);
ipcAddSendBuffer(&c, bos, bos_size, 0);
struct {
u64 magic;
u64 cmd_id;
} *raw;
raw = ipcPrepareHeader(&c, sizeof(*raw));
raw->magic = SFCI_MAGIC;
raw->cmd_id = 9;
Result rc = serviceIpcDispatch(&g_usbDsSrv);
if (R_SUCCEEDED(rc)) {
IpcParsedCommand r;
ipcParse(&r);
struct {
u64 magic;
u64 result;
} *resp = r.Raw;
rc = resp->result;
}
return rc;
}
Result usbDsEnable(void) {
return _usbDsCmdNoParams(&g_usbDsSrv, 10);
}
Result usbDsDisable(void) {
return _usbDsCmdNoParams(&g_usbDsSrv, 11);
}
//IDsInterface
void usbDsInterface_Close(UsbDsInterface* interface)
@ -576,6 +816,41 @@ 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);
IpcCommand c;
ipcInitialize(&c);
ipcAddRecvBuffer(&c, buffer, size, 0);
struct {
u64 magic;
u64 cmd_id;
} *raw;
raw = ipcPrepareHeader(&c, sizeof(*raw));
raw->magic = SFCI_MAGIC;
raw->cmd_id = 2;
Result rc = serviceIpcDispatch(&interface->h);
if (R_SUCCEEDED(rc)) {
IpcParsedCommand r;
ipcParse(&r);
struct {
u64 magic;
u64 result;
} *resp = r.Raw;
rc = resp->result;
}
return rc;
}
Result usbDsInterface_GetDsEndpoint(UsbDsInterface* interface, UsbDsEndpoint** endpoint, struct usb_endpoint_descriptor* descriptor)
{
if(!interface->initialized)return MAKERESULT(Module_Libnx, LibnxError_NotInitialized);
@ -583,7 +858,7 @@ Result usbDsInterface_GetDsEndpoint(UsbDsInterface* interface, UsbDsEndpoint** e
UsbDsEndpoint* ptr = _usbDsAllocateEndpoint(interface);
if(ptr==NULL)return MAKERESULT(Module_Libnx, LibnxError_OutOfMemory);
Result rc = _usbDsGetSession(&interface->h, &ptr->h, 0, descriptor, sizeof(struct usb_endpoint_descriptor), NULL, 0);
Result rc = _usbDsGetSession(&interface->h, &ptr->h, 0, descriptor, USB_DT_ENDPOINT_SIZE, NULL, 0);
if (R_SUCCEEDED(rc)) rc = _usbDsGetEvent(&ptr->h, &ptr->CompletionEvent, 2);//GetCompletionEvent
@ -642,6 +917,93 @@ Result usbDsInterface_StallCtrl(UsbDsInterface* interface)
return _usbDsCmdNoParams(&interface->h, 11);
}
Result usbDsInterface_RegisterEndpoint(UsbDsInterface* interface, UsbDsEndpoint** endpoint, u8 endpoint_address)
{
if(!interface->initialized)return MAKERESULT(Module_Libnx, LibnxError_NotInitialized);
UsbDsEndpoint* ptr = _usbDsAllocateEndpoint(interface);
if(ptr==NULL)return MAKERESULT(Module_Libnx, LibnxError_OutOfMemory);
IpcCommand c;
ipcInitialize(&c);
struct {
u64 magic;
u64 cmd_id;
u32 ep_addr;
} *raw;
raw = ipcPrepareHeader(&c, sizeof(*raw));
raw->magic = SFCI_MAGIC;
raw->cmd_id = 0;
raw->ep_addr = endpoint_address;
Result rc = serviceIpcDispatch(&interface->h);
if (R_SUCCEEDED(rc)) {
IpcParsedCommand r;
ipcParse(&r);
struct {
u64 magic;
u64 result;
} *resp = r.Raw;
rc = resp->result;
if (R_SUCCEEDED(rc)) {
serviceCreate(&ptr->h, r.Handles[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_AppendConfigurationData(UsbDsInterface* interface, UsbDeviceSpeed speed, void* buffer, size_t size) {
if(!interface->initialized)return MAKERESULT(Module_Libnx, LibnxError_NotInitialized);
IpcCommand c;
ipcInitialize(&c);
ipcAddSendBuffer(&c, buffer, size, 0);
struct {
u64 magic;
u64 cmd_id;
u32 intf_num;
u32 speed;
} *raw;
raw = ipcPrepareHeader(&c, sizeof(*raw));
raw->magic = SFCI_MAGIC;
raw->cmd_id = 12;
raw->intf_num = interface->interface_index;
raw->speed = speed;
Result rc = serviceIpcDispatch(&interface->h);
if (R_SUCCEEDED(rc)) {
IpcParsedCommand r;
ipcParse(&r);
struct {
u64 magic;
u64 result;
} *resp = r.Raw;
rc = resp->result;
}
return rc;
}
//IDsEndpoint
void usbDsEndpoint_Close(UsbDsEndpoint* endpoint)
@ -656,6 +1018,13 @@ Result usbDsEndpoint_PostBufferAsync(UsbDsEndpoint* endpoint, void* buffer, size
return _usbDsPostBuffer(&endpoint->h, 0, buffer, size, urbId);
}
Result usbDsEndpoint_Cancel(UsbDsEndpoint* endpoint)
{
if(!endpoint->initialized)return MAKERESULT(Module_Libnx, LibnxError_NotInitialized);
return _usbDsCmdNoParams(&endpoint->h, 1);
}
Result usbDsEndpoint_GetReportData(UsbDsEndpoint* endpoint, UsbDsReportData *out)
{
if(!endpoint->initialized)return MAKERESULT(Module_Libnx, LibnxError_NotInitialized);
@ -670,3 +1039,39 @@ Result usbDsEndpoint_StallCtrl(UsbDsEndpoint* endpoint)
return _usbDsCmdNoParams(&endpoint->h, 4);
}
Result usbDsEndpoint_SetZlt(UsbDsEndpoint* endpoint, bool zlt)
{
if(!endpoint->initialized)return MAKERESULT(Module_Libnx, LibnxError_NotInitialized);
IpcCommand c;
ipcInitialize(&c);
struct {
u64 magic;
u64 cmd_id;
u32 zlt;
} *raw;
raw = ipcPrepareHeader(&c, sizeof(*raw));
raw->magic = SFCI_MAGIC;
raw->cmd_id = 12;
raw->zlt = zlt ? 1 : 0;
Result rc = serviceIpcDispatch(&endpoint->h);
if (R_SUCCEEDED(rc)) {
IpcParsedCommand r;
ipcParse(&r);
struct {
u64 magic;
u64 result;
} *resp = r.Raw;
rc = resp->result;
}
return rc;
}