USB services: Difference between revisions
No edit summary |
No edit summary |
||
Line 1: | Line 1: | ||
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_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 85: | 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 [[# | 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 147: | Line 71: | ||
bInterval 16 | 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|factory setup]] by [[Manu Services|manu]]. This is also used by [[Capmtp_services|capmtp]]. | |||
This service session is used as an IPC [[IPC_Marshalling|domain]] by [[Manu Services|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. | |||
{| class="wikitable" border="1" | {| class="wikitable" border="1" | ||
|- | |- | ||
! | ! Cmd || Name | ||
|- | |- | ||
| | | 0 || [[#OpenDsService]] | ||
|} | |} | ||
== OpenDsService == | |||
Unofficial name. | |||
No input. Returns an [[#IDsService]]. | |||
Returns an | |||
== | == IDsService == | ||
This is "nn::usb::ds::IDsService". | |||
{| class="wikitable" border="1" | {| class="wikitable" border="1" | ||
|- | |- | ||
! | ! Cmd || Name | ||
|- | |- | ||
| 0 || | | 0 || [[#BindComplex]] | ||
|- | |- | ||
| 1 || | | 1 || [11.0.0+] [[#RegisterInterface]] ([1.0.0-10.2.0] [[#BindClientProcess]])) | ||
|- | |- | ||
| 2 || | | 2 || [11.0.0+] [[#GetStateChangeEvent]] ([1.0.0-10.2.0] [[#RegisterInterface]]) | ||
|- | |- | ||
| 3 || | | 3 || [11.0.0+] [[#GetState]] ([1.0.0-10.2.0] [[#GetStateChangeEvent]]) | ||
|- | |- | ||
| 4 || | | 4 || [11.0.0+] ClearDeviceData ([1.0.0-10.2.0] [[#GetState]]) | ||
|- | |- | ||
| 5 || | | 5 || [11.0.0+] AddUsbStringDescriptor ([5.0.0-10.2.0] ClearDeviceData, [2.0.0-4.1.0] [[#SetVidPidBcd]]) | ||
|- | |- | ||
| 6 || | | 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+] Enable ([5.0.0-10.2.0] SetBinaryObjectStore) | ||
|- | |- | ||
| | | 10 || [11.0.0+] Disable ([5.0.0-10.2.0] Enable) | ||
|- | |- | ||
| | | 11 || [11.0.0+] ([5.0.0-10.2.0] Disable) | ||
|- | |- | ||
| | | 12 || [8.0.0-10.2.0] | ||
|} | |} | ||
== BindComplex == | |||
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 [[#BindComplex]] wasn't used. | |||
Up to 4 interfaces can be used and [[#EnableInterface|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 [[#BindComplex]] wasn't used. | |||
== SetVidPidBcd == | |||
Takes a type-0x5 input buffer containing an [[#UsbVidPidBcd]]. No output. | |||
== Cmd12 == | == Cmd12 == | ||
Line 246: | Line 173: | ||
== IDsInterface == | == IDsInterface == | ||
This is "nn::usb::ds::IDsInterface". | This is "nn::usb::ds::IDsInterface". | ||
{| class="wikitable" border="1" | {| class="wikitable" border="1" | ||
Line 253: | Line 178: | ||
! Cmd || Name | ! Cmd || Name | ||
|- | |- | ||
| 0 || [[# | | 0 || [[#RegisterEndpoint]] | ||
|- | |- | ||
| 1 || [[#GetSetupEvent]] | | 1 || [[#GetSetupEvent]] | ||
Line 259: | Line 184: | ||
| 2 || [[#GetSetupPacket]] | | 2 || [[#GetSetupPacket]] | ||
|- | |- | ||
| 3 || [[#Enable]] | | 3 || [11.0.0+] [[#CtrlIn]] ([1.0.0-10.2.0] [[#Enable]]) | ||
|- | |- | ||
| 4 || [[#Disable]] | | 4 || [11.0.0+] [[#CtrlOut]] ([1.0.0-10.2.0] [[#Disable]]) | ||
|- | |- | ||
| 5 || [[#CtrlIn]] | | 5 || [11.0.0+] [[#GetCtrlInCompletionEvent]] ([1.0.0-10.2.0] [[#CtrlIn]]) | ||
|- | |- | ||
| 6 || [[#CtrlOut]] | | 6 || [11.0.0+] [[#GetCtrlInUrbReport]] ([1.0.0-10.2.0] [[#CtrlOut]]) | ||
|- | |- | ||
| 7 || [[#GetCtrlInCompletionEvent]] | | 7 || [11.0.0+] [[#GetCtrlOutCompletionEvent]] ([1.0.0-10.2.0] [[#GetCtrlInCompletionEvent]]) | ||
|- | |- | ||
| 8 || [[#GetCtrlInUrbReport]] | | 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 || [[#GetCtrlOutUrbReport]] | | 10 || [11.0.0+] AppendConfigurationData ([1.0.0-10.2.0] [[#GetCtrlOutUrbReport]]) | ||
|- | |- | ||
| 11 || [[#CtrlStall]] | | 11 || [13.0.0+] ([1.0.0-10.2.0] [[#CtrlStall]]) | ||
|- | |- | ||
| 12 || [5.0.0 | | 12 || [15.0.0+] ([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_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 292: | Line 219: | ||
** 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). | ||
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 303: | Line 226: | ||
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 | No input. Returns an output Event handle. | ||
Unknown what triggers signalling, not signalled during interface-enable / device<>host USB-comms init. | |||
=== GetSetupPacket === | === GetSetupPacket === | ||
Takes a type-0x6 output buffer | 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 === | === 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 [[# | 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. | ||
=== Disable === | === Disable === | ||
No input/output. | |||
Disables the current interface. | |||
=== CtrlIn === | === CtrlIn === | ||
Same as [[#PostBufferAsync]] | Same as [[#PostBufferAsync]], except this uses control input endpoint 0x80. | ||
Throws an error if the interface is not [[#EnableInterface|enabled]]. | |||
=== CtrlOut === | === CtrlOut === | ||
Same as [[#PostBufferAsync]] | Same as [[#PostBufferAsync]], except this uses control output endpoint 0x00. | ||
Throws an error if the interface is not [[#EnableInterface|enabled]]. | |||
=== GetCtrlInCompletionEvent === | === GetCtrlInCompletionEvent === | ||
Same as [[#GetCompletionEvent]], except this uses control input endpoint 0x80. | |||
=== GetCtrlInUrbReport === | === GetCtrlInUrbReport === | ||
Same as [[#GetUrbReport]] | Same as [[#GetUrbReport]], except this uses control input endpoint 0x80. | ||
=== GetCtrlOutCompletionEvent === | === GetCtrlOutCompletionEvent === | ||
Same as [[#GetCompletionEvent]], except this uses control output endpoint 0x00. | |||
=== GetCtrlOutUrbReport === | === GetCtrlOutUrbReport === | ||
Same as [[#GetUrbReport]] | Same as [[#GetUrbReport]], except this uses control output endpoint 0x00. | ||
=== CtrlStall === | === 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]]. | |||
=== IDsEndpoint === | === IDsEndpoint === | ||
Line 371: | Line 312: | ||
|} | |} | ||
==== 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. | 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 390: | Line 331: | ||
==== GetCompletionEvent ==== | ==== GetCompletionEvent ==== | ||
No input. Returns an output | No input. Returns an output Event handle. | ||
==== GetUrbReport ==== | ==== GetUrbReport ==== | ||
No input. Returns | 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 ==== | ==== Stall ==== | ||
Line 427: | Line 346: | ||
==== SetZeroLengthTransfer ==== | ==== SetZeroLengthTransfer ==== | ||
Takes an input | Takes an input bool. No output. | ||
==== Cmd6 ==== | ==== Cmd6 ==== | ||
No input | No input. Returns an output bool. | ||
==== Cmd7 ==== | ==== Cmd7 ==== | ||
No input | No input. Returns an output handle. | ||
==== Cmd8 ==== | ==== Cmd8 ==== | ||
Takes an input u64 and | Takes an input u64 and an input handle. No output. | ||
Stubbed, just returns an error. | |||
==== Cmd9 ==== | ==== Cmd9 ==== | ||
Takes an input u32 | Takes an input u32 '''Size''' and an input u64 '''Offset'''. Returns an u32 '''UrbId'''. | ||
Stubbed, just returns an error. | |||
= usb:hs, usb:hs:a = | = usb:hs, usb:hs:a = | ||
Line 632: | Line 555: | ||
| 3 || [[#PopulateRing]] ([1.0.0] [[#Close]]) | | 3 || [[#PopulateRing]] ([1.0.0] [[#Close]]) | ||
|- | |- | ||
| [2.0.0+] | | 4 || [2.0.0+] [[#PostBufferAsync_2|#PostBufferAsync]] | ||
|- | |- | ||
| [2.0.0+] | | 5 || [2.0.0+] [[#GetXferReport]] | ||
|- | |- | ||
| [2.0.0+] | | 6 || [2.0.0+] [[#BatchBufferAsync]] | ||
|- | |- | ||
| [4.0.0+] | | 7 || [4.0.0+] [[#CreateSmmuSpace]] | ||
|- | |- | ||
| [4.0.0+] | | 8 || [4.0.0+] [[#ShareReportRing]] | ||
|} | |} | ||
Line 936: | Line 859: | ||
|- | |- | ||
| 1 || | | 1 || | ||
|} | |||
= 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) | |||
|} | |} | ||