Skip to content
This repository has been archived by the owner on Sep 21, 2021. It is now read-only.

Commit

Permalink
Add usb definitions
Browse files Browse the repository at this point in the history
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.
268 changes: 268 additions & 0 deletions ipcdefs/usb.id
@@ -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);
}

0 comments on commit 753f767

Please sign in to comment.