Difference between revisions of "USB services"

From Nintendo Switch Brew
Jump to navigation Jump to search
 
(65 intermediate revisions by 4 users not shown)
Line 1: Line 1:
One symbol in USB-sysmodule is: "vtable for nn::usb::detail::UsbComplexTegra21x".
+
USB provides services for interacting with the USB stack.
  
= usb:ds =
+
= Configuration =
This is "nn::usb::ds::IDsService".
 
 
 
Used for Switch<>PC USB comms, aka Switch-as-device. This seems to only be usable in handheld-mode with the Switch directly connected to a host via an USB cable, not(?) in docked-mode.
 
This service is used during [[Factory Setup|factory setup]] by [[Manu Services|manu]].
 
 
 
This service session is used as an IPC [[IPC_Marshalling|domain]] by [[Manu Services|manu]]. All of these {get-session} commands also return an output u8 and the u32 [[IPC_Marshalling|domainID]], for using those sessions as domains.
 
 
 
This service can be used by multiple processes at the same time, with separate interfaces. However, if one process does usbds shutdown, usbds will reset to defaults even if there's a process still using it.
 
 
 
{| class="wikitable" border="1"
 
|-
 
! Cmd || Name
 
|-
 
| 0 || [[#BindDevice]]
 
|-
 
| 1 || [[#BindClientProcess]]
 
|-
 
| 2 || [[#GetDsInterface]]
 
|-
 
| 3 || [[#GetStateChangeEvent]]
 
|-
 
| 4 || [[#GetState]]
 
|-
 
| [2.0.0-4.1.0] 5 || [[#SetVidPidBcd]]
 
|-
 
| [5.0.0+] 5 || ClearDeviceData
 
|-
 
| 6 || [5.0.0+] AddUsbStringDescriptor
 
|-
 
| 7 || [5.0.0+] DeleteUsbStringDescriptor
 
|-
 
| 8 || [5.0.0+] SetUsbDeviceDescriptor
 
|-
 
| 9 || [5.0.0+] SetBinaryObjectStore
 
|-
 
| 10 || [5.0.0+] Enable
 
|-
 
| 11 || [5.0.0+] Disable
 
|}
 
 
 
Initialization done by [[Manu Services|manu]]:
 
* Initial service init:
 
** Get service/etc.
 
** Uses [[#BindDevice]].
 
** Uses [[#BindClientProcess]].
 
** Uses [[#GetStateChangeEvent]].
 
** Uses [[#SetVidPidBcd]].
 
* Interface init:
 
** Uses [[#GetDsInterface]], then uses commands from that with the rest of the following.
 
** Uses [[#GetSetupEvent]].
 
** Uses [[#GetCtrlInCompletionEvent]].
 
** Uses [[#GetCtrlOutCompletionEvent]].
 
* Initializes two endpoints via using [[#GetDsEndpoint]] twice.
 
 
 
== Configuration ==
 
 
The following is the default USB config strings, while the usbds service isn't being used. All of the below configuration will reset to the defaults when all usbds-related sessions are closed. These can be set with [[#SetVidPidBcd]]. The default string for Product is loaded from [[Settings_services|settings]] config. The default is referred to by usb-sysmodule as "Dummy".
 
The following is the default USB config strings, while the usbds service isn't being used. All of the below configuration will reset to the defaults when all usbds-related sessions are closed. These can be set with [[#SetVidPidBcd]]. The default string for Product is loaded from [[Settings_services|settings]] config. The default is referred to by usb-sysmodule as "Dummy".
 
   Product: Nintendo Switch
 
   Product: Nintendo Switch
Line 64: Line 9:
 
The following is the default <code>lsusb -v {...}</code> output when the usbds service wasn't used.
 
The following is the default <code>lsusb -v {...}</code> output when the usbds service wasn't used.
  
pre-5.0.0: The endpoints are configured using [[#GetDsEndpoint]], the total number of endpoints is the number of open [[#IDsEndpoint]] sessions. bInterfaceNumber is {0-based index for the enabled [[#IDsInterface]] session.} Some of the interface fields are configured using [[#GetDsInterface]].
+
pre-5.0.0: The endpoints are configured using [[#RegisterEndpoint]], the total number of endpoints is the number of open [[#IDsEndpoint]] sessions. bInterfaceNumber is {0-based index for the enabled [[#IDsInterface]] session.} Some of the interface fields are configured using [[#RegisterInterface]].
  
 
When usbds is in use where [[#SetVidPidBcd]] wasn't used, the VID/PID is 057e:3000.
 
When usbds is in use where [[#SetVidPidBcd]] wasn't used, the VID/PID is 057e:3000.
Line 126: Line 71:
 
           bInterval              16
 
           bInterval              16
  
== BindDevice ==
+
= usb:ds =
Takes an u32 ('''complexId'''). [[Manu_Services|Manu]] sends 0x02.
+
[1.0.0-10.2.0] This is "nn::usb::ds::IDsService".
  
Binding more than once with the current session is not allowed. Once this command is used, the USB device will not be listed with <code>lsusb</code> until [[#EnableInterface]] is used.
+
[11.0.0+] This is "nn::usb::ds::IDsRootSession".
  
Returns a not-found error when complexId isn't 0x02, for values 0x0-0x4 at least.
+
This service is used during [[Factory Setup|factory setup]] by [[Manu Services|manu]]. This is also used by [[Capmtp_services|capmtp]].
  
== BindClientProcess ==
+
This service session is used as an IPC [[IPC_Marshalling|domain]] by [[Manu Services|manu]].
Takes 1 copy-handle for the current process (0xFFFF8001).
 
  
== GetDsInterface ==
+
This service can be used by multiple processes at the same time, with separate interfaces. However, if one process does usbds shutdown, usbds will reset to defaults even if there's a process still using it.
Takes 2 type-5 buffers and returns an output u8 and an [[#IDsInterface]]. [[Manu_Services|Manu]] sends a 0x09-byte descriptor (e.g.: 0x09, 0x04, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x00) in the first buffer and a string ("usb") in the second buffer.
 
  
When the strlen output for the second buffer is >=0x40, size 0x40 is used instead for copying the string. This is the interface name, it's not sent over USB.
+
{| class="wikitable" border="1"
 +
|-
 +
! Cmd || Name
 +
|-
 +
| 0 || [[#CreateDsService]]
 +
|}
  
Returns an error when [[#BindDevice]] wasn't used.
+
== CreateDsService ==
 +
No input. Returns an [[#IDsService]].
  
Up to 4 interfaces can be used+[[#EnableInterface|enabled]].
+
== IDsService ==
 +
This is "nn::usb::ds::IDsService".
  
Structure of the first buffer(this is the same as [http://libusb.sourceforge.net/api-1.0/structlibusb__interface__descriptor.html libusb__interface__descriptor]):
 
 
{| class="wikitable" border="1"
 
{| class="wikitable" border="1"
 
|-
 
|-
! Offset || Size || Description
+
! Cmd || Name
 +
|-
 +
| 0 || [[#Initialize]]
 +
|-
 +
| 1 || [11.0.0+] [[#RegisterInterface]] ([1.0.0-10.2.0] [[#BindClientProcess]]))
 +
|-
 +
| 2 || [11.0.0+] [[#GetStateChangeEvent]] ([1.0.0-10.2.0] [[#RegisterInterface]])
 +
|-
 +
| 3 || [11.0.0+] [[#GetState]] ([1.0.0-10.2.0] [[#GetStateChangeEvent]])
 
|-
 
|-
| 0x0 || 0x1 || bLength. Must match 0x9.
+
| 4 || [11.0.0+] ClearDeviceData ([1.0.0-10.2.0] [[#GetState]])
 
|-
 
|-
| 0x1 || 0x1 || bDescriptorType. Must match 0x4.
+
| 5 || [11.0.0+] AddUsbStringDescriptor ([5.0.0-10.2.0] ClearDeviceData, [2.0.0-4.1.0] [[#SetVidPidBcd]])
 
|-
 
|-
| 0x2 || 0x1 || bInterfaceNumber. When 0x4, the bInterfaceNumber is automatically allocated(error will be thrown if no space). Otherwise, it's used directly and must be <=3.
+
| 6 || [11.0.0+] DeleteUsbStringDescriptor ([5.0.0-10.2.0] AddUsbStringDescriptor)
 
|-
 
|-
| 0x3 || 0x1 || bAlternateSetting. Must match 0x0.
+
| 7 || [11.0.0+] SetUsbDeviceDescriptor ([5.0.0-10.2.0] DeleteUsbStringDescriptor)
 
|-
 
|-
| 0x4 || 0x1 || bNumEndpoints. Ignored.
+
| 8 || [11.0.0+] SetBinaryObjectStore ([5.0.0-10.2.0] SetUsbDeviceDescriptor)
 
|-
 
|-
| 0x5 || 0x1 || bInterfaceClass
+
| 9 || [11.0.0+] EnableDevice ([5.0.0-10.2.0] SetBinaryObjectStore)
 
|-
 
|-
| 0x6 || 0x1 || bInterfaceSubClass
+
| 10 || [11.0.0+] DisableDevice ([5.0.0-10.2.0] EnableDevice)
 
|-
 
|-
| 0x7 || 0x1 || bInterfaceProtocol
+
| 11 || [11.0.0+] [[#GetSpeed]] ([5.0.0-10.2.0] DisableDevice)
 
|-
 
|-
| 0x8 || 0x1 || iInterface. Ignored.
+
| 12 || [8.0.0-10.2.0] [[#GetSpeed]]
 
|}
 
|}
  
Only the first 0x9-bytes are used.
+
== Initialize ==
 +
Takes an input u32 '''ComplexId'''. No output.
 +
 
 +
[11.0.0+] Now takes an additional input Process handle.
 +
 
 +
[[Manu_Services|Manu]] uses '''ComplexId''' 0x02.
 +
 
 +
Binding more than once with the current session is not allowed. Once this command is used, the USB device will not be listed with <code>lsusb</code> until [[#EnableInterface]] is used.
 +
 
 +
Returns a not-found error when '''ComplexId''' isn't 0x02, for values 0x0-0x4 at least.
 +
 
 +
== BindClientProcess ==
 +
Takes an input Process handle. No output.
 +
 
 +
== RegisterInterface ==
 +
Takes two type-0x5 input buffers containing an [[#UsbInterfaceDescriptor]] and an [[#UsbStringDescriptor]], respectively. Returns an output u8 '''InterfaceNumber''' and an [[#IDsInterface]].
 +
 
 +
[5.0.0+] This now only takes an input u8 and returns an [[#IDsInterface]].
 +
 
 +
[[Manu_Services|Manu]] sends a 0x09-byte descriptor (e.g.: 0x09, 0x04, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x00) in the first buffer and a string ("usb") in the second buffer.
 +
 
 +
When the strlen output for the second buffer is >=0x40, size 0x40 is used instead for copying the string. This is the interface name, it's not sent over USB.
 +
 
 +
Returns an error when [[#Initialize]] wasn't used.
 +
 
 +
Up to 4 interfaces can be used and [[#EnableInterface|enabled]].
  
 
== GetStateChangeEvent ==
 
== GetStateChangeEvent ==
Returns an event handle for when the state returned by [[#GetState]] changes. Signalled when Switch<->host USB comms change between started/stopped. USB cable connected/disconnected while at least 1 interface was enabled, or interface enabled/disabled while the USB cable was connected which then caused USB-comms state to change.
+
No input. Returns an output Event handle.
 +
 
 +
Signalled when Switch<->host USB comms change between started/stopped. USB cable connected/disconnected while at least 1 interface was enabled, or interface enabled/disabled while the USB cable was connected which then caused USB-comms state to change.
  
 
== GetState ==
 
== GetState ==
No input. Returns an output u32. Returns an error when [[#BindDevice]] wasn't used.
+
No input. Returns an output [[#UsbState]].
  
Returns the current state. Values:
+
Returns an error when [[#Initialize]] wasn't used.
* 0: Initial state.
 
* 6: Device init starting.
 
* 3: {Initialization}, previous state is 6.
 
* 4: {Initialization}, previous state is 3.
 
* 5: Initialization done, data-transfer is now available.
 
  
 
== SetVidPidBcd ==
 
== SetVidPidBcd ==
Takes a type-5 buffer with 0x66 bytes of USB descriptor data (see [[Manu_Services#manu|manu]]).
+
Takes a type-0x5 input buffer containing an [[#UsbVidPidBcd]]. No output.
  
{| class="wikitable" border="1"
+
== GetSpeed ==
|-
+
No input. Returns an output [[#UsbDeviceSpeed]].
! Offset || Size || Description
 
|-
 
| 0x0 || 0x2 || VID (idVendor)
 
|-
 
| 0x2 || 0x2 || PID (idProduct)
 
|-
 
| 0x4 || 0x2 || bcdDevice
 
|-
 
| 0x6 || 0x20 || Manufacturer
 
|-
 
| 0x26 || 0x20 || Product
 
|-
 
| 0x46 || 0x20 || SerialNumber
 
|}
 
 
 
The last 3 blocks are ASCII strings. The data following each string is all-zero, for padding to size 0x20.
 
  
 
== IDsInterface ==
 
== IDsInterface ==
Line 211: Line 174:
 
{| class="wikitable" border="1"
 
{| class="wikitable" border="1"
 
|-
 
|-
! Cmd || Name || Notes
+
! Cmd || Name
 
|-
 
|-
| 0 || [[#GetDsEndpoint]] ||
+
| 0 || [[#RegisterEndpoint]]
 
|-
 
|-
| 1 || [[#GetSetupEvent]] ||
+
| 1 || [[#GetSetupEvent]]
 
|-
 
|-
| 2 || || Takes a type-0x6 output buffer, no other output. Memcpys data to outbuf with outsize, uses size 0x8 if outsize is too large.
+
| 2 || [[#GetSetupPacket]]
 
|-
 
|-
| 3 || [[#EnableInterface]] ||
+
| 3 || [11.0.0+] [[#CtrlInAsync]] ([1.0.0-10.2.0] [[#Enable]])
 
|-
 
|-
| 4 || [[#DisableInterface]] ||
+
| 4 || [11.0.0+] [[#CtrlOutAsync]] ([1.0.0-10.2.0] [[#Disable]])
 
|-
 
|-
| 5 || [[#CtrlInPostBufferAsync]] ||
+
| 5 || [11.0.0+] [[#GetCtrlInCompletionEvent]] ([1.0.0-10.2.0] [[#CtrlInAsync]])
 
|-
 
|-
| 6 || [[#CtrlOutPostBufferAsync]] ||
+
| 6 || [11.0.0+] [[#GetCtrlInUrbReport]] ([1.0.0-10.2.0] [[#CtrlOutAsync]])
 
|-
 
|-
| 7 || [[#GetCtrlInCompletionEvent]] ||
+
| 7 || [11.0.0+] [[#GetCtrlOutCompletionEvent]] ([1.0.0-10.2.0] [[#GetCtrlInCompletionEvent]])
 
|-
 
|-
| 8 || [[#GetCtrlInReportData]] ||
+
| 8 || [11.0.0+] [[#GetCtrlOutUrbReport]] ([1.0.0-10.2.0] [[#GetCtrlInUrbReport]])
 
|-
 
|-
| 9 || [[#GetCtrlOutCompletionEvent]] ||
+
| 9 || [11.0.0+] [[#CtrlStall]] ([1.0.0-10.2.0] [[#GetCtrlOutCompletionEvent]])
 
|-
 
|-
| 10 || [[#GetCtrlOutReportData]] ||
+
| 10 || [11.0.0+] [[#AppendConfigurationData]] ([1.0.0-10.2.0] [[#GetCtrlOutUrbReport]])
 
|-
 
|-
| 11 || [[#StallCtrl]] ||
+
| 11 || [13.0.0+] [[#SetGuid]] ([1.0.0-10.2.0] [[#CtrlStall]])
 
|-
 
|-
| 12 || [5.0.0+] AppendConfigurationData ||
+
| 12 || [15.0.0+] [[#RegisterTransferMemory]] ([5.0.0-10.2.0] [[#AppendConfigurationData]])
 
|}
 
|}
  
Commands cmd2, [[#CtrlInPostBufferAsync]], [[#CtrlOutPostBufferAsync]], and [[#StallCtrl]], will throw an error if the interface is not [[#EnableInterface|enabled]]. [[#GetDsEndpoint]] will throw an error if the interface is [[#EnableInterface|enabled]].
+
=== RegisterEndpoint ===
 +
Takes a type-0x5 input buffer containing an [[#UsbEndpointDescriptor]]. Returns an output u8 '''EndpointNumber''' and an [[#IDsEndpoint]].
  
=== GetDsEndpoint ===
+
[5.0.0+] This now only takes an input u8 and returns an [[#IDsEndpoint]].
Takes a type-5 buffer and returns an output u8 and an [[#IDsEndpoint]]. [[Manu_Services|Manu]] uses this twice for getting two endpoint sessions, with the following 0x7-byte buffer data:
+
 
 +
[[Manu_Services|Manu]] uses this twice for getting two endpoint sessions, with the following 0x7-byte buffer data:
 
* First endpoint: 0x07, 0x05, 0x80, 0x02, 0x00, 0x02, 0x00
 
* First endpoint: 0x07, 0x05, 0x80, 0x02, 0x00, 0x02, 0x00
 
** bLength=0x7
 
** bLength=0x7
Line 252: Line 217:
 
** bInterval=0
 
** bInterval=0
 
* Second endpoint: Same as above except byte2 is 0x00(bEndpointAddress=LIBUSB_ENDPOINT_OUT).
 
* Second endpoint: Same as above except byte2 is 0x00(bEndpointAddress=LIBUSB_ENDPOINT_OUT).
 
Each field is an u8, except for offset 0x4-0x5 which is an u16.
 
 
This structure matches [http://libusb.sourceforge.net/api-1.0/structlibusb__endpoint__descriptor.html libusb_endpoint_descriptor], with audio-only-devices fields bRefresh and bSynchAddress removed.
 
  
 
The buffer size must be >=0x7. Only the first 0x7-bytes from the buffer are used.
 
The buffer size must be >=0x7. Only the first 0x7-bytes from the buffer are used.
Line 263: Line 224:
  
 
From the Tegra datasheet: "The maximum packet size supported on any endpoint is 1024 bytes in high-speed mode, for both device and host modes."
 
From the Tegra datasheet: "The maximum packet size supported on any endpoint is 1024 bytes in high-speed mode, for both device and host modes."
 +
 +
Throws an error if the interface is [[#EnableInterface|enabled]].
  
 
=== GetSetupEvent ===
 
=== GetSetupEvent ===
Returns an event handle. Unknown what triggers signalling, not signalled during interface-enable / device<>host USB-comms init.
+
No input. Returns an output Event handle.
 +
 
 +
Unknown what triggers signalling, not signalled during interface-enable / device<>host USB-comms init.
 +
 
 +
=== GetSetupPacket ===
 +
Takes a type-0x6 output buffer. No output.
 +
 
 +
Memcpys data to outbuf with outsize, uses size 0x8 if outsize is too large.
 +
 
 +
Throws an error if the interface is not [[#EnableInterface|enabled]].
 +
 
 +
=== Enable ===
 +
No input/output.
  
=== EnableInterface ===
+
Enables the current interface.
Takes no arguments. Enables the current interface.
 
  
Only one interface can be enabled at a time per bInterfaceNumber. When bInterfaceNumber is auto-allocate(0x4) for [[#GetDsEndpoint]] this isn't an issue since the final bInterfaceNumber will be unique.
+
Only one interface can be enabled at a time per bInterfaceNumber. When bInterfaceNumber is auto-allocate(0x4) for [[#RegisterEndpoint]] this isn't an issue since the final bInterfaceNumber will be unique.
  
 
Once enabled, the device/interface can then actually be used over USB.
 
Once enabled, the device/interface can then actually be used over USB.
  
=== DisableInterface ===
+
=== Disable ===
Takes no arguments. Disables the current interface.
+
No input/output.  
  
=== CtrlInPostBufferAsync ===
+
Disables the current interface.
Same as [[#PostBufferAsync]](with same input/output), except this uses control input endpoint 0x80.
 
  
=== CtrlOutPostBufferAsync ===
+
=== CtrlInAsync ===
Same as [[#PostBufferAsync]](with same input/output), except this uses control output endpoint 0x00.
+
Same as [[#PostBufferAsync]], except this uses control input endpoint 0x80.
 +
 
 +
Throws an error if the interface is not [[#EnableInterface|enabled]].
 +
 
 +
=== CtrlOutAsync ===
 +
Same as [[#PostBufferAsync]], except this uses control output endpoint 0x00.
 +
 
 +
Throws an error if the interface is not [[#EnableInterface|enabled]].
  
 
=== GetCtrlInCompletionEvent ===
 
=== GetCtrlInCompletionEvent ===
Returns an event handle for polling the completion of input control commands. Same as [[#GetCompletionEvent]], except this uses control input endpoint 0x80.
+
Same as [[#GetCompletionEvent]], except this uses control input endpoint 0x80.
  
=== GetCtrlInReportData ===
+
=== GetCtrlInUrbReport ===
Same as [[#GetReportData]](with same input/output), except this uses control input endpoint 0x80.
+
Same as [[#GetUrbReport]], except this uses control input endpoint 0x80.
  
 
=== GetCtrlOutCompletionEvent ===
 
=== GetCtrlOutCompletionEvent ===
Returns an event handle for polling the completion of output control commands. Same as [[#GetCompletionEvent]], except this uses control output endpoint 0x00.
+
Same as [[#GetCompletionEvent]], except this uses control output endpoint 0x00.
  
=== GetCtrlOutReportData ===
+
=== GetCtrlOutUrbReport ===
Same as [[#GetReportData]](with same input/output), except this uses control output endpoint 0x00.
+
Same as [[#GetUrbReport]], except this uses control output endpoint 0x00.
  
=== StallCtrl ===
+
=== CtrlStall ===
 
No input/output.
 
No input/output.
  
Calls a function with both control endpoints(0x80 and 0x00) with the same function. From strings: "m_pProtocol->Stall(0x80)" "m_pProtocol->Stall(0x00)".
+
Calls a function with both control endpoints (0x80 and 0x00) with the same function. From strings: "m_pProtocol->Stall(0x80)" "m_pProtocol->Stall(0x00)".
 +
 
 +
Throws an error if the interface is not [[#EnableInterface|enabled]].
 +
 
 +
=== AppendConfigurationData ===
 +
Takes an input u32 '''InterfaceNumber''', an input [[#UsbDeviceSpeed]] and a type-0x5 input buffer. No output.
 +
 
 +
[5.0.0+] This now only takes an input [[#UsbDeviceSpeed]] and a type-0x5 input buffer.
 +
 
 +
=== SetGuid ===
 +
Takes a type-0x5 input buffer. No output.
 +
 
 +
=== RegisterTransferMemory ===
 +
Takes an input u64 and an input handle. No output.
 +
 
 +
Stubbed, just returns an error.
  
 
=== IDsEndpoint ===
 
=== IDsEndpoint ===
Line 305: Line 300:
 
{| class="wikitable" border="1"
 
{| class="wikitable" border="1"
 
|-
 
|-
! Cmd || Name || Notes
+
! Cmd || Name
 +
|-
 +
| 0 || [[#PostBufferAsync]]
 +
|-
 +
| 1 || [[#Cancel]]
 +
|-
 +
| 2 || [[#GetCompletionEvent]]
 +
|-
 +
| 3 || [[#GetUrbReport]]
 
|-
 
|-
| 0 || [[#PostBufferAsync]] ||
+
| 4 || [[#Stall]]
 
|-
 
|-
| 1 || || No input/output.
+
| 5 || [[#SetZlt]]
 
|-
 
|-
| 2 || [[#GetCompletionEvent]] ||
+
| 6 || [7.0.0+] [[#IsStalled]]
 
|-
 
|-
| 3 || [[#GetReportData]] ||
+
| 7 || [7.0.0+] [[#GetStallClearedEvent]]
 
|-
 
|-
| 4 || [[#Stall]] ||
+
| 8 || [11.0.0-14.1.2]
 
|-
 
|-
| 5 || || Takes an input u8, no output.
+
| 9 || [11.0.0-14.1.2]
 
|}
 
|}
  
 
==== PostBufferAsync ====
 
==== PostBufferAsync ====
Takes an u32 ('''size''') and an u64 ('''buffer'''). Returns an output u32 ('''urbId'''). The output urbId can then be used while parsing the output of [[#GetReportData]], after waiting for the CompletionEvent to be signalled.
+
Takes an input u32 '''Size''' and an input u64 '''Buffer'''. Returns an output u32 '''UrbId'''.
 +
 
 +
The output urbId can then be used while parsing the output of [[#GetUrbReport]], after waiting for the CompletionEvent to be signalled.
  
 
The buffer address must be 0x1000-byte aligned. The input size doesn't matter. It helps to use svcSetMemoryAttribute to turn off caching on the buffer.
 
The buffer address must be 0x1000-byte aligned. The input size doesn't matter. It helps to use svcSetMemoryAttribute to turn off caching on the buffer.
Line 332: Line 337:
  
 
For receiving data, if size is less than {actual received USB packet size} the rest of the packet will be discarded. Later PostBufferAsync cmd(s) will only return data from new packets, not the remainder of the earlier packet(s).
 
For receiving data, if size is less than {actual received USB packet size} the rest of the packet will be discarded. Later PostBufferAsync cmd(s) will only return data from new packets, not the remainder of the earlier packet(s).
 +
 +
==== Cancel ====
 +
No input/output.
  
 
==== GetCompletionEvent ====
 
==== GetCompletionEvent ====
No input. Returns an output event handle for polling the completion of [[#PostBufferAsync]], even when it finished via [[#Stall]].
+
No input. Returns an output Event handle.
 
 
==== GetReportData ====
 
No input. Returns 0x84 bytes of report data from the endpoint. Seems to be eventually loaded from state, since this doesn't trigger any USB bus activity. All-zero before PostBufferAsync was used at least once.
 
  
{| class="wikitable" border="1"
+
==== GetUrbReport ====
|-
+
No input. Returns an output [[#UrbReport]].
! Offset || Size || Description
 
|-
 
| 0x0 || 0x10*0x8(0x80) || 0x8 entries 0x10-bytes each for each report.
 
|-
 
| 0x80 || 0x4 || u32 report count
 
|}
 
  
Entry data:
+
Seems to be eventually loaded from state, since this doesn't trigger any USB bus activity. All-zero before PostBufferAsync was used at least once.
{| class="wikitable" border="1"
 
|-
 
! Offset || Size || Description
 
|-
 
| 0x0 || 0x4 || u32 id (urbId from post-buffer commands)
 
|-
 
| 0x4 || 0x4 || u32 requestedSize
 
|-
 
| 0x8 || 0x4 || u32 transferredSize
 
|-
 
| 0xC || 0x4 || u32 urb status, converted to error-codes. 0x3 = success, 0x4 = 0x828c, 0x5 = 0x748c. All other values are invalid.
 
|-
 
|}
 
  
 
==== Stall ====
 
==== Stall ====
 
No input/output.
 
No input/output.
  
Calls the same function used by [[#StallCtrl]], except this uses the endpoint associated with the current session.
+
Calls the same function used by [[#CtrlStall]], except this uses the endpoint associated with the current session.
  
 
Stops in-progress data-transfer done by [[#PostBufferAsync]].
 
Stops in-progress data-transfer done by [[#PostBufferAsync]].
  
= usb:hs =
+
==== SetZlt ====
 +
Takes an input bool. No output.
 +
 
 +
==== IsStalled ====
 +
No input. Returns an output bool.
 +
 
 +
==== GetStallClearedEvent ====
 +
No input. Returns an output Event handle.
 +
 
 +
==== Cmd8 ====
 +
Takes an input u64 and an input handle. No output.
 +
 
 +
Stubbed, just returns an error.
 +
 
 +
==== Cmd9 ====
 +
Takes an input u32 '''Size''' and an input u64 '''Offset'''. Returns an u32 '''UrbId'''.
 +
 
 +
Stubbed, just returns an error.
 +
 
 +
= usb:hs, usb:hs:a =
 
This is "nn::usb::hs::IClientRootSession".
 
This is "nn::usb::hs::IClientRootSession".
 +
 +
[7.0.0+] usb:hs:a opens an nn::usb::hs::IClientRootSession, but sets an "isSystemClient" field in the object (and in interfaces/eps opened by the session) to false.
  
 
{| class="wikitable" border="1"
 
{| class="wikitable" border="1"
 
|-
 
|-
! Cmd || Name || Notes
+
! Cmd || Name
|-
 
| [2.0.0+] 0 || [[#BindClientProcess_2|#BindClientProcess]] ||
 
 
|-
 
|-
| 1 ([1.0.0] 0) || [[#QueryAllInterfaces]] ||
+
| 0 || [2.0.0+] [[#BindClientProcess_2|#BindClientProcess]] ([1.0.0] [[#QueryAllInterfaces]])
 
|-
 
|-
| 2 ([1.0.0] 1) || [[#QueryAvailableInterfaces]] ||
+
| 1 || [[#QueryAllInterfaces]] ([1.0.0] [[#QueryAvailableInterfaces]])
 
|-
 
|-
| 3 ([1.0.0] 2) || [[#QueryAcquiredInterfaces]] ||
+
| 2 || [[#QueryAvailableInterfaces]] ([1.0.0] [[#QueryAcquiredInterfaces]])
 
|-
 
|-
| 4 ([1.0.0] 3) || [[#CreateInterfaceAvailableEvent]] ||
+
| 3 || [[#QueryAcquiredInterfaces]] ([1.0.0] [[#CreateInterfaceAvailableEvent]])
 
|-
 
|-
| 5 ([1.0.0] 4) || [[#DestroyInterfaceAvailableEvent]] ||
+
| 4 || [[#CreateInterfaceAvailableEvent]] ([1.0.0] [[#DestroyInterfaceAvailableEvent]])
 
|-
 
|-
| 6 ([1.0.0] 5) || [[#GetInterfaceStateChangeEvent]] ||
+
| 5 || [[#DestroyInterfaceAvailableEvent]] ([1.0.0] [[#GetInterfaceStateChangeEvent]])
 
|-
 
|-
| 7 ([1.0.0] 6) || [[#AcquireUsbIf]] ||
+
| 6 || [[#GetInterfaceStateChangeEvent]] ([1.0.0] [[#AcquireUsbIf]])
 
|-
 
|-
| 8 ([1.0.0] 7) || [6.0.0+] ([1.0.0] [[#GetDescriptorString]]) ||
+
| 7 || [[#AcquireUsbIf]] ([1.0.0] [[#GetDescriptorString]])
 
|-
 
|-
| [1.0.0] 8 || || Takes an input u32, no output. Stubbed, just returns 0.
+
| 8 || [6.0.0+] SetTestMode ([1.0.0] [[#ResetDevice]])
 
|}
 
|}
  
Line 406: Line 411:
  
 
== QueryAllInterfaces ==
 
== QueryAllInterfaces ==
Takes an [[#UsbHsInterfaceFilter]] and a type-0x6 output buffer, returns an output s32 total_entries.
+
Takes an [[#DeviceFilter]] and a type-0x6 output buffer, returns an output s32 total_entries.
  
The output buffer contains an array of [[#UsbHsInterface]]. This returns the same interfaces as [[#QueryAvailableInterfaces]], followed by the interfaces also returned by [[#QueryAcquiredInterfaces]].
+
The output buffer contains an array of [[#InterfaceQueryOutput]]. This returns the same interfaces as [[#QueryAvailableInterfaces]], followed by the interfaces also returned by [[#QueryAcquiredInterfaces]]. Except for the ID field in [[#InterfaceQueryOutput]], which is set to -1.
  
 
== QueryAvailableInterfaces ==
 
== QueryAvailableInterfaces ==
Takes an [[#UsbHsInterfaceFilter]] and a type-0x6 output buffer, returns an output s32 total_entries.
+
Takes an [[#DeviceFilter]] and a type-0x6 output buffer, returns an output s32 total_entries.
  
The output buffer contains an array of [[#UsbHsInterface]].
+
The output buffer contains an array of [[#InterfaceQueryOutput]]. This only returns interfaces which are not acquired by any process.
  
 
== QueryAcquiredInterfaces ==
 
== QueryAcquiredInterfaces ==
 
Takes a type-0x6 output buffer and returns an output s32 total_entries.
 
Takes a type-0x6 output buffer and returns an output s32 total_entries.
  
The output buffer contains an array of [[#UsbHsInterface]], for each interface which was acquired with [[#AcquireUsbIf]].
+
The output buffer contains an array of [[#InterfaceQueryOutput]], for each interface which was acquired with [[#AcquireUsbIf]].
  
 
== CreateInterfaceAvailableEvent ==
 
== CreateInterfaceAvailableEvent ==
Takes an input u8 and an [[#UsbHsInterfaceFilter]], and returns an output handle. The input value must be 0..2. This is used as an index in a table.
+
Takes an input u8 and an [[#DeviceFilter]], and returns an output handle. The input value must be 0..2. This is used as an index in a table.
  
 
The struct is located at +2 from the u8 in IPC rawdata.
 
The struct is located at +2 from the u8 in IPC rawdata.
Line 435: Line 440:
 
No input, returns an output event handle with autoclear disabled.
 
No input, returns an output event handle with autoclear disabled.
  
When signaled, this indicates that the user-process should use [[#QueryAcquiredInterfaces]] and cleanup state for interfaces which were already initialized with a matching ID in [[#UsbHsInterface]] +0x0. When total_entries is less than 1, this indicates that cleanup should be done for all currently open interfaces.
+
When signaled, this indicates that the user-process should use [[#QueryAcquiredInterfaces]] and cleanup state for interfaces which were already initialized and don't have any matching ID in [[#InterfaceProfile]] +0x0. When total_entries is less than 1, this indicates that cleanup should be done for all currently open interfaces.
  
 
== AcquireUsbIf ==
 
== AcquireUsbIf ==
Takes an input u32 and a type-0x6 output buffer, returns an [[#IClientIfSession]].
+
Takes an input u32 and a type-0x6 output buffer, returns an [[#IClientIfSession]]. On [3.0.0+] this takes an additional type-0x6 output buffer, before the original buffer. On [3.0.0+] the first buffer is 0x70-bytes, while the second one is 0x1B8. The user-process has the second buffer address immediately after the first one, which allows getting a complete [[#InterfaceProfile]].
 +
 
 +
The input u32 is from the u32 at entry+0 from the associated [[#QueryAvailableInterfaces]] output entry. User-processes use size 0x1B8 for the first output buffer. The first output buffer contains an [[#InterfaceProfile]].
  
The input u32 is from the u32 at entry+0 from the associated [[#QueryAvailableInterfaces]] output entry. User-processes use size 0x1B8 for the output buffer. The output buffer contains the first 0x1B8-bytes from [[#UsbHsInterface]].
+
This returns an error if the interface was already acquired by another process.
  
 
== GetDescriptorString ==
 
== GetDescriptorString ==
Line 452: Line 459:
  
 
Official sw doesn't use this, [[#SubmitControlInRequest]] is used instead.
 
Official sw doesn't use this, [[#SubmitControlInRequest]] is used instead.
 +
 +
== ResetDevice ==
 +
Takes an input u32, no output. Stubbed, just returns 0.
  
 
== IClientIfSession ==
 
== IClientIfSession ==
Line 458: Line 468:
 
{| class="wikitable" border="1"
 
{| class="wikitable" border="1"
 
|-
 
|-
! Cmd || Name || Notes
+
! Cmd || Name  
 
|-
 
|-
| 0 || || No input, returns an output handle.
+
| 0 || [[#GetStateChangeEvent]]
 
|-
 
|-
| 1 || [[#SetInterface]] ||
+
| 1 || [[#SetInterface]]
 
|-
 
|-
| 2 || [[#GetInterface]] ||
+
| 2 || [[#GetInterface]]
 
|-
 
|-
| 3 || [[#GetAlternateInterface]] ||
+
| 3 || [[#GetAlternateInterface]]
 
|-
 
|-
| 4 [1.0.0] 5) || [[#GetCurrentFrame]] ||
+
| 4 || [[#GetCurrentFrame]] ([1.0.0] [[#CtrlXferAsync]])
 
|-
 
|-
| [2.0.0+] 5 || [[#CtrlXferAsync]] ||
+
| 5 || [2.0.0+] [[#CtrlXferAsync]]
 
|-
 
|-
| 6 || ([1.0.0] [[#SubmitControlInRequest]]) || No input, returns an output handle. Signaled when [[#CtrlXferAsync]] finishes.
+
| 6 || [[#GetCtrlXferCompletionEvent]] ([1.0.0] [[#SubmitControlInRequest]])
 
|-
 
|-
| 7 || [[#GetCtrlXferReport]] ([1.0.0] [[#SubmitControlOutRequest]]) ||
+
| 7 || [[#GetCtrlXferReport]] ([1.0.0] [[#SubmitControlOutRequest]])
 
|-
 
|-
| 8 || [[#ResetDevice]] ||
+
| 8 || [[#ResetDevice]]
 
|-
 
|-
| 9 ([1.0.0] 4)  || [[#OpenUsbEp]] ||
+
| 9 || [[#OpenUsbEp]] ([1.0.0] [[#GetCurrentFrame]])
 
|}
 
|}
  
Line 484: Line 494:
  
 
Immediately after opening the session, official sw uses cmd0 and cmd6.
 
Immediately after opening the session, official sw uses cmd0 and cmd6.
 +
 +
=== GetStateChangeEvent ===
 +
No input, returns an output handle.
  
 
=== SetInterface ===
 
=== SetInterface ===
Takes an input u8 and a type-0x6 output buffer, no output. The output buffer contains the first 0x1B8-bytes from [[#UsbHsInterface]].
+
Takes an input u8 and a type-0x6 output buffer, no output. The output buffer contains an [[#InterfaceProfile]].
  
 
=== GetInterface ===
 
=== GetInterface ===
Takes a type-0x6 output buffer, no output. The output buffer contains the first 0x1B8-bytes from [[#UsbHsInterface]].
+
Takes a type-0x6 output buffer, no output. The output buffer contains an [[#InterfaceProfile]].
  
 
=== GetAlternateInterface ===
 
=== GetAlternateInterface ===
Takes an input u8 and a type-0x6 output buffer, no output. The output buffer contains the first 0x1B8-bytes from [[#UsbHsInterface]]. The buffer size must match 0x1B8.
+
Takes an input u8 and a type-0x6 output buffer, no output. The output buffer contains an [[#InterfaceProfile]]. The buffer size must match 0x1B8.
  
 
=== GetCurrentFrame ===
 
=== GetCurrentFrame ===
Line 501: Line 514:
 
=== CtrlXferAsync ===
 
=== CtrlXferAsync ===
 
Takes 2 input u8s ('''bmRequestType''' and '''bRequest'''), 3 input u16s ('''wValue''', '''wIndex''', and '''wLength'''), and an input u64 '''buffer''', no output.
 
Takes 2 input u8s ('''bmRequestType''' and '''bRequest'''), 3 input u16s ('''wValue''', '''wIndex''', and '''wLength'''), and an input u64 '''buffer''', no output.
 +
 +
=== GetCtrlXferCompletionEvent ===
 +
No input, returns an output handle. Signaled when [[#CtrlXferAsync]] finishes.
  
 
=== GetCtrlXferReport ===
 
=== GetCtrlXferReport ===
Line 517: Line 533:
 
=== ResetDevice ===
 
=== ResetDevice ===
 
No input/output.
 
No input/output.
 +
 +
Resets the device: has the same affect as unplugging the device and plugging it back in.
  
 
=== OpenUsbEp ===
 
=== OpenUsbEp ===
Line 536: Line 554:
 
{| class="wikitable" border="1"
 
{| class="wikitable" border="1"
 
|-
 
|-
! Cmd || Name || Notes
+
! Cmd || Name
 
|-
 
|-
| 0 || [[#Open]] ([1.0.0] [[#SubmitOutRequest]]) ||
+
| 0 || [[#ReOpen]] ([1.0.0] [[#SubmitOutRequest]])
 
|-
 
|-
| 1 || [[#Close]] ([1.0.0] [[#SubmitInRequest]]) ||
+
| 1 || [[#Close]] ([1.0.0] [[#SubmitInRequest]])
 
|-
 
|-
| 2 || || No input, returns an output handle. Signaled when [[#PostBufferAsync_2|#PostBufferAsync]] finishes. ([1.0.0] [[#Open]])
+
| 2 || [[#GetCompletionEvent_2|GetCompletionEvent]] ([1.0.0] Reset)
 
|-
 
|-
| 3 || [[#Populate]] ([1.0.0] [[#Close]]) ||
+
| 3 || [[#PopulateRing]] ([1.0.0] [[#Close]])
 
|-
 
|-
| [2.0.0+] 4 || [[#PostBufferAsync_2|#PostBufferAsync]] ||
+
| 4 || [2.0.0+] [[#PostBufferAsync_2|#PostBufferAsync]]
 
|-
 
|-
| [2.0.0+] 5 || [[#GetXferReport]] ||
+
| 5 || [2.0.0+] [[#GetXferReport]]
 
|-
 
|-
| [2.0.0+] 6 || || Takes 3 input u32s, 2 input u64s, and a type-0x5 input buffer, returns an output u32.
+
| 6 || [2.0.0+] [[#BatchBufferAsync]]
 
|-
 
|-
| [4.0.0+] 7 || ||
+
| 7 || [4.0.0+] [[#CreateSmmuSpace]]
 
|-
 
|-
| [4.0.0+] 8 || ||
+
| 8 || [4.0.0+] [[#ShareReportRing]]
 
|}
 
|}
  
Line 571: Line 589:
 
Official user-processes uses a buffer where the buffer address/size is page-aligned, where '''size''' is the original size before alignment. The user-process also flushes dcache for this buffer using '''size''', before/after using this command. Official sw passes value 0 for '''unk'''.
 
Official user-processes uses a buffer where the buffer address/size is page-aligned, where '''size''' is the original size before alignment. The user-process also flushes dcache for this buffer using '''size''', before/after using this command. Official sw passes value 0 for '''unk'''.
  
==== Open ====
+
==== ReOpen ====
 
No input/output.
 
No input/output.
  
Line 581: Line 599:
 
No input/output.
 
No input/output.
  
Used by official sw prior to closing the endpoint session.
+
Used by official sw prior to closing the endpoint session. The func for this is also called automatically by the sysmodule during the IClientEpSession dtor.
  
==== Populate ====
+
==== GetCompletionEvent ====
 +
No input, returns an output handle. Signaled when [[#PostBufferAsync_2|#PostBufferAsync]] finishes. ([1.0.0] [[#Open]])
 +
 
 +
==== PopulateRing ====
 
No input/output.
 
No input/output.
  
Line 589: Line 610:
  
 
==== PostBufferAsync ====
 
==== PostBufferAsync ====
Takes an input u32 '''size''', an input u64 '''buffer''', and an input u64, returns an output u32 '''xferId'''.
+
Takes an input u32 '''size''', an input u64 '''buffer''', and an input u64 '''Id''', returns an output u32 '''xferId'''.
 +
 
 +
Starts a data transfer with a single urb.
 +
 
 +
The official sw func which uses this cmd and handles waiting on the Event passes value 0 for the '''Id'''. The async version which exposes this cmd directly uses '''Id''' from user input - the user funcs pass a value starting at 0 then increment it each time (for sending multiple requests at once, with 0 for the first request in this set of requests). Note that '''Id''' can be arbitrary, the sysmodule doesn't require a certain range for this.
  
HID-sysmodule passes value 0 for the last u64.
+
On newer sysvers the state fields setup by [[#CreateSmmuSpace]] are compared with '''buffer'''. If it's within bounds of the SmmuSpace (which won't be the case when [[#CreateSmmuSpace]] wasn't used), it will then verify that buffer+buffer_size is within bounds throwing an error otherwise. Note that a buffer from [[#CreateSmmuSpace]] can't be reused by another endpoint with PostBufferAsync.
  
 
==== GetXferReport ====
 
==== GetXferReport ====
Takes an input u32 and a type-0x6 output buffer, returns an output u32 '''count'''.
+
Takes an input u32 and a type-0x6 ([3.0.0+] type-0x22) output buffer, returns an output u32 '''count'''.
  
 
The input u32 specifies the total number of entries to read, this must fit within the specified buffer size. The output u32 is the total actual output entries.
 
The input u32 specifies the total number of entries to read, this must fit within the specified buffer size. The output u32 is the total actual output entries.
Line 600: Line 625:
 
The buffer contains an array of [[#XferReport]].
 
The buffer contains an array of [[#XferReport]].
  
= UsbHsInterface =
+
On newer sysvers this runs code which is the same as the user-process code handling the tmem from [[#ShareReportRing]]. This cmd can handle getting reports from that tmem as well, however normally user-processes would just access the tmem [[#ShareReportRing|directly]].
 +
 
 +
==== BatchBufferAsync ====
 +
Takes 3 input u32s ('''urbCount''', '''unk1''', and '''unk2'''), an input u64 '''buffer''' and u64 '''Id''', and a type-0x5 ([3.0.0+] type-0x21) input buffer, returns an output u32 '''xferId'''.
 +
 
 +
Where '''Id''' is the same as [[#PostBufferAsync_2|#PostBufferAsync]].
 +
 
 +
This uses the same func internally as [[#PostBufferAsync_2|#PostBufferAsync]] except multiple urbs are specified by the user: the input buffer contains an array of u32s for the size of each urb, where '''urbCount''' is the total number of entries in this array. With [[#PostBufferAsync_2|#PostBufferAsync]] the last 2 params passed to the internal func are hard-coded to 0, while with this command it's '''unk1''' and '''unk2'''.
 +
 
 +
==== CreateSmmuSpace ====
 +
Takes an input u32 '''size''' and an u64 '''buffer''', no output.
 +
 
 +
This validates that both input params are page-aligned. Official user-processes also validate this.
 +
 
 +
This maps the specified buffer as devicemem and stores the fields for this in object state. If this was already used with this IClientEpSession object, an error is thrown.
 +
 
 +
==== ShareReportRing ====
 +
Takes an input u32 '''size''' and an input TransferMemory handle, no output.
 +
 
 +
Official user-processes create the TransferMemory with permissions=RW. This validates that the input size is page-aligned, and this is also validated against data from state. Official user-processes also do this same validation.
 +
 
 +
When this was used, official user-processes read data from this TransferMemory buffer instead of using [[#GetXferReport]].
 +
 
 +
This maps a TransferMemory buffer which is used for the [[#XferReport]] Ring, replacing the heap Ring buffer which was setup during [[#PopulateRing]].
 +
 
 +
Structure of the tmem:
 +
 
 +
{| class="wikitable" border="1"
 +
|-
 +
! Offset || Size || Description
 +
|-
 +
| 0x0 || 0x8 || Write index
 +
|-
 +
| 0x8 || 0x8 || Read index
 +
|-
 +
| 0x10 || 0x18*count || Array of [[#XferReport]]
 +
|}
 +
 
 +
write_index!=read_index indicates that report data is available. The index fields are updated with: <code>index = index+1 % count;</code> write_index is updated by sysmodule, read_index is updated by the user-process. If the index field being used is >=count, this will Abort.
 +
 
 +
= usb:pd =
 +
This is "nn::usb::pd::detail::IPdManager".
 +
 
 +
{| class="wikitable" border="1"
 +
|-
 +
! Cmd || Name
 +
|-
 +
| 0 || [[#OpenSession]]
 +
|}
 +
 
 +
Only system-titles with access to this are [[PTM_services|ptm]] and [[AM_services|am]].
 +
 
 +
== OpenSession ==
 +
No input. Returns an [[#IPdSession]].
 +
 
 +
== IPdSession ==
 +
This is "nn::usb::pd::detail::IPdSession".
 +
 
 +
{| class="wikitable" border="1"
 +
|-
 +
! Cmd || Name
 +
|-
 +
| 0 || BindNoticeEvent
 +
|-
 +
| 1 || UnbindNoticeEvent
 +
|-
 +
| 2 || GetStatus
 +
|-
 +
| 3 || GetNotice
 +
|-
 +
| 4 || EnablePowerRequestNotice
 +
|-
 +
| 5 || DisablePowerRequestNotice
 +
|-
 +
| 6 || ReplyPowerRequest
 +
|}
 +
 
 +
= usb:pd:c =
 +
This is "nn::usb::pd::detail::IPdCradleManager".
 +
 
 +
{| class="wikitable" border="1"
 +
|-
 +
! Cmd || Name
 +
|-
 +
| 0 || [[#OpenCradleSession]]
 +
|}
 +
 
 +
USB-sysmodule symbols for this refer to "Cradle", which is the [[Dock]].
 +
 
 +
== OpenCradleSession ==
 +
No input. Returns an [[#IPdCradleSession]].
 +
 
 +
== IPdCradleSession ==
 +
This is "nn::usb::pd::detail::IPdCradleSession".
 +
 
 +
{| class="wikitable" border="1"
 +
|-
 +
! Cmd || Name
 +
|-
 +
| 0 || [[#SetCradleVdo]]
 +
|-
 +
| 1 || [[#GetCradleVdo]]
 +
|-
 +
| 2 || [[#ResetCradleUsbHub]]
 +
|-
 +
| 3 || [2.0.0+] [[#GetHostPdcFirmwareType]]
 +
|-
 +
| 4 || [2.0.0+] [[#GetHostPdcFirmwareRevision]]
 +
|-
 +
| 5 || [2.0.0+] [[#GetHostPdcManufactureId]]
 +
|-
 +
| 6 || [2.0.0+] [[#GetHostPdcDeviceId]]
 +
|-
 +
| 7 || [3.0.0+] [[#EnableCradleRecovery]]
 +
|-
 +
| 8 || [3.0.0+] [[#DisableCradleRecovery]]
 +
|}
 +
 
 +
=== SetCradleVdo ===
 +
Takes an input u32 '''Value''' and a [[#CradleVdmCommand]]. No output.
 +
 
 +
[[#CradleVdmCommand]] is translated to the actual [[#VdmCommand]] to send.
 +
 
 +
=== GetCradleVdo ===
 +
Takes an input [[#CradleVdmCommand]]. Returns an u32 '''Value'''.
 +
 
 +
[[#CradleVdmCommand]] is translated to the actual [[#VdmCommand]] to send.
 +
 
 +
=== ResetCradleUsbHub ===
 +
No input/output.
 +
 
 +
Sends [[#VdmCommand|VdmCommands]] 32 and 30.
 +
 
 +
=== GetHostPdcFirmwareType ===
 +
No input. Returns an output u16.
 +
 
 +
=== GetHostPdcFirmwareRevision ===
 +
No input. Returns an output u16.
 +
 
 +
=== GetHostPdcManufactureId ===
 +
No input. Returns an output u16.
 +
 
 +
=== GetHostPdcDeviceId ===
 +
No input. Returns an output u16.
 +
 
 +
=== EnableCradleRecovery ===
 +
No input. Returns an output u8.
 +
 
 +
=== DisableCradleRecovery ===
 +
No input. Returns an output u8.
 +
 
 +
= usb:pd:m =
 +
This is "nn::usb::pd::detail::IPdManufactureManager".
 +
 
 +
[2.0.0+] This service was merged into [[#usb:pd:c|usb:pd:c]].
 +
 
 +
{| class="wikitable" border="1"
 +
|-
 +
! Cmd || Name
 +
|-
 +
| 0 || [[#OpenManufactureSession]]
 +
|}
 +
 
 +
== OpenManufactureSession ==
 +
No input. Returns an [[#IPdManufactureSession]].
 +
 
 +
== IPdManufactureSession ==
 +
This is "nn::usb::pd::detail::IPdManufactureSession".
 +
 
 +
{| class="wikitable" border="1"
 +
|-
 +
! Cmd || Name
 +
|-
 +
| 0 || [[#GetHostPdcFirmwareType]]
 +
|-
 +
| 1 || [[#GetHostPdcFirmwareRevision]]
 +
|-
 +
| 2 || [[#GetHostPdcManufactureId]]
 +
|-
 +
| 3 || [[#GetHostPdcDeviceId]]
 +
|}
 +
 
 +
= usb:pm =
 +
This is "nn::usb::pm::IPmService".
 +
 
 +
[8.0.0+] This is "nn::usb::pm::IPmMainService".
 +
 
 +
{| class="wikitable" border="1"
 +
|-
 +
! Cmd || Name
 +
|-
 +
| 0 || GetPowerEvent
 +
|-
 +
| 1 || GetPowerState
 +
|-
 +
| 2 || GetDataEvent
 +
|-
 +
| 3 || GetDataRole
 +
|-
 +
| 4 || SetDiagData
 +
|-
 +
| 5 || GetDiagData
 +
|}
 +
 
 +
USB Port Manager, only system-title using this is [[PTM_services|ptm]].
 +
 
 +
= usb:qdb =
 +
This is "nn::usb::qdb::IQdbManager".
 +
 
 +
This was added with [7.0.0+].
 +
 
 +
{| class="wikitable" border="1"
 +
|-
 +
! Cmd || Name
 +
|-
 +
| 0 || [[#ImportQuirkDevices]]
 +
|-
 +
| 1 || [[#HasQuirk]]
 +
|}
 +
 
 +
== ImportQuirkDevices ==
 +
No input/output, takes a type-0x5 input buffer.
 +
 
 +
This loads data for [[#HidGamepad]] with the input .json.
 +
 
 +
== HasQuirk ==
 +
Takes 6-bytes of input (u16s '''vid''', '''pid''', '''bcdDevice''') and a type-0x5 input buffer, returns an output u8 bool indicating success.
 +
 
 +
Locates an entry in the [[#HidGamepad]] state with the input u16s, and checks for a '''quirks''' array entry where '''name''' matches the input buffer string. Returns 1 when found, 0 otherwise.
 +
 
 +
= usb:obsv =
 +
This is "nn::usb::pm::IPmObserverService".
 +
 
 +
This was added with [8.0.0+].
 +
 
 +
{| class="wikitable" border="1"
 +
|-
 +
! Cmd || Name
 +
|-
 +
| 0 || GetTopologyChangeEvent
 +
|-
 +
| 1 || GetFlattenedTopology
 +
|}
 +
 
 +
= UsbInterfaceDescriptor =
 +
This is "nn::usb::UsbInterfaceDescriptor".
 +
 
 +
{| class="wikitable" border="1"
 +
|-
 +
! Offset || Size || Description
 +
|-
 +
| 0x0 || 0x1 || bLength
 +
|-
 +
| 0x1 || 0x1 || bDescriptorType
 +
|-
 +
| 0x2 || 0x1 || bInterfaceNumber
 +
|-
 +
| 0x3 || 0x1 || bAlternateSetting
 +
|-
 +
| 0x4 || 0x1 || bNumEndpoints
 +
|-
 +
| 0x5 || 0x1 || bInterfaceClass
 +
|-
 +
| 0x6 || 0x1 || bInterfaceSubClass
 +
|-
 +
| 0x7 || 0x1 || bInterfaceProtocol
 +
|-
 +
| 0x8 || 0x1 || iInterface
 +
|}
 +
 
 +
= UsbStringDescriptor =
 +
This is "nn::usb::UsbStringDescriptor". This is a string.
 +
 
 +
= UsbEndpointDescriptor =
 +
This is "nn::usb::UsbEndpointDescriptor".
 +
 
 +
{| class="wikitable" border="1"
 +
|-
 +
! Offset || Size || Description
 +
|-
 +
| 0x0 || 0x1 || bLength
 +
|-
 +
| 0x1 || 0x1 || bDescriptorType
 +
|-
 +
| 0x2 || 0x1 || bEndpointAddress
 +
|-
 +
| 0x3 || 0x1 || bmAttributes
 +
|-
 +
| 0x4 || 0x2 || wMaxPacketSize
 +
|-
 +
| 0x6 || 0x1 || bInterval
 +
|-
 +
| 0x7 || 0x1 || bRefresh
 +
|-
 +
| 0x8 || 0x1 || bSynchAddress
 +
|}
 +
 
 +
= UsbState =
 +
{| class="wikitable" border="1"
 +
|-
 +
! Value || Name || Description
 +
|-
 +
| 0 || Detached || Device is not attached to USB.
 +
|-
 +
| 1 || Attached || Device is attached, but is not powered.
 +
|-
 +
| 2 || Powered || Device is attached and powered, but has not been reset.
 +
|-
 +
| 3 || Default || Device is attached, powered, and has been reset, but does not have an address.
 +
|-
 +
| 4 || Address || Device is attached, powered, has been reset, has an address, but is not configured.
 +
|-
 +
| 5 || Configured || Device is attached, powered, has been reset, has an address, configured, and may be used.
 +
|-
 +
| 6 || Suspended || Device is attached and powered, but has not seen bus activity for 3ms. Device is suspended and cannot be used.
 +
|}
 +
 
 +
= UsbVidPidBcd =
 +
{| class="wikitable" border="1"
 +
|-
 +
! Offset || Size || Description
 +
|-
 +
| 0x0 || 0x2 || Vid (idVendor)
 +
|-
 +
| 0x2 || 0x2 || Pid (idProduct)
 +
|-
 +
| 0x4 || 0x2 || bcdDevice
 +
|-
 +
| 0x6 || 0x20 || Manufacturer (ASCII string padded to 0x20 bytes)
 +
|-
 +
| 0x26 || 0x20 || Product (ASCII string padded to 0x20 bytes)
 +
|-
 +
| 0x46 || 0x20 || SerialNumber (ASCII string padded to 0x20 bytes)
 +
|}
 +
 
 +
= UrbReport =
 +
{| class="wikitable" border="1"
 +
|-
 +
! Offset || Size || Description
 +
|-
 +
| 0x0 || 0x10 * 8 || Array of [[#UrbReportEntry]]
 +
|-
 +
| 0x80 || 0x4 || ReportEntryCount
 +
|}
 +
 
 +
= UrbReportEntry =
 +
{| class="wikitable" border="1"
 +
|-
 +
! Offset || Size || Description
 +
|-
 +
| 0x0 || 0x4 || Id
 +
|-
 +
| 0x4 || 0x4 || RequestedSize
 +
|-
 +
| 0x8 || 0x4 || TransferredSize
 +
|-
 +
| 0xC || 0x4 || Status (converted to error-codes: 0x3 = success, 0x4 = 0x828c, 0x5 = 0x748c; all other values are invalid)
 +
|}
 +
 
 +
= UsbDeviceSpeed =
 +
{| class="wikitable" border="1"
 +
|-
 +
! Value || Name
 +
|-
 +
| 0 || None
 +
|-
 +
| 1 || Low
 +
|-
 +
| 2 || Full
 +
|-
 +
| 3 || High
 +
|-
 +
| 4 || Super
 +
|}
 +
 
 +
= InterfaceQueryOutput =
 +
This is "nn::usb::InterfaceQueryOutput".
 +
 
 +
{| class="wikitable" border="1"
 +
|-
 +
! Offset || Size || Description
 +
|-
 +
| 0x0 || 0x1B8 || [[#InterfaceProfile|InterfaceProfile]]
 +
|-
 +
| 0x1B8 || 0x40 || "HsDevice-/L<unk0>/P<portnum>/A<unk1>" string (this is "FsDevice..." for the Dock USB 3.0 bus).
 +
|-
 +
| 0x1F8 || 0x4 || busID
 +
|-
 +
| 0x1FC || 0x4 || deviceID
 +
|-
 +
| 0x200 || 0x12+0x9(0x1B) || usb_device_descriptor, then usb_config_descriptor immediately afterwards.
 +
|-
 +
| 0x21B || 0x5 || Padding
 +
|-
 +
| 0x220 || 0x8 || Unknown u64 timestamp for when the device was inserted?
 +
|}
 +
 
 +
The INPUT/OUTPUT endpoint descriptors (usb_endpoint_descriptors/usb_ss_endpoint_companion_descriptors) were swapped with [8.0.0+] (above layout has the pre-8.0.0 layout), however the sysmodule code which writes this output struct was basically unchanged.
 +
 
 +
= InterfaceProfile =
 +
This is "nn::usb::InterfaceProfile".
 +
 
 
{| class="wikitable" border="1"
 
{| class="wikitable" border="1"
 
|-
 
|-
Line 630: Line 1,056:
 
|-
 
|-
 
| 0x1B5 || 0x3 || Padding
 
| 0x1B5 || 0x3 || Padding
|-
 
| 0x1B8 || 0x40 || "HsDevice-/L<unk0>/P<portnum>/A<unk1>" string (this is "FsDevice..." for the Dock USB 3.0 bus).
 
|-
 
| 0x1F8 || 0x4 || busID
 
|-
 
| 0x1FC || 0x4 || deviceID
 
|-
 
| 0x200 || 0x12+0x9(0x1B) || usb_device_descriptor, then usb_config_descriptor immediately afterwards.
 
|-
 
| 0x21B || 0x5 || Padding
 
|-
 
| 0x220 || 0x8 || Unknown u64 timestamp for when the device was inserted?
 
 
|}
 
|}
  
This is a 0x228-byte struct (unofficial name).
+
= DeviceFilter =
 +
This is "nn::usb::DeviceFilter".
  
= UsbHsInterfaceFilter =
 
 
{| class="wikitable" border="1"
 
{| class="wikitable" border="1"
 
|-
 
|-
Line 676: Line 1,090:
 
This is a 0x10-byte struct (unofficial name).
 
This is a 0x10-byte struct (unofficial name).
  
This is used to filter [[#UsbHsInterface]], the query commands will only return the interface when these checks pass. This is also used for events with [[#CreateInterfaceAvailableEvent]]. When a bit in Flags is set, the associated descriptor field and the field in this struct are compared, on mismatch the interface will be filtered out. Passing Flags=0 is equivalent to disabling filtering since none of these checks will run.
+
This is used to filter [[#InterfaceQueryOutput]], the query commands will only return the interface when these checks pass. This is also used for events with [[#CreateInterfaceAvailableEvent]]. When a bit in Flags is set, the associated descriptor field and the field in this struct are compared, on mismatch the interface will be filtered out. Passing Flags=0 is equivalent to disabling filtering since none of these checks will run.
 +
 
 +
[7.0.0+]: The filter struct has to be unique, it can't match any existing filter structs used by [[#CreateInterfaceAvailableEvent]] (including other processes). Hence, Flags has to be non-zero. When initialized with usb:hs:a and VID and/or PID filtering is enabled, the VID/PID will be checked [[#HidGamepad]], with an error being throw if a matching device is found with quirk "ApplicationBlacklist" ([[#usb:qdb|usb:qdb]] cmd1 is used internally for this with bcdDevice=0).
  
 
Flags bits 0..6 use usb_device_descriptor, while 7..9 use usb_interface_descriptor. Support for bits 2..6 and the associated fields was added with [6.0.0+].
 
Flags bits 0..6 use usb_device_descriptor, while 7..9 use usb_interface_descriptor. Support for bits 2..6 and the associated fields was added with [6.0.0+].
Line 699: Line 1,115:
 
! Offset || Size || Description
 
! Offset || Size || Description
 
|-
 
|-
| 0x0 || 0x4 || ?
+
| 0x0 || 0x4 || xferId (Only set for [[#GetXferReport]])
 
|-
 
|-
 
| 0x4 || 0x4 || Result
 
| 0x4 || 0x4 || Result
 
|-
 
|-
| 0x8 || 0x4 || ?
+
| 0x8 || 0x4 || requestedSize
 
|-
 
|-
| 0xC || 0x4 || Actual transferred size.
+
| 0xC || 0x4 || transferredSize
 
|-
 
|-
| 0x10 || 0x8? || ?
+
| 0x10 || 0x8 || [[#PostBufferAsync_2|Id]]
 
|}
 
|}
  
Line 714: Line 1,130:
 
Official sw only uses the Result/size fields.
 
Official sw only uses the Result/size fields.
  
= usb:pd =
+
= CradleVdmCommand =
This is "nn::usb::pd::detail::IPdManager".
+
This is "nn::usb::pd::CradleVdmCommand".
  
 
{| class="wikitable" border="1"
 
{| class="wikitable" border="1"
 
|-
 
|-
! Cmd || Name
+
! Value || Name
 +
|-
 +
| 0 ||
 +
|-
 +
| 1 ||
 +
|-
 +
| 2 ||
 +
|-
 +
| 3 ||
 +
|-
 +
| 4 ||
 +
|-
 +
| 5 ||
 +
|-
 +
| 6 ||
 +
|-
 +
| 7 ||
 +
|-
 +
| 8 ||
 +
|-
 +
| 9 ||
 
|-
 
|-
| 0 || GetPdSession
+
| 10 ||  
 
|}
 
|}
  
Only system-titles with access to this are [[PTM_services|ptm]] and [[AM_services|am]].
+
= VdmCommand =
 
+
This is "nn::usb::pd::driver::detail::VdmCommand".
== IPdSession ==
 
This is "nn::usb::pd::detail::IPdSession".
 
  
 
{| class="wikitable" border="1"
 
{| class="wikitable" border="1"
 
|-
 
|-
! Cmd || Name
+
! Value || [[#CradleVdmCommand|CradleVdmCommand]] || [[#VdmCommandType|VdmCommandType]] || VdmCommandLength || Name
 +
|-
 +
| 0 || - || -1 || 0 || None
 +
|-
 +
| 1 || 0 || 0 || 3 || LedControlRequest
 +
|-
 +
| 2 || - || 1 || 3 || LedControlReply
 +
|-
 +
| 3 || 10 || 0 || 3 || DeviceTypeRequest
 +
|-
 +
| 4 || - || 1 || 3 || DeviceTypeReply
 +
|-
 +
| 5 || - || -1 || 0 || UsbPowerErrorNotice
 +
|-
 +
| 6 || 1 || 0 || 2 || Dp2HdmiFwVerRequest
 
|-
 
|-
| 0 || BindNoticeEvent
+
| 7 || - || 1 || 3 || Dp2HdmiFwVerReply
 
|-
 
|-
| 1 || UnbindNoticeEvent
+
| 8 || - || -1 || 0 || Dp2HdmiFwUpdateRequest
 
|-
 
|-
| 2 || GetStatus
+
| 9 || - || -1 || 0 || Dp2HdmiFwUpdateReply
 
|-
 
|-
| 3 || GetNotice
+
| 10 || - || -1 || 0 || Dp2HdmiFwUpdateNotice
 
|-
 
|-
| 4 || EnablePowerRequestNotice
+
| 11 || 2 || 0 || 2 || PdcHFwVerRequest
 
|-
 
|-
| 5 || DisablePowerRequestNotice
+
| 12 || - || 1 || 3 || PdcHFwVerReply
 
|-
 
|-
| 6 || ReplyPowerRequest
+
| 13 || - || -1 || 0 || PdcHFwUpdateRequest
|}
 
 
 
= usb:pd:c =
 
This is "nn::usb::pd::detail::IPdCradleManager".
 
 
 
{| class="wikitable" border="1"
 
 
|-
 
|-
! Cmd || Name || Notes
+
| 14 || - || -1 || 0 || PdcHFwUpdateReply
 
|-
 
|-
| 0 || GetPdCradleSession || Returns a session handle for the below.
+
| 15 || - || -1 || 0 || PdcHFwUpdateNotice
|}
 
 
 
USB-sysmodule symbols for this refer to "Cradle", which is the [[Dock]].
 
 
 
== IPdCradleSession ==
 
This is "nn::usb::pd::detail::IPdCradleSession".
 
 
 
{| class="wikitable" border="1"
 
 
|-
 
|-
! Cmd || Name || Notes
+
| 16 || 3 || 0 || 2 || PdcAFwVerRequest
 
|-
 
|-
| 0 || VdmUserWrite || Input { u32 value; u32 VdmUserId; }. Output {}.
+
| 17 || - || 1 || 3 || PdcAFwVerReply
If Cradle or RelayBox is connected, issues given VDM with value.
 
 
|-
 
|-
| 1 || VdmUserRead || Input { u32 VdmUserId; }. Output { u32 value; }.
+
| 18 || - || -1 || 0 || PdcAFwUpdateRequest
If Cradle or RelayBox is connected, returns u32 response from issuing given VDM.
 
 
|-
 
|-
| 2 || Vdm20Init || No input/output.
+
| 19 || - || -1 || 0 || PdcAFwUpdateReply
If Cradle or RelayBox is connected, caches result of VDM 0x20.
 
 
|-
 
|-
| 3 || [2.0.0+] GetFwType || No input. Returns an output u16.
+
| 20 || - || -1 || 0 || PdcAFwUpdateNotice
 
|-
 
|-
| 4 || [2.0.0+] GetFwRevision || No input. Returns an output u16.
+
| 21 || - || 0 || 3 || DeviceErrorNotice
 
|-
 
|-
| 5 || [2.0.0+] GetManufacturerId || No input. Returns an output u16.
+
| 22 || 4 || 0 || 2 || DeviceStateRequest
 
|-
 
|-
| 6 || [2.0.0+] GetDeviceId || No input. Returns an output u16.
+
| 23 || - || 1 || 3 || DeviceStateReply
 
|-
 
|-
| 7 || [3.0.0+] ||
+
| 24 || 5 || 0 || 2 || McuFwVerRequest
 
|-
 
|-
| 8 || [3.0.0+] ||
+
| 25 || - || 1 || 3 || McuFwVerReply
|}
 
 
 
Note: The VdmUserId given to VdmUserRead/VdmUserWrite is translated from the given (enum) value to the actual cmd to send.
 
 
 
= usb:pd:m =
 
This is "nn::usb::pd::detail::IPdManufactureManager". This is only available on 1.0.0.
 
 
 
{| class="wikitable" border="1"
 
 
|-
 
|-
! Cmd || Name || Notes
+
| 26 || 6 || 0 || 2 || McuFwUpdateRequest
 
|-
 
|-
| 0 || || No input, returns an [[#IPdManufactureSession]].
+
| 27 || - || 1 || 2 || McuFwUpdateReply
|}
 
 
 
== IPdManufactureSession ==
 
This is "nn::usb::pd::detail::IPdManufactureSession".
 
 
 
These commands just load the u16 from global state.
 
 
 
{| class="wikitable" border="1"
 
 
|-
 
|-
! Cmd || Name || Notes
+
| 28 || 7 || 0 || 2 || UsbHubSleepRequest
 
|-
 
|-
| 0 || || No input, returns an output u16.
+
| 29 || - || 1 || 2 || UsbHubSleepReply
 
|-
 
|-
| 1 || || No input, returns an output u16.
+
| 30 || 8 || 0 || 2 || UsbHubResetRequest
 
|-
 
|-
| 2 || || No input, returns an output u16.
+
| 31 || - || 1 || 2 || UsbHubResetReply
 
|-
 
|-
| 3 || || No input, returns an output u16.
+
| 32 || 9 || 0 || 2 || UsbHubControlRequest
 
|-
 
|-
 +
| 33 || - || 1 || 2 || UsbHubControlReply
 
|}
 
|}
  
= usb:pm =
+
= VdmCommandType =
This is "nn::usb::pm::IPmService".
+
This is "nn::usb::pd::driver::detail::VdmCommandType".
  
 
{| class="wikitable" border="1"
 
{| class="wikitable" border="1"
 
|-
 
|-
! Cmd || Name
+
! Value || Name
|-
 
| 0 ||
 
 
|-
 
|-
| 1 ||
+
| -1 || None
 
|-
 
|-
| 2 ||
+
| 0 || Initiator
 
|-
 
|-
| 3 ||
+
| 1 || Ack
 
|-
 
|-
| 4 ||
+
| 2 || Nak
 
|-
 
|-
| 5 ||
+
| 3 || Busy
 
|}
 
|}
  
USB Port Manager, only system-title using this is [[PTM_services|ptm]].
+
= HidGamepad =
 +
With [7.0.0+] usb-sysmodule now has .json data embedded in the codebin.
 +
 
 +
This contains a list of USB devices' VID/PID, with the following structure:
 +
 
 +
  [
 +
      {
 +
          "vid" : "<VID>",
 +
          "pid" : "<PID>",
 +
          "quirks" : [
 +
              {
 +
                  "name" : "<string>" //The .json has the following for <string> for various devices: "HidGamepadWhitelist", "ApplicationBlacklist", and "NoClearHaltOnEpInit".
 +
              }
 +
              //There can be multiple entries here.
 +
          ]
 +
      },
 +
      <more entries>
 +
  ]
 +
 
 +
[12.1.0+]: The following entries were added in this json, all of which have quirks set for "HidGamepadWhitelist".
 +
** vid "0F0D", where pid is "0200" - "022F".
 +
** vid "33DD", where pid is "0001" - "000B".
  
 
[[Category:Services]]
 
[[Category:Services]]

Latest revision as of 20:28, 15 October 2023

USB provides services for interacting with the USB stack.

Configuration

The following is the default USB config strings, while the usbds service isn't being used. All of the below configuration will reset to the defaults when all usbds-related sessions are closed. These can be set with #SetVidPidBcd. The default string for Product is loaded from settings config. The default is referred to by usb-sysmodule as "Dummy".

 Product: Nintendo Switch
 Manufacturer: Nintendo
 SerialNumber: SerialNumber

The following is the default lsusb -v {...} output when the usbds service wasn't used.

pre-5.0.0: The endpoints are configured using #RegisterEndpoint, the total number of endpoints is the number of open #IDsEndpoint sessions. bInterfaceNumber is {0-based index for the enabled #IDsInterface session.} Some of the interface fields are configured using #RegisterInterface.

When usbds is in use where #SetVidPidBcd wasn't used, the VID/PID is 057e:3000.

 Bus 003 Device 006: ID 057e:2000 Nintendo Co., Ltd 
 Couldn't open device, some information will be missing
 Device Descriptor:
   bLength                18
   bDescriptorType         1
   bcdUSB               2.00
   bDeviceClass            0 (Defined at Interface level)
   bDeviceSubClass         0 
   bDeviceProtocol         0 
   bMaxPacketSize0        64
   idVendor           0x057e Nintendo Co., Ltd
   idProduct          0x2000 
   bcdDevice            1.00
   iManufacturer           1 
   iProduct                2 
   iSerial                 3 
   bNumConfigurations      1
   Configuration Descriptor:
     bLength                 9
     bDescriptorType         2
     wTotalLength           34
     bNumInterfaces          1
     bConfigurationValue     1
     iConfiguration          0 
     bmAttributes         0xc0
       Self Powered
     MaxPower              {...}
     Interface Descriptor:
       bLength                 9
       bDescriptorType         4
       bInterfaceNumber        0
       bAlternateSetting       0
       bNumEndpoints           1
       bInterfaceClass         3 Human Interface Device
       bInterfaceSubClass      0 No Subclass
       bInterfaceProtocol      0 None
       iInterface              0 
         HID Device Descriptor:
           bLength                 9
           bDescriptorType        33
           bcdHID               2.00
           bCountryCode            0 Not supported
           bNumDescriptors         1
           bDescriptorType        34 Report
           wDescriptorLength      26
          Report Descriptors: 
            ** UNAVAILABLE **
       Endpoint Descriptor:
         bLength                 7
         bDescriptorType         5
         bEndpointAddress     0x81  EP 1 IN
         bmAttributes            3
           Transfer Type            Interrupt
           Synch Type               None
           Usage Type               Data
         wMaxPacketSize     0x0001  1x 1 bytes
         bInterval              16

usb:ds

[1.0.0-10.2.0] This is "nn::usb::ds::IDsService".

[11.0.0+] This is "nn::usb::ds::IDsRootSession".

This service is used during factory setup by manu. This is also used by capmtp.

This service session is used as an IPC domain by manu.

This service can be used by multiple processes at the same time, with separate interfaces. However, if one process does usbds shutdown, usbds will reset to defaults even if there's a process still using it.

Cmd Name
0 #CreateDsService

CreateDsService

No input. Returns an #IDsService.

IDsService

This is "nn::usb::ds::IDsService".

Cmd Name
0 #Initialize
1 [11.0.0+] #RegisterInterface ([1.0.0-10.2.0] #BindClientProcess))
2 [11.0.0+] #GetStateChangeEvent ([1.0.0-10.2.0] #RegisterInterface)
3 [11.0.0+] #GetState ([1.0.0-10.2.0] #GetStateChangeEvent)
4 [11.0.0+] ClearDeviceData ([1.0.0-10.2.0] #GetState)
5 [11.0.0+] AddUsbStringDescriptor ([5.0.0-10.2.0] ClearDeviceData, [2.0.0-4.1.0] #SetVidPidBcd)
6 [11.0.0+] DeleteUsbStringDescriptor ([5.0.0-10.2.0] AddUsbStringDescriptor)
7 [11.0.0+] SetUsbDeviceDescriptor ([5.0.0-10.2.0] DeleteUsbStringDescriptor)
8 [11.0.0+] SetBinaryObjectStore ([5.0.0-10.2.0] SetUsbDeviceDescriptor)
9 [11.0.0+] EnableDevice ([5.0.0-10.2.0] SetBinaryObjectStore)
10 [11.0.0+] DisableDevice ([5.0.0-10.2.0] EnableDevice)
11 [11.0.0+] #GetSpeed ([5.0.0-10.2.0] DisableDevice)
12 [8.0.0-10.2.0] #GetSpeed

Initialize

Takes an input u32 ComplexId. No output.

[11.0.0+] Now takes an additional input Process handle.

Manu uses ComplexId 0x02.

Binding more than once with the current session is not allowed. Once this command is used, the USB device will not be listed with lsusb until #EnableInterface is used.

Returns a not-found error when ComplexId isn't 0x02, for values 0x0-0x4 at least.

BindClientProcess

Takes an input Process handle. No output.

RegisterInterface

Takes two type-0x5 input buffers containing an #UsbInterfaceDescriptor and an #UsbStringDescriptor, respectively. Returns an output u8 InterfaceNumber and an #IDsInterface.

[5.0.0+] This now only takes an input u8 and returns an #IDsInterface.

Manu sends a 0x09-byte descriptor (e.g.: 0x09, 0x04, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x00) in the first buffer and a string ("usb") in the second buffer.

When the strlen output for the second buffer is >=0x40, size 0x40 is used instead for copying the string. This is the interface name, it's not sent over USB.

Returns an error when #Initialize wasn't used.

Up to 4 interfaces can be used and enabled.

GetStateChangeEvent

No input. Returns an output Event handle.

Signalled when Switch<->host USB comms change between started/stopped. USB cable connected/disconnected while at least 1 interface was enabled, or interface enabled/disabled while the USB cable was connected which then caused USB-comms state to change.

GetState

No input. Returns an output #UsbState.

Returns an error when #Initialize wasn't used.

SetVidPidBcd

Takes a type-0x5 input buffer containing an #UsbVidPidBcd. No output.

GetSpeed

No input. Returns an output #UsbDeviceSpeed.

IDsInterface

This is "nn::usb::ds::IDsInterface".

Cmd Name
0 #RegisterEndpoint
1 #GetSetupEvent
2 #GetSetupPacket
3 [11.0.0+] #CtrlInAsync ([1.0.0-10.2.0] #Enable)
4 [11.0.0+] #CtrlOutAsync ([1.0.0-10.2.0] #Disable)
5 [11.0.0+] #GetCtrlInCompletionEvent ([1.0.0-10.2.0] #CtrlInAsync)
6 [11.0.0+] #GetCtrlInUrbReport ([1.0.0-10.2.0] #CtrlOutAsync)
7 [11.0.0+] #GetCtrlOutCompletionEvent ([1.0.0-10.2.0] #GetCtrlInCompletionEvent)
8 [11.0.0+] #GetCtrlOutUrbReport ([1.0.0-10.2.0] #GetCtrlInUrbReport)
9 [11.0.0+] #CtrlStall ([1.0.0-10.2.0] #GetCtrlOutCompletionEvent)
10 [11.0.0+] #AppendConfigurationData ([1.0.0-10.2.0] #GetCtrlOutUrbReport)
11 [13.0.0+] #SetGuid ([1.0.0-10.2.0] #CtrlStall)
12 [15.0.0+] #RegisterTransferMemory ([5.0.0-10.2.0] #AppendConfigurationData)

RegisterEndpoint

Takes a type-0x5 input buffer containing an #UsbEndpointDescriptor. Returns an output u8 EndpointNumber and an #IDsEndpoint.

[5.0.0+] This now only takes an input u8 and returns an #IDsEndpoint.

Manu uses this twice for getting two endpoint sessions, with the following 0x7-byte buffer data:

  • First endpoint: 0x07, 0x05, 0x80, 0x02, 0x00, 0x02, 0x00
    • bLength=0x7
    • bDescriptorType=LIBUSB_DT_ENDPOINT
    • bEndpointAddress=LIBUSB_ENDPOINT_IN
    • bmAttributes=LIBUSB_TRANSFER_TYPE_BULK
    • wMaxPacketSize=0x200
    • bInterval=0
  • Second endpoint: Same as above except byte2 is 0x00(bEndpointAddress=LIBUSB_ENDPOINT_OUT).

The buffer size must be >=0x7. Only the first 0x7-bytes from the buffer are used.

  • Byte0(bLength) must match 0x7.
  • Byte1(bDescriptorType) must match 0x5.
  • Byte2(bEndpointAddress) is only compared with 0x80 to determine whether to use an input or output endpoint, the actual endpoint-number is allocated automatically by checking state. Hence, all input endpoints must use bEndpointAddress==0x80. Up to endpoint-number 0xF can be allocated for each endpoint-direction, for a total of 16 endpoints including control, and 15 for non-control endpoints(#IDsEndpoint sessions for each direction). This matches the Tegra maximum.

From the Tegra datasheet: "The maximum packet size supported on any endpoint is 1024 bytes in high-speed mode, for both device and host modes."

Throws an error if the interface is enabled.

GetSetupEvent

No input. Returns an output Event handle.

Unknown what triggers signalling, not signalled during interface-enable / device<>host USB-comms init.

GetSetupPacket

Takes a type-0x6 output buffer. No output.

Memcpys data to outbuf with outsize, uses size 0x8 if outsize is too large.

Throws an error if the interface is not enabled.

Enable

No input/output.

Enables the current interface.

Only one interface can be enabled at a time per bInterfaceNumber. When bInterfaceNumber is auto-allocate(0x4) for #RegisterEndpoint this isn't an issue since the final bInterfaceNumber will be unique.

Once enabled, the device/interface can then actually be used over USB.

Disable

No input/output.

Disables the current interface.

CtrlInAsync

Same as #PostBufferAsync, except this uses control input endpoint 0x80.

Throws an error if the interface is not enabled.

CtrlOutAsync

Same as #PostBufferAsync, except this uses control output endpoint 0x00.

Throws an error if the interface is not enabled.

GetCtrlInCompletionEvent

Same as #GetCompletionEvent, except this uses control input endpoint 0x80.

GetCtrlInUrbReport

Same as #GetUrbReport, except this uses control input endpoint 0x80.

GetCtrlOutCompletionEvent

Same as #GetCompletionEvent, except this uses control output endpoint 0x00.

GetCtrlOutUrbReport

Same as #GetUrbReport, except this uses control output endpoint 0x00.

CtrlStall

No input/output.

Calls a function with both control endpoints (0x80 and 0x00) with the same function. From strings: "m_pProtocol->Stall(0x80)" "m_pProtocol->Stall(0x00)".

Throws an error if the interface is not enabled.

AppendConfigurationData

Takes an input u32 InterfaceNumber, an input #UsbDeviceSpeed and a type-0x5 input buffer. No output.

[5.0.0+] This now only takes an input #UsbDeviceSpeed and a type-0x5 input buffer.

SetGuid

Takes a type-0x5 input buffer. No output.

RegisterTransferMemory

Takes an input u64 and an input handle. No output.

Stubbed, just returns an error.

IDsEndpoint

This is "nn::usb::ds::IDsEndpoint".

Cmd Name
0 #PostBufferAsync
1 #Cancel
2 #GetCompletionEvent
3 #GetUrbReport
4 #Stall
5 #SetZlt
6 [7.0.0+] #IsStalled
7 [7.0.0+] #GetStallClearedEvent
8 [11.0.0-14.1.2]
9 [11.0.0-14.1.2]

PostBufferAsync

Takes an input u32 Size and an input u64 Buffer. Returns an output u32 UrbId.

The output urbId can then be used while parsing the output of #GetUrbReport, after waiting for the CompletionEvent to be signalled.

The buffer address must be 0x1000-byte aligned. The input size doesn't matter. It helps to use svcSetMemoryAttribute to turn off caching on the buffer.

Used for data-transfer with input/output endpoints.

The user-process must flush dcache for the buffer before using this command.

When sending data where size is larger than wMaxPacketSize, it will automatically send multiple USB packets where last packet size = {remaining size}. Every {wMaxPacketSize}-bytes is a different packet. This only occurs in some cases. When size is ~0x1000000(exact size unknown), Switch-side silently hangs, while host-side will timeout(no traffic on USB bus indicating failure).

For receiving data, if size is less than {actual received USB packet size} the rest of the packet will be discarded. Later PostBufferAsync cmd(s) will only return data from new packets, not the remainder of the earlier packet(s).

Cancel

No input/output.

GetCompletionEvent

No input. Returns an output Event handle.

GetUrbReport

No input. Returns an output #UrbReport.

Seems to be eventually loaded from state, since this doesn't trigger any USB bus activity. All-zero before PostBufferAsync was used at least once.

Stall

No input/output.

Calls the same function used by #CtrlStall, except this uses the endpoint associated with the current session.

Stops in-progress data-transfer done by #PostBufferAsync.

SetZlt

Takes an input bool. No output.

IsStalled

No input. Returns an output bool.

GetStallClearedEvent

No input. Returns an output Event handle.

Cmd8

Takes an input u64 and an input handle. No output.

Stubbed, just returns an error.

Cmd9

Takes an input u32 Size and an input u64 Offset. Returns an u32 UrbId.

Stubbed, just returns an error.

usb:hs, usb:hs:a

This is "nn::usb::hs::IClientRootSession".

[7.0.0+] usb:hs:a opens an nn::usb::hs::IClientRootSession, but sets an "isSystemClient" field in the object (and in interfaces/eps opened by the session) to false.

Cmd Name
0 [2.0.0+] #BindClientProcess ([1.0.0] #QueryAllInterfaces)
1 #QueryAllInterfaces ([1.0.0] #QueryAvailableInterfaces)
2 #QueryAvailableInterfaces ([1.0.0] #QueryAcquiredInterfaces)
3 #QueryAcquiredInterfaces ([1.0.0] #CreateInterfaceAvailableEvent)
4 #CreateInterfaceAvailableEvent ([1.0.0] #DestroyInterfaceAvailableEvent)
5 #DestroyInterfaceAvailableEvent ([1.0.0] #GetInterfaceStateChangeEvent)
6 #GetInterfaceStateChangeEvent ([1.0.0] #AcquireUsbIf)
7 #AcquireUsbIf ([1.0.0] #GetDescriptorString)
8 [6.0.0+] SetTestMode ([1.0.0] #ResetDevice)

General USB devices usage, used by hid, bsdsockets, and [5.1.0+] audio.

Get-service-handle will fail if the current process is already using usb:ds, however it's successful if it's done separately from the process using usb:ds.

BindClientProcess

Takes 1 copy-handle for the current process (0xFFFF8001).

QueryAllInterfaces

Takes an #DeviceFilter and a type-0x6 output buffer, returns an output s32 total_entries.

The output buffer contains an array of #InterfaceQueryOutput. This returns the same interfaces as #QueryAvailableInterfaces, followed by the interfaces also returned by #QueryAcquiredInterfaces. Except for the ID field in #InterfaceQueryOutput, which is set to -1.

QueryAvailableInterfaces

Takes an #DeviceFilter and a type-0x6 output buffer, returns an output s32 total_entries.

The output buffer contains an array of #InterfaceQueryOutput. This only returns interfaces which are not acquired by any process.

QueryAcquiredInterfaces

Takes a type-0x6 output buffer and returns an output s32 total_entries.

The output buffer contains an array of #InterfaceQueryOutput, for each interface which was acquired with #AcquireUsbIf.

CreateInterfaceAvailableEvent

Takes an input u8 and an #DeviceFilter, and returns an output handle. The input value must be 0..2. This is used as an index in a table.

The struct is located at +2 from the u8 in IPC rawdata.

When signaled, this indicates that the user-process should use #QueryAvailableInterfaces and #AcquireUsbIf with the output interfaces (and the rest of interface setup).

DestroyInterfaceAvailableEvent

Takes an input u8, no output. The input value must be 0..2.

Clears state associated with the input index. This is the same state setup by #CreateInterfaceAvailableEvent.

GetInterfaceStateChangeEvent

No input, returns an output event handle with autoclear disabled.

When signaled, this indicates that the user-process should use #QueryAcquiredInterfaces and cleanup state for interfaces which were already initialized and don't have any matching ID in #InterfaceProfile +0x0. When total_entries is less than 1, this indicates that cleanup should be done for all currently open interfaces.

AcquireUsbIf

Takes an input u32 and a type-0x6 output buffer, returns an #IClientIfSession. On [3.0.0+] this takes an additional type-0x6 output buffer, before the original buffer. On [3.0.0+] the first buffer is 0x70-bytes, while the second one is 0x1B8. The user-process has the second buffer address immediately after the first one, which allows getting a complete #InterfaceProfile.

The input u32 is from the u32 at entry+0 from the associated #QueryAvailableInterfaces output entry. User-processes use size 0x1B8 for the first output buffer. The first output buffer contains an #InterfaceProfile.

This returns an error if the interface was already acquired by another process.

GetDescriptorString

Takes a type-0x6 output buffer, an input u8, an input u8 bool, and an u32. Returns an output u32.

The u32 is located at +4 from the first u8 in the IPC rawdata.

Gets a string from a string descriptor.

The UTF-16 string from descriptor+2 is copied to descriptor/outbuf +0, where each character is transferred using u8 not u16.

Official sw doesn't use this, #SubmitControlInRequest is used instead.

ResetDevice

Takes an input u32, no output. Stubbed, just returns 0.

IClientIfSession

This is "nn::usb::hs::IClientIfSession".

Cmd Name
0 #GetStateChangeEvent
1 #SetInterface
2 #GetInterface
3 #GetAlternateInterface
4 #GetCurrentFrame ([1.0.0] #CtrlXferAsync)
5 [2.0.0+] #CtrlXferAsync
6 #GetCtrlXferCompletionEvent ([1.0.0] #SubmitControlInRequest)
7 #GetCtrlXferReport ([1.0.0] #SubmitControlOutRequest)
8 #ResetDevice
9 #OpenUsbEp ([1.0.0] #GetCurrentFrame)

Official sw uses autoclear=false for the above events.

Immediately after opening the session, official sw uses cmd0 and cmd6.

GetStateChangeEvent

No input, returns an output handle.

SetInterface

Takes an input u8 and a type-0x6 output buffer, no output. The output buffer contains an #InterfaceProfile.

GetInterface

Takes a type-0x6 output buffer, no output. The output buffer contains an #InterfaceProfile.

GetAlternateInterface

Takes an input u8 and a type-0x6 output buffer, no output. The output buffer contains an #InterfaceProfile. The buffer size must match 0x1B8.

GetCurrentFrame

No input, returns an output u32.

On 1.0.0 this stubbed: this returns 0 with output u32 = hard-coded 0.

CtrlXferAsync

Takes 2 input u8s (bmRequestType and bRequest), 3 input u16s (wValue, wIndex, and wLength), and an input u64 buffer, no output.

GetCtrlXferCompletionEvent

No input, returns an output handle. Signaled when #CtrlXferAsync finishes.

GetCtrlXferReport

Takes a type-0x6 output buffer, no output. The output buffer contains a #XferReport.

SubmitControlInRequest

Takes a type-0x6 output buffer, 2 input u8s (bRequest and bRequestType), 3 input u16s (wValue, wIndex, and wLength), and an input u32 timeoutInMs. Returns an output u32 for the actual transferred size.

Official user-processes uses a buffer where the buffer address/size is page-aligned, where wLength is the original size before alignment. The user-process also flushes dcache for this buffer using wLength, before/after using this command. Official sw passes value 0 for timeoutInMs.

SubmitControlOutRequest

Takes a type-0x5 input buffer, 2 input u8s (bRequest and bRequestType), 3 input u16s (wValue, wIndex, and wLength), and an input u32 timeoutInMs. Returns an output u32 for the actual transferred size.

Official user-processes uses a buffer where the buffer address/size is page-aligned, where wLength is the original size before alignment. The user-process also flushes dcache for this buffer using wLength, before using this command. Official sw passes value 0 for timeoutInMs.

ResetDevice

No input/output.

Resets the device: has the same affect as unplugging the device and plugging it back in.

OpenUsbEp

Takes an input u16 and 4 input u32s, returns a 0x7-byte output struct and an #IClientEpSession.

The u16 is maxUrbCount. The u32s are: epType, epNumber, epDirection, and maxXferSize.

The user-process loads the input params from the endpoint descriptor. HID-sysmodule sets maxXferSize to wMaxPacketSize from the endpoint descriptor. However, other sysmodules pass hard-coded values for maxXferSize.

HID-sysmodule passes hard-coded value 0x1 for maxUrbCount, for bsd-sysmodule this is 0x2. For audio-sysmodule, this is 0x3 or 0x6.

epType is libusb_transfer_type+1. epNumber is bEndpointAddress & LIBUSB_ENDPOINT_ADDRESS_MASK. epDirection is 0x1 for LIBUSB_ENDPOINT_OUT, and 0x2 for LIBUSB_ENDPOINT_IN.

epType, epNumber, and epDirection must all match USB-sysmodule state for the associated endpoint. maxUrbCount must be <0x11. maxXferSize must be <=0xFF0000. maxUrbCount*maxXferSize must be non-zero.

IClientEpSession

This is "nn::usb::hs::IClientEpSession".

Cmd Name
0 #ReOpen ([1.0.0] #SubmitOutRequest)
1 #Close ([1.0.0] #SubmitInRequest)
2 GetCompletionEvent ([1.0.0] Reset)
3 #PopulateRing ([1.0.0] #Close)
4 [2.0.0+] #PostBufferAsync
5 [2.0.0+] #GetXferReport
6 [2.0.0+] #BatchBufferAsync
7 [4.0.0+] #CreateSmmuSpace
8 [4.0.0+] #ShareReportRing

Official sw uses autoclear=false for the above event.

Immediately after opening the endpoint session, official sw uses #Populate and cmd2.

SubmitOutRequest

Takes an type-0x5 input buffer, an u32 size, and an u32 unk. Returns an output u32 for the actual transferred size.

Official user-processes uses a buffer where the buffer address/size is page-aligned, where size is the original size before alignment. The user-process also flushes dcache for this buffer using size, before using this command. Official sw passes value 0 for unk.

SubmitInRequest

Takes an type-0x6 output buffer, an u32 size, and an u32 unk. Returns an output u32 for the actual transferred size.

Official user-processes uses a buffer where the buffer address/size is page-aligned, where size is the original size before alignment. The user-process also flushes dcache for this buffer using size, before/after using this command. Official sw passes value 0 for unk.

ReOpen

No input/output.

This is similar to #OpenUsbEp with the params being loaded from state instead.

This is likely intended for re-opening after #Close was used, but this is not known to be used by any official user-processes.

Close

No input/output.

Used by official sw prior to closing the endpoint session. The func for this is also called automatically by the sysmodule during the IClientEpSession dtor.

GetCompletionEvent

No input, returns an output handle. Signaled when #PostBufferAsync finishes. ([1.0.0] #Open)

PopulateRing

No input/output.

Used after opening the endpoint session (see above).

PostBufferAsync

Takes an input u32 size, an input u64 buffer, and an input u64 Id, returns an output u32 xferId.

Starts a data transfer with a single urb.

The official sw func which uses this cmd and handles waiting on the Event passes value 0 for the Id. The async version which exposes this cmd directly uses Id from user input - the user funcs pass a value starting at 0 then increment it each time (for sending multiple requests at once, with 0 for the first request in this set of requests). Note that Id can be arbitrary, the sysmodule doesn't require a certain range for this.

On newer sysvers the state fields setup by #CreateSmmuSpace are compared with buffer. If it's within bounds of the SmmuSpace (which won't be the case when #CreateSmmuSpace wasn't used), it will then verify that buffer+buffer_size is within bounds throwing an error otherwise. Note that a buffer from #CreateSmmuSpace can't be reused by another endpoint with PostBufferAsync.

GetXferReport

Takes an input u32 and a type-0x6 ([3.0.0+] type-0x22) output buffer, returns an output u32 count.

The input u32 specifies the total number of entries to read, this must fit within the specified buffer size. The output u32 is the total actual output entries.

The buffer contains an array of #XferReport.

On newer sysvers this runs code which is the same as the user-process code handling the tmem from #ShareReportRing. This cmd can handle getting reports from that tmem as well, however normally user-processes would just access the tmem directly.

BatchBufferAsync

Takes 3 input u32s (urbCount, unk1, and unk2), an input u64 buffer and u64 Id, and a type-0x5 ([3.0.0+] type-0x21) input buffer, returns an output u32 xferId.

Where Id is the same as #PostBufferAsync.

This uses the same func internally as #PostBufferAsync except multiple urbs are specified by the user: the input buffer contains an array of u32s for the size of each urb, where urbCount is the total number of entries in this array. With #PostBufferAsync the last 2 params passed to the internal func are hard-coded to 0, while with this command it's unk1 and unk2.

CreateSmmuSpace

Takes an input u32 size and an u64 buffer, no output.

This validates that both input params are page-aligned. Official user-processes also validate this.

This maps the specified buffer as devicemem and stores the fields for this in object state. If this was already used with this IClientEpSession object, an error is thrown.

ShareReportRing

Takes an input u32 size and an input TransferMemory handle, no output.

Official user-processes create the TransferMemory with permissions=RW. This validates that the input size is page-aligned, and this is also validated against data from state. Official user-processes also do this same validation.

When this was used, official user-processes read data from this TransferMemory buffer instead of using #GetXferReport.

This maps a TransferMemory buffer which is used for the #XferReport Ring, replacing the heap Ring buffer which was setup during #PopulateRing.

Structure of the tmem:

Offset Size Description
0x0 0x8 Write index
0x8 0x8 Read index
0x10 0x18*count Array of #XferReport

write_index!=read_index indicates that report data is available. The index fields are updated with: index = index+1 % count; write_index is updated by sysmodule, read_index is updated by the user-process. If the index field being used is >=count, this will Abort.

usb:pd

This is "nn::usb::pd::detail::IPdManager".

Cmd Name
0 #OpenSession

Only system-titles with access to this are ptm and am.

OpenSession

No input. Returns an #IPdSession.

IPdSession

This is "nn::usb::pd::detail::IPdSession".

Cmd Name
0 BindNoticeEvent
1 UnbindNoticeEvent
2 GetStatus
3 GetNotice
4 EnablePowerRequestNotice
5 DisablePowerRequestNotice
6 ReplyPowerRequest

usb:pd:c

This is "nn::usb::pd::detail::IPdCradleManager".

Cmd Name
0 #OpenCradleSession

USB-sysmodule symbols for this refer to "Cradle", which is the Dock.

OpenCradleSession

No input. Returns an #IPdCradleSession.

IPdCradleSession

This is "nn::usb::pd::detail::IPdCradleSession".

Cmd Name
0 #SetCradleVdo
1 #GetCradleVdo
2 #ResetCradleUsbHub
3 [2.0.0+] #GetHostPdcFirmwareType
4 [2.0.0+] #GetHostPdcFirmwareRevision
5 [2.0.0+] #GetHostPdcManufactureId
6 [2.0.0+] #GetHostPdcDeviceId
7 [3.0.0+] #EnableCradleRecovery
8 [3.0.0+] #DisableCradleRecovery

SetCradleVdo

Takes an input u32 Value and a #CradleVdmCommand. No output.

#CradleVdmCommand is translated to the actual #VdmCommand to send.

GetCradleVdo

Takes an input #CradleVdmCommand. Returns an u32 Value.

#CradleVdmCommand is translated to the actual #VdmCommand to send.

ResetCradleUsbHub

No input/output.

Sends VdmCommands 32 and 30.

GetHostPdcFirmwareType

No input. Returns an output u16.

GetHostPdcFirmwareRevision

No input. Returns an output u16.

GetHostPdcManufactureId

No input. Returns an output u16.

GetHostPdcDeviceId

No input. Returns an output u16.

EnableCradleRecovery

No input. Returns an output u8.

DisableCradleRecovery

No input. Returns an output u8.

usb:pd:m

This is "nn::usb::pd::detail::IPdManufactureManager".

[2.0.0+] This service was merged into usb:pd:c.

Cmd Name
0 #OpenManufactureSession

OpenManufactureSession

No input. Returns an #IPdManufactureSession.

IPdManufactureSession

This is "nn::usb::pd::detail::IPdManufactureSession".

Cmd Name
0 #GetHostPdcFirmwareType
1 #GetHostPdcFirmwareRevision
2 #GetHostPdcManufactureId
3 #GetHostPdcDeviceId

usb:pm

This is "nn::usb::pm::IPmService".

[8.0.0+] This is "nn::usb::pm::IPmMainService".

Cmd Name
0 GetPowerEvent
1 GetPowerState
2 GetDataEvent
3 GetDataRole
4 SetDiagData
5 GetDiagData

USB Port Manager, only system-title using this is ptm.

usb:qdb

This is "nn::usb::qdb::IQdbManager".

This was added with [7.0.0+].

Cmd Name
0 #ImportQuirkDevices
1 #HasQuirk

ImportQuirkDevices

No input/output, takes a type-0x5 input buffer.

This loads data for #HidGamepad with the input .json.

HasQuirk

Takes 6-bytes of input (u16s vid, pid, bcdDevice) and a type-0x5 input buffer, returns an output u8 bool indicating success.

Locates an entry in the #HidGamepad state with the input u16s, and checks for a quirks array entry where name matches the input buffer string. Returns 1 when found, 0 otherwise.

usb:obsv

This is "nn::usb::pm::IPmObserverService".

This was added with [8.0.0+].

Cmd Name
0 GetTopologyChangeEvent
1 GetFlattenedTopology

UsbInterfaceDescriptor

This is "nn::usb::UsbInterfaceDescriptor".

Offset Size Description
0x0 0x1 bLength
0x1 0x1 bDescriptorType
0x2 0x1 bInterfaceNumber
0x3 0x1 bAlternateSetting
0x4 0x1 bNumEndpoints
0x5 0x1 bInterfaceClass
0x6 0x1 bInterfaceSubClass
0x7 0x1 bInterfaceProtocol
0x8 0x1 iInterface

UsbStringDescriptor

This is "nn::usb::UsbStringDescriptor". This is a string.

UsbEndpointDescriptor

This is "nn::usb::UsbEndpointDescriptor".

Offset Size Description
0x0 0x1 bLength
0x1 0x1 bDescriptorType
0x2 0x1 bEndpointAddress
0x3 0x1 bmAttributes
0x4 0x2 wMaxPacketSize
0x6 0x1 bInterval
0x7 0x1 bRefresh
0x8 0x1 bSynchAddress

UsbState

Value Name Description
0 Detached Device is not attached to USB.
1 Attached Device is attached, but is not powered.
2 Powered Device is attached and powered, but has not been reset.
3 Default Device is attached, powered, and has been reset, but does not have an address.
4 Address Device is attached, powered, has been reset, has an address, but is not configured.
5 Configured Device is attached, powered, has been reset, has an address, configured, and may be used.
6 Suspended Device is attached and powered, but has not seen bus activity for 3ms. Device is suspended and cannot be used.

UsbVidPidBcd

Offset Size Description
0x0 0x2 Vid (idVendor)
0x2 0x2 Pid (idProduct)
0x4 0x2 bcdDevice
0x6 0x20 Manufacturer (ASCII string padded to 0x20 bytes)
0x26 0x20 Product (ASCII string padded to 0x20 bytes)
0x46 0x20 SerialNumber (ASCII string padded to 0x20 bytes)

UrbReport

Offset Size Description
0x0 0x10 * 8 Array of #UrbReportEntry
0x80 0x4 ReportEntryCount

UrbReportEntry

Offset Size Description
0x0 0x4 Id
0x4 0x4 RequestedSize
0x8 0x4 TransferredSize
0xC 0x4 Status (converted to error-codes: 0x3 = success, 0x4 = 0x828c, 0x5 = 0x748c; all other values are invalid)

UsbDeviceSpeed

Value Name
0 None
1 Low
2 Full
3 High
4 Super

InterfaceQueryOutput

This is "nn::usb::InterfaceQueryOutput".

Offset Size Description
0x0 0x1B8 InterfaceProfile
0x1B8 0x40 "HsDevice-/L<unk0>/P<portnum>/A<unk1>" string (this is "FsDevice..." for the Dock USB 3.0 bus).
0x1F8 0x4 busID
0x1FC 0x4 deviceID
0x200 0x12+0x9(0x1B) usb_device_descriptor, then usb_config_descriptor immediately afterwards.
0x21B 0x5 Padding
0x220 0x8 Unknown u64 timestamp for when the device was inserted?

The INPUT/OUTPUT endpoint descriptors (usb_endpoint_descriptors/usb_ss_endpoint_companion_descriptors) were swapped with [8.0.0+] (above layout has the pre-8.0.0 layout), however the sysmodule code which writes this output struct was basically unchanged.

InterfaceProfile

This is "nn::usb::InterfaceProfile".

Offset Size Description
0x0 0x4 ID value passed to other cmds. This is -1 with #QueryAllInterfaces output, hence this field is unused with that cmd.
0x4 0x4 deviceID
0x8 0x4 ?
0xC 0x9 usb_interface_descriptor
0x15 0x7 Padding
0x1C 0x69 OUTPUT usb_endpoint_descriptors, 15 max.
0x85 0x7 Padding
0x8C 0x69 INPUT usb_endpoint_descriptors, 15 max.
0xF5 0x6 Padding
0xFB 0x5A OUTPUT usb_ss_endpoint_companion_descriptors(?), 15 max.
0x155 0x6 Padding
0x15B 0x5A INPUT usb_ss_endpoint_companion_descriptors(?), 15 max.
0x1B5 0x3 Padding

DeviceFilter

This is "nn::usb::DeviceFilter".

Offset Size Description
0x0 0x2 Flags
0x2 0x2 idVendor
0x4 0x2 idProduct
0x6 0x2 bcdDevice_Min
0x8 0x2 bcdDevice_Max
0xA 0x1 bDeviceClass
0xB 0x1 bDeviceSubClass
0xC 0x1 bDeviceProtocol
0xD 0x1 bInterfaceClass
0xE 0x1 bInterfaceSubClass
0xF 0x1 bInterfaceProtocol

This is a 0x10-byte struct (unofficial name).

This is used to filter #InterfaceQueryOutput, the query commands will only return the interface when these checks pass. This is also used for events with #CreateInterfaceAvailableEvent. When a bit in Flags is set, the associated descriptor field and the field in this struct are compared, on mismatch the interface will be filtered out. Passing Flags=0 is equivalent to disabling filtering since none of these checks will run.

[7.0.0+]: The filter struct has to be unique, it can't match any existing filter structs used by #CreateInterfaceAvailableEvent (including other processes). Hence, Flags has to be non-zero. When initialized with usb:hs:a and VID and/or PID filtering is enabled, the VID/PID will be checked #HidGamepad, with an error being throw if a matching device is found with quirk "ApplicationBlacklist" (usb:qdb cmd1 is used internally for this with bcdDevice=0).

Flags bits 0..6 use usb_device_descriptor, while 7..9 use usb_interface_descriptor. Support for bits 2..6 and the associated fields was added with [6.0.0+].

Flags bits:

  • 0: idVendor
  • 1: idProduct
  • 2: bcdDevice (Descriptor value must be >= struct bcdDevice_Min)
  • 3: bcdDevice (Descriptor value must be <= struct bcdDevice_Max)
  • 4: bDeviceClass
  • 5: bDeviceSubClass
  • 6: bDeviceProtocol
  • 7: bInterfaceClass
  • 8: bInterfaceSubClass
  • 9: bInterfaceProtocol

HID-sysmodule uses the following for the input struct: 80 00 00 00 00 00 00 00 00 00 00 00 00 03 00 00

XferReport

Offset Size Description
0x0 0x4 xferId (Only set for #GetXferReport)
0x4 0x4 Result
0x8 0x4 requestedSize
0xC 0x4 transferredSize
0x10 0x8 Id

This is a 0x18-byte struct.

Official sw only uses the Result/size fields.

CradleVdmCommand

This is "nn::usb::pd::CradleVdmCommand".

Value Name
0
1
2
3
4
5
6
7
8
9
10

VdmCommand

This is "nn::usb::pd::driver::detail::VdmCommand".

Value CradleVdmCommand VdmCommandType VdmCommandLength Name
0 - -1 0 None
1 0 0 3 LedControlRequest
2 - 1 3 LedControlReply
3 10 0 3 DeviceTypeRequest
4 - 1 3 DeviceTypeReply
5 - -1 0 UsbPowerErrorNotice
6 1 0 2 Dp2HdmiFwVerRequest
7 - 1 3 Dp2HdmiFwVerReply
8 - -1 0 Dp2HdmiFwUpdateRequest
9 - -1 0 Dp2HdmiFwUpdateReply
10 - -1 0 Dp2HdmiFwUpdateNotice
11 2 0 2 PdcHFwVerRequest
12 - 1 3 PdcHFwVerReply
13 - -1 0 PdcHFwUpdateRequest
14 - -1 0 PdcHFwUpdateReply
15 - -1 0 PdcHFwUpdateNotice
16 3 0 2 PdcAFwVerRequest
17 - 1 3 PdcAFwVerReply
18 - -1 0 PdcAFwUpdateRequest
19 - -1 0 PdcAFwUpdateReply
20 - -1 0 PdcAFwUpdateNotice
21 - 0 3 DeviceErrorNotice
22 4 0 2 DeviceStateRequest
23 - 1 3 DeviceStateReply
24 5 0 2 McuFwVerRequest
25 - 1 3 McuFwVerReply
26 6 0 2 McuFwUpdateRequest
27 - 1 2 McuFwUpdateReply
28 7 0 2 UsbHubSleepRequest
29 - 1 2 UsbHubSleepReply
30 8 0 2 UsbHubResetRequest
31 - 1 2 UsbHubResetReply
32 9 0 2 UsbHubControlRequest
33 - 1 2 UsbHubControlReply

VdmCommandType

This is "nn::usb::pd::driver::detail::VdmCommandType".

Value Name
-1 None
0 Initiator
1 Ack
2 Nak
3 Busy

HidGamepad

With [7.0.0+] usb-sysmodule now has .json data embedded in the codebin.

This contains a list of USB devices' VID/PID, with the following structure:

 [
     {
         "vid" : "<VID>",
         "pid" : "<PID>",
         "quirks" : [
             {
                 "name" : "<string>" //The .json has the following for <string> for various devices: "HidGamepadWhitelist", "ApplicationBlacklist", and "NoClearHaltOnEpInit".
             }
             //There can be multiple entries here.
         ]
     },
     <more entries>
 ]

[12.1.0+]: The following entries were added in this json, all of which have quirks set for "HidGamepadWhitelist".

    • vid "0F0D", where pid is "0200" - "022F".
    • vid "33DD", where pid is "0001" - "000B".