troposphere: add haze MTP server (#2095)
* troposphere: add haze MTP server
* haze: adjust banner, new lines between class sections, single-statement if
* haze: remove additional newlines between sections
* haze: avoid use of reference out parameter
* haze: console_main_loop: style
* haze: event_reactor, file_system_proxy, ptp: style
* haze: ptp_data_builder, ptp_object_database, ptp_object_heap, results, usb_session: style
* haze: event_reactor, ptp_data_parser, async_usb_server, ptp_object_database, ptp_object_heap: style
* haze: ptp_responder: style
* haze: usb_session: style
* haze: use svc defs from vapours
* haze: misc comments
* haze: fix pointer overflow check
* haze: fix copy paste error
* haze: use alignment, invalidate cached use values in console
* haze: fix stray hex constant
* haze: misc style
* haze: fixes for windows
* haze: add GetObjectPropsSupported, GetObjectPropDesc, GetObjectPropValue
* haze: add SetObjectPropValue
* haze: improve object database API
* haze: improve WriteVariableLengthData readability
* haze: fix directory renames on windows
* haze: ptp_object_database: fix collation
* haze: event_reactor: fix size validation
* haze: ptp_responder: improve documentation
* haze: ptp_responder: avoid unnecessary fs interaction in GetObjectPropValue
* haze: ptp_responder: fix object deletion on windows
* haze: tear down sessions on suspension
* haze: prefer more specific data types
* haze: misc
* haze: fix usb 3.0 support
* haze: improve host-to-device file transfer performance
* haze: report device serial
* haze: move static_assert to requires, fix alignment
2023-04-17 22:08:05 +01:00
|
|
|
/*
|
|
|
|
* Copyright (c) Atmosphère-NX
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
|
|
* under the terms and conditions of the GNU General Public License,
|
|
|
|
* version 2, as published by the Free Software Foundation.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope it will be useful, but WITHOUT
|
|
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
|
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
|
|
* more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
#include <haze.hpp>
|
|
|
|
|
|
|
|
namespace haze {
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
|
|
|
constexpr const u32 DefaultInterfaceNumber = 0;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
Result UsbSession::Initialize1x(const UsbCommsInterfaceInfo *info) {
|
|
|
|
struct usb_interface_descriptor interface_descriptor = {
|
|
|
|
.bLength = USB_DT_INTERFACE_SIZE,
|
|
|
|
.bDescriptorType = USB_DT_INTERFACE,
|
|
|
|
.bInterfaceNumber = DefaultInterfaceNumber,
|
|
|
|
.bInterfaceClass = info->bInterfaceClass,
|
|
|
|
.bInterfaceSubClass = info->bInterfaceSubClass,
|
|
|
|
.bInterfaceProtocol = info->bInterfaceProtocol,
|
|
|
|
};
|
|
|
|
|
|
|
|
struct usb_endpoint_descriptor endpoint_descriptor_in = {
|
|
|
|
.bLength = USB_DT_ENDPOINT_SIZE,
|
|
|
|
.bDescriptorType = USB_DT_ENDPOINT,
|
|
|
|
.bEndpointAddress = USB_ENDPOINT_IN,
|
|
|
|
.bmAttributes = USB_TRANSFER_TYPE_BULK,
|
|
|
|
.wMaxPacketSize = PtpUsbBulkHighSpeedMaxPacketLength,
|
|
|
|
};
|
|
|
|
|
|
|
|
struct usb_endpoint_descriptor endpoint_descriptor_out = {
|
|
|
|
.bLength = USB_DT_ENDPOINT_SIZE,
|
|
|
|
.bDescriptorType = USB_DT_ENDPOINT,
|
|
|
|
.bEndpointAddress = USB_ENDPOINT_OUT,
|
|
|
|
.bmAttributes = USB_TRANSFER_TYPE_BULK,
|
|
|
|
.wMaxPacketSize = PtpUsbBulkHighSpeedMaxPacketLength,
|
|
|
|
};
|
|
|
|
|
|
|
|
struct usb_endpoint_descriptor endpoint_descriptor_interrupt = {
|
|
|
|
.bLength = USB_DT_ENDPOINT_SIZE,
|
|
|
|
.bDescriptorType = USB_DT_ENDPOINT,
|
|
|
|
.bEndpointAddress = USB_ENDPOINT_IN,
|
|
|
|
.bmAttributes = USB_TRANSFER_TYPE_INTERRUPT,
|
|
|
|
.wMaxPacketSize = 0x18,
|
|
|
|
.bInterval = 0x4,
|
|
|
|
};
|
|
|
|
|
|
|
|
/* Set up interface. */
|
|
|
|
R_TRY(usbDsGetDsInterface(std::addressof(m_interface), std::addressof(interface_descriptor), "usb"));
|
|
|
|
|
|
|
|
/* Set up endpoints. */
|
|
|
|
R_TRY(usbDsInterface_GetDsEndpoint(m_interface, std::addressof(m_endpoints[UsbSessionEndpoint_Write]), std::addressof(endpoint_descriptor_in)));
|
|
|
|
R_TRY(usbDsInterface_GetDsEndpoint(m_interface, std::addressof(m_endpoints[UsbSessionEndpoint_Read]), std::addressof(endpoint_descriptor_out)));
|
|
|
|
R_TRY(usbDsInterface_GetDsEndpoint(m_interface, std::addressof(m_endpoints[UsbSessionEndpoint_Interrupt]), std::addressof(endpoint_descriptor_interrupt)));
|
|
|
|
|
|
|
|
R_RETURN(usbDsInterface_EnableInterface(m_interface));
|
|
|
|
}
|
|
|
|
|
|
|
|
Result UsbSession::Initialize5x(const UsbCommsInterfaceInfo *info) {
|
|
|
|
struct usb_interface_descriptor interface_descriptor = {
|
|
|
|
.bLength = USB_DT_INTERFACE_SIZE,
|
|
|
|
.bDescriptorType = USB_DT_INTERFACE,
|
|
|
|
.bInterfaceNumber = DefaultInterfaceNumber,
|
|
|
|
.bNumEndpoints = 3,
|
|
|
|
.bInterfaceClass = info->bInterfaceClass,
|
|
|
|
.bInterfaceSubClass = info->bInterfaceSubClass,
|
|
|
|
.bInterfaceProtocol = info->bInterfaceProtocol,
|
|
|
|
};
|
|
|
|
|
|
|
|
struct usb_endpoint_descriptor endpoint_descriptor_in = {
|
|
|
|
.bLength = USB_DT_ENDPOINT_SIZE,
|
|
|
|
.bDescriptorType = USB_DT_ENDPOINT,
|
|
|
|
.bEndpointAddress = USB_ENDPOINT_IN,
|
|
|
|
.bmAttributes = USB_TRANSFER_TYPE_BULK,
|
|
|
|
.wMaxPacketSize = PtpUsbBulkHighSpeedMaxPacketLength,
|
|
|
|
};
|
|
|
|
|
|
|
|
struct usb_endpoint_descriptor endpoint_descriptor_out = {
|
|
|
|
.bLength = USB_DT_ENDPOINT_SIZE,
|
|
|
|
.bDescriptorType = USB_DT_ENDPOINT,
|
|
|
|
.bEndpointAddress = USB_ENDPOINT_OUT,
|
|
|
|
.bmAttributes = USB_TRANSFER_TYPE_BULK,
|
|
|
|
.wMaxPacketSize = PtpUsbBulkHighSpeedMaxPacketLength,
|
|
|
|
};
|
|
|
|
|
|
|
|
struct usb_endpoint_descriptor endpoint_descriptor_interrupt = {
|
|
|
|
.bLength = USB_DT_ENDPOINT_SIZE,
|
|
|
|
.bDescriptorType = USB_DT_ENDPOINT,
|
|
|
|
.bEndpointAddress = USB_ENDPOINT_IN,
|
|
|
|
.bmAttributes = USB_TRANSFER_TYPE_INTERRUPT,
|
|
|
|
.wMaxPacketSize = 0x18,
|
|
|
|
.bInterval = 0x6,
|
|
|
|
};
|
|
|
|
|
|
|
|
struct usb_ss_endpoint_companion_descriptor endpoint_companion = {
|
|
|
|
.bLength = sizeof(struct usb_ss_endpoint_companion_descriptor),
|
|
|
|
.bDescriptorType = USB_DT_SS_ENDPOINT_COMPANION,
|
|
|
|
.bMaxBurst = 0x0f,
|
|
|
|
.bmAttributes = 0x00,
|
|
|
|
.wBytesPerInterval = 0x00,
|
|
|
|
};
|
|
|
|
|
|
|
|
struct usb_ss_endpoint_companion_descriptor endpoint_companion_interrupt = {
|
|
|
|
.bLength = sizeof(struct usb_ss_endpoint_companion_descriptor),
|
|
|
|
.bDescriptorType = USB_DT_SS_ENDPOINT_COMPANION,
|
|
|
|
.bMaxBurst = 0x00,
|
|
|
|
.bmAttributes = 0x00,
|
|
|
|
.wBytesPerInterval = 0x00,
|
|
|
|
};
|
|
|
|
|
|
|
|
R_TRY(usbDsRegisterInterface(std::addressof(m_interface)));
|
|
|
|
|
|
|
|
u8 iInterface;
|
|
|
|
R_TRY(usbDsAddUsbStringDescriptor(std::addressof(iInterface), "MTP"));
|
|
|
|
|
|
|
|
interface_descriptor.bInterfaceNumber = m_interface->interface_index;
|
|
|
|
interface_descriptor.iInterface = iInterface;
|
|
|
|
endpoint_descriptor_in.bEndpointAddress += interface_descriptor.bInterfaceNumber + 1;
|
|
|
|
endpoint_descriptor_out.bEndpointAddress += interface_descriptor.bInterfaceNumber + 1;
|
|
|
|
endpoint_descriptor_interrupt.bEndpointAddress += interface_descriptor.bInterfaceNumber + 2;
|
|
|
|
|
|
|
|
/* High speed config. */
|
|
|
|
R_TRY(usbDsInterface_AppendConfigurationData(m_interface, UsbDeviceSpeed_High, std::addressof(interface_descriptor), USB_DT_INTERFACE_SIZE));
|
|
|
|
R_TRY(usbDsInterface_AppendConfigurationData(m_interface, UsbDeviceSpeed_High, std::addressof(endpoint_descriptor_in), USB_DT_ENDPOINT_SIZE));
|
|
|
|
R_TRY(usbDsInterface_AppendConfigurationData(m_interface, UsbDeviceSpeed_High, std::addressof(endpoint_descriptor_out), USB_DT_ENDPOINT_SIZE));
|
|
|
|
R_TRY(usbDsInterface_AppendConfigurationData(m_interface, UsbDeviceSpeed_High, std::addressof(endpoint_descriptor_interrupt), USB_DT_ENDPOINT_SIZE));
|
|
|
|
|
|
|
|
/* Super speed config. */
|
|
|
|
endpoint_descriptor_in.wMaxPacketSize = PtpUsbBulkSuperSpeedMaxPacketLength;
|
|
|
|
endpoint_descriptor_out.wMaxPacketSize = PtpUsbBulkSuperSpeedMaxPacketLength;
|
|
|
|
R_TRY(usbDsInterface_AppendConfigurationData(m_interface, UsbDeviceSpeed_Super, std::addressof(interface_descriptor), USB_DT_INTERFACE_SIZE));
|
|
|
|
R_TRY(usbDsInterface_AppendConfigurationData(m_interface, UsbDeviceSpeed_Super, std::addressof(endpoint_descriptor_in), USB_DT_ENDPOINT_SIZE));
|
|
|
|
R_TRY(usbDsInterface_AppendConfigurationData(m_interface, UsbDeviceSpeed_Super, std::addressof(endpoint_companion), USB_DT_SS_ENDPOINT_COMPANION_SIZE));
|
|
|
|
R_TRY(usbDsInterface_AppendConfigurationData(m_interface, UsbDeviceSpeed_Super, std::addressof(endpoint_descriptor_out), USB_DT_ENDPOINT_SIZE));
|
|
|
|
R_TRY(usbDsInterface_AppendConfigurationData(m_interface, UsbDeviceSpeed_Super, std::addressof(endpoint_companion), USB_DT_SS_ENDPOINT_COMPANION_SIZE));
|
|
|
|
R_TRY(usbDsInterface_AppendConfigurationData(m_interface, UsbDeviceSpeed_Super, std::addressof(endpoint_descriptor_interrupt), USB_DT_ENDPOINT_SIZE));
|
|
|
|
R_TRY(usbDsInterface_AppendConfigurationData(m_interface, UsbDeviceSpeed_Super, std::addressof(endpoint_companion_interrupt), USB_DT_SS_ENDPOINT_COMPANION_SIZE));
|
|
|
|
|
|
|
|
/* Set up endpoints. */
|
|
|
|
R_TRY(usbDsInterface_RegisterEndpoint(m_interface, std::addressof(m_endpoints[UsbSessionEndpoint_Write]), endpoint_descriptor_in.bEndpointAddress));
|
|
|
|
R_TRY(usbDsInterface_RegisterEndpoint(m_interface, std::addressof(m_endpoints[UsbSessionEndpoint_Read]), endpoint_descriptor_out.bEndpointAddress));
|
|
|
|
R_TRY(usbDsInterface_RegisterEndpoint(m_interface, std::addressof(m_endpoints[UsbSessionEndpoint_Interrupt]), endpoint_descriptor_interrupt.bEndpointAddress));
|
|
|
|
|
|
|
|
R_RETURN(usbDsInterface_EnableInterface(m_interface));
|
|
|
|
}
|
|
|
|
|
|
|
|
Result UsbSession::Initialize(const UsbCommsInterfaceInfo *info, u16 id_vendor, u16 id_product) {
|
|
|
|
R_TRY(usbDsInitialize());
|
|
|
|
|
|
|
|
if (hosversionAtLeast(5, 0, 0)) {
|
|
|
|
/* Report language as US English. */
|
|
|
|
static const u16 supported_langs[1] = { 0x0409 };
|
|
|
|
R_TRY(usbDsAddUsbLanguageStringDescriptor(nullptr, supported_langs, util::size(supported_langs)));
|
|
|
|
|
|
|
|
/* Report strings. */
|
|
|
|
u8 iManufacturer, iProduct, iSerialNumber;
|
|
|
|
R_TRY(usbDsAddUsbStringDescriptor(std::addressof(iManufacturer), "Nintendo"));
|
|
|
|
R_TRY(usbDsAddUsbStringDescriptor(std::addressof(iProduct), "Nintendo Switch"));
|
2023-04-18 23:36:25 +01:00
|
|
|
R_TRY(usbDsAddUsbStringDescriptor(std::addressof(iSerialNumber), GetSerialNumber()));
|
troposphere: add haze MTP server (#2095)
* troposphere: add haze MTP server
* haze: adjust banner, new lines between class sections, single-statement if
* haze: remove additional newlines between sections
* haze: avoid use of reference out parameter
* haze: console_main_loop: style
* haze: event_reactor, file_system_proxy, ptp: style
* haze: ptp_data_builder, ptp_object_database, ptp_object_heap, results, usb_session: style
* haze: event_reactor, ptp_data_parser, async_usb_server, ptp_object_database, ptp_object_heap: style
* haze: ptp_responder: style
* haze: usb_session: style
* haze: use svc defs from vapours
* haze: misc comments
* haze: fix pointer overflow check
* haze: fix copy paste error
* haze: use alignment, invalidate cached use values in console
* haze: fix stray hex constant
* haze: misc style
* haze: fixes for windows
* haze: add GetObjectPropsSupported, GetObjectPropDesc, GetObjectPropValue
* haze: add SetObjectPropValue
* haze: improve object database API
* haze: improve WriteVariableLengthData readability
* haze: fix directory renames on windows
* haze: ptp_object_database: fix collation
* haze: event_reactor: fix size validation
* haze: ptp_responder: improve documentation
* haze: ptp_responder: avoid unnecessary fs interaction in GetObjectPropValue
* haze: ptp_responder: fix object deletion on windows
* haze: tear down sessions on suspension
* haze: prefer more specific data types
* haze: misc
* haze: fix usb 3.0 support
* haze: improve host-to-device file transfer performance
* haze: report device serial
* haze: move static_assert to requires, fix alignment
2023-04-17 22:08:05 +01:00
|
|
|
|
|
|
|
/* Send device descriptors */
|
|
|
|
struct usb_device_descriptor device_descriptor = {
|
|
|
|
.bLength = USB_DT_DEVICE_SIZE,
|
|
|
|
.bDescriptorType = USB_DT_DEVICE,
|
|
|
|
.bcdUSB = 0x0200,
|
|
|
|
.bDeviceClass = 0x00,
|
|
|
|
.bDeviceSubClass = 0x00,
|
|
|
|
.bDeviceProtocol = 0x00,
|
|
|
|
.bMaxPacketSize0 = 0x40,
|
|
|
|
.idVendor = id_vendor,
|
|
|
|
.idProduct = id_product,
|
|
|
|
.bcdDevice = 0x0100,
|
|
|
|
.iManufacturer = iManufacturer,
|
|
|
|
.iProduct = iProduct,
|
|
|
|
.iSerialNumber = iSerialNumber,
|
|
|
|
.bNumConfigurations = 0x01
|
|
|
|
};
|
|
|
|
R_TRY(usbDsSetUsbDeviceDescriptor(UsbDeviceSpeed_High, std::addressof(device_descriptor)));
|
|
|
|
|
|
|
|
device_descriptor.bcdUSB = 0x0300;
|
|
|
|
R_TRY(usbDsSetUsbDeviceDescriptor(UsbDeviceSpeed_Super, std::addressof(device_descriptor)));
|
|
|
|
|
|
|
|
/* Binary Object Store */
|
|
|
|
u8 bos[0x16] = {
|
|
|
|
0x05, /* .bLength */
|
|
|
|
USB_DT_BOS, /* .bDescriptorType */
|
|
|
|
0x16, 0x00, /* .wTotalLength */
|
|
|
|
0x02, /* .bNumDeviceCaps */
|
|
|
|
|
|
|
|
/* USB 2.0 */
|
|
|
|
0x07, /* .bLength */
|
|
|
|
USB_DT_DEVICE_CAPABILITY, /* .bDescriptorType */
|
|
|
|
0x02, /* .bDevCapabilityType */
|
|
|
|
0x02, 0x00, 0x00, 0x00, /* .bmAttributes */
|
|
|
|
|
|
|
|
/* USB 3.0 */
|
|
|
|
0x0a, /* .bLength */
|
|
|
|
USB_DT_DEVICE_CAPABILITY, /* .bDescriptorType */
|
|
|
|
0x03, /* .bDevCapabilityType */
|
|
|
|
0x00, /* .bmAttributes */
|
|
|
|
0x0c, 0x00, /* .wSpeedSupported */
|
|
|
|
0x03, /* .bFunctionalitySupport */
|
|
|
|
0x00, /* .bU1DevExitLat */
|
|
|
|
0x00, 0x00 /* .bU2DevExitLat */
|
|
|
|
};
|
|
|
|
R_TRY(usbDsSetBinaryObjectStore(bos, sizeof(bos)));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (hosversionAtLeast(5, 0, 0)) {
|
|
|
|
R_TRY(this->Initialize5x(info));
|
|
|
|
R_TRY(usbDsEnable());
|
|
|
|
} else {
|
|
|
|
R_TRY(this->Initialize1x(info));
|
|
|
|
}
|
|
|
|
|
|
|
|
R_SUCCEED();
|
|
|
|
}
|
|
|
|
|
|
|
|
void UsbSession::Finalize() {
|
|
|
|
usbDsExit();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool UsbSession::GetConfigured() const {
|
|
|
|
UsbState usb_state;
|
|
|
|
|
|
|
|
HAZE_R_ABORT_UNLESS(usbDsGetState(std::addressof(usb_state)));
|
|
|
|
|
|
|
|
return usb_state == UsbState_Configured;
|
|
|
|
}
|
|
|
|
|
|
|
|
Event *UsbSession::GetCompletionEvent(UsbSessionEndpoint ep) const {
|
|
|
|
return std::addressof(m_endpoints[ep]->CompletionEvent);
|
|
|
|
}
|
|
|
|
|
|
|
|
Result UsbSession::TransferAsync(UsbSessionEndpoint ep, void *buffer, u32 size, u32 *out_urb_id) {
|
|
|
|
R_RETURN(usbDsEndpoint_PostBufferAsync(m_endpoints[ep], buffer, size, out_urb_id));
|
|
|
|
}
|
|
|
|
|
|
|
|
Result UsbSession::GetTransferResult(UsbSessionEndpoint ep, u32 urb_id, u32 *out_transferred_size) {
|
|
|
|
UsbDsReportData report_data;
|
|
|
|
|
|
|
|
R_TRY(eventClear(std::addressof(m_endpoints[ep]->CompletionEvent)));
|
|
|
|
R_TRY(usbDsEndpoint_GetReportData(m_endpoints[ep], std::addressof(report_data)));
|
|
|
|
R_TRY(usbDsParseReportData(std::addressof(report_data), urb_id, nullptr, out_transferred_size));
|
|
|
|
|
|
|
|
R_SUCCEED();
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|