This repository has been archived by the owner on Sep 21, 2021. It is now read-only.
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
This is based on @misson20000 works and adds some missing type definitions.
- Loading branch information
Thog
committed
Sep 18, 2018
1 parent
9bb8420
commit 753f767
Showing
1 changed file
with
268 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,268 @@ | ||
type nn::usb::usb_interface_descriptor = struct { | ||
u8 bLength; | ||
u8 bDescriptorType; | ||
u8 bInterfaceNumber; | ||
u8 bAlternateSetting; | ||
u8 bNumEndpoints; | ||
u8 bInterfaceClass; | ||
u8 bInterfaceSubClass; | ||
u8 bInterfaceProtocol; | ||
u8 iInterface; | ||
}; | ||
|
||
type nn::usb::usb_descriptor_data = struct { | ||
u16 idVendor; | ||
u16 idProduct; | ||
u16 bcdDevice; | ||
bytes<0x20> manufacturer; | ||
bytes<0x20> product; | ||
bytes<0x20> serialNumber; | ||
}; | ||
|
||
type nn::usb::usb_endpoint_descriptor = struct { | ||
u8 bLength; | ||
u8 bDescriptorType; | ||
u8 bEndpointAddress; | ||
u8 bmAttributes; | ||
u16 wMaxPacketSize; | ||
u8 bInterval; | ||
}; | ||
|
||
type nn::usb::usb_report_entry = struct { | ||
u32 urbId; | ||
u32 requestedSize; | ||
u32 transferredSize; | ||
u32 urbStatus; | ||
}; | ||
|
||
type nn::usb::usb_device_descriptor = struct { | ||
u8 bLength; | ||
u8 bDescriptorType; | ||
u16 bcdUSB; | ||
u8 bDeviceClass; | ||
u8 bDeviceSubClass; | ||
u8 bDeviceProtocol; | ||
u8 bMaxPacketSize0; | ||
u16 idVendor; | ||
u16 idProduct; | ||
u16 bcdDevice; | ||
u8 iManufacturer; | ||
u8 iProduct; | ||
u8 iSerialNumber; | ||
u8 bNumConfigurations; | ||
}; | ||
|
||
type nn::usb::usb_bos_dev_capability_descriptor = struct { | ||
u8 bLength; | ||
u8 bDescriptorType; | ||
u8 bDevCapabilityType; | ||
array<u8, 0x0> dev_capability_data; | ||
}; | ||
|
||
type nn::usb::usb_bos_descriptor = struct { | ||
u8 bLength; | ||
u8 bDescriptorType; | ||
u16 wTotalLength; | ||
u8 bNumDeviceCaps; | ||
array<nn::usb::usb_bos_dev_capability_descriptor, 0x0> dev_capability; | ||
}; | ||
|
||
type nn::usb::usb_device_speed = enum<u32> { | ||
Unknown = 0; | ||
Low = 1; | ||
Full = 2; | ||
High = 3; | ||
SuperSpeed = 4; | ||
}; | ||
|
||
# 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 by | ||
# manu. | ||
# | ||
# This service session is used as an IPC domain by manu. All of these | ||
# {get-session} commands also return an output u8 and the u32 <domainID>, for | ||
# using those sessions as domains. | ||
interface nn::usb::ds::IDsService is usb:ds { | ||
# Manu sends 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](#nn::usb::ds::IDsInterface(3)) is used. | ||
[0] BindDevice(u32 complexId); | ||
[1] BindClientProcess(KObject<copy, process>); | ||
|
||
# Manu sends a 0x09-byte command (e.g.: `09 04 04 00 00 FF FF FF 00`) 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 [#BindDevice](#nn::usb::ds::IDsService(0)) wasn't | ||
# used. | ||
# | ||
# Up to 4 interfaces can be used+enabled. | ||
@version(1.0.0-4.0.0) | ||
[2] GetDsInterface(buffer<nn::usb::usb_interface_descriptor, 5, 9> interface_descriptor, buffer<u8, 5, 0>) -> (u8, object<nn::usb::ds::IDsInterface>); | ||
|
||
@version(5.0.0+) | ||
[2] RegisterInterface(u8 address) -> object<nn::usb::ds::IDsInterface>; | ||
|
||
# Returns an event handle for when the state returned by | ||
# [#GetChange](#nn::usb::ds::IDsService(4)) 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. | ||
[3] GetStateChangeEvent() -> KObject; | ||
# Returns an error when [#BindDevice](#nn::usb::ds::IDsService(0)) wasn't | ||
# used. | ||
# | ||
# Returns the current state. Values: | ||
# - 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. | ||
[4] GetState() -> u32; | ||
@version(2.0.0-4.0.0) | ||
[5] SetVidPidBcd(buffer<nn::usb::usb_descriptor_data, 5, 0x66> descriptor); | ||
|
||
@version(5.0.0+) | ||
[5] ClearDeviceData(); | ||
@version(5.0.0+) | ||
[6] AddUsbStringDescriptor(buffer<bytes<0x82>, 5, 0x82> string_descriptor) -> u8 index; | ||
@version(5.0.0+) | ||
[7] DeleteUsbStringDescriptor(u8 index); | ||
@version(5.0.0+) | ||
[8] SetUsbDeviceDescriptor(nn::usb::usb_device_speed speed_mode, buffer<nn::usb::usb_device_descriptor, 5, 0x12> descriptor); | ||
@version(5.0.0+) | ||
[9] SetBinaryObjectStore(buffer<nn::usb::usb_bos_descriptor, 5, 0>); | ||
@version(5.0.0+) | ||
[10] Enable(); | ||
@version(5.0.0+) | ||
[11] Disable(); | ||
} | ||
|
||
interface nn::usb::ds::IDsInterface { | ||
# Manu uses this twice for getting two endpoints, with the following | ||
# 0x7-byte buffer data: | ||
# - First endpoint: `07 05 80 02 00 02 00` | ||
# - bLength = 7 | ||
# - bDescriptorType = LIBnn::usb::usb_DT_ENDPOINT | ||
# - bEndpointAddress = LIBnn::usb::usb_ENDPOINT_IN | ||
# - bmAttributes = LIBnn::usb::usb_TRANSFER_TYPE_BULK | ||
# - wMaxPacketSize = 0x200 | ||
# - bInterval = 0 | ||
# - Second endpoint: Same as above except byte2 is 00 (bEndpointAddress | ||
# = LIBnn::usb::usb_ENDPOINT_OUT) | ||
# | ||
# The structure matches libnn::usb::usb_endpoint_descriptor, with fields for | ||
# audio devices bRefresh and bSynchAddress removed. | ||
# | ||
# - bLength must match 7 | ||
# - bDescriptorType must match 5 | ||
# - bEndpointAddress is only compared with 0x80 to determine whether to | ||
# user an input or output endpoint, the actual endpoint-number is | ||
# allocated automatically by checking state. Hence, all input | ||
# endpoints must use a 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](#nn::usb::ds::IDsEndpoint) sessions for each | ||
# directions). 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." | ||
@version(1.0.0-4.0.0) | ||
[0] GetDsEndpoint(buffer<nn::usb::usb_endpoint_descriptor, 5, 7>) -> (u8, object<nn::usb::ds::IDsEndpoint>); | ||
@version(5.0.0+) | ||
[0] RegisterEndpoint(u8 address) -> object<nn::usb::ds::IDsEndpoint>; | ||
# Returns an event handle. Unknown what triggers signalling, not | ||
# signalled during interface-enable / device<>host USB-comms init. | ||
[1] GetSetupEvent() -> KObject; | ||
[2] GetSetupPacket() -> buffer<unknown, 0x6, 0>; | ||
# Enables the current interface. | ||
# | ||
# Only one interface can be enabled at a time per bInterfaceNumber. When | ||
# bInterfaceNumber is auto-allocated (0x4) for | ||
# [#GetDsEndpoint](#nn::usb::ds::IDsInterface(0)) this isn't an issue | ||
# since the final bInterfaceNumber will be unique. | ||
# | ||
# Once enabled, the device/interface can then actually be used over USB. | ||
[3] EnableInterface(); | ||
# Disables the current interface | ||
[4] DisableInterface(); | ||
# Same as [#PostBufferAsync](#nn::usb::ds::IDsEndpoint(0)), except this | ||
# uses control input endpoint 0x80. | ||
[5] CtrlInPostBufferAsync(u32 size, u64 buffer) -> u32 urbId; | ||
# Same as [#PostBufferAsync](#nn::usb::ds::IDsEndpoint(0)), except this | ||
# uses control output endpoint 0x00. | ||
[6] CtrlOutPostBufferAsync(u32 size, u64 buffer) -> u32 urbId; | ||
# Returns an event handle for polling the completion of input control | ||
# commands. Same as [#GetCompletionEvent](#nn::usb::ds::IDsEndpoint(2)), | ||
# except this uses control input endpoint 0x80. | ||
[7] GetCtrlInCompletionEvent() -> KObject; | ||
# Same as [#GetReportData](#nn::usb::ds::IDsEndpoint(3)), except this | ||
# uses control input endpoint 0x80. | ||
[8] GetCtrlInReportData() -> (nn::usb::usb_report_entry<0x8> entries, u32 report_count); | ||
# Returns an event handle for polling the completion of input control | ||
# commands. Same as [#GetCompletionEvent](#nn::usb::ds::IDsEndpoint(2)), | ||
# except this uses control output endpoint 0x00. | ||
[9] GetCtrlOutCompletionEvent() -> KObject; | ||
# Same as [#GetReportData](#nn::usb::ds::IDsEndpoint(3)), except this | ||
# uses control output endpoint 0x00. | ||
[10] GetCtrlOutReportData() -> (nn::usb::usb_report_entry<0x8> entries, u32 report_count); | ||
# Calls a function with both control endpoints (0x80 and 0x00) with the | ||
# same function. From strings: "m_pProtocol->Stall(0x80)" | ||
# "m_pProtocol->Stall(0x00)" | ||
[11] StallCtrl(); | ||
|
||
@version(5.0.0+) | ||
[12] AppendConfigurationData(u8 interface_number, nn::usb::usb_device_speed speed_mode, buffer<unknown, 5, 0> descriptor); | ||
} | ||
|
||
interface nn::usb::ds::IDsEndpoint { | ||
# The output urbId can be used while parsing the output of | ||
# [#GetReportData](#nn::usb::ds::IDsEndpoint(3)), after waiting for the | ||
# CompletionEvent to be signaled. | ||
# | ||
# The buffer address must be 0x10000-byte aligned. The input size | ||
# doesn't matter. | ||
# | ||
# 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`-byte 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). | ||
[0] PostBufferAsync(u32 size, u64 buffer) -> u32 urbId; | ||
[1] Cancel(); | ||
# Returns an event handle for polling the completion of | ||
# [#PostBufferAsync](#nn::usb::ds::IDsEndpoint(0)), even when it finished via | ||
# [#Stall](#nn::usb::ds::IDsEndpoint(4)). | ||
[2] GetCompletionEvent() -> KObject; | ||
# Returns 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](#nn::usb::ds::IDsEndpoint(0)) was used at least once. | ||
[3] GetReportData() -> (nn::usb::usb_report_entry<0x8> entries, u32 report_count); | ||
# Calls the same function used by [#StallCtrl](nn::usb::ds::IDsInterface(11)), | ||
# except this uses the endpoint associated with the current session. | ||
# | ||
# Stops in-progress data-transfer done by | ||
# [#PostBufferAsync](nn::usb::ds::IDsEndpoint(0)). | ||
[4] Stall(); | ||
# zlt likely stands for zero-length termination | ||
[5] SetZlt(bool); | ||
} |