From 13b17a584802157b1ee410da4b603d54ac838960 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Sun, 7 Feb 2021 16:29:38 -0800 Subject: [PATCH] usb: add ds client api --- .../libstratosphere/include/stratosphere.hpp | 1 + .../include/stratosphere/usb.hpp | 21 + .../stratosphere/usb/ds/usb_i_ds_endpoint.hpp | 33 + .../usb/ds/usb_i_ds_interface.hpp | 41 + .../stratosphere/usb/ds/usb_i_ds_service.hpp | 44 + .../include/stratosphere/usb/usb_device.hpp | 145 ++++ .../stratosphere/usb/usb_device_types.hpp | 60 ++ .../include/stratosphere/usb/usb_limits.hpp | 34 + .../include/stratosphere/usb/usb_types.hpp | 214 +++++ .../source/usb/impl/usb_util.hpp | 45 + .../libstratosphere/source/usb/usb_device.cpp | 803 ++++++++++++++++++ .../source/usb/usb_remote_ds_endpoint.cpp | 61 ++ .../source/usb/usb_remote_ds_endpoint.hpp | 37 + .../source/usb/usb_remote_ds_interface.cpp | 139 +++ .../source/usb/usb_remote_ds_interface.hpp | 48 ++ .../source/usb/usb_remote_ds_root_service.cpp | 33 + .../source/usb/usb_remote_ds_root_service.hpp | 36 + .../source/usb/usb_remote_ds_service.cpp | 114 +++ .../source/usb/usb_remote_ds_service.hpp | 46 + .../libvapours/include/vapours/results.hpp | 1 + .../include/vapours/results/usb_results.hpp | 37 + 21 files changed, 1993 insertions(+) create mode 100644 libraries/libstratosphere/include/stratosphere/usb.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/usb/ds/usb_i_ds_endpoint.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/usb/ds/usb_i_ds_interface.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/usb/ds/usb_i_ds_service.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/usb/usb_device.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/usb/usb_device_types.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/usb/usb_limits.hpp create mode 100644 libraries/libstratosphere/include/stratosphere/usb/usb_types.hpp create mode 100644 libraries/libstratosphere/source/usb/impl/usb_util.hpp create mode 100644 libraries/libstratosphere/source/usb/usb_device.cpp create mode 100644 libraries/libstratosphere/source/usb/usb_remote_ds_endpoint.cpp create mode 100644 libraries/libstratosphere/source/usb/usb_remote_ds_endpoint.hpp create mode 100644 libraries/libstratosphere/source/usb/usb_remote_ds_interface.cpp create mode 100644 libraries/libstratosphere/source/usb/usb_remote_ds_interface.hpp create mode 100644 libraries/libstratosphere/source/usb/usb_remote_ds_root_service.cpp create mode 100644 libraries/libstratosphere/source/usb/usb_remote_ds_root_service.hpp create mode 100644 libraries/libstratosphere/source/usb/usb_remote_ds_service.cpp create mode 100644 libraries/libstratosphere/source/usb/usb_remote_ds_service.hpp create mode 100644 libraries/libvapours/include/vapours/results/usb_results.hpp diff --git a/libraries/libstratosphere/include/stratosphere.hpp b/libraries/libstratosphere/include/stratosphere.hpp index c6bbe862b..f9c66eb31 100644 --- a/libraries/libstratosphere/include/stratosphere.hpp +++ b/libraries/libstratosphere/include/stratosphere.hpp @@ -79,6 +79,7 @@ #include #include #include +#include #include /* Include FS last. */ diff --git a/libraries/libstratosphere/include/stratosphere/usb.hpp b/libraries/libstratosphere/include/stratosphere/usb.hpp new file mode 100644 index 000000000..8939f64b5 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/usb.hpp @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2018-2020 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 . + */ +#pragma once + +#include +#include +#include +#include diff --git a/libraries/libstratosphere/include/stratosphere/usb/ds/usb_i_ds_endpoint.hpp b/libraries/libstratosphere/include/stratosphere/usb/ds/usb_i_ds_endpoint.hpp new file mode 100644 index 000000000..f4c72a0a5 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/usb/ds/usb_i_ds_endpoint.hpp @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2018-2020 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 . + */ +#pragma once +#include +#include +#include +#include + +#define AMS_USB_I_DS_ENDPOINT_INTERFACE_INFO(C, H) \ + AMS_SF_METHOD_INFO(C, H, 0, Result, PostBufferAsync, (sf::Out out_urb_id, u64 address, u32 size), (out_urb_id, address, size)) \ + AMS_SF_METHOD_INFO(C, H, 1, Result, Cancel, (), ()) \ + AMS_SF_METHOD_INFO(C, H, 2, Result, GetCompletionEvent, (sf::OutCopyHandle out), (out)) \ + AMS_SF_METHOD_INFO(C, H, 3, Result, GetUrbReport, (sf::Out out), (out)) \ + AMS_SF_METHOD_INFO(C, H, 4, Result, Stall, (), ()) \ + AMS_SF_METHOD_INFO(C, H, 5, Result, SetZlt, (bool zlt), (zlt)) + +/* TODO: Deprecated interface? */ + +AMS_SF_DEFINE_INTERFACE(ams::usb::ds, IDsEndpoint, AMS_USB_I_DS_ENDPOINT_INTERFACE_INFO) + diff --git a/libraries/libstratosphere/include/stratosphere/usb/ds/usb_i_ds_interface.hpp b/libraries/libstratosphere/include/stratosphere/usb/ds/usb_i_ds_interface.hpp new file mode 100644 index 000000000..ceb30c184 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/usb/ds/usb_i_ds_interface.hpp @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2018-2020 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 . + */ +#pragma once +#include +#include +#include +#include +#include + +#define AMS_USB_I_DS_INTERFACE_INTERFACE_INFO(C, H) \ + AMS_SF_METHOD_INFO(C, H, 0, Result, RegisterEndpoint, (u8 endpoint_address, sf::Out> out), (endpoint_address, out)) \ + AMS_SF_METHOD_INFO(C, H, 1, Result, GetSetupEvent, (sf::OutCopyHandle out), (out)) \ + AMS_SF_METHOD_INFO(C, H, 2, Result, GetSetupPacket, (const sf::OutBuffer &out), (out)) \ + AMS_SF_METHOD_INFO(C, H, 3, Result, CtrlInAsync, (sf::Out out_urb_id, u64 address, u32 size), (out_urb_id, address, size)) \ + AMS_SF_METHOD_INFO(C, H, 4, Result, CtrlOutAsync, (sf::Out out_urb_id, u64 address, u32 size), (out_urb_id, address, size)) \ + AMS_SF_METHOD_INFO(C, H, 5, Result, GetCtrlInCompletionEvent, (sf::OutCopyHandle out), (out)) \ + AMS_SF_METHOD_INFO(C, H, 6, Result, GetCtrlInUrbReport, (sf::Out out), (out)) \ + AMS_SF_METHOD_INFO(C, H, 7, Result, GetCtrlOutCompletionEvent, (sf::OutCopyHandle out), (out)) \ + AMS_SF_METHOD_INFO(C, H, 8, Result, GetCtrlOutUrbReport, (sf::Out out), (out)) \ + AMS_SF_METHOD_INFO(C, H, 9, Result, CtrlStall, (), ()) \ + AMS_SF_METHOD_INFO(C, H, 10, Result, AppendConfigurationData, (u8 bInterfaceNumber, usb::UsbDeviceSpeed device_speed, const sf::InBuffer &data), (bInterfaceNumber, device_speed, data)) \ + AMS_SF_METHOD_INFO(C, H, 65000, Result, Enable, (), ()) \ + AMS_SF_METHOD_INFO(C, H, 65001, Result, Disable, (), ()) + +/* TODO: Deprecated interface? */ + +AMS_SF_DEFINE_INTERFACE(ams::usb::ds, IDsInterface, AMS_USB_I_DS_INTERFACE_INTERFACE_INFO) + diff --git a/libraries/libstratosphere/include/stratosphere/usb/ds/usb_i_ds_service.hpp b/libraries/libstratosphere/include/stratosphere/usb/ds/usb_i_ds_service.hpp new file mode 100644 index 000000000..32ec155b9 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/usb/ds/usb_i_ds_service.hpp @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2018-2020 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 . + */ +#pragma once +#include +#include +#include +#include +#include + +#define AMS_USB_I_DS_SERVICE_INTERFACE_INFO(C, H) \ + AMS_SF_METHOD_INFO(C, H, 0, Result, Bind, (usb::ComplexId complex_id, sf::CopyHandle process_h), (complex_id, process_h)) \ + AMS_SF_METHOD_INFO(C, H, 1, Result, RegisterInterface, (sf::Out> out, u8 bInterfaceNumber), (out, bInterfaceNumber)) \ + AMS_SF_METHOD_INFO(C, H, 2, Result, GetStateChangeEvent, (sf::OutCopyHandle out), (out)) \ + AMS_SF_METHOD_INFO(C, H, 3, Result, GetState, (sf::Out out), (out)) \ + AMS_SF_METHOD_INFO(C, H, 4, Result, ClearDeviceData, (), ()) \ + AMS_SF_METHOD_INFO(C, H, 5, Result, AddUsbStringDescriptor, (sf::Out out, const sf::InBuffer &desc), (out, desc)) \ + AMS_SF_METHOD_INFO(C, H, 6, Result, DeleteUsbStringDescriptor, (u8 index), (index)) \ + AMS_SF_METHOD_INFO(C, H, 7, Result, SetUsbDeviceDescriptor, (const sf::InBuffer &desc, usb::UsbDeviceSpeed speed), (desc, speed)) \ + AMS_SF_METHOD_INFO(C, H, 8, Result, SetBinaryObjectStore, (const sf::InBuffer &bos), (bos)) \ + AMS_SF_METHOD_INFO(C, H, 9, Result, Enable, (), ()) \ + AMS_SF_METHOD_INFO(C, H, 10, Result, Disable, (), ()) + +/* TODO: Deprecated interface? */ + +AMS_SF_DEFINE_INTERFACE(ams::usb::ds, IDsService, AMS_USB_I_DS_SERVICE_INTERFACE_INFO) + +#define AMS_USB_I_DS_ROOT_SERVICE_INTERFACE_INFO(C, H) \ + AMS_SF_METHOD_INFO(C, H, 0, Result, GetService, (sf::Out> out), (out)) + +AMS_SF_DEFINE_INTERFACE(ams::usb::ds, IDsRootService, AMS_USB_I_DS_ROOT_SERVICE_INTERFACE_INFO) + diff --git a/libraries/libstratosphere/include/stratosphere/usb/usb_device.hpp b/libraries/libstratosphere/include/stratosphere/usb/usb_device.hpp new file mode 100644 index 000000000..0549eb4e1 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/usb/usb_device.hpp @@ -0,0 +1,145 @@ +/* + * Copyright (c) 2018-2020 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 . + */ +#pragma once +#include +#include +#include +#include +#include +#include + +namespace ams::usb { + + class DsInterface; + class DsEndpoint; + + class DsClient { + friend class DsInterface; + friend class DsEndpoint; + private: + /* NOTE: Nintendo uses a UnitHeap here on newer firmware versions. */ + /* For now, we'll use an ExpHeap and do it the old way. */ + sf::ExpHeapAllocator m_allocator{}; + u8 m_heap_buffer[32_KB]; + lmem::HeapHandle m_heap_handle{}; + sf::SharedPointer m_root_service{}; + sf::SharedPointer m_ds_service{}; + bool m_is_initialized{false}; + std::atomic m_reference_count{0}; + os::SystemEventType m_state_change_event{}; + DsInterface *m_interfaces[DsLimitMaxInterfacesPerConfigurationCount]{}; + bool m_is_enabled{false}; + public: + DsClient() = default; + ~DsClient() { /* ... */ } + public: + Result Initialize(ComplexId complex_id); + Result Finalize(); + + bool IsInitialized(); + + Result EnableDevice(); + Result DisableDevice(); + + os::SystemEventType *GetStateChangeEvent(); + Result GetState(UsbState *out); + + Result ClearDeviceData(); + + Result AddUsbStringDescriptor(u8 *out_index, UsbStringDescriptor *desc); + Result DeleteUsbStringDescriptor(u8 index); + + Result SetUsbDeviceDescriptor(UsbDeviceDescriptor *desc, UsbDeviceSpeed speed); + + Result SetBinaryObjectStore(u8 *data, int size); + private: + Result AddInterface(DsInterface *intf, sf::SharedPointer *out_srv, uint8_t bInterfaceNumber); + Result DeleteInterface(uint8_t bInterfaceNumber); + }; + + class DsInterface { + friend class DsEndpoint; + private: + DsClient *m_client; + sf::SharedPointer m_interface; + bool m_is_initialized; + std::atomic m_reference_count; + os::SystemEventType m_setup_event; + os::SystemEventType m_ctrl_in_completion_event; + os::SystemEventType m_ctrl_out_completion_event; + UrbReport m_report; + u8 m_interface_num; + DsEndpoint *m_endpoints[UsbLimitMaxEndpointsCount]; + public: + DsInterface() : m_client(nullptr), m_is_initialized(false), m_reference_count(0) { /* ... */ } + ~DsInterface() { /* ... */ } + public: + Result Initialize(DsClient *client, u8 bInterfaceNumber); + Result Finalize(); + + Result AppendConfigurationData(UsbDeviceSpeed speed, void *data, u32 size); + + bool IsInitialized(); + + os::SystemEventType *GetSetupEvent(); + Result GetSetupPacket(UsbCtrlRequest *out); + + Result Enable(); + Result Disable(); + + Result CtrlRead(u32 *out_transferred, void *dst, u32 size); + Result CtrlWrite(u32 *out_transferred, void *dst, u32 size); + Result CtrlDone(); + Result CtrlStall(); + private: + Result AddEndpoint(DsEndpoint *ep, u8 bEndpointAddress, sf::SharedPointer *out); + Result DeleteEndpoint(u8 bEndpointAddress); + + Result CtrlIn(u32 *out_transferred, void *dst, u32 size); + Result CtrlOut(u32 *out_transferred, void *dst, u32 size); + }; + + class DsEndpoint { + private: + bool m_is_initialized; + bool m_is_new_format; + std::atomic m_reference_count; + DsInterface *m_interface; + sf::SharedPointer m_endpoint; + u8 m_address; + os::SystemEventType m_completion_event; + os::SystemEventType m_unknown_event; + public: + DsEndpoint() : m_is_initialized(false), m_is_new_format(false), m_reference_count(0) { /* ... */ } + public: + Result Initialize(DsInterface *interface, u8 bEndpointAddress); + Result Finalize(); + + bool IsInitialized(); + + Result PostBuffer(u32 *out_transferred, void *buf, u32 size); + Result PostBufferAsync(u32 *out_urb_id, void *buf, u32 size); + + os::SystemEventType *GetCompletionEvent(); + + Result GetUrbReport(UrbReport *out); + + Result Cancel(); + + Result SetZeroLengthTransfer(bool zlt); + }; + +} diff --git a/libraries/libstratosphere/include/stratosphere/usb/usb_device_types.hpp b/libraries/libstratosphere/include/stratosphere/usb/usb_device_types.hpp new file mode 100644 index 000000000..26c67f595 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/usb/usb_device_types.hpp @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2018-2020 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 . + */ +#pragma once +#include +#include +#include + +namespace ams::usb { + + constexpr inline u8 InterfaceNumberAuto = DsLimitMaxInterfacesPerConfigurationCount; + constexpr inline u8 EndpointAddressAutoIn = UsbEndpointAddressMask_DirDevicetoHost; + constexpr inline u8 EndpointAddressAutoOut = UsbEndpointAddressMask_DirHostToDevice; + + enum UrbStatus { + UrbStatus_Invalid = 0, + UrbStatus_Pending = 1, + UrbStatus_Running = 2, + UrbStatus_Finished = 3, + UrbStatus_Cancelled = 4, + UrbStatus_Failed = 5, + }; + + struct UrbReport { + struct Report { + u32 id; + u32 requested_size; + u32 transferred_size; + UrbStatus status; + } reports[DsLimitRingSize]; + u32 count; + }; + + enum DsString { + DsString_Max = 0x20, + }; + + struct DsVidPidBcd { + uint16_t idVendor; + uint16_t idProduct; + uint16_t bcdDevice; + + char manufacturer[DsString_Max]; + char product[DsString_Max]; + char serial_number[DsString_Max]; + }; + +} diff --git a/libraries/libstratosphere/include/stratosphere/usb/usb_limits.hpp b/libraries/libstratosphere/include/stratosphere/usb/usb_limits.hpp new file mode 100644 index 000000000..f1b0ac8fb --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/usb/usb_limits.hpp @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2018-2020 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 . + */ +#pragma once +#include +#include + +namespace ams::usb { + + constexpr inline int HwLimitDmaBufferAlignmentSize = dd::DeviceAddressSpaceMemoryRegionAlignment; + constexpr inline int HwLimitDataCacheLineSize = 0x40; + constexpr inline int HwLimitMaxPortCount = 0x4; + + constexpr inline int UsbLimitMaxEndpointsCount = 0x20; + constexpr inline int UsbLimitMaxEndpointPairCount = 0x10; + + constexpr inline int DsLimitMaxConfigurationsPerDeviceCount = 1; + constexpr inline int DsLimitMaxInterfacesPerConfigurationCount = 4; + constexpr inline int DsLimitMaxNameSize = 0x40; + constexpr inline int DsLimitRingSize = 8; + +} diff --git a/libraries/libstratosphere/include/stratosphere/usb/usb_types.hpp b/libraries/libstratosphere/include/stratosphere/usb/usb_types.hpp new file mode 100644 index 000000000..26ccecad6 --- /dev/null +++ b/libraries/libstratosphere/include/stratosphere/usb/usb_types.hpp @@ -0,0 +1,214 @@ +/* + * Copyright (c) 2018-2020 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 . + */ +#pragma once +#include +#include + +namespace ams::usb { + + constexpr ALWAYS_INLINE bool IsDmaAligned(u64 address) { + return util::IsAligned(address, static_cast(HwLimitDmaBufferAlignmentSize)); + } + + enum ComplexId { + ComplexId_Tegra21x = 2, + }; + + enum UsbDescriptorType { + UsbDescriptorType_Device = 1, + UsbDescriptorType_Config = 2, + UsbDescriptorType_String = 3, + UsbDescriptorType_Interface = 4, + UsbDescriptorType_Endpoint = 5, + UsbDescriptorType_DeviceQualifier = 6, + UsbDescriptorType_OtherSpeedConfig = 7, + UsbDescriptorType_InterfacePower = 8, + UsbDescriptorType_Otg = 9, + UsbDescriptorType_Debug = 10, + UsbDescriptorType_InterfaceAssociation = 11, + UsbDescriptorType_Bos = 15, + UsbDescriptorType_DeviceCapability = 16, + + UsbDescriptorType_Hid = 33, + UsbDescriptorType_Report = 34, + UsbDescriptorType_Physical = 35, + + UsbDescriptorType_Hub = 41, + + UsbDescriptorType_EndpointCompanion = 48, + UsbDescriptorType_IsocEndpointCompanion = 49, + }; + + struct UsbDescriptorHeader { + uint8_t bLength; + uint8_t bDescriptorType; + } PACKED; + + struct UsbInterfaceDescriptor { + uint8_t bLength; + uint8_t bDescriptorType; + uint8_t bInterfaceNumber; + uint8_t bAlternateSetting; + uint8_t bNumEndpoints; + uint8_t bInterfaceClass; + uint8_t bInterfaceSubClass; + uint8_t bInterfaceProtocol; + uint8_t iInterface; + } PACKED; + + struct UsbEndpointDescriptor { + uint8_t bLength; + uint8_t bDescriptorType; + uint8_t bEndpointAddress; + uint8_t bmAttributes; + uint16_t wMaxPacketSize; + uint8_t bInterval; + } PACKED; + + struct UsbDeviceDescriptor { + uint8_t bLength; + uint8_t bDescriptorType; + uint16_t bcdUSB; + uint8_t bDeviceClass; + uint8_t bDeviceSubClass; + uint8_t bDeviceProtocol; + uint8_t bMaxPacketSize0; + uint16_t idVendor; + uint16_t idProduct; + uint16_t bcdDevice; + uint8_t iManufacturer; + uint8_t iProduct; + uint8_t iSerialNumber; + uint8_t bNumConfigurations; + } PACKED; + + struct UsbConfigDescriptor { + uint8_t bLength; + uint8_t bDescriptorType; + uint16_t wTotalLength; + uint8_t bNumInterfaces; + uint8_t bConfigurationValue; + uint8_t iConfiguration; + uint8_t bmAttributes; + uint8_t bMaxPower; + } PACKED; + + struct UsbEndpointCompanionDescriptor { + uint8_t bLength; + uint8_t bDescriptorType; + uint8_t bMaxBurst; + uint8_t bmAttributes; + uint16_t wBytesPerInterval; + } PACKED; + + struct UsbStringDescriptor { + uint8_t bLength; + uint8_t bDescriptorType; + uint16_t wData[DsLimitMaxNameSize]; + } PACKED; + + struct UsbCtrlRequest { + uint8_t bmRequestType; + uint8_t bRequest; + uint16_t wValue; + uint16_t wIndex; + uint16_t wLength; + } PACKED; + + enum UsbState { + UsbState_Detached = 0, + UsbState_Attached = 1, + UsbState_Powered = 2, + UsbState_Default = 3, + UsbState_Address = 4, + UsbState_Configured = 5, + UsbState_Suspended = 6, + }; + + enum UsbDescriptorSize { + UsbDescriptorSize_Interface = sizeof(UsbInterfaceDescriptor), + UsbDescriptorSize_Endpoint = sizeof(UsbEndpointDescriptor), + UsbDescriptorSize_Device = sizeof(UsbDeviceDescriptor), + UsbDescriptorSize_Config = sizeof(UsbConfigDescriptor), + UsbDescriptorSize_EndpointCompanion = sizeof(UsbEndpointCompanionDescriptor), + }; + + enum UsbDeviceSpeed { + UsbDeviceSpeed_Invalid = 0, + UsbDeviceSpeed_Low = 1, + UsbDeviceSpeed_Full = 2, + UsbDeviceSpeed_High = 3, + UsbDeviceSpeed_Super = 4, + UsbDeviceSpeed_SuperPlus = 5, + }; + + + enum UsbEndpointAddressMask { + UsbEndpointAddressMask_EndpointNumber = (0xF << 0), + + UsbEndpointAddressMask_Dir = (0x1 << 7), + + UsbEndpointAddressMask_DirHostToDevice = (0x0 << 7), + UsbEndpointAddressMask_DirDevicetoHost = (0x1 << 7), + }; + + enum UsbEndpointDirection { + UsbEndpointDirection_Invalid = 0, + UsbEndpointDirection_ToDevice = 1, + UsbEndpointDirection_ToHost = 2, + UsbEndpointDirection_Control = 3, + }; + + constexpr inline u8 UsbGetEndpointNumber(const UsbEndpointDescriptor *desc) { + return desc->bEndpointAddress & UsbEndpointAddressMask_EndpointNumber; + } + + constexpr inline bool UsbEndpointIsHostToDevice(const UsbEndpointDescriptor *desc) { + return (desc->bEndpointAddress & UsbEndpointAddressMask_Dir) == UsbEndpointAddressMask_DirHostToDevice; + } + + constexpr inline bool UsbEndpointIsDeviceToHost(const UsbEndpointDescriptor *desc) { + return (desc->bEndpointAddress & UsbEndpointAddressMask_Dir) == UsbEndpointAddressMask_DirDevicetoHost; + } + + constexpr inline u8 UsbGetEndpointAddress(u8 number, UsbEndpointDirection dir) { + u8 val = static_cast(number & UsbEndpointAddressMask_EndpointNumber); + if (dir == UsbEndpointDirection_ToHost) { + val |= UsbEndpointAddressMask_DirDevicetoHost; + } else { + val |= UsbEndpointAddressMask_DirHostToDevice; + } + return val; + } + + constexpr inline UsbEndpointDirection GetUsbEndpointDirection(const UsbEndpointDescriptor *desc) { + if (UsbEndpointIsDeviceToHost(desc)) { + return UsbEndpointDirection_ToHost; + } else { + return UsbEndpointDirection_ToDevice; + } + } + + constexpr inline bool UsbEndpointIsValid(const UsbEndpointDescriptor *desc) { + return desc != nullptr && desc->bLength >= UsbDescriptorSize_Endpoint && desc->bEndpointAddress != 0; + } + + constexpr inline void UsbMarkEndpointInvalid(UsbEndpointDescriptor *desc) { + desc->bLength = 0; + desc->bEndpointAddress = 0; + } + +} diff --git a/libraries/libstratosphere/source/usb/impl/usb_util.hpp b/libraries/libstratosphere/source/usb/impl/usb_util.hpp new file mode 100644 index 000000000..e9ac43514 --- /dev/null +++ b/libraries/libstratosphere/source/usb/impl/usb_util.hpp @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2018-2020 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 . + */ +#pragma once +#include + +namespace ams::usb::impl { + + constexpr int GetEndpointIndex(u8 address) { + int idx = address & UsbEndpointAddressMask_EndpointNumber; + if ((address & UsbEndpointAddressMask_Dir) == UsbEndpointAddressMask_DirDevicetoHost) { + idx += 0x10; + } + return idx; + } + + template + class ScopedRefCount { + NON_COPYABLE(ScopedRefCount); + NON_MOVEABLE(ScopedRefCount); + private: + T &m_obj; + public: + ALWAYS_INLINE ScopedRefCount(T &o) : m_obj(o) { + ++m_obj; + } + + ALWAYS_INLINE ~ScopedRefCount() { + --m_obj; + } + }; + +} diff --git a/libraries/libstratosphere/source/usb/usb_device.cpp b/libraries/libstratosphere/source/usb/usb_device.cpp new file mode 100644 index 000000000..cbc52439c --- /dev/null +++ b/libraries/libstratosphere/source/usb/usb_device.cpp @@ -0,0 +1,803 @@ +/* + * Copyright (c) 2018-2020 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 . + */ +#include +#include "usb_remote_ds_root_service.hpp" +#include "usb_remote_ds_service.hpp" +#include "impl/usb_util.hpp" + +namespace ams::usb { + + Result DsClient::Initialize(ComplexId complex_id) { + /* Clear interfaces. */ + for (size_t i = 0; i < util::size(m_interfaces); ++i) { + m_interfaces[i] = nullptr; + } + + /* Initialize heap. */ + m_heap_handle = lmem::CreateExpHeap(m_heap_buffer, sizeof(m_heap_buffer), lmem::CreateOption_None); + R_UNLESS(m_heap_handle != nullptr, usb::ResultMemAllocFailure()); + + /* Attach our allocator. */ + m_allocator.Attach(m_heap_handle); + + /* Connect to usb:ds. */ + /* NOTE: Here, Nintendo does m_domain.InitializeByDomain<...>(...); m_domain.SetSessionCount(1); */ + { + Service srv; + R_TRY(sm::GetService(std::addressof(srv), sm::ServiceName::Encode("usb:ds"))); + + R_ABORT_UNLESS(serviceConvertToDomain(std::addressof(srv))); + + using Allocator = decltype(m_allocator); + using ObjectFactory = sf::ObjectFactory; + + if (hos::GetVersion() >= hos::Version_11_0_0) { + m_root_service = ObjectFactory::CreateSharedEmplaced(std::addressof(m_allocator), srv, std::addressof(m_allocator)); + + R_TRY(m_root_service->GetService(std::addressof(m_ds_service))); + } else { + m_ds_service = ObjectFactory::CreateSharedEmplaced(std::addressof(m_allocator), srv, std::addressof(m_allocator)); + } + } + + /* Bind the client process. */ + R_TRY(m_ds_service->Bind(complex_id, dd::GetCurrentProcessHandle())); + + /* Get the state change event. */ + sf::CopyHandle event_handle; + R_TRY(m_ds_service->GetStateChangeEvent(std::addressof(event_handle))); + + /* Attach the state change event handle to our event. */ + os::AttachReadableHandleToSystemEvent(std::addressof(m_state_change_event), event_handle.GetValue(), true, os::EventClearMode_ManualClear); + + /* Mark ourselves as initialized. */ + m_is_initialized = true; + + return ResultSuccess(); + } + + Result DsClient::Finalize() { + /* Create a scoped reference. */ + impl::ScopedRefCount scoped_ref(m_reference_count); + + /* Check that we're initialized. */ + R_UNLESS(m_is_initialized, usb::ResultNotInitialized()); + + /* Disable and finalize all interfaces. */ + R_TRY(this->DisableDevice()); + for (size_t i = 0; i < util::size(m_interfaces); ++i) { + if (m_interfaces[i] != nullptr) { + R_TRY(m_interfaces[i]->Finalize()); + } + } + + /* Check our reference count .*/ + R_UNLESS(m_reference_count <= 1, usb::ResultResourceBusy()); + + /* Finalize members. */ + m_is_initialized = false; + os::DestroySystemEvent(std::addressof(m_state_change_event)); + lmem::DestroyExpHeap(m_heap_handle); + m_heap_handle = nullptr; + + /* Destroy interface objects. */ + m_ds_service = nullptr; + m_root_service = nullptr; + + return ResultSuccess(); + } + + bool DsClient::IsInitialized() { + return m_is_initialized; + } + + Result DsClient::EnableDevice() { + /* Create a scoped reference. */ + impl::ScopedRefCount scoped_ref(m_reference_count); + + /* Check that we're initialized. */ + R_UNLESS(m_is_initialized, usb::ResultNotInitialized()); + + /* Enable all interfaces. */ + if (hos::GetVersion() < hos::Version_11_0_0) { + for (size_t i = 0; i < util::size(m_interfaces); ++i) { + if (m_interfaces[i] != nullptr) { + R_TRY(m_interfaces[i]->Enable()); + } + } + } + + /* Enable the device. */ + R_TRY(m_ds_service->Enable()); + + /* Mark disabled. */ + m_is_enabled = true; + return ResultSuccess(); + } + + Result DsClient::DisableDevice() { + /* Create a scoped reference. */ + impl::ScopedRefCount scoped_ref(m_reference_count); + + /* Check that we're initialized. */ + R_UNLESS(m_is_initialized, usb::ResultNotInitialized()); + + /* Disable the device. */ + R_TRY(m_ds_service->Disable()); + + /* Disable all interfaces. */ + if (hos::GetVersion() < hos::Version_11_0_0) { + for (size_t i = 0; i < util::size(m_interfaces); ++i) { + if (m_interfaces[i] != nullptr) { + R_TRY(m_interfaces[i]->Disable()); + } + } + } + + /* Mark disabled. */ + m_is_enabled = false; + return ResultSuccess(); + } + + os::SystemEventType *DsClient::GetStateChangeEvent() { + /* Create a scoped reference. */ + impl::ScopedRefCount scoped_ref(m_reference_count); + + return m_is_initialized ? std::addressof(m_state_change_event) : nullptr; + } + + Result DsClient::GetState(UsbState *out) { + /* Create a scoped reference. */ + impl::ScopedRefCount scoped_ref(m_reference_count); + + /* Check that we're initialized. */ + R_UNLESS(m_is_initialized, usb::ResultNotInitialized()); + + /* Check that we have a service. */ + AMS_ABORT_UNLESS(m_ds_service != nullptr); + + return m_ds_service->GetState(out); + } + + Result DsClient::ClearDeviceData() { + return m_ds_service->ClearDeviceData(); + } + + Result DsClient::AddUsbStringDescriptor(u8 *out_index, UsbStringDescriptor *desc) { + return m_ds_service->AddUsbStringDescriptor(out_index, sf::InBuffer(reinterpret_cast(desc), sizeof(*desc))); + } + + Result DsClient::DeleteUsbStringDescriptor(u8 index) { + return m_ds_service->DeleteUsbStringDescriptor(index); + } + + Result DsClient::SetUsbDeviceDescriptor(UsbDeviceDescriptor *desc, UsbDeviceSpeed speed) { + return m_ds_service->SetUsbDeviceDescriptor(sf::InBuffer(reinterpret_cast(desc), sizeof(*desc)), speed); + } + + Result DsClient::SetBinaryObjectStore(u8 *data, int size) { + return m_ds_service->SetBinaryObjectStore(sf::InBuffer(reinterpret_cast(data), size)); + } + + Result DsClient::AddInterface(DsInterface *intf, sf::SharedPointer *out_srv, uint8_t bInterfaceNumber) { + /* Create a scoped reference. */ + impl::ScopedRefCount scoped_ref(m_reference_count); + + /* Check that we're initialized. */ + R_UNLESS(m_is_initialized, usb::ResultNotInitialized()); + + /* Check that we have a service. */ + AMS_ABORT_UNLESS(m_ds_service != nullptr); + + /* Register the interface. */ + R_TRY(m_ds_service->RegisterInterface(out_srv, bInterfaceNumber)); + + /* Set interface. */ + m_interfaces[bInterfaceNumber] = intf; + + return ResultSuccess(); + } + + Result DsClient::DeleteInterface(uint8_t bInterfaceNumber) { + /* Create a scoped reference. */ + impl::ScopedRefCount scoped_ref(m_reference_count); + + /* Check that we're initialized. */ + R_UNLESS(m_is_initialized, usb::ResultNotInitialized()); + + /* Check that we have the interface. */ + R_UNLESS(m_interfaces[bInterfaceNumber] != nullptr, usb::ResultOperationDenied()); + + /* Clear the interface. */ + m_interfaces[bInterfaceNumber] = nullptr; + + return ResultSuccess(); + } + + Result DsInterface::Initialize(DsClient *client, u8 bInterfaceNumber) { + /* Check that we haven't already initialized. */ + R_UNLESS(!m_is_initialized, usb::ResultAlreadyInitialized()); + + /* Set our client. */ + m_client = client; + + /* Clear all endpoints. */ + for (size_t i = 0; i < util::size(m_endpoints); ++i) { + m_endpoints[i] = nullptr; + } + + /* Set our interface number. */ + R_UNLESS(bInterfaceNumber < util::size(m_client->m_interfaces), usb::ResultInvalidParameter()); + m_interface_num = bInterfaceNumber; + + /* Add the interface. */ + R_TRY(m_client->AddInterface(this, std::addressof(m_interface), m_interface_num)); + + /* Ensure we cleanup if we fail after this. */ + auto intf_guard = SCOPE_GUARD { m_client->DeleteInterface(m_interface_num); m_interface = nullptr; }; + + /* Get events. */ + sf::CopyHandle setup_event_handle; + sf::CopyHandle ctrl_in_event_handle; + sf::CopyHandle ctrl_out_event_handle; + R_TRY(m_interface->GetSetupEvent(std::addressof(setup_event_handle))); + R_TRY(m_interface->GetCtrlInCompletionEvent(std::addressof(ctrl_in_event_handle))); + R_TRY(m_interface->GetCtrlOutCompletionEvent(std::addressof(ctrl_out_event_handle))); + + /* Attach events. */ + os::AttachReadableHandleToSystemEvent(std::addressof(m_setup_event), setup_event_handle.GetValue(), true, os::EventClearMode_ManualClear); + os::AttachReadableHandleToSystemEvent(std::addressof(m_ctrl_in_completion_event), ctrl_in_event_handle.GetValue(), true, os::EventClearMode_ManualClear); + os::AttachReadableHandleToSystemEvent(std::addressof(m_ctrl_out_completion_event), ctrl_out_event_handle.GetValue(), true, os::EventClearMode_ManualClear); + + /* Increment our client's reference count. */ + ++m_client->m_reference_count; + + /* Set ourselves as initialized. */ + m_is_initialized = true; + + intf_guard.Cancel(); + return ResultSuccess(); + } + + Result DsInterface::Finalize() { + /* Validate that we have a service. */ + R_ABORT_UNLESS(m_interface != nullptr); + + /* We must be disabled. */ + R_UNLESS(!m_client->m_is_enabled, usb::ResultResourceBusy()); + + /* Finalize all endpoints. */ + for (size_t i = 0; i < util::size(m_endpoints); ++i) { + if (m_endpoints[i] != nullptr) { + R_TRY(m_endpoints[i]->Finalize()); + } + } + + /* Create a scoped reference. */ + impl::ScopedRefCount scoped_ref(m_reference_count); + + /* Check that we're initialized. */ + R_UNLESS(m_is_initialized, usb::ResultNotInitialized()); + + /* Check our reference count .*/ + R_UNLESS(m_reference_count <= 1, usb::ResultResourceBusy()); + + /* Finalize members. */ + m_is_initialized = false; + os::DestroySystemEvent(std::addressof(m_setup_event)); + os::DestroySystemEvent(std::addressof(m_ctrl_in_completion_event)); + os::DestroySystemEvent(std::addressof(m_ctrl_out_completion_event)); + + /* Delete ourselves from our cleint. */ + m_client->DeleteInterface(m_interface_num); + + /* Destroy our service. */ + m_interface = nullptr; + + /* Close our reference to our client. */ + --m_client->m_reference_count; + m_client = nullptr; + + return ResultSuccess(); + } + + Result DsInterface::AppendConfigurationData(UsbDeviceSpeed speed, void *data, u32 size) { + return m_interface->AppendConfigurationData(m_interface_num, speed, sf::InBuffer(data, size)); + } + + bool DsInterface::IsInitialized() { + return m_is_initialized; + } + + os::SystemEventType *DsInterface::GetSetupEvent() { + /* Create a scoped reference. */ + impl::ScopedRefCount scoped_ref(m_reference_count); + + return m_is_initialized ? std::addressof(m_setup_event) : nullptr; + } + + Result DsInterface::GetSetupPacket(UsbCtrlRequest *out) { + /* Create a scoped reference. */ + impl::ScopedRefCount scoped_ref(m_reference_count); + + /* Check that we're initialized. */ + R_UNLESS(m_is_initialized, usb::ResultNotInitialized()); + + /* Check that we have a service. */ + AMS_ABORT_UNLESS(m_interface != nullptr); + + return m_interface->GetSetupPacket(sf::OutBuffer(out, sizeof(*out))); + } + + Result DsInterface::Enable() { + /* Create a scoped reference. */ + impl::ScopedRefCount scoped_ref(m_reference_count); + + /* Check that we're initialized. */ + R_UNLESS(m_is_initialized, usb::ResultNotInitialized()); + + /* If we're already enabled, nothing to do. */ + R_SUCCEED_IF(m_client->m_is_enabled); + + /* Check that we have a service. */ + AMS_ABORT_UNLESS(m_interface != nullptr); + + /* Perform the enable. */ + R_TRY(m_interface->Enable()); + + return ResultSuccess(); + } + + Result DsInterface::Disable() { + /* Create a scoped reference. */ + impl::ScopedRefCount scoped_ref(m_reference_count); + + /* Check that we're initialized. */ + R_UNLESS(m_is_initialized, usb::ResultNotInitialized()); + + /* If we're already disabled, nothing to do. */ + R_SUCCEED_IF(!m_client->m_is_enabled); + + /* Check that we have a service. */ + AMS_ABORT_UNLESS(m_interface != nullptr); + + /* Perform the disable. */ + R_TRY(m_interface->Disable()); + + return ResultSuccess(); + } + + Result DsInterface::AddEndpoint(DsEndpoint *ep, u8 bEndpointAddress, sf::SharedPointer *out) { + /* Create a scoped reference. */ + impl::ScopedRefCount scoped_ref(m_reference_count); + + /* Check that we're initialized. */ + R_UNLESS(m_is_initialized, usb::ResultNotInitialized()); + + /* Check that we're not already enabled. */ + R_UNLESS(!m_client->m_is_enabled, usb::ResultOperationDenied()); + + /* Check that we have a service. */ + AMS_ABORT_UNLESS(m_interface != nullptr); + + /* Register the endpoint. */ + R_TRY(m_interface->RegisterEndpoint(bEndpointAddress, out)); + + /* Set the endpoint. */ + m_endpoints[impl::GetEndpointIndex(bEndpointAddress)] = ep; + + return ResultSuccess(); + } + + Result DsInterface::DeleteEndpoint(u8 bEndpointAddress) { + /* Create a scoped reference. */ + impl::ScopedRefCount scoped_ref(m_reference_count); + + /* Check that we're initialized. */ + R_UNLESS(m_is_initialized, usb::ResultNotInitialized()); + + /* Check that we're disabled and have the endpoint. */ + const auto index = impl::GetEndpointIndex(bEndpointAddress); + R_UNLESS(!m_client->m_is_enabled, usb::ResultOperationDenied()); + R_UNLESS(m_endpoints[index] != nullptr, usb::ResultOperationDenied()); + + /* Clear the endpoint. */ + m_endpoints[index] = nullptr; + + return ResultSuccess(); + } + + Result DsInterface::CtrlIn(u32 *out_transferred, void *dst, u32 size) { + /* Create a scoped reference. */ + impl::ScopedRefCount scoped_ref(m_reference_count); + + /* Check that we're initialized. */ + R_UNLESS(m_is_initialized, usb::ResultNotInitialized()); + + /* Check that we're enabled. */ + R_UNLESS(m_client->m_is_enabled, usb::ResultOperationDenied()); + + /* Check that the data is aligned. */ + R_UNLESS(usb::IsDmaAligned(reinterpret_cast(dst)), usb::ResultAlignmentError()); + + /* If we should, flush cache. */ + if (size != 0) { + dd::FlushDataCache(dst, size); + } + + /* Check that we have a service. */ + AMS_ABORT_UNLESS(m_interface != nullptr); + + /* Perform the transfer. */ + u32 urb_id; + R_TRY(m_interface->CtrlInAsync(std::addressof(urb_id), reinterpret_cast(dst), size)); + + /* Wait for control to finish. */ + os::WaitSystemEvent(std::addressof(m_ctrl_in_completion_event)); + os::ClearSystemEvent(std::addressof(m_ctrl_in_completion_event)); + + /* Get the urb report. */ + R_ABORT_UNLESS(m_interface->GetCtrlInUrbReport(std::addressof(m_report))); + + /* Check the report is for our urb. */ + R_UNLESS(m_report.count == 1, usb::ResultInternalStateError()); + R_UNLESS(m_report.reports[0].id == urb_id, usb::ResultInternalStateError()); + + /* Set output bytes. */ + if (out_transferred != nullptr) { + *out_transferred = m_report.reports[0].transferred_size; + } + + /* Handle the report. */ + switch (m_report.reports[0].status) { + case UrbStatus_Cancelled: + return usb::ResultInterrupted(); + case UrbStatus_Failed: + return usb::ResultTransactionError(); + case UrbStatus_Finished: + return ResultSuccess(); + default: + return usb::ResultInternalStateError(); + } + } + + Result DsInterface::CtrlOut(u32 *out_transferred, void *dst, u32 size) { + /* Create a scoped reference. */ + impl::ScopedRefCount scoped_ref(m_reference_count); + + /* Check that we're initialized. */ + R_UNLESS(m_is_initialized, usb::ResultNotInitialized()); + + /* Check that we're enabled. */ + R_UNLESS(m_client->m_is_enabled, usb::ResultOperationDenied()); + + /* Check that the data is aligned. */ + R_UNLESS(usb::IsDmaAligned(reinterpret_cast(dst)), usb::ResultAlignmentError()); + + /* If we should, invalidate cache. */ + if (size != 0) { + dd::InvalidateDataCache(dst, size); + } + + /* Check that we have a service. */ + AMS_ABORT_UNLESS(m_interface != nullptr); + + /* Perform the transfer. */ + u32 urb_id; + R_TRY(m_interface->CtrlOutAsync(std::addressof(urb_id), reinterpret_cast(dst), size)); + + /* Wait for control to finish. */ + os::WaitSystemEvent(std::addressof(m_ctrl_out_completion_event)); + os::ClearSystemEvent(std::addressof(m_ctrl_out_completion_event)); + + /* Ensure that cache remains consistent. */ + ON_SCOPE_EXIT { + if (size != 0) { + dd::InvalidateDataCache(dst, size); + } + }; + + /* Get the urb report. */ + R_ABORT_UNLESS(m_interface->GetCtrlOutUrbReport(std::addressof(m_report))); + + /* Check the report is for our urb. */ + R_UNLESS(m_report.count == 1, usb::ResultInternalStateError()); + R_UNLESS(m_report.reports[0].id == urb_id, usb::ResultInternalStateError()); + + /* Set output bytes. */ + if (out_transferred != nullptr) { + *out_transferred = m_report.reports[0].transferred_size; + } + + /* Handle the report. */ + switch (m_report.reports[0].status) { + case UrbStatus_Cancelled: + return usb::ResultInterrupted(); + case UrbStatus_Failed: + return usb::ResultTransactionError(); + case UrbStatus_Finished: + return ResultSuccess(); + default: + return usb::ResultInternalStateError(); + } + } + + Result DsInterface::CtrlRead(u32 *out_transferred, void *dst, u32 size) { + /* Create a scoped reference. */ + impl::ScopedRefCount scoped_ref(m_reference_count); + + /* Check that we're initialized. */ + R_UNLESS(m_is_initialized, usb::ResultNotInitialized()); + + /* Do the data transfer. */ + Result result = this->CtrlOut(out_transferred, dst, size); + + /* Do the status transfer. */ + if (R_SUCCEEDED(result)) { + result = this->CtrlIn(nullptr, nullptr, 0); + } + + /* If we failed, stall. */ + if (R_FAILED(result)) { + result = this->CtrlStall(); + } + + return result; + } + + Result DsInterface::CtrlWrite(u32 *out_transferred, void *dst, u32 size) { + /* Create a scoped reference. */ + impl::ScopedRefCount scoped_ref(m_reference_count); + + /* Check that we're initialized. */ + R_UNLESS(m_is_initialized, usb::ResultNotInitialized()); + + /* Do the data transfer. */ + Result result = this->CtrlIn(out_transferred, dst, size); + + /* Do the status transfer. */ + if (R_SUCCEEDED(result)) { + result = this->CtrlOut(nullptr, nullptr, 0); + } + + /* If we failed, stall. */ + if (R_FAILED(result)) { + result = this->CtrlStall(); + } + + return result; + } + + Result DsInterface::CtrlDone() { + /* Create a scoped reference. */ + impl::ScopedRefCount scoped_ref(m_reference_count); + + /* Check that we're initialized. */ + R_UNLESS(m_is_initialized, usb::ResultNotInitialized()); + + /* Do the status transfer. */ + Result result = this->CtrlIn(nullptr, nullptr, 0); + + /* If we failed, stall. */ + if (R_FAILED(result)) { + result = this->CtrlStall(); + } + + return result; + } + + Result DsInterface::CtrlStall() { + /* Create a scoped reference. */ + impl::ScopedRefCount scoped_ref(m_reference_count); + + /* Check that we're initialized. */ + R_UNLESS(m_is_initialized, usb::ResultNotInitialized()); + + /* Check that we're enabled. */ + R_UNLESS(m_client->m_is_enabled, usb::ResultOperationDenied()); + + /* Check that we have a service. */ + AMS_ABORT_UNLESS(m_interface != nullptr); + + return m_interface->CtrlStall(); + } + + Result DsEndpoint::Initialize(DsInterface *interface, u8 bEndpointAddress) { + /* Check that the interface is valid. */ + AMS_ABORT_UNLESS(interface != nullptr); + + /* Check that we're not already initialized. */ + R_UNLESS(!m_is_initialized, usb::ResultAlreadyInitialized()); + + /* Set our interface. */ + m_interface = interface; + + /* Add the endpoint. */ + R_TRY(m_interface->AddEndpoint(this, bEndpointAddress, std::addressof(m_endpoint))); + + /* Set our address. */ + m_address = bEndpointAddress; + + /* Ensure we clean up if we fail after this. */ + auto ep_guard = SCOPE_GUARD { m_interface->DeleteEndpoint(m_address); m_endpoint = nullptr; }; + + /* Get completion event. */ + sf::CopyHandle event_handle; + R_TRY(m_endpoint->GetCompletionEvent(std::addressof(event_handle))); + + /* Increment our interface's reference count. */ + ++m_interface->m_reference_count; + ++m_interface->m_client->m_reference_count; + + /* Attach our event. */ + os::AttachReadableHandleToSystemEvent(std::addressof(m_completion_event), event_handle, true, os::EventClearMode_ManualClear); + + /* Mark initialized. */ + m_is_initialized = true; + + ep_guard.Cancel(); + return ResultSuccess(); + } + + Result DsEndpoint::Finalize() { + /* Create a scoped reference. */ + impl::ScopedRefCount scoped_ref(m_reference_count); + + /* Check that we're initialized. */ + R_UNLESS(m_is_initialized, usb::ResultNotInitialized()); + + /* Cancel any pending transactions. */ + m_endpoint->Cancel(); + + /* Wait for us to be at one reference count. */ + while (m_reference_count > 1) { + os::SleepThread(TimeSpan::FromMilliSeconds(25)); + } + + /* Destroy our event. */ + os::DestroySystemEvent(std::addressof(m_completion_event)); + + /* Decrement our interface's reference count. */ + --m_interface->m_reference_count; + --m_interface->m_client->m_reference_count; + + /* Delete ourselves. */ + R_TRY(m_interface->DeleteEndpoint(m_address)); + + /* Clear ourselves. */ + m_interface = nullptr; + m_endpoint = nullptr; + + /* Mark uninitialized. */ + m_is_initialized = false; + + return ResultSuccess(); + } + + bool DsEndpoint::IsInitialized() { + return m_is_initialized; + } + + Result DsEndpoint::PostBuffer(u32 *out_transferred, void *buf, u32 size) { + /* Create a scoped reference. */ + impl::ScopedRefCount scoped_ref(m_reference_count); + + /* Check that we're initialized. */ + R_UNLESS(m_is_initialized, usb::ResultNotInitialized()); + + /* Post buffer. */ + u32 urb_id; + R_TRY(this->PostBufferAsync(std::addressof(urb_id), buf, size)); + + /* Wait for completion. */ + os::WaitSystemEvent(std::addressof(m_completion_event)); + os::ClearSystemEvent(std::addressof(m_completion_event)); + + /* Get URB report. */ + UrbReport report; + AMS_ABORT_UNLESS(m_endpoint != nullptr); + R_ABORT_UNLESS(m_endpoint->GetUrbReport(std::addressof(report))); + + /* Check the report is for our urb. */ + R_UNLESS(report.count == 1, usb::ResultInternalStateError()); + R_UNLESS(report.reports[0].id == urb_id, usb::ResultInternalStateError()); + + /* Set output bytes. */ + if (out_transferred != nullptr) { + *out_transferred = report.reports[0].transferred_size; + } + + /* Handle the report. */ + switch (report.reports[0].status) { + case UrbStatus_Cancelled: + return usb::ResultInterrupted(); + case UrbStatus_Failed: + return usb::ResultTransactionError(); + case UrbStatus_Finished: + return ResultSuccess(); + default: + return usb::ResultInternalStateError(); + } + } + + Result DsEndpoint::PostBufferAsync(u32 *out_urb_id, void *buf, u32 size) { + /* Create a scoped reference. */ + impl::ScopedRefCount scoped_ref(m_reference_count); + + /* Check that we're initialized. */ + R_UNLESS(m_is_initialized, usb::ResultNotInitialized()); + + /* Check that the buffer is DMA aligned. */ + R_UNLESS(usb::IsDmaAligned(reinterpret_cast(buf)), usb::ResultAlignmentError()); + + /* Check that we have a service. */ + AMS_ABORT_UNLESS(m_endpoint != nullptr); + + /* Post */ + u32 urb_id = 0; + R_TRY(m_endpoint->PostBufferAsync(std::addressof(urb_id), reinterpret_cast(buf), size)); + + *out_urb_id = urb_id; + return ResultSuccess(); + } + + os::SystemEventType *DsEndpoint::GetCompletionEvent() { + /* Create a scoped reference. */ + impl::ScopedRefCount scoped_ref(m_reference_count); + + return m_is_initialized ? std::addressof(m_completion_event) : nullptr; + } + + Result DsEndpoint::GetUrbReport(UrbReport *out) { + /* Create a scoped reference. */ + impl::ScopedRefCount scoped_ref(m_reference_count); + + /* Check that we're initialized. */ + R_UNLESS(m_is_initialized, usb::ResultNotInitialized()); + + /* Check that we have a service. */ + AMS_ABORT_UNLESS(m_endpoint != nullptr); + + return m_endpoint->GetUrbReport(out); + } + + Result DsEndpoint::Cancel() { + /* Create a scoped reference. */ + impl::ScopedRefCount scoped_ref(m_reference_count); + + /* Check that we're initialized. */ + R_UNLESS(m_is_initialized, usb::ResultNotInitialized()); + + /* Check that we have a service. */ + AMS_ABORT_UNLESS(m_endpoint != nullptr); + + return m_endpoint->Cancel(); + } + + Result DsEndpoint::SetZeroLengthTransfer(bool zlt) { + /* Create a scoped reference. */ + impl::ScopedRefCount scoped_ref(m_reference_count); + + /* Check that we're initialized. */ + R_UNLESS(m_is_initialized, usb::ResultNotInitialized()); + + /* Check that we have a service. */ + AMS_ABORT_UNLESS(m_endpoint != nullptr); + + return m_endpoint->SetZlt(zlt); + } + +} diff --git a/libraries/libstratosphere/source/usb/usb_remote_ds_endpoint.cpp b/libraries/libstratosphere/source/usb/usb_remote_ds_endpoint.cpp new file mode 100644 index 000000000..9c75a125c --- /dev/null +++ b/libraries/libstratosphere/source/usb/usb_remote_ds_endpoint.cpp @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2018-2020 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 . + */ +#include +#include "usb_remote_ds_endpoint.hpp" + +namespace ams::usb { + + Result RemoteDsEndpoint::PostBufferAsync(sf::Out out_urb_id, u64 address, u32 size) { + const struct { + u32 size; + u64 address; + } in = { size, address }; + + serviceAssumeDomain(std::addressof(m_srv)); + return serviceDispatchInOut(std::addressof(m_srv), 0, in, *out_urb_id); + } + + Result RemoteDsEndpoint::Cancel() { + serviceAssumeDomain(std::addressof(m_srv)); + return serviceDispatch(std::addressof(m_srv), 1); + } + + Result RemoteDsEndpoint::GetCompletionEvent(sf::OutCopyHandle out) { + serviceAssumeDomain(std::addressof(m_srv)); + return serviceDispatch(std::addressof(m_srv), 2, + .out_handle_attrs = { SfOutHandleAttr_HipcCopy }, + .out_handles = out.GetHandlePointer(), + ); + } + + Result RemoteDsEndpoint::GetUrbReport(sf::Out out) { + serviceAssumeDomain(std::addressof(m_srv)); + return serviceDispatchOut(std::addressof(m_srv), 3, *out); + } + + Result RemoteDsEndpoint::Stall() { + serviceAssumeDomain(std::addressof(m_srv)); + return serviceDispatch(std::addressof(m_srv), 4); + } + + Result RemoteDsEndpoint::SetZlt(bool zlt) { + const u8 in = zlt ? 1 : 0; + + serviceAssumeDomain(std::addressof(m_srv)); + return serviceDispatchIn(std::addressof(m_srv), 5, in); + } + +} diff --git a/libraries/libstratosphere/source/usb/usb_remote_ds_endpoint.hpp b/libraries/libstratosphere/source/usb/usb_remote_ds_endpoint.hpp new file mode 100644 index 000000000..79e0e2051 --- /dev/null +++ b/libraries/libstratosphere/source/usb/usb_remote_ds_endpoint.hpp @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2018-2020 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 . + */ +#pragma once +#include + +namespace ams::usb { + + class RemoteDsEndpoint { + private: + Service m_srv; + public: + RemoteDsEndpoint(Service &srv) : m_srv(srv) { /* ... */ } + virtual ~RemoteDsEndpoint() { serviceClose(std::addressof(m_srv)); } + public: + Result PostBufferAsync(sf::Out out_urb_id, u64 address, u32 size); + Result Cancel(); + Result GetCompletionEvent(sf::OutCopyHandle out); + Result GetUrbReport(sf::Out out); + Result Stall(); + Result SetZlt(bool zlt); + }; + static_assert(ds::IsIDsEndpoint); + +} diff --git a/libraries/libstratosphere/source/usb/usb_remote_ds_interface.cpp b/libraries/libstratosphere/source/usb/usb_remote_ds_interface.cpp new file mode 100644 index 000000000..bbe8b1c8c --- /dev/null +++ b/libraries/libstratosphere/source/usb/usb_remote_ds_interface.cpp @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2018-2020 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 . + */ +#include +#include "usb_remote_ds_interface.hpp" +#include "usb_remote_ds_endpoint.hpp" + +namespace ams::usb { + + Result RemoteDsInterface::RegisterEndpoint(u8 endpoint_address, sf::Out> out) { + Service srv; + + serviceAssumeDomain(std::addressof(m_srv)); + R_TRY(serviceDispatchIn(std::addressof(m_srv), 0, endpoint_address, + .out_num_objects = 1, + .out_objects = std::addressof(srv), + )); + + *out = ObjectFactory::CreateSharedEmplaced(m_allocator, srv); + + return ResultSuccess(); + } + + Result RemoteDsInterface::GetSetupEvent(sf::OutCopyHandle out) { + serviceAssumeDomain(std::addressof(m_srv)); + return serviceDispatch(std::addressof(m_srv), 1, + .out_handle_attrs = { SfOutHandleAttr_HipcCopy }, + .out_handles = out.GetHandlePointer(), + ); + } + + Result RemoteDsInterface::GetSetupPacket(const sf::OutBuffer &out) { + serviceAssumeDomain(std::addressof(m_srv)); + return serviceDispatch(std::addressof(m_srv), 2, + .buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_Out }, + .buffers = { { out.GetPointer(), out.GetSize() } }, + ); + } + + Result RemoteDsInterface::CtrlInAsync(sf::Out out_urb_id, u64 address, u32 size) { + const struct { + u32 size; + u64 address; + } in = { size, address }; + + serviceAssumeDomain(std::addressof(m_srv)); + return serviceDispatchInOut(std::addressof(m_srv), hos::GetVersion() >= hos::Version_11_0_0 ? 3 : 5, in, *out_urb_id); + } + + Result RemoteDsInterface::CtrlOutAsync(sf::Out out_urb_id, u64 address, u32 size) { + const struct { + u32 size; + u64 address; + } in = { size, address }; + + serviceAssumeDomain(std::addressof(m_srv)); + return serviceDispatchInOut(std::addressof(m_srv), hos::GetVersion() >= hos::Version_11_0_0 ? 4 : 6, in, *out_urb_id); + } + + Result RemoteDsInterface::GetCtrlInCompletionEvent(sf::OutCopyHandle out) { + serviceAssumeDomain(std::addressof(m_srv)); + return serviceDispatch(std::addressof(m_srv), hos::GetVersion() >= hos::Version_11_0_0 ? 5 : 7, + .out_handle_attrs = { SfOutHandleAttr_HipcCopy }, + .out_handles = out.GetHandlePointer(), + ); + } + + Result RemoteDsInterface::GetCtrlInUrbReport(sf::Out out) { + serviceAssumeDomain(std::addressof(m_srv)); + return serviceDispatchOut(std::addressof(m_srv), hos::GetVersion() >= hos::Version_11_0_0 ? 6 : 8, *out); + } + + Result RemoteDsInterface::GetCtrlOutCompletionEvent(sf::OutCopyHandle out) { + serviceAssumeDomain(std::addressof(m_srv)); + return serviceDispatch(std::addressof(m_srv), hos::GetVersion() >= hos::Version_11_0_0 ? 7 : 9, + .out_handle_attrs = { SfOutHandleAttr_HipcCopy }, + .out_handles = out.GetHandlePointer(), + ); + } + + Result RemoteDsInterface::GetCtrlOutUrbReport(sf::Out out) { + serviceAssumeDomain(std::addressof(m_srv)); + return serviceDispatchOut(std::addressof(m_srv), hos::GetVersion() >= hos::Version_11_0_0 ? 8 : 10, *out); + } + + Result RemoteDsInterface::CtrlStall() { + serviceAssumeDomain(std::addressof(m_srv)); + return serviceDispatch(std::addressof(m_srv), hos::GetVersion() >= hos::Version_11_0_0 ? 9 : 11); + } + + Result RemoteDsInterface::AppendConfigurationData(u8 bInterfaceNumber, usb::UsbDeviceSpeed device_speed, const sf::InBuffer &data) { + if (hos::GetVersion() >= hos::Version_11_0_0) { + serviceAssumeDomain(std::addressof(m_srv)); + return serviceDispatchIn(std::addressof(m_srv), 12, device_speed, + .buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_In }, + .buffers = { { data.GetPointer(), data.GetSize() } }, + ); + } else { + const struct { + u8 bInterfaceNumber; + usb::UsbDeviceSpeed device_speed; + } in = { bInterfaceNumber, device_speed }; + + serviceAssumeDomain(std::addressof(m_srv)); + return serviceDispatchIn(std::addressof(m_srv), 10, in, + .buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_In }, + .buffers = { { data.GetPointer(), data.GetSize() } }, + ); + } + } + + Result RemoteDsInterface::Enable() { + R_SUCCEED_IF(hos::GetVersion() >= hos::Version_11_0_0); + + serviceAssumeDomain(std::addressof(m_srv)); + return serviceDispatch(std::addressof(m_srv), 3); + } + + Result RemoteDsInterface::Disable() { + R_SUCCEED_IF(hos::GetVersion() >= hos::Version_11_0_0); + + serviceAssumeDomain(std::addressof(m_srv)); + return serviceDispatch(std::addressof(m_srv), 3); + } + + +} diff --git a/libraries/libstratosphere/source/usb/usb_remote_ds_interface.hpp b/libraries/libstratosphere/source/usb/usb_remote_ds_interface.hpp new file mode 100644 index 000000000..300241524 --- /dev/null +++ b/libraries/libstratosphere/source/usb/usb_remote_ds_interface.hpp @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2018-2020 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 . + */ +#pragma once +#include + +namespace ams::usb { + + class RemoteDsInterface { + private: + using Allocator = sf::ExpHeapAllocator; + using ObjectFactory = sf::ObjectFactory; + private: + Service m_srv; + Allocator *m_allocator; + public: + RemoteDsInterface(Service &srv, sf::ExpHeapAllocator *allocator) : m_srv(srv), m_allocator(allocator) { /* ... */ } + virtual ~RemoteDsInterface() { serviceClose(std::addressof(m_srv)); } + public: + Result RegisterEndpoint(u8 endpoint_address, sf::Out> out); + Result GetSetupEvent(sf::OutCopyHandle out); + Result GetSetupPacket(const sf::OutBuffer & out); + Result CtrlInAsync(sf::Out out_urb_id, u64 address, u32 size); + Result CtrlOutAsync(sf::Out out_urb_id, u64 address, u32 size); + Result GetCtrlInCompletionEvent(sf::OutCopyHandle out); + Result GetCtrlInUrbReport(sf::Out out); + Result GetCtrlOutCompletionEvent(sf::OutCopyHandle out); + Result GetCtrlOutUrbReport(sf::Out out); + Result CtrlStall(); + Result AppendConfigurationData(u8 bInterfaceNumber, usb::UsbDeviceSpeed device_speed, const sf::InBuffer &data); + Result Enable(); + Result Disable(); + }; + static_assert(ds::IsIDsInterface); + +} diff --git a/libraries/libstratosphere/source/usb/usb_remote_ds_root_service.cpp b/libraries/libstratosphere/source/usb/usb_remote_ds_root_service.cpp new file mode 100644 index 000000000..9bf16c3dc --- /dev/null +++ b/libraries/libstratosphere/source/usb/usb_remote_ds_root_service.cpp @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2018-2020 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 . + */ +#include +#include "usb_remote_ds_root_service.hpp" +#include "usb_remote_ds_service.hpp" + +namespace ams::usb { + + Result RemoteDsRootService::GetService(sf::Out> out) { + Service srv; + + serviceAssumeDomain(std::addressof(m_srv)); + R_TRY(serviceDispatch(std::addressof(m_srv), 0, .out_num_objects = 1, .out_objects = std::addressof(srv))); + + *out = ObjectFactory::CreateSharedEmplaced(m_allocator, srv, m_allocator); + + return ResultSuccess(); + } + +} diff --git a/libraries/libstratosphere/source/usb/usb_remote_ds_root_service.hpp b/libraries/libstratosphere/source/usb/usb_remote_ds_root_service.hpp new file mode 100644 index 000000000..16df3b8e1 --- /dev/null +++ b/libraries/libstratosphere/source/usb/usb_remote_ds_root_service.hpp @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2018-2020 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 . + */ +#pragma once +#include + +namespace ams::usb { + + class RemoteDsRootService { + private: + using Allocator = sf::ExpHeapAllocator; + using ObjectFactory = sf::ObjectFactory; + private: + Service m_srv; + Allocator *m_allocator; + public: + RemoteDsRootService(Service &srv, sf::ExpHeapAllocator *allocator) : m_srv(srv), m_allocator(allocator) { /* ... */ } + virtual ~RemoteDsRootService() { serviceClose(std::addressof(m_srv)); } + public: + Result GetService(sf::Out> out); + }; + static_assert(ds::IsIDsRootService); + +} diff --git a/libraries/libstratosphere/source/usb/usb_remote_ds_service.cpp b/libraries/libstratosphere/source/usb/usb_remote_ds_service.cpp new file mode 100644 index 000000000..04b72e358 --- /dev/null +++ b/libraries/libstratosphere/source/usb/usb_remote_ds_service.cpp @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2018-2020 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 . + */ +#include +#include "usb_remote_ds_service.hpp" +#include "usb_remote_ds_interface.hpp" + +namespace ams::usb { + + Result RemoteDsService::Bind(usb::ComplexId complex_id, sf::CopyHandle process_h) { + if (hos::GetVersion() >= hos::Version_11_0_0) { + serviceAssumeDomain(std::addressof(m_srv)); + R_TRY(serviceDispatchIn(std::addressof(m_srv), 0, complex_id, + .in_num_handles = 1, + .in_handles = { process_h.GetValue() } + )); + } else { + serviceAssumeDomain(std::addressof(m_srv)); + R_TRY(serviceDispatchIn(std::addressof(m_srv), 0, complex_id)); + + serviceAssumeDomain(std::addressof(m_srv)); + R_TRY(serviceDispatch(std::addressof(m_srv), 1, + .in_num_handles = 1, + .in_handles = { process_h.GetValue() }) + ); + } + + return ResultSuccess(); + } + + Result RemoteDsService::RegisterInterface(sf::Out> out, u8 bInterfaceNumber) { + Service srv; + + serviceAssumeDomain(std::addressof(m_srv)); + R_TRY(serviceDispatchIn(std::addressof(m_srv), (hos::GetVersion() >= hos::Version_11_0_0 ? 1 : 2), bInterfaceNumber, + .out_num_objects = 1, + .out_objects = std::addressof(srv), + )); + + *out = ObjectFactory::CreateSharedEmplaced(m_allocator, srv, m_allocator); + + return ResultSuccess(); + } + + Result RemoteDsService::GetStateChangeEvent(sf::OutCopyHandle out) { + serviceAssumeDomain(std::addressof(m_srv)); + return serviceDispatch(std::addressof(m_srv), hos::GetVersion() >= hos::Version_11_0_0 ? 2 : 3, + .out_handle_attrs = { SfOutHandleAttr_HipcCopy }, + .out_handles = out.GetHandlePointer(), + ); + } + + Result RemoteDsService::GetState(sf::Out out) { + serviceAssumeDomain(std::addressof(m_srv)); + return serviceDispatchOut(std::addressof(m_srv), hos::GetVersion() >= hos::Version_11_0_0 ? 3 : 4, *out); + } + + Result RemoteDsService::ClearDeviceData() { + serviceAssumeDomain(std::addressof(m_srv)); + return serviceDispatch(std::addressof(m_srv), hos::GetVersion() >= hos::Version_11_0_0 ? 4 : 5); + } + + Result RemoteDsService::AddUsbStringDescriptor(sf::Out out, const sf::InBuffer &desc) { + serviceAssumeDomain(std::addressof(m_srv)); + return serviceDispatchOut(std::addressof(m_srv), hos::GetVersion() >= hos::Version_11_0_0 ? 5 : 6, *out, + .buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_In }, + .buffers = { { desc.GetPointer(), desc.GetSize() } }, + ); + } + + Result RemoteDsService::DeleteUsbStringDescriptor(u8 index) { + serviceAssumeDomain(std::addressof(m_srv)); + return serviceDispatchIn(std::addressof(m_srv), hos::GetVersion() >= hos::Version_11_0_0 ? 6 : 7, index); + } + + Result RemoteDsService::SetUsbDeviceDescriptor(const sf::InBuffer &desc, usb::UsbDeviceSpeed speed) { + serviceAssumeDomain(std::addressof(m_srv)); + return serviceDispatchIn(std::addressof(m_srv), hos::GetVersion() >= hos::Version_11_0_0 ? 7 : 8, speed, + .buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_In }, + .buffers = { { desc.GetPointer(), desc.GetSize() } }, + ); + } + + Result RemoteDsService::SetBinaryObjectStore(const sf::InBuffer &bos) { + serviceAssumeDomain(std::addressof(m_srv)); + return serviceDispatch(std::addressof(m_srv), hos::GetVersion() >= hos::Version_11_0_0 ? 8 : 9, + .buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_In }, + .buffers = { { bos.GetPointer(), bos.GetSize() } }, + ); + } + + Result RemoteDsService::Enable() { + serviceAssumeDomain(std::addressof(m_srv)); + return serviceDispatch(std::addressof(m_srv), hos::GetVersion() >= hos::Version_11_0_0 ? 9 : 10); + } + + Result RemoteDsService::Disable() { + serviceAssumeDomain(std::addressof(m_srv)); + return serviceDispatch(std::addressof(m_srv), hos::GetVersion() >= hos::Version_11_0_0 ? 10 : 11); + } + +} diff --git a/libraries/libstratosphere/source/usb/usb_remote_ds_service.hpp b/libraries/libstratosphere/source/usb/usb_remote_ds_service.hpp new file mode 100644 index 000000000..68d93763e --- /dev/null +++ b/libraries/libstratosphere/source/usb/usb_remote_ds_service.hpp @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2018-2020 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 . + */ +#pragma once +#include + +namespace ams::usb { + + class RemoteDsService { + private: + using Allocator = sf::ExpHeapAllocator; + using ObjectFactory = sf::ObjectFactory; + private: + Service m_srv; + Allocator *m_allocator; + public: + RemoteDsService(Service &srv, sf::ExpHeapAllocator *allocator) : m_srv(srv), m_allocator(allocator) { /* ... */ } + virtual ~RemoteDsService() { serviceClose(std::addressof(m_srv)); } + public: + Result Bind(usb::ComplexId complex_id, sf::CopyHandle process_h); + Result RegisterInterface(sf::Out> out, u8 bInterfaceNumber); + Result GetStateChangeEvent(sf::OutCopyHandle out); + Result GetState(sf::Out out); + Result ClearDeviceData(); + Result AddUsbStringDescriptor(sf::Out out, const sf::InBuffer &desc); + Result DeleteUsbStringDescriptor(u8 index); + Result SetUsbDeviceDescriptor(const sf::InBuffer &desc, usb::UsbDeviceSpeed speed); + Result SetBinaryObjectStore(const sf::InBuffer &bos); + Result Enable(); + Result Disable(); + }; + static_assert(ds::IsIDsService); + +} diff --git a/libraries/libvapours/include/vapours/results.hpp b/libraries/libvapours/include/vapours/results.hpp index f10e5af32..0781b5d9e 100644 --- a/libraries/libvapours/include/vapours/results.hpp +++ b/libraries/libvapours/include/vapours/results.hpp @@ -59,6 +59,7 @@ #include #include #include +#include #include /* Unofficial. */ diff --git a/libraries/libvapours/include/vapours/results/usb_results.hpp b/libraries/libvapours/include/vapours/results/usb_results.hpp new file mode 100644 index 000000000..6aed0d46e --- /dev/null +++ b/libraries/libvapours/include/vapours/results/usb_results.hpp @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2018-2020 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 . + */ +#pragma once +#include + +namespace ams::usb { + + R_DEFINE_NAMESPACE_RESULT_MODULE(140); + + R_DEFINE_ERROR_RESULT(NotInitialized, 0); + R_DEFINE_ERROR_RESULT(AlreadyInitialized, 1); + + R_DEFINE_ERROR_RANGE(InvalidParameter, 100, 199); + R_DEFINE_ERROR_RESULT(AlignmentError, 103); + + R_DEFINE_ERROR_RESULT(OperationDenied, 201); + R_DEFINE_ERROR_RESULT(MemAllocFailure, 202); + R_DEFINE_ERROR_RESULT(ResourceBusy, 206); + R_DEFINE_ERROR_RESULT(InternalStateError, 207); + + R_DEFINE_ERROR_RESULT(TransactionError, 401); + R_DEFINE_ERROR_RESULT(Interrupted, 409); + +}