mirror of
https://github.com/Atmosphere-NX/Atmosphere
synced 2024-12-22 20:31:14 +00:00
usb: add ds client api
This commit is contained in:
parent
621520c30b
commit
13b17a5848
21 changed files with 1993 additions and 0 deletions
|
@ -79,6 +79,7 @@
|
||||||
#include <stratosphere/spl.hpp>
|
#include <stratosphere/spl.hpp>
|
||||||
#include <stratosphere/time.hpp>
|
#include <stratosphere/time.hpp>
|
||||||
#include <stratosphere/updater.hpp>
|
#include <stratosphere/updater.hpp>
|
||||||
|
#include <stratosphere/usb.hpp>
|
||||||
#include <stratosphere/wec.hpp>
|
#include <stratosphere/wec.hpp>
|
||||||
|
|
||||||
/* Include FS last. */
|
/* Include FS last. */
|
||||||
|
|
21
libraries/libstratosphere/include/stratosphere/usb.hpp
Normal file
21
libraries/libstratosphere/include/stratosphere/usb.hpp
Normal file
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stratosphere/usb/usb_limits.hpp>
|
||||||
|
#include <stratosphere/usb/usb_types.hpp>
|
||||||
|
#include <stratosphere/usb/usb_device_types.hpp>
|
||||||
|
#include <stratosphere/usb/usb_device.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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include <vapours.hpp>
|
||||||
|
#include <stratosphere/usb/usb_limits.hpp>
|
||||||
|
#include <stratosphere/usb/usb_types.hpp>
|
||||||
|
#include <stratosphere/usb/usb_device_types.hpp>
|
||||||
|
|
||||||
|
#define AMS_USB_I_DS_ENDPOINT_INTERFACE_INFO(C, H) \
|
||||||
|
AMS_SF_METHOD_INFO(C, H, 0, Result, PostBufferAsync, (sf::Out<u32> 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<usb::UrbReport> 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)
|
||||||
|
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include <vapours.hpp>
|
||||||
|
#include <stratosphere/usb/usb_limits.hpp>
|
||||||
|
#include <stratosphere/usb/usb_types.hpp>
|
||||||
|
#include <stratosphere/usb/usb_device_types.hpp>
|
||||||
|
#include <stratosphere/usb/ds/usb_i_ds_endpoint.hpp>
|
||||||
|
|
||||||
|
#define AMS_USB_I_DS_INTERFACE_INTERFACE_INFO(C, H) \
|
||||||
|
AMS_SF_METHOD_INFO(C, H, 0, Result, RegisterEndpoint, (u8 endpoint_address, sf::Out<sf::SharedPointer<usb::ds::IDsEndpoint>> 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<u32> out_urb_id, u64 address, u32 size), (out_urb_id, address, size)) \
|
||||||
|
AMS_SF_METHOD_INFO(C, H, 4, Result, CtrlOutAsync, (sf::Out<u32> 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<usb::UrbReport> 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<usb::UrbReport> 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)
|
||||||
|
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include <vapours.hpp>
|
||||||
|
#include <stratosphere/usb/usb_limits.hpp>
|
||||||
|
#include <stratosphere/usb/usb_types.hpp>
|
||||||
|
#include <stratosphere/usb/usb_device_types.hpp>
|
||||||
|
#include <stratosphere/usb/ds/usb_i_ds_interface.hpp>
|
||||||
|
|
||||||
|
#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<sf::SharedPointer<usb::ds::IDsInterface>> 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<usb::UsbState> out), (out)) \
|
||||||
|
AMS_SF_METHOD_INFO(C, H, 4, Result, ClearDeviceData, (), ()) \
|
||||||
|
AMS_SF_METHOD_INFO(C, H, 5, Result, AddUsbStringDescriptor, (sf::Out<u8> 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<sf::SharedPointer<usb::ds::IDsService>> out), (out))
|
||||||
|
|
||||||
|
AMS_SF_DEFINE_INTERFACE(ams::usb::ds, IDsRootService, AMS_USB_I_DS_ROOT_SERVICE_INTERFACE_INFO)
|
||||||
|
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include <vapours.hpp>
|
||||||
|
#include <stratosphere/os/os_system_event.hpp>
|
||||||
|
#include <stratosphere/sf/sf_lmem_utility.hpp>
|
||||||
|
#include <stratosphere/usb/usb_limits.hpp>
|
||||||
|
#include <stratosphere/usb/usb_device_types.hpp>
|
||||||
|
#include <stratosphere/usb/ds/usb_i_ds_service.hpp>
|
||||||
|
|
||||||
|
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<ds::IDsRootService> m_root_service{};
|
||||||
|
sf::SharedPointer<ds::IDsService> m_ds_service{};
|
||||||
|
bool m_is_initialized{false};
|
||||||
|
std::atomic<int> 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<ds::IDsInterface> *out_srv, uint8_t bInterfaceNumber);
|
||||||
|
Result DeleteInterface(uint8_t bInterfaceNumber);
|
||||||
|
};
|
||||||
|
|
||||||
|
class DsInterface {
|
||||||
|
friend class DsEndpoint;
|
||||||
|
private:
|
||||||
|
DsClient *m_client;
|
||||||
|
sf::SharedPointer<ds::IDsInterface> m_interface;
|
||||||
|
bool m_is_initialized;
|
||||||
|
std::atomic<int> 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<ds::IDsEndpoint> *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<int> m_reference_count;
|
||||||
|
DsInterface *m_interface;
|
||||||
|
sf::SharedPointer<ds::IDsEndpoint> 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);
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include <vapours.hpp>
|
||||||
|
#include <stratosphere/usb/usb_limits.hpp>
|
||||||
|
#include <stratosphere/usb/usb_types.hpp>
|
||||||
|
|
||||||
|
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];
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include <vapours.hpp>
|
||||||
|
#include <stratosphere/dd/dd_device_address_space_common.hpp>
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
}
|
214
libraries/libstratosphere/include/stratosphere/usb/usb_types.hpp
Normal file
214
libraries/libstratosphere/include/stratosphere/usb/usb_types.hpp
Normal file
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include <vapours.hpp>
|
||||||
|
#include <stratosphere/usb/usb_limits.hpp>
|
||||||
|
|
||||||
|
namespace ams::usb {
|
||||||
|
|
||||||
|
constexpr ALWAYS_INLINE bool IsDmaAligned(u64 address) {
|
||||||
|
return util::IsAligned(address, static_cast<u64>(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<u8>(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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
45
libraries/libstratosphere/source/usb/impl/usb_util.hpp
Normal file
45
libraries/libstratosphere/source/usb/impl/usb_util.hpp
Normal file
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include <stratosphere.hpp>
|
||||||
|
|
||||||
|
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<typename T>
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
803
libraries/libstratosphere/source/usb/usb_device.cpp
Normal file
803
libraries/libstratosphere/source/usb/usb_device.cpp
Normal file
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#include <stratosphere.hpp>
|
||||||
|
#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<Allocator::Policy>;
|
||||||
|
|
||||||
|
if (hos::GetVersion() >= hos::Version_11_0_0) {
|
||||||
|
m_root_service = ObjectFactory::CreateSharedEmplaced<ds::IDsRootService, RemoteDsRootService>(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<ds::IDsService, RemoteDsService>(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<const u8 *>(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<const u8 *>(desc), sizeof(*desc)), speed);
|
||||||
|
}
|
||||||
|
|
||||||
|
Result DsClient::SetBinaryObjectStore(u8 *data, int size) {
|
||||||
|
return m_ds_service->SetBinaryObjectStore(sf::InBuffer(reinterpret_cast<const u8 *>(data), size));
|
||||||
|
}
|
||||||
|
|
||||||
|
Result DsClient::AddInterface(DsInterface *intf, sf::SharedPointer<ds::IDsInterface> *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<ds::IDsEndpoint> *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<u64>(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<u64>(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<u64>(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<u64>(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<u64>(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<u64>(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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#include <stratosphere.hpp>
|
||||||
|
#include "usb_remote_ds_endpoint.hpp"
|
||||||
|
|
||||||
|
namespace ams::usb {
|
||||||
|
|
||||||
|
Result RemoteDsEndpoint::PostBufferAsync(sf::Out<u32> 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<usb::UrbReport> 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include <stratosphere.hpp>
|
||||||
|
|
||||||
|
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<u32> out_urb_id, u64 address, u32 size);
|
||||||
|
Result Cancel();
|
||||||
|
Result GetCompletionEvent(sf::OutCopyHandle out);
|
||||||
|
Result GetUrbReport(sf::Out<usb::UrbReport> out);
|
||||||
|
Result Stall();
|
||||||
|
Result SetZlt(bool zlt);
|
||||||
|
};
|
||||||
|
static_assert(ds::IsIDsEndpoint<RemoteDsEndpoint>);
|
||||||
|
|
||||||
|
}
|
139
libraries/libstratosphere/source/usb/usb_remote_ds_interface.cpp
Normal file
139
libraries/libstratosphere/source/usb/usb_remote_ds_interface.cpp
Normal file
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#include <stratosphere.hpp>
|
||||||
|
#include "usb_remote_ds_interface.hpp"
|
||||||
|
#include "usb_remote_ds_endpoint.hpp"
|
||||||
|
|
||||||
|
namespace ams::usb {
|
||||||
|
|
||||||
|
Result RemoteDsInterface::RegisterEndpoint(u8 endpoint_address, sf::Out<sf::SharedPointer<usb::ds::IDsEndpoint>> 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<ds::IDsEndpoint, RemoteDsEndpoint>(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<u32> 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<u32> 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<usb::UrbReport> 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<usb::UrbReport> 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include <stratosphere.hpp>
|
||||||
|
|
||||||
|
namespace ams::usb {
|
||||||
|
|
||||||
|
class RemoteDsInterface {
|
||||||
|
private:
|
||||||
|
using Allocator = sf::ExpHeapAllocator;
|
||||||
|
using ObjectFactory = sf::ObjectFactory<Allocator::Policy>;
|
||||||
|
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<sf::SharedPointer<usb::ds::IDsEndpoint>> out);
|
||||||
|
Result GetSetupEvent(sf::OutCopyHandle out);
|
||||||
|
Result GetSetupPacket(const sf::OutBuffer & out);
|
||||||
|
Result CtrlInAsync(sf::Out<u32> out_urb_id, u64 address, u32 size);
|
||||||
|
Result CtrlOutAsync(sf::Out<u32> out_urb_id, u64 address, u32 size);
|
||||||
|
Result GetCtrlInCompletionEvent(sf::OutCopyHandle out);
|
||||||
|
Result GetCtrlInUrbReport(sf::Out<usb::UrbReport> out);
|
||||||
|
Result GetCtrlOutCompletionEvent(sf::OutCopyHandle out);
|
||||||
|
Result GetCtrlOutUrbReport(sf::Out<usb::UrbReport> out);
|
||||||
|
Result CtrlStall();
|
||||||
|
Result AppendConfigurationData(u8 bInterfaceNumber, usb::UsbDeviceSpeed device_speed, const sf::InBuffer &data);
|
||||||
|
Result Enable();
|
||||||
|
Result Disable();
|
||||||
|
};
|
||||||
|
static_assert(ds::IsIDsInterface<RemoteDsInterface>);
|
||||||
|
|
||||||
|
}
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#include <stratosphere.hpp>
|
||||||
|
#include "usb_remote_ds_root_service.hpp"
|
||||||
|
#include "usb_remote_ds_service.hpp"
|
||||||
|
|
||||||
|
namespace ams::usb {
|
||||||
|
|
||||||
|
Result RemoteDsRootService::GetService(sf::Out<sf::SharedPointer<usb::ds::IDsService>> 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<ds::IDsService, RemoteDsService>(m_allocator, srv, m_allocator);
|
||||||
|
|
||||||
|
return ResultSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include <stratosphere.hpp>
|
||||||
|
|
||||||
|
namespace ams::usb {
|
||||||
|
|
||||||
|
class RemoteDsRootService {
|
||||||
|
private:
|
||||||
|
using Allocator = sf::ExpHeapAllocator;
|
||||||
|
using ObjectFactory = sf::ObjectFactory<Allocator::Policy>;
|
||||||
|
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<sf::SharedPointer<usb::ds::IDsService>> out);
|
||||||
|
};
|
||||||
|
static_assert(ds::IsIDsRootService<RemoteDsRootService>);
|
||||||
|
|
||||||
|
}
|
114
libraries/libstratosphere/source/usb/usb_remote_ds_service.cpp
Normal file
114
libraries/libstratosphere/source/usb/usb_remote_ds_service.cpp
Normal file
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#include <stratosphere.hpp>
|
||||||
|
#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<sf::SharedPointer<usb::ds::IDsInterface>> 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<ds::IDsInterface, RemoteDsInterface>(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<usb::UsbState> 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<u8> 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include <stratosphere.hpp>
|
||||||
|
|
||||||
|
namespace ams::usb {
|
||||||
|
|
||||||
|
class RemoteDsService {
|
||||||
|
private:
|
||||||
|
using Allocator = sf::ExpHeapAllocator;
|
||||||
|
using ObjectFactory = sf::ObjectFactory<Allocator::Policy>;
|
||||||
|
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<sf::SharedPointer<usb::ds::IDsInterface>> out, u8 bInterfaceNumber);
|
||||||
|
Result GetStateChangeEvent(sf::OutCopyHandle out);
|
||||||
|
Result GetState(sf::Out<usb::UsbState> out);
|
||||||
|
Result ClearDeviceData();
|
||||||
|
Result AddUsbStringDescriptor(sf::Out<u8> 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<RemoteDsService>);
|
||||||
|
|
||||||
|
}
|
|
@ -59,6 +59,7 @@
|
||||||
#include <vapours/results/svc_results.hpp>
|
#include <vapours/results/svc_results.hpp>
|
||||||
#include <vapours/results/time_results.hpp>
|
#include <vapours/results/time_results.hpp>
|
||||||
#include <vapours/results/updater_results.hpp>
|
#include <vapours/results/updater_results.hpp>
|
||||||
|
#include <vapours/results/usb_results.hpp>
|
||||||
#include <vapours/results/vi_results.hpp>
|
#include <vapours/results/vi_results.hpp>
|
||||||
|
|
||||||
/* Unofficial. */
|
/* Unofficial. */
|
||||||
|
|
37
libraries/libvapours/include/vapours/results/usb_results.hpp
Normal file
37
libraries/libvapours/include/vapours/results/usb_results.hpp
Normal file
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
#include <vapours/results/results_common.hpp>
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in a new issue