mirror of
https://github.com/Atmosphere-NX/Atmosphere.git
synced 2025-01-31 04:03:44 +01:00
htc: implement complete usb driver
This commit is contained in:
parent
c878123274
commit
c59388caf1
@ -16,7 +16,88 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <vapours.hpp>
|
#include <vapours.hpp>
|
||||||
#include <stratosphere/os/os_message_queue_common.hpp>
|
#include <stratosphere/os/os_message_queue_common.hpp>
|
||||||
|
#include <stratosphere/os/os_waitable_api.hpp>
|
||||||
|
#include <stratosphere/os/os_waitable_types.hpp>
|
||||||
|
|
||||||
namespace ams::os {
|
namespace ams::os {
|
||||||
|
|
||||||
|
namespace impl {
|
||||||
|
|
||||||
|
class AutoWaitableHolder {
|
||||||
|
private:
|
||||||
|
WaitableHolderType m_holder;
|
||||||
|
public:
|
||||||
|
template<typename T>
|
||||||
|
ALWAYS_INLINE explicit AutoWaitableHolder(WaitableManagerType *manager, T &&arg) {
|
||||||
|
InitializeWaitableHolder(std::addressof(m_holder), std::forward<T>(arg));
|
||||||
|
LinkWaitableHolder(manager, std::addressof(m_holder));
|
||||||
|
}
|
||||||
|
|
||||||
|
ALWAYS_INLINE ~AutoWaitableHolder() {
|
||||||
|
UnlinkWaitableHolder(std::addressof(m_holder));
|
||||||
|
FinalizeWaitableHolder(std::addressof(m_holder));
|
||||||
|
}
|
||||||
|
|
||||||
|
ALWAYS_INLINE std::pair<WaitableHolderType *, int> ConvertResult(const std::pair<WaitableHolderType *, int> result, int index) {
|
||||||
|
if (result.first == std::addressof(m_holder)) {
|
||||||
|
return std::make_pair(static_cast<WaitableHolderType *>(nullptr), index);
|
||||||
|
} else {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename F>
|
||||||
|
inline std::pair<WaitableHolderType *, int> WaitAnyImpl(F &&func, WaitableManagerType *manager, int) {
|
||||||
|
return std::pair<WaitableHolderType *, int>(func(manager), -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename F, typename T, typename... Args>
|
||||||
|
inline std::pair<WaitableHolderType *, int> WaitAnyImpl(F &&func, WaitableManagerType *manager, int index, T &&x, Args &&... args) {
|
||||||
|
AutoWaitableHolder holder(manager, std::forward<T>(x));
|
||||||
|
return holder.ConvertResult(WaitAnyImpl(std::forward<F>(func), manager, index + 1, std::forward<Args>(args)...), index);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename F, typename... Args>
|
||||||
|
inline std::pair<WaitableHolderType *, int> WaitAnyImpl(F &&func, WaitableManagerType *manager, Args &&... args) {
|
||||||
|
return WaitAnyImpl(std::forward<F>(func), manager, 0, std::forward<Args>(args)...);
|
||||||
|
}
|
||||||
|
|
||||||
|
class TempWaitableManager {
|
||||||
|
private:
|
||||||
|
WaitableManagerType m_manager;
|
||||||
|
public:
|
||||||
|
ALWAYS_INLINE TempWaitableManager() {
|
||||||
|
os::InitializeWaitableManager(std::addressof(m_manager));
|
||||||
|
}
|
||||||
|
|
||||||
|
ALWAYS_INLINE ~TempWaitableManager() {
|
||||||
|
os::FinalizeWaitableManager(std::addressof(m_manager));
|
||||||
|
}
|
||||||
|
|
||||||
|
WaitableManagerType *Get() {
|
||||||
|
return std::addressof(m_manager);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename F, typename... Args>
|
||||||
|
inline std::pair<WaitableHolderType *, int> WaitAnyImpl(F &&func, Args &&... args) {
|
||||||
|
TempWaitableManager temp_manager;
|
||||||
|
return WaitAnyImpl(std::forward<F>(func), temp_manager.Get(), 0, std::forward<Args>(args)...);
|
||||||
|
}
|
||||||
|
|
||||||
|
using WaitAnyFunction = WaitableHolderType * (*)(WaitableManagerType *);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename... Args> requires (sizeof...(Args) > 0)
|
||||||
|
inline std::pair<WaitableHolderType *, int> WaitAny(WaitableManagerType *manager, Args &&... args) {
|
||||||
|
return impl::WaitAnyImpl(static_cast<impl::WaitAnyFunction>(&::ams::os::WaitAny), manager, std::forward<Args>(args)...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename... Args> requires (sizeof...(Args) > 0)
|
||||||
|
inline int WaitAny(Args &&... args) {
|
||||||
|
return impl::WaitAnyImpl(static_cast<impl::WaitAnyFunction>(&::ams::os::WaitAny), std::forward<Args>(args)...).second;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -73,5 +73,12 @@ namespace ams::htclow::ctrl {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void HtcctrlService::SetDriverType(impl::DriverType driver_type) {
|
||||||
|
/* Lock ourselves. */
|
||||||
|
std::scoped_lock lk(m_mutex);
|
||||||
|
|
||||||
|
/* Update our beacon response. */
|
||||||
|
this->UpdateBeaconResponse(this->GetConnectionType(driver_type));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -57,6 +57,8 @@ namespace ams::htclow::ctrl {
|
|||||||
void UpdateBeaconResponse(const char *connection);
|
void UpdateBeaconResponse(const char *connection);
|
||||||
public:
|
public:
|
||||||
HtcctrlService(HtcctrlPacketFactory *pf, HtcctrlStateMachine *sm, mux::Mux *mux);
|
HtcctrlService(HtcctrlPacketFactory *pf, HtcctrlStateMachine *sm, mux::Mux *mux);
|
||||||
|
|
||||||
|
void SetDriverType(impl::DriverType driver_type);
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,63 @@
|
|||||||
|
/*
|
||||||
|
* 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 "htclow_driver_manager.hpp"
|
||||||
|
|
||||||
|
namespace ams::htclow::driver {
|
||||||
|
|
||||||
|
Result DriverManager::OpenDriver(impl::DriverType driver_type) {
|
||||||
|
/* Lock ourselves. */
|
||||||
|
std::scoped_lock lk(m_mutex);
|
||||||
|
|
||||||
|
/* Check that we're not already open. */
|
||||||
|
R_UNLESS(m_open_driver == nullptr, htclow::ResultDriverOpened());
|
||||||
|
|
||||||
|
/* Open the driver. */
|
||||||
|
switch (driver_type) {
|
||||||
|
case impl::DriverType::Debug:
|
||||||
|
R_TRY(m_debug_driver->Open());
|
||||||
|
m_open_driver = m_debug_driver;
|
||||||
|
break;
|
||||||
|
case impl::DriverType::Socket:
|
||||||
|
//m_socket_driver.Open();
|
||||||
|
//m_open_driver = std::addressof(m_socket_driver);
|
||||||
|
//break;
|
||||||
|
return htclow::ResultUnknownDriverType();
|
||||||
|
case impl::DriverType::Usb:
|
||||||
|
//m_usb_driver.Open();
|
||||||
|
//m_open_driver = std::addressof(m_usb_driver);
|
||||||
|
//break;
|
||||||
|
return htclow::ResultUnknownDriverType();
|
||||||
|
case impl::DriverType::PlainChannel:
|
||||||
|
//m_plain_channel_driver.Open();
|
||||||
|
//m_open_driver = std::addressof(m_plain_channel_driver);
|
||||||
|
//break;
|
||||||
|
return htclow::ResultUnknownDriverType();
|
||||||
|
default:
|
||||||
|
return htclow::ResultUnknownDriverType();
|
||||||
|
}
|
||||||
|
|
||||||
|
return ResultSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
|
IDriver *DriverManager::GetCurrentDriver() {
|
||||||
|
/* Lock ourselves. */
|
||||||
|
std::scoped_lock lk(m_mutex);
|
||||||
|
|
||||||
|
return m_open_driver;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -16,19 +16,25 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <stratosphere.hpp>
|
#include <stratosphere.hpp>
|
||||||
#include "htclow_i_driver.hpp"
|
#include "htclow_i_driver.hpp"
|
||||||
|
#include "htclow_usb_driver.hpp"
|
||||||
|
|
||||||
namespace ams::htclow::driver {
|
namespace ams::htclow::driver {
|
||||||
|
|
||||||
class DriverManager {
|
class DriverManager {
|
||||||
private:
|
private:
|
||||||
std::optional<htclow::impl::DriverType> m_driver_type{};
|
std::optional<htclow::impl::DriverType> m_driver_type{};
|
||||||
|
IDriver *m_debug_driver;
|
||||||
/* TODO: SocketDriver m_socket_driver; */
|
/* TODO: SocketDriver m_socket_driver; */
|
||||||
/* TODO: UsbDriver m_usb_driver; */
|
UsbDriver m_usb_driver{};
|
||||||
/* TODO: PlainChannelDriver m_plain_channel_driver; */
|
/* TODO: PlainChannelDriver m_plain_channel_driver; */
|
||||||
os::SdkMutex m_mutex{};
|
os::SdkMutex m_mutex{};
|
||||||
IDriver *m_open_driver{};
|
IDriver *m_open_driver{};
|
||||||
public:
|
public:
|
||||||
DriverManager() = default;
|
DriverManager() = default;
|
||||||
|
|
||||||
|
Result OpenDriver(impl::DriverType driver_type);
|
||||||
|
|
||||||
|
IDriver *GetCurrentDriver();
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,125 @@
|
|||||||
|
/*
|
||||||
|
* 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 "htclow_usb_driver.hpp"
|
||||||
|
#include "htclow_usb_impl.hpp"
|
||||||
|
|
||||||
|
namespace ams::htclow::driver {
|
||||||
|
|
||||||
|
void UsbDriver::OnUsbAvailabilityChange(UsbAvailability availability, void *param) {
|
||||||
|
/* Convert the argument to a driver. */
|
||||||
|
UsbDriver *driver = static_cast<UsbDriver *>(param);
|
||||||
|
|
||||||
|
/* Handle the change. */
|
||||||
|
switch (availability) {
|
||||||
|
case UsbAvailability_Unavailable:
|
||||||
|
CancelUsbSendReceive();
|
||||||
|
break;
|
||||||
|
case UsbAvailability_Available:
|
||||||
|
driver->m_event.Signal();
|
||||||
|
break;
|
||||||
|
case UsbAvailability_Unknown:
|
||||||
|
driver->CancelSendReceive();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Result UsbDriver::Open() {
|
||||||
|
/* Clear our event. */
|
||||||
|
m_event.Clear();
|
||||||
|
|
||||||
|
/* Set the availability change callback. */
|
||||||
|
SetUsbAvailabilityChangeCallback(OnUsbAvailabilityChange, this);
|
||||||
|
|
||||||
|
/* Initialize the interface. */
|
||||||
|
return InitializeUsbInterface();
|
||||||
|
}
|
||||||
|
|
||||||
|
void UsbDriver::Close() {
|
||||||
|
/* Finalize the interface. */
|
||||||
|
FinalizeUsbInterface();
|
||||||
|
|
||||||
|
/* Clear the availability callback. */
|
||||||
|
ClearUsbAvailabilityChangeCallback();
|
||||||
|
}
|
||||||
|
|
||||||
|
Result UsbDriver::Connect(os::EventType *event) {
|
||||||
|
/* We must not already be connected. */
|
||||||
|
AMS_ABORT_UNLESS(!m_connected);
|
||||||
|
|
||||||
|
/* Perform a wait on our event. */
|
||||||
|
const int idx = os::WaitAny(m_event.GetBase(), event);
|
||||||
|
R_UNLESS(idx == 0, htclow::ResultCancelled());
|
||||||
|
|
||||||
|
/* Clear our event. */
|
||||||
|
m_event.Clear();
|
||||||
|
|
||||||
|
/* We're connected. */
|
||||||
|
m_connected = true;
|
||||||
|
return ResultSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
|
void UsbDriver::Shutdown() {
|
||||||
|
/* If we're connected, cancel anything we're doing. */
|
||||||
|
if (m_connected) {
|
||||||
|
this->CancelSendReceive();
|
||||||
|
m_connected = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Result UsbDriver::Send(const void *src, int src_size) {
|
||||||
|
/* Check size. */
|
||||||
|
R_UNLESS(src_size >= 0, htclow::ResultInvalidArgument());
|
||||||
|
|
||||||
|
/* Send until we've sent everything. */
|
||||||
|
for (auto transferred = 0; transferred < src_size; /* ... */) {
|
||||||
|
int cur;
|
||||||
|
R_TRY(SendUsb(std::addressof(cur), reinterpret_cast<const void *>(reinterpret_cast<uintptr_t>(src) + transferred), src_size - transferred));
|
||||||
|
|
||||||
|
transferred += cur;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ResultSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
|
Result UsbDriver::Receive(void *dst, int dst_size) {
|
||||||
|
/* Check size. */
|
||||||
|
R_UNLESS(dst_size >= 0, htclow::ResultInvalidArgument());
|
||||||
|
|
||||||
|
/* Send until we've sent everything. */
|
||||||
|
for (auto transferred = 0; transferred < dst_size; /* ... */) {
|
||||||
|
int cur;
|
||||||
|
R_TRY(SendUsb(std::addressof(cur), reinterpret_cast<void *>(reinterpret_cast<uintptr_t>(dst) + transferred), dst_size - transferred));
|
||||||
|
|
||||||
|
transferred += cur;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ResultSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
|
void UsbDriver::CancelSendReceive() {
|
||||||
|
CancelUsbSendReceive();
|
||||||
|
}
|
||||||
|
|
||||||
|
void UsbDriver::Suspend() {
|
||||||
|
this->Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
void UsbDriver::Resume() {
|
||||||
|
R_ABORT_UNLESS(this->Open());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,43 @@
|
|||||||
|
/*
|
||||||
|
* 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>
|
||||||
|
#include "htclow_i_driver.hpp"
|
||||||
|
#include "htclow_usb_impl.hpp"
|
||||||
|
|
||||||
|
namespace ams::htclow::driver {
|
||||||
|
|
||||||
|
class UsbDriver final : public IDriver {
|
||||||
|
private:
|
||||||
|
bool m_connected;
|
||||||
|
os::Event m_event;
|
||||||
|
public:
|
||||||
|
UsbDriver() : m_connected(false), m_event(os::EventClearMode_ManualClear) { /* ... */ }
|
||||||
|
public:
|
||||||
|
static void OnUsbAvailabilityChange(UsbAvailability availability, void *param);
|
||||||
|
public:
|
||||||
|
virtual Result Open() override;
|
||||||
|
virtual void Close() override;
|
||||||
|
virtual Result Connect(os::EventType *event) override;
|
||||||
|
virtual void Shutdown() override;
|
||||||
|
virtual Result Send(const void *src, int src_size) override;
|
||||||
|
virtual Result Receive(void *dst, int dst_size) override;
|
||||||
|
virtual void CancelSendReceive() override;
|
||||||
|
virtual void Suspend() override;
|
||||||
|
virtual void Resume() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,460 @@
|
|||||||
|
/*
|
||||||
|
* 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 "htclow_usb_impl.hpp"
|
||||||
|
|
||||||
|
namespace ams::htclow::driver {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
/* TODO: Should we identify differently than Nintendo does? */
|
||||||
|
/* It's kind of silly to identify as "NintendoSDK DevKit", but it's also kind of amusing. */
|
||||||
|
/* TBD */
|
||||||
|
|
||||||
|
constinit usb::UsbStringDescriptor LanguageStringDescriptor = { 4, usb::UsbDescriptorType_String, {0x0409}};
|
||||||
|
constinit usb::UsbStringDescriptor ManufacturerStringDescriptor = {18, usb::UsbDescriptorType_String, {'N', 'i', 'n', 't', 'e', 'n', 'd', 'o'}};
|
||||||
|
constinit usb::UsbStringDescriptor ProductStringFullSpeedDescriptor = {38, usb::UsbDescriptorType_String, {'N', 'i', 'n', 't', 'e', 'n', 'd', 'o', 'S', 'D', 'K', ' ', 'D', 'e', 'v', 'K', 'i', 't'}};
|
||||||
|
constinit usb::UsbStringDescriptor SerialNumberStringDescriptor = { 0, usb::UsbDescriptorType_String, {}};
|
||||||
|
constinit usb::UsbStringDescriptor InterfaceStringDescriptor = {16, usb::UsbDescriptorType_String, {'h', 't', 'c', ' ', 'u', 's', 'b'}};
|
||||||
|
|
||||||
|
constinit usb::UsbDeviceDescriptor UsbDeviceDescriptorFullSpeed = {
|
||||||
|
.bLength = usb::UsbDescriptorSize_Device,
|
||||||
|
.bDescriptorType = usb::UsbDescriptorType_Device,
|
||||||
|
.bcdUSB = 0x0110,
|
||||||
|
.bDeviceClass = 0x00,
|
||||||
|
.bDeviceSubClass = 0x00,
|
||||||
|
.bDeviceProtocol = 0x00,
|
||||||
|
.bMaxPacketSize0 = 0x40,
|
||||||
|
.idVendor = 0x057E,
|
||||||
|
.idProduct = 0x3005,
|
||||||
|
.bcdDevice = 0x0100,
|
||||||
|
.iManufacturer = 0x01,
|
||||||
|
.iProduct = 0x02,
|
||||||
|
.iSerialNumber = 0x03,
|
||||||
|
.bNumConfigurations = 0x01,
|
||||||
|
};
|
||||||
|
|
||||||
|
constinit usb::UsbDeviceDescriptor UsbDeviceDescriptorHighSpeed = {
|
||||||
|
.bLength = usb::UsbDescriptorSize_Device,
|
||||||
|
.bDescriptorType = usb::UsbDescriptorType_Device,
|
||||||
|
.bcdUSB = 0x0200,
|
||||||
|
.bDeviceClass = 0x00,
|
||||||
|
.bDeviceSubClass = 0x00,
|
||||||
|
.bDeviceProtocol = 0x00,
|
||||||
|
.bMaxPacketSize0 = 0x40,
|
||||||
|
.idVendor = 0x057E,
|
||||||
|
.idProduct = 0x3005,
|
||||||
|
.bcdDevice = 0x0100,
|
||||||
|
.iManufacturer = 0x01,
|
||||||
|
.iProduct = 0x02,
|
||||||
|
.iSerialNumber = 0x03,
|
||||||
|
.bNumConfigurations = 0x01,
|
||||||
|
};
|
||||||
|
|
||||||
|
constinit usb::UsbDeviceDescriptor UsbDeviceDescriptorSuperSpeed = {
|
||||||
|
.bLength = usb::UsbDescriptorSize_Device,
|
||||||
|
.bDescriptorType = usb::UsbDescriptorType_Device,
|
||||||
|
.bcdUSB = 0x0300,
|
||||||
|
.bDeviceClass = 0x00,
|
||||||
|
.bDeviceSubClass = 0x00,
|
||||||
|
.bDeviceProtocol = 0x00,
|
||||||
|
.bMaxPacketSize0 = 0x09,
|
||||||
|
.idVendor = 0x057E,
|
||||||
|
.idProduct = 0x3005,
|
||||||
|
.bcdDevice = 0x0100,
|
||||||
|
.iManufacturer = 0x01,
|
||||||
|
.iProduct = 0x02,
|
||||||
|
.iSerialNumber = 0x03,
|
||||||
|
.bNumConfigurations = 0x01,
|
||||||
|
};
|
||||||
|
|
||||||
|
constinit u8 BinaryObjectStore[] = {
|
||||||
|
0x05,
|
||||||
|
usb::UsbDescriptorType_Bos,
|
||||||
|
0x16, 0x00,
|
||||||
|
0x02,
|
||||||
|
|
||||||
|
0x07,
|
||||||
|
usb::UsbDescriptorType_DeviceCapability,
|
||||||
|
0x02,
|
||||||
|
0x02, 0x00, 0x00, 0x00,
|
||||||
|
|
||||||
|
0x0A,
|
||||||
|
usb::UsbDescriptorType_DeviceCapability,
|
||||||
|
0x03,
|
||||||
|
0x00,
|
||||||
|
0x0E, 0x00,
|
||||||
|
0x03,
|
||||||
|
0x00,
|
||||||
|
0x00, 0x00,
|
||||||
|
};
|
||||||
|
|
||||||
|
constinit usb::UsbInterfaceDescriptor UsbInterfaceDescriptor = {
|
||||||
|
.bLength = usb::UsbDescriptorSize_Interface,
|
||||||
|
.bDescriptorType = usb::UsbDescriptorType_Interface,
|
||||||
|
.bInterfaceNumber = 0x00,
|
||||||
|
.bAlternateSetting = 0x00,
|
||||||
|
.bNumEndpoints = 0x02,
|
||||||
|
.bInterfaceClass = 0xFF,
|
||||||
|
.bInterfaceSubClass = 0xFF,
|
||||||
|
.bInterfaceProtocol = 0xFF,
|
||||||
|
.iInterface = 0x04,
|
||||||
|
};
|
||||||
|
|
||||||
|
constinit usb::UsbEndpointDescriptor UsbEndpointDescriptorsFullSpeed[2] = {
|
||||||
|
{
|
||||||
|
.bLength = usb::UsbDescriptorSize_Endpoint,
|
||||||
|
.bDescriptorType = usb::UsbDescriptorType_Endpoint,
|
||||||
|
.bEndpointAddress = 0x81,
|
||||||
|
.bmAttributes = usb::UsbEndpointAttributeMask_XferTypeBulk,
|
||||||
|
.wMaxPacketSize = 0x40,
|
||||||
|
.bInterval = 0x00,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.bLength = usb::UsbDescriptorSize_Endpoint,
|
||||||
|
.bDescriptorType = usb::UsbDescriptorType_Endpoint,
|
||||||
|
.bEndpointAddress = 0x01,
|
||||||
|
.bmAttributes = usb::UsbEndpointAttributeMask_XferTypeBulk,
|
||||||
|
.wMaxPacketSize = 0x40,
|
||||||
|
.bInterval = 0x00,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
constinit usb::UsbEndpointDescriptor UsbEndpointDescriptorsHighSpeed[2] = {
|
||||||
|
{
|
||||||
|
.bLength = usb::UsbDescriptorSize_Endpoint,
|
||||||
|
.bDescriptorType = usb::UsbDescriptorType_Endpoint,
|
||||||
|
.bEndpointAddress = 0x81,
|
||||||
|
.bmAttributes = usb::UsbEndpointAttributeMask_XferTypeBulk,
|
||||||
|
.wMaxPacketSize = 0x200,
|
||||||
|
.bInterval = 0x00,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.bLength = usb::UsbDescriptorSize_Endpoint,
|
||||||
|
.bDescriptorType = usb::UsbDescriptorType_Endpoint,
|
||||||
|
.bEndpointAddress = 0x01,
|
||||||
|
.bmAttributes = usb::UsbEndpointAttributeMask_XferTypeBulk,
|
||||||
|
.wMaxPacketSize = 0x200,
|
||||||
|
.bInterval = 0x00,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
constinit usb::UsbEndpointDescriptor UsbEndpointDescriptorsSuperSpeed[2] = {
|
||||||
|
{
|
||||||
|
.bLength = usb::UsbDescriptorSize_Endpoint,
|
||||||
|
.bDescriptorType = usb::UsbDescriptorType_Endpoint,
|
||||||
|
.bEndpointAddress = 0x81,
|
||||||
|
.bmAttributes = usb::UsbEndpointAttributeMask_XferTypeBulk,
|
||||||
|
.wMaxPacketSize = 0x400,
|
||||||
|
.bInterval = 0x00,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.bLength = usb::UsbDescriptorSize_Endpoint,
|
||||||
|
.bDescriptorType = usb::UsbDescriptorType_Endpoint,
|
||||||
|
.bEndpointAddress = 0x01,
|
||||||
|
.bmAttributes = usb::UsbEndpointAttributeMask_XferTypeBulk,
|
||||||
|
.wMaxPacketSize = 0x400,
|
||||||
|
.bInterval = 0x00,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
constinit usb::UsbEndpointCompanionDescriptor UsbEndpointCompanionDescriptor = {
|
||||||
|
.bLength = usb::UsbDescriptorSize_EndpointCompanion,
|
||||||
|
.bDescriptorType = usb::UsbDescriptorType_EndpointCompanion,
|
||||||
|
.bMaxBurst = 0x0F,
|
||||||
|
.bmAttributes = 0x00,
|
||||||
|
.wBytesPerInterval = 0x0000,
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr size_t UsbDmaBufferSize = 0x60000;
|
||||||
|
|
||||||
|
alignas(os::MemoryPageSize) constinit u8 g_usb_receive_buffer[UsbDmaBufferSize];
|
||||||
|
alignas(os::MemoryPageSize) constinit u8 g_usb_send_buffer[UsbDmaBufferSize];
|
||||||
|
alignas(os::ThreadStackAlignment) constinit u8 g_usb_indication_thread_stack[16_KB];
|
||||||
|
|
||||||
|
constinit UsbAvailabilityChangeCallback g_availability_change_callback = nullptr;
|
||||||
|
constinit void *g_availability_change_param = nullptr;
|
||||||
|
|
||||||
|
constinit bool g_usb_interface_initialized = false;
|
||||||
|
|
||||||
|
os::Event g_usb_break_event(os::EventClearMode_ManualClear);
|
||||||
|
|
||||||
|
constinit os::ThreadType g_usb_indication_thread = {};
|
||||||
|
|
||||||
|
constinit os::SdkMutex g_usb_driver_mutex;
|
||||||
|
|
||||||
|
usb::DsClient g_ds_client;
|
||||||
|
usb::DsInterface g_ds_interface;
|
||||||
|
usb::DsEndpoint g_ds_endpoints[2];
|
||||||
|
|
||||||
|
void InvokeAvailabilityChangeCallback(UsbAvailability availability) {
|
||||||
|
if (g_availability_change_callback) {
|
||||||
|
g_availability_change_callback(availability, g_availability_change_param);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Result ConvertUsbDriverResult(Result result) {
|
||||||
|
if (result.GetModule() == R_NAMESPACE_MODULE_ID(usb)) {
|
||||||
|
if (usb::ResultResourceBusy::Includes(result)) {
|
||||||
|
return htclow::ResultUsbDriverBusyError();
|
||||||
|
} else if (usb::ResultMemAllocFailure::Includes(result)) {
|
||||||
|
return htclow::ResultOutOfMemory();
|
||||||
|
} else {
|
||||||
|
return htclow::ResultUsbDriverUnknownError();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Result InitializeDsClient() {
|
||||||
|
/* Initialize the client. */
|
||||||
|
R_TRY(ConvertUsbDriverResult(g_ds_client.Initialize(usb::ComplexId_Tegra21x)));
|
||||||
|
|
||||||
|
/* Clear device data. */
|
||||||
|
R_ABORT_UNLESS(g_ds_client.ClearDeviceData());
|
||||||
|
|
||||||
|
/* Add string descriptors. */
|
||||||
|
u8 index;
|
||||||
|
R_TRY(g_ds_client.AddUsbStringDescriptor(std::addressof(index), std::addressof(LanguageStringDescriptor)));
|
||||||
|
R_TRY(g_ds_client.AddUsbStringDescriptor(std::addressof(index), std::addressof(ManufacturerStringDescriptor)));
|
||||||
|
R_TRY(g_ds_client.AddUsbStringDescriptor(std::addressof(index), std::addressof(ProductStringFullSpeedDescriptor)));
|
||||||
|
R_TRY(g_ds_client.AddUsbStringDescriptor(std::addressof(index), std::addressof(SerialNumberStringDescriptor)));
|
||||||
|
R_TRY(g_ds_client.AddUsbStringDescriptor(std::addressof(index), std::addressof(InterfaceStringDescriptor)));
|
||||||
|
|
||||||
|
/* Add device descriptors. */
|
||||||
|
R_TRY(g_ds_client.SetUsbDeviceDescriptor(std::addressof(UsbDeviceDescriptorFullSpeed), usb::UsbDeviceSpeed_Full));
|
||||||
|
R_TRY(g_ds_client.SetUsbDeviceDescriptor(std::addressof(UsbDeviceDescriptorHighSpeed), usb::UsbDeviceSpeed_High));
|
||||||
|
R_TRY(g_ds_client.SetUsbDeviceDescriptor(std::addressof(UsbDeviceDescriptorSuperSpeed), usb::UsbDeviceSpeed_Super));
|
||||||
|
|
||||||
|
/* Set binary object store. */
|
||||||
|
R_TRY(g_ds_client.SetBinaryObjectStore(BinaryObjectStore, sizeof(BinaryObjectStore)));
|
||||||
|
|
||||||
|
return ResultSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
|
Result InitializeDsInterface() {
|
||||||
|
/* Initialize the interface. */
|
||||||
|
R_TRY(ConvertUsbDriverResult(g_ds_interface.Initialize(std::addressof(g_ds_client), 0)));
|
||||||
|
|
||||||
|
/* Append the interface descriptors for all speeds. */
|
||||||
|
R_TRY(g_ds_interface.AppendConfigurationData(usb::UsbDeviceSpeed_Full, std::addressof(UsbInterfaceDescriptor), sizeof(usb::UsbInterfaceDescriptor)));
|
||||||
|
R_TRY(g_ds_interface.AppendConfigurationData(usb::UsbDeviceSpeed_Full, std::addressof(UsbEndpointDescriptorsFullSpeed[0]), sizeof(usb::UsbEndpointDescriptor)));
|
||||||
|
R_TRY(g_ds_interface.AppendConfigurationData(usb::UsbDeviceSpeed_Full, std::addressof(UsbEndpointDescriptorsFullSpeed[1]), sizeof(usb::UsbEndpointDescriptor)));
|
||||||
|
|
||||||
|
R_TRY(g_ds_interface.AppendConfigurationData(usb::UsbDeviceSpeed_High, std::addressof(UsbInterfaceDescriptor), sizeof(usb::UsbInterfaceDescriptor)));
|
||||||
|
R_TRY(g_ds_interface.AppendConfigurationData(usb::UsbDeviceSpeed_High, std::addressof(UsbEndpointDescriptorsHighSpeed[0]), sizeof(usb::UsbEndpointDescriptor)));
|
||||||
|
R_TRY(g_ds_interface.AppendConfigurationData(usb::UsbDeviceSpeed_High, std::addressof(UsbEndpointDescriptorsHighSpeed[1]), sizeof(usb::UsbEndpointDescriptor)));
|
||||||
|
|
||||||
|
R_TRY(g_ds_interface.AppendConfigurationData(usb::UsbDeviceSpeed_Super, std::addressof(UsbInterfaceDescriptor), sizeof(usb::UsbInterfaceDescriptor)));
|
||||||
|
R_TRY(g_ds_interface.AppendConfigurationData(usb::UsbDeviceSpeed_Super, std::addressof(UsbEndpointDescriptorsSuperSpeed[0]), sizeof(usb::UsbEndpointDescriptor)));
|
||||||
|
R_TRY(g_ds_interface.AppendConfigurationData(usb::UsbDeviceSpeed_Super, std::addressof(UsbEndpointCompanionDescriptor), sizeof(usb::UsbEndpointCompanionDescriptor)));
|
||||||
|
R_TRY(g_ds_interface.AppendConfigurationData(usb::UsbDeviceSpeed_Super, std::addressof(UsbEndpointDescriptorsSuperSpeed[1]), sizeof(usb::UsbEndpointDescriptor)));
|
||||||
|
R_TRY(g_ds_interface.AppendConfigurationData(usb::UsbDeviceSpeed_Super, std::addressof(UsbEndpointCompanionDescriptor), sizeof(usb::UsbEndpointCompanionDescriptor)));
|
||||||
|
|
||||||
|
return ResultSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
|
Result InitializeDsEndpoints() {
|
||||||
|
R_TRY(g_ds_endpoints[0].Initialize(std::addressof(g_ds_interface), 0x81));
|
||||||
|
R_TRY(g_ds_endpoints[1].Initialize(std::addressof(g_ds_interface), 0x01));
|
||||||
|
return ResultSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
|
void UsbIndicationThreadFunction(void *arg) {
|
||||||
|
/* Get the state change event. */
|
||||||
|
os::SystemEventType *state_change_event = g_ds_client.GetStateChangeEvent();
|
||||||
|
|
||||||
|
/* Setup waitable manager. */
|
||||||
|
os::WaitableManagerType manager;
|
||||||
|
os::InitializeWaitableManager(std::addressof(manager));
|
||||||
|
|
||||||
|
/* Link waitable holders. */
|
||||||
|
os::WaitableHolderType state_change_holder;
|
||||||
|
os::WaitableHolderType break_holder;
|
||||||
|
os::InitializeWaitableHolder(std::addressof(state_change_holder), state_change_event);
|
||||||
|
os::LinkWaitableHolder(std::addressof(manager), std::addressof(state_change_holder));
|
||||||
|
os::InitializeWaitableHolder(std::addressof(break_holder), g_usb_break_event.GetBase());
|
||||||
|
os::LinkWaitableHolder(std::addressof(manager), std::addressof(break_holder));
|
||||||
|
|
||||||
|
/* Loop forever. */
|
||||||
|
while (true) {
|
||||||
|
/* If we should break, do so. */
|
||||||
|
if (os::WaitAny(std::addressof(manager)) == std::addressof(break_holder)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Clear the state change event. */
|
||||||
|
os::ClearSystemEvent(state_change_event);
|
||||||
|
|
||||||
|
/* Get the new state. */
|
||||||
|
usb::UsbState usb_state;
|
||||||
|
R_ABORT_UNLESS(g_ds_client.GetState(std::addressof(usb_state)));
|
||||||
|
|
||||||
|
switch (usb_state) {
|
||||||
|
case UsbState_Detached:
|
||||||
|
case UsbState_Suspended:
|
||||||
|
InvokeAvailabilityChangeCallback(UsbAvailability_Unavailable);
|
||||||
|
break;
|
||||||
|
case UsbState_Configured:
|
||||||
|
InvokeAvailabilityChangeCallback(UsbAvailability_Available);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
/* Nothing to do. */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Clear the break event. */
|
||||||
|
g_usb_break_event.Clear();
|
||||||
|
|
||||||
|
/* Unlink all holders. */
|
||||||
|
os::UnlinkAllWaitableHolder(std::addressof(manager));
|
||||||
|
|
||||||
|
/* Finalize the waitable holders and manager. */
|
||||||
|
os::FinalizeWaitableHolder(std::addressof(break_holder));
|
||||||
|
os::FinalizeWaitableHolder(std::addressof(state_change_holder));
|
||||||
|
os::FinalizeWaitableManager(std::addressof(manager));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetUsbAvailabilityChangeCallback(UsbAvailabilityChangeCallback callback, void *param) {
|
||||||
|
g_availability_change_callback = callback;
|
||||||
|
g_availability_change_param = param;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ClearUsbAvailabilityChangeCallback() {
|
||||||
|
g_availability_change_callback = nullptr;
|
||||||
|
g_availability_change_param = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result InitializeUsbInterface() {
|
||||||
|
/* Set the interface as initialized. */
|
||||||
|
g_usb_interface_initialized = true;
|
||||||
|
|
||||||
|
/* If we fail somewhere, finalize. */
|
||||||
|
auto init_guard = SCOPE_GUARD { FinalizeUsbInterface(); };
|
||||||
|
|
||||||
|
/* Get the serial number. */
|
||||||
|
{
|
||||||
|
settings::factory::SerialNumber serial_number;
|
||||||
|
serial_number.str[0] = '\x00';
|
||||||
|
|
||||||
|
if (R_FAILED(settings::factory::GetSerialNumber(std::addressof(serial_number))) || serial_number.str[0] == '\x00') {
|
||||||
|
std::strcpy(serial_number.str, "Corrupted S/N");
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma GCC diagnostic push
|
||||||
|
#pragma GCC diagnostic ignored "-Waddress-of-packed-member"
|
||||||
|
|
||||||
|
u16 *dst = SerialNumberStringDescriptor.wData;
|
||||||
|
u8 *src = reinterpret_cast<u8 *>(serial_number.str);
|
||||||
|
u8 count = 0;
|
||||||
|
|
||||||
|
#pragma GCC diagnostic pop
|
||||||
|
|
||||||
|
while (*src) {
|
||||||
|
*dst++ = static_cast<u16>(*src++);
|
||||||
|
++count;
|
||||||
|
}
|
||||||
|
|
||||||
|
SerialNumberStringDescriptor.bLength = 2 + (2 * count);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Initialize the client. */
|
||||||
|
R_TRY(InitializeDsClient());
|
||||||
|
|
||||||
|
/* Initialize the interface. */
|
||||||
|
R_TRY(InitializeDsInterface());
|
||||||
|
|
||||||
|
/* Initialize the endpoints. */
|
||||||
|
R_TRY(ConvertUsbDriverResult(InitializeDsEndpoints()));
|
||||||
|
|
||||||
|
/* Create the indication thread. */
|
||||||
|
R_ABORT_UNLESS(os::CreateThread(std::addressof(g_usb_indication_thread), &UsbIndicationThreadFunction, nullptr, g_usb_indication_thread_stack, sizeof(g_usb_indication_thread_stack), AMS_GET_SYSTEM_THREAD_PRIORITY(htc, HtclowUsbIndication)));
|
||||||
|
|
||||||
|
/* Set the thread name. */
|
||||||
|
os::SetThreadNamePointer(std::addressof(g_usb_indication_thread), AMS_GET_SYSTEM_THREAD_NAME(htc, HtclowUsbIndication));
|
||||||
|
|
||||||
|
/* Start the indication thread. */
|
||||||
|
os::StartThread(std::addressof(g_usb_indication_thread));
|
||||||
|
|
||||||
|
/* Enable the usb device. */
|
||||||
|
R_TRY(g_ds_client.EnableDevice());
|
||||||
|
|
||||||
|
/* We succeeded! */
|
||||||
|
init_guard.Cancel();
|
||||||
|
return ResultSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
|
void FinalizeUsbInterface() {
|
||||||
|
g_usb_break_event.Signal();
|
||||||
|
os::WaitThread(std::addressof(g_usb_indication_thread));
|
||||||
|
os::DestroyThread(std::addressof(g_usb_indication_thread));
|
||||||
|
g_ds_client.DisableDevice();
|
||||||
|
g_ds_endpoints[1].Finalize();
|
||||||
|
g_ds_endpoints[0].Finalize();
|
||||||
|
g_ds_interface.Finalize();
|
||||||
|
g_ds_client.Finalize();
|
||||||
|
g_usb_interface_initialized = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result SendUsb(int *out_transferred, const void *src, int src_size) {
|
||||||
|
/* Acquire exclusive access to the driver. */
|
||||||
|
std::scoped_lock lk(g_usb_driver_mutex);
|
||||||
|
|
||||||
|
/* Check that we can send the data. */
|
||||||
|
R_UNLESS(src_size <= static_cast<int>(UsbDmaBufferSize), htclow::ResultInvalidArgument());
|
||||||
|
|
||||||
|
/* Copy the data to the dma buffer. */
|
||||||
|
std::memcpy(g_usb_send_buffer, src, src_size);
|
||||||
|
|
||||||
|
/* Transfer data. */
|
||||||
|
u32 transferred;
|
||||||
|
R_UNLESS(R_SUCCEEDED(g_ds_endpoints[0].PostBuffer(std::addressof(transferred), g_usb_send_buffer, src_size)), htclow::ResultUsbDriverSendError());
|
||||||
|
R_UNLESS(transferred == static_cast<u32>(src_size), htclow::ResultUsbDriverSendError());
|
||||||
|
|
||||||
|
/* Set output transferred size. */
|
||||||
|
*out_transferred = src_size;
|
||||||
|
return ResultSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
|
Result ReceiveUsb(int *out_transferred, void *dst, int dst_size) {
|
||||||
|
/* Check that we can send the data. */
|
||||||
|
R_UNLESS(dst_size <= static_cast<int>(UsbDmaBufferSize), htclow::ResultInvalidArgument());
|
||||||
|
|
||||||
|
/* Transfer data. */
|
||||||
|
u32 transferred;
|
||||||
|
R_UNLESS(R_SUCCEEDED(g_ds_endpoints[1].PostBuffer(std::addressof(transferred), g_usb_receive_buffer, dst_size)), htclow::ResultUsbDriverReceiveError());
|
||||||
|
R_UNLESS(transferred == static_cast<u32>(dst_size), htclow::ResultUsbDriverReceiveError());
|
||||||
|
|
||||||
|
/* Copy the data. */
|
||||||
|
std::memcpy(dst, g_usb_receive_buffer, dst_size);
|
||||||
|
|
||||||
|
/* Set output transferred size. */
|
||||||
|
*out_transferred = dst_size;
|
||||||
|
return ResultSuccess();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CancelUsbSendReceive() {
|
||||||
|
if (g_usb_interface_initialized) {
|
||||||
|
g_ds_endpoints[0].Cancel();
|
||||||
|
g_ds_endpoints[1].Cancel();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -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 <stratosphere.hpp>
|
||||||
|
|
||||||
|
namespace ams::htclow::driver {
|
||||||
|
|
||||||
|
enum UsbAvailability {
|
||||||
|
UsbAvailability_Unavailable = 1,
|
||||||
|
UsbAvailability_Available = 2,
|
||||||
|
UsbAvailability_Unknown = 3,
|
||||||
|
};
|
||||||
|
|
||||||
|
using UsbAvailabilityChangeCallback = void (*)(UsbAvailability availability, void *param);
|
||||||
|
|
||||||
|
void SetUsbAvailabilityChangeCallback(UsbAvailabilityChangeCallback callback, void *param);
|
||||||
|
void ClearUsbAvailabilityChangeCallback();
|
||||||
|
|
||||||
|
Result InitializeUsbInterface();
|
||||||
|
void FinalizeUsbInterface();
|
||||||
|
|
||||||
|
Result SendUsb(int *out_transferred, const void *src, int src_size);
|
||||||
|
Result ReceiveUsb(int *out_transferred, void *dst, int dst_size);
|
||||||
|
|
||||||
|
void CancelUsbSendReceive();
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -18,11 +18,46 @@
|
|||||||
|
|
||||||
namespace ams::htclow {
|
namespace ams::htclow {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
constexpr inline size_t ThreadStackSize = 4_KB;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
Listener::Listener(mem::StandardAllocator *allocator, mux::Mux *mux, ctrl::HtcctrlService *ctrl_srv, Worker *worker)
|
Listener::Listener(mem::StandardAllocator *allocator, mux::Mux *mux, ctrl::HtcctrlService *ctrl_srv, Worker *worker)
|
||||||
: m_thread_stack_size(4_KB), m_allocator(allocator), m_mux(mux), m_service(ctrl_srv), m_worker(worker), m_event(os::EventClearMode_ManualClear), m_driver(nullptr), m_thread_running(false), m_cancelled(false)
|
: m_thread_stack_size(ThreadStackSize), m_allocator(allocator), m_mux(mux), m_service(ctrl_srv), m_worker(worker), m_event(os::EventClearMode_ManualClear), m_driver(nullptr), m_thread_running(false), m_cancelled(false)
|
||||||
{
|
{
|
||||||
/* Allocate stack. */
|
/* Allocate stack. */
|
||||||
m_listen_thread_stack = m_allocator->Allocate(m_thread_stack_size, os::ThreadStackAlignment);
|
m_listen_thread_stack = m_allocator->Allocate(m_thread_stack_size, os::ThreadStackAlignment);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Listener::Start(driver::IDriver *driver) {
|
||||||
|
/* Check pre-conditions. */
|
||||||
|
AMS_ASSERT(!m_thread_running);
|
||||||
|
|
||||||
|
/* Create the thread. */
|
||||||
|
R_ABORT_UNLESS(os::CreateThread(std::addressof(m_listen_thread), ListenThreadEntry, this, m_listen_thread_stack, ThreadStackSize, AMS_GET_SYSTEM_THREAD_PRIORITY(htc, HtclowListen)));
|
||||||
|
|
||||||
|
/* Set the thread name. */
|
||||||
|
os::SetThreadNamePointer(std::addressof(m_listen_thread), AMS_GET_SYSTEM_THREAD_NAME(htc, HtclowListen));
|
||||||
|
|
||||||
|
/* Set our driver. */
|
||||||
|
m_driver = driver;
|
||||||
|
|
||||||
|
/* Set state. */
|
||||||
|
m_thread_running = true;
|
||||||
|
m_cancelled = false;
|
||||||
|
|
||||||
|
/* Clear our event. */
|
||||||
|
m_event.Clear();
|
||||||
|
|
||||||
|
/* Start the thread. */
|
||||||
|
os::StartThread(std::addressof(m_listen_thread));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Listener::ListenThread() {
|
||||||
|
/* TODO */
|
||||||
|
AMS_ABORT("Listener::ListenThread");
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -32,8 +32,16 @@ namespace ams::htclow {
|
|||||||
driver::IDriver *m_driver;
|
driver::IDriver *m_driver;
|
||||||
bool m_thread_running;
|
bool m_thread_running;
|
||||||
bool m_cancelled;
|
bool m_cancelled;
|
||||||
|
private:
|
||||||
|
static void ListenThreadEntry(void *arg) {
|
||||||
|
static_cast<Listener *>(arg)->ListenThread();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ListenThread();
|
||||||
public:
|
public:
|
||||||
Listener(mem::StandardAllocator *allocator, mux::Mux *mux, ctrl::HtcctrlService *ctrl_srv, Worker *worker);
|
Listener(mem::StandardAllocator *allocator, mux::Mux *mux, ctrl::HtcctrlService *ctrl_srv, Worker *worker);
|
||||||
|
|
||||||
|
void Start(driver::IDriver *driver);
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -22,7 +22,8 @@ namespace ams::htclow {
|
|||||||
: m_packet_factory(allocator), m_driver_manager(), m_mux(std::addressof(m_packet_factory), std::addressof(m_ctrl_state_machine)),
|
: m_packet_factory(allocator), m_driver_manager(), m_mux(std::addressof(m_packet_factory), std::addressof(m_ctrl_state_machine)),
|
||||||
m_ctrl_packet_factory(allocator), m_ctrl_state_machine(), m_ctrl_service(std::addressof(m_ctrl_packet_factory), std::addressof(m_ctrl_state_machine), std::addressof(m_mux)),
|
m_ctrl_packet_factory(allocator), m_ctrl_state_machine(), m_ctrl_service(std::addressof(m_ctrl_packet_factory), std::addressof(m_ctrl_state_machine), std::addressof(m_mux)),
|
||||||
m_worker(allocator, std::addressof(m_mux), std::addressof(m_ctrl_service)),
|
m_worker(allocator, std::addressof(m_mux), std::addressof(m_ctrl_service)),
|
||||||
m_listener(allocator, std::addressof(m_mux), std::addressof(m_ctrl_service), std::addressof(m_worker))
|
m_listener(allocator, std::addressof(m_mux), std::addressof(m_ctrl_service), std::addressof(m_worker)),
|
||||||
|
m_is_driver_open(false)
|
||||||
{
|
{
|
||||||
/* ... */
|
/* ... */
|
||||||
}
|
}
|
||||||
@ -32,7 +33,23 @@ namespace ams::htclow {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Result HtclowManagerImpl::OpenDriver(impl::DriverType driver_type) {
|
Result HtclowManagerImpl::OpenDriver(impl::DriverType driver_type) {
|
||||||
AMS_ABORT("TODO");
|
/* Set the driver type. */
|
||||||
|
m_ctrl_service.SetDriverType(driver_type);
|
||||||
|
|
||||||
|
/* Ensure that we don't end up in an invalid state. */
|
||||||
|
auto drv_guard = SCOPE_GUARD { m_ctrl_service.SetDriverType(impl::DriverType::Unknown); };
|
||||||
|
|
||||||
|
/* Try to open the driver. */
|
||||||
|
R_TRY(m_driver_manager.OpenDriver(driver_type));
|
||||||
|
|
||||||
|
/* Start the listener. */
|
||||||
|
m_listener.Start(m_driver_manager.GetCurrentDriver());
|
||||||
|
|
||||||
|
/* Note the driver as open. */
|
||||||
|
m_is_driver_open = true;
|
||||||
|
|
||||||
|
drv_guard.Cancel();
|
||||||
|
return ResultSuccess();
|
||||||
}
|
}
|
||||||
|
|
||||||
//void HtclowManagerImpl::CloseDriver();
|
//void HtclowManagerImpl::CloseDriver();
|
||||||
|
@ -31,7 +31,7 @@ namespace ams::htclow::mux {
|
|||||||
GlobalSendBuffer m_global_send_buffer;
|
GlobalSendBuffer m_global_send_buffer;
|
||||||
os::SdkMutex m_mutex;
|
os::SdkMutex m_mutex;
|
||||||
bool m_is_sleeping;
|
bool m_is_sleeping;
|
||||||
u16 m_version;
|
s16 m_version;
|
||||||
public:
|
public:
|
||||||
Mux(PacketFactory *pf, ctrl::HtcctrlStateMachine *sm);
|
Mux(PacketFactory *pf, ctrl::HtcctrlStateMachine *sm);
|
||||||
|
|
||||||
|
@ -36,6 +36,7 @@
|
|||||||
#include <vapours/results/fs_results.hpp>
|
#include <vapours/results/fs_results.hpp>
|
||||||
#include <vapours/results/gpio_results.hpp>
|
#include <vapours/results/gpio_results.hpp>
|
||||||
#include <vapours/results/hipc_results.hpp>
|
#include <vapours/results/hipc_results.hpp>
|
||||||
|
#include <vapours/results/htclow_results.hpp>
|
||||||
#include <vapours/results/i2c_results.hpp>
|
#include <vapours/results/i2c_results.hpp>
|
||||||
#include <vapours/results/kvdb_results.hpp>
|
#include <vapours/results/kvdb_results.hpp>
|
||||||
#include <vapours/results/loader_results.hpp>
|
#include <vapours/results/loader_results.hpp>
|
||||||
|
@ -0,0 +1,39 @@
|
|||||||
|
/*
|
||||||
|
* 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::htclow {
|
||||||
|
|
||||||
|
R_DEFINE_NAMESPACE_RESULT_MODULE(29);
|
||||||
|
|
||||||
|
R_DEFINE_ERROR_RESULT(UnknownDriverType, 3);
|
||||||
|
|
||||||
|
R_DEFINE_ERROR_RANGE(InternalError, 1000, 2999);
|
||||||
|
R_DEFINE_ERROR_RESULT(OutOfMemory, 1002);
|
||||||
|
R_DEFINE_ERROR_RESULT(InvalidArgument, 1003);
|
||||||
|
R_DEFINE_ERROR_RESULT(Cancelled, 1005);
|
||||||
|
|
||||||
|
R_DEFINE_ERROR_RANGE(DriverError, 1200, 1999);
|
||||||
|
R_DEFINE_ERROR_RESULT(DriverOpened, 1201);
|
||||||
|
|
||||||
|
R_DEFINE_ERROR_RANGE(UsbDriverError, 1400, 1499);
|
||||||
|
R_DEFINE_ERROR_RESULT(UsbDriverUnknownError, 1401);
|
||||||
|
R_DEFINE_ERROR_RESULT(UsbDriverBusyError, 1402);
|
||||||
|
R_DEFINE_ERROR_RESULT(UsbDriverReceiveError, 1403);
|
||||||
|
R_DEFINE_ERROR_RESULT(UsbDriverSendError, 1404);
|
||||||
|
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user