fusee-primary: add fastboot usb gadget

This commit is contained in:
misson20000 2020-10-18 15:26:22 -07:00
parent d29baa337e
commit ebc3de045a
34 changed files with 5444 additions and 9 deletions

View file

@ -10,3 +10,7 @@ stage2_entrypoint = 0xF0000000
; To force-disable nogc, add nogc = 0
; To opt out of using Atmosphere's NCM reimplementation, add disable_ncm = 1
[fastboot]
force_enable = 0
button_timeout_ms = 0

View file

@ -27,7 +27,7 @@ endif
#---------------------------------------------------------------------------------
TARGET := $(notdir $(CURDIR))
BUILD := build
SOURCES := src src/sdmmc src/lib src/lib/fatfs src/display
SOURCES := src src/sdmmc src/lib src/lib/fatfs src/display src/fastboot
DATA := data
INCLUDES := include ../../libraries/libvapours/include
@ -39,11 +39,10 @@ DEFINES := -D__BPMP__ -DFUSEE_STAGE1_SRC -DATMOSPHERE_GIT_BRANCH=\"$(AMSBRANCH)\
CFLAGS := \
-g \
-O2 \
-Os \
-fomit-frame-pointer \
-ffunction-sections \
-fdata-sections \
-std=gnu11 \
-Werror \
-Wall \
-fstrict-volatile-bitfields \
@ -51,7 +50,7 @@ CFLAGS := \
CFLAGS += $(INCLUDE)
CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -std=gnu++11
CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -std=c++2a
ASFLAGS := -g $(ARCH)
LDFLAGS = -specs=$(TOPDIR)/linker.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map)

View file

@ -20,12 +20,15 @@
#include <string.h>
#define CONFIG_LOG_LEVEL_KEY "log_level"
#define STAGE2_NAME_KEY "stage2_path"
#define STAGE2_MTC_NAME_KEY "stage2_mtc_path"
#define STAGE2_ADDRESS_KEY "stage2_addr"
#define STAGE2_ENTRYPOINT_KEY "stage2_entrypoint"
#define CONFIG_LOG_LEVEL_KEY "log_level"
#define FASTBOOT_FORCE_ENABLE_KEY "force_enable"
#define FASTBOOT_BUTTON_TIMEOUT_KEY "button_timeout_ms"
static int bct0_ini_handler(void *user, const char *section, const char *name, const char *value) {
bct0_t *bct0 = (bct0_t*) user;
@ -61,6 +64,18 @@ static int bct0_ini_handler(void *user, const char *section, const char *name, c
} else {
return 0;
}
} else if (strcmp(section, "fastboot") == 0) {
if (strcmp(name, FASTBOOT_FORCE_ENABLE_KEY) == 0) {
int tmp = 0;
sscanf(value, "%d", &tmp);
bct0->fastboot_force_enable = (tmp != 0);
} else if (strcmp(name, FASTBOOT_BUTTON_TIMEOUT_KEY) == 0) {
int tmp = 0;
sscanf(value, "%d", &tmp);
bct0->fastboot_button_timeout = tmp;
} else {
return 0;
}
} else {
return 0;
}
@ -77,5 +92,8 @@ int bct0_parse(const char *ini, bct0_t *out) {
out->stage2_load_address = 0xf0000000;
out->stage2_entrypoint = 0xf0000000;
out->fastboot_force_enable = false;
out->fastboot_button_timeout = 3000;
return ini_parse_string(ini, bct0_ini_handler, out);
}

View file

@ -33,6 +33,10 @@ typedef struct {
char stage2_mtc_path[0x100];
uintptr_t stage2_load_address;
uintptr_t stage2_entrypoint;
/* [fastboot] */
bool fastboot_force_enable;
int fastboot_button_timeout;
} bct0_t;
int bct0_parse(const char *ini, bct0_t *out);

View file

@ -0,0 +1,76 @@
/*
* 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 "fastboot.h"
#include "fastboot_gadget.h"
extern "C" {
#include "../lib/log.h"
#include "../timers.h"
#include "../utils.h"
#include "../btn.h"
}
static ams::fastboot::FastbootGadget fastboot_gadget((uint8_t*) 0xf0000000, 1024 * 1024 * 256); // 256 MiB download buffer reaches end of address space
extern "C" fastboot_return fastboot_enter(const bct0_t *bct0) {
bool should_enter = bct0->fastboot_force_enable;
log_setup_display();
if (!should_enter) {
if (bct0->fastboot_button_timeout > 0) {
uint32_t timeout = bct0->fastboot_button_timeout;
uint32_t start_time = get_time_ms();
uint32_t last_message_secs = 0;
while (get_time_ms() - start_time < timeout) {
uint32_t seconds_remaining = (start_time + timeout - get_time_ms() + 999) / 1000;
if (seconds_remaining != last_message_secs) {
print((ScreenLogLevel) (SCREEN_LOG_LEVEL_NONE | SCREEN_LOG_LEVEL_NO_PREFIX), "\rPress volume up in %d seconds to enter fastboot. ", seconds_remaining);
last_message_secs = seconds_remaining;
}
if (btn_read() & BTN_VOL_UP) {
should_enter = true;
break;
}
}
print((ScreenLogLevel) (SCREEN_LOG_LEVEL_NONE | SCREEN_LOG_LEVEL_NO_PREFIX), "\n\n");
}
}
if (!should_enter) {
log_cleanup_display();
return FASTBOOT_SKIPPED;
}
print(SCREEN_LOG_LEVEL_DEBUG, "Entering fastboot.\n");
ams::xusb::Initialize();
print(SCREEN_LOG_LEVEL_DEBUG, "Finished initializing USB hardware.\n");
ams::xusb::EnableDevice(fastboot_gadget);
print((ScreenLogLevel) (SCREEN_LOG_LEVEL_NONE | SCREEN_LOG_LEVEL_NO_PREFIX), "Fastboot mode:\n");
print((ScreenLogLevel) (SCREEN_LOG_LEVEL_NONE | SCREEN_LOG_LEVEL_NO_PREFIX), "-------------------------------\n");
print((ScreenLogLevel) (SCREEN_LOG_LEVEL_NONE | SCREEN_LOG_LEVEL_NO_PREFIX), "Volume up: Reboot to RCM.\n");
print((ScreenLogLevel) (SCREEN_LOG_LEVEL_NONE | SCREEN_LOG_LEVEL_NO_PREFIX), "Volume down: Boot normally.\n");
print((ScreenLogLevel) (SCREEN_LOG_LEVEL_NONE | SCREEN_LOG_LEVEL_NO_PREFIX), "POWER: Reboot to fusee-primary.\n");
fastboot_return r = fastboot_gadget.Run();
log_cleanup_display();
return r;
}

View file

@ -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
#ifdef __cplusplus
extern "C" {
#endif
#include "../bct0.h"
enum fastboot_return {
FASTBOOT_INVALID,
FASTBOOT_SKIPPED,
FASTBOOT_LOAD_STAGE2,
FASTBOOT_CHAINLOAD,
};
enum fastboot_return fastboot_enter(const bct0_t *bct0);
#ifdef __cplusplus
}
#endif

View file

@ -0,0 +1,192 @@
/*
* 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/>.
*/
namespace ams {
namespace fastboot {
namespace descriptors {
USB_DECLARE_STRING_DESCRIPTOR(langid, u"\x0409"); // English (US)
USB_DECLARE_STRING_DESCRIPTOR(manu, u"Atmosphère");
USB_DECLARE_STRING_DESCRIPTOR(product, u"Fusée Fastboot Gadget");
USB_DECLARE_STRING_DESCRIPTOR(serial, u"Serial Number");
USB_DECLARE_STRING_DESCRIPTOR(configuration, u"Fastboot");
USB_DECLARE_STRING_DESCRIPTOR(interface, u"Fastboot");
static constexpr usb::StringDescriptorIndexer<SD_langid,
SD_manu,
SD_product,
SD_serial,
SD_configuration,
SD_interface> sd_indexer;
static constexpr usb::DeviceDescriptor device_descriptor = {
{
.bLength = sizeof(usb::DeviceDescriptor),
.bDescriptorType = usb::DescriptorType::DEVICE,
},
{
.bcdUSB = 0x210,
.bDeviceClass = 0,
.bDeviceSubClass = 0,
.bDeviceProtocol = 0,
.bMaxPacketSize0 = 64,
.idVendor = 0x1209,
.idProduct = 0x8b01,
.bcdDevice = 0x100,
.iManufacturer = sd_indexer.manu,
.iProduct = sd_indexer.product,
.iSerialNumber = sd_indexer.serial,
.bNumConfigurations = 1,
}
};
static constexpr usb::DeviceQualifierDescriptor device_qualifier_descriptor = {
{
.bLength = sizeof(usb::DeviceQualifierDescriptor),
.bDescriptorType = usb::DescriptorType::DEVICE_QUALIFIER,
},
{
.bcdUSB = 0x210,
.bDeviceClass = 0,
.bDeviceSubClass = 0,
.bDeviceProtocol = 0,
.bMaxPacketSize0 = 64,
.bNumConfigurations = 1,
.bReserved = 0,
}
};
static constexpr struct WholeConfigurationDescriptor {
usb::ConfigurationDescriptor configuration_descriptor = {
{
.bLength = sizeof(usb::ConfigurationDescriptor),
.bDescriptorType = usb::DescriptorType::CONFIGURATION,
},
{
.wTotalLength = sizeof(WholeConfigurationDescriptor),
.bNumInterfaces = 1,
.bConfigurationValue = 1,
.iConfiguration = 4,
.bmAttributes = 0x80,
.bMaxPower = 0,
}
};
usb::InterfaceDescriptor interface_descriptor {
{
.bLength = sizeof(usb::InterfaceDescriptor),
.bDescriptorType = usb::DescriptorType::INTERFACE,
},
{
.bInterfaceNumber = 0,
.bAlternateSetting = 0,
.bNumEndpoints = 2,
.bInterfaceClass = 0xff, // Vendor Specific Class.
.bInterfaceSubClass = 0x42, // Fastboot.
.bInterfaceProtocol = 0x03, // Fastboot.
.iInterface = 5,
}
};
usb::EndpointDescriptor endpoint_in_descriptor {
{
.bLength = sizeof(usb::EndpointDescriptor),
.bDescriptorType = usb::DescriptorType::ENDPOINT,
},
{
.bEndpointAddress = 0x81,
.bmAttributes = 0x2,
.wMaxPacketSize = 512,
.bInterval = 1,
}
};
usb::EndpointDescriptor endpoint_out_descriptor {
{
.bLength = sizeof(usb::EndpointDescriptor),
.bDescriptorType = usb::DescriptorType::ENDPOINT,
},
{
.bEndpointAddress = 0x01,
.bmAttributes = 0x2,
.wMaxPacketSize = 512,
.bInterval = 1,
}
};
} __attribute__((packed)) whole_configuration_descriptor;
static const uint8_t bMS_VendorCode = 37; /* chosen by fair dice roll */
/* This helps Windows figure out that it should use the ADB driver with our device without having to get a driver package signed. */
static constexpr struct {
usb::ms::os20::DescriptorSetHeader descriptor_set_header1 = {
.wLength = sizeof(usb::ms::os20::DescriptorSetHeader),
.wDescriptorType = usb::ms::os20::DescriptorType::SetHeaderDescriptor,
.dwWindowsVersion = usb::ms::NTDDI_WIN8_1,
.wTotalLength = sizeof(*this),
};
/* https://docs.microsoft.com/en-us/windows-hardware/drivers/usbcon/automatic-installation-of-winusb#how-to-configure-a-winusb-device */
usb::ms::os20::FeatureCompatibleID feature_compatible_id = usb::ms::os20::FeatureCompatibleID("WINUSB", "");
/* https://android.googlesource.com/platform/development/+/8c78ba643862731c603677284ae88089a959cc52/host/windows/usb/api/adb_api_extra.h#64 */
usb::ms::os20::FeatureRegistryProperty<usb::ms::os20::PropertyDataType::REG_SZ, u"DeviceInterfaceGUID", u"{F72FE0D4-CBCB-407d-8814-9ED673D0DD6B}"> feature_registry_device_interface_guid;
} __attribute__((packed)) ms_os20_descriptor;
static constexpr auto bos_container = usb::make_bos_descriptor_container(usb::caps::USB_2_0_EXTENSION {
{
{
.bLength = sizeof(usb::caps::USB_2_0_EXTENSION),
.bDescriptorType = usb::DescriptorType::DEVICE_CAPABILITY,
},
{
.bDevCapabilityType = usb::DeviceCapabilityType::USB_2_0_EXTENSION,
}
},
{
.bmAttributes = 0,
}
}, usb::caps::SUPERSPEED {
{
{
.bLength = sizeof(usb::caps::SUPERSPEED),
.bDescriptorType = usb::DescriptorType::DEVICE_CAPABILITY,
},
{
.bDevCapabilityType = usb::DeviceCapabilityType::SUPERSPEED_USB,
}
},
{
.bmAttributes = 0,
.wSpeedsSupported = 0b1111,
.bFunctionalitySupport = 1,
.bU1DevExitLat = 0,
.wU2DevExitLat = 0
}
}, usb::caps::make_platform_capability(
/* Indicate to Windows hosts that they can retrieve their OS-specific descriptor through a special vendor-specific request. */
usb::ms::DescriptorSetInformation {
.dwWindowsVersion = usb::ms::NTDDI_WIN8_1,
.wMSOSDescriptorSetTotalLength = sizeof(ms_os20_descriptor),
.bMS_VendorCode = bMS_VendorCode,
.bAltEnumCode = 0,
})
);
} // namespace descriptors
} // namespace fastboot
} // namespace ams

View file

@ -0,0 +1,402 @@
/*
* 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 "fastboot_gadget.h"
#include "fastboot.h"
#include "xusb.h"
#include "xusb_control.h"
#include "xusb_endpoint.h"
extern "C" {
#include "../lib/log.h"
#include "../lib/vsprintf.h"
#include "../utils.h"
#include "../btn.h"
}
#include "fastboot_descriptors.inc"
#include<vapours/util/util_scope_guard.hpp>
#include<algorithm>
#include<cstdarg>
namespace ams {
namespace fastboot {
namespace {
xusb::TRB in_ring[5];
xusb::TRB out_ring[5];
namespace ep {
static constexpr auto out = xusb::endpoints[xusb::EpAddr {1, xusb::Dir::Out}];
static constexpr auto in = xusb::endpoints[xusb::EpAddr {1, xusb::Dir::In}];
}
} // anonymous namespace
FastbootGadget::FastbootGadget(uint8_t *download_buffer, size_t download_buffer_size) :
impl(*this),
download_buffer(download_buffer),
download_buffer_size(download_buffer_size) {
}
fastboot_return FastbootGadget::Run() {
bool usb_error = false;
uint32_t last_button = btn_read();
while (this->state < State::Exit || usb_error) {
if (!usb_error) {
xusb::Process();
}
if (this->state == State::UsbError && !usb_error) {
print((ScreenLogLevel) (SCREEN_LOG_LEVEL_NONE | SCREEN_LOG_LEVEL_NO_PREFIX), "\nFastboot encountered a USB error (0x%x).\nPress one of the buttons listed above to continue.\n", this->usb_error.GetValue());
usb_error = true;
}
uint32_t button = btn_read();
if (button & BTN_VOL_UP && !(last_button & BTN_VOL_UP)) {
/* Reboot to RCM. */
pmc_reboot(0);
return FASTBOOT_INVALID;
}
if (button & BTN_VOL_DOWN && !(last_button & BTN_VOL_DOWN)) {
/* Boot normally. */
print(SCREEN_LOG_LEVEL_INFO, "Booting normally.\n");
return FASTBOOT_LOAD_STAGE2;
}
if (button & BTN_POWER && !(last_button & BTN_POWER)) {
/* Reboot to fusee-primary. */
reboot_to_self();
return FASTBOOT_INVALID;
}
last_button = button;
}
switch(this->state) {
case State::Exit:
return FASTBOOT_LOAD_STAGE2;
case State::Reboot:
reboot_to_self();
return FASTBOOT_INVALID;
case State::Chainload:
return FASTBOOT_CHAINLOAD;
default:
return FASTBOOT_INVALID;
}
}
void FastbootGadget::FormatResponse(ResponseToken token, const char *fmt, std::va_list args) {
const char *token_str = nullptr;
switch(token) {
case ResponseToken::OKAY:
token_str = "OKAY";
break;
case ResponseToken::INFO:
token_str = "INFO";
break;
case ResponseToken::FAIL:
token_str = "FAIL";
break;
case ResponseToken::DATA:
token_str = "DATA";
break;
}
strcpy(this->response_buffer, token_str);
vsnprintf(this->response_buffer + 4, sizeof(this->response_buffer) - 4, fmt, args);
}
Result FastbootGadget::SendResponse(ResponseDisposition disposition, ResponseToken token, const char *fmt, ...) {
std::va_list args;
va_start(args, fmt);
FormatResponse(token, fmt, args);
va_end(args);
R_TRY(ep::in.TransferNormal((void*) this->response_buffer, strlen(this->response_buffer), &last_trb));
this->response_disposition = disposition;
this->state = State::SendingResponse;
return ResultSuccess();
}
static const size_t max_trb_size = 0x10000; // 64 KiB
Result FastbootGadget::PrepareDownload(size_t size) {
R_UNLESS(size <= this->download_buffer_size, xusb::ResultDownloadTooLarge());
this->download_head = 0;
this->download_size = size;
this->download_needs_zlp = (size % max_trb_size) == 0;
return ResultSuccess();
}
size_t FastbootGadget::GetMaxDownloadSize() const {
return this->download_buffer_size;
}
size_t FastbootGadget::GetLastDownloadSize() const {
return this->download_size;
}
void *FastbootGadget::GetLastDownloadBuffer() const {
return this->download_buffer;
}
Result FastbootGadget::ReadHostCommand() {
R_TRY(ep::out.TransferNormal((void*) this->command_buffer, 64, &last_trb));
this->state = State::WaitingForHostCommand;
return ResultSuccess();
}
Result FastbootGadget::QueueTRBsForReceive() {
xusb::TRBBorrow trb;
bool has_queued_any = false;
while (ep::out.GetFreeTRBCount() > 0 && (this->download_head < this->download_size || this->download_needs_zlp)) {
size_t remaining_in_download = this->download_size - this->download_head;
size_t num_trbs_remaining = (remaining_in_download + max_trb_size - 1) / max_trb_size;
if (this->download_needs_zlp) {
num_trbs_remaining++;
}
size_t td_size = std::min(std::min(num_trbs_remaining, (size_t) 32), (size_t) 32) - 1;
/* Attempt to queue a TRB. */
size_t attempted_size = std::min(remaining_in_download, max_trb_size);
/* If TRBBorrow is already holding a TRB, it will be queued implicitly when EnqueueTRB overwrites it. */
R_TRY(ep::out.EnqueueTRB(&trb, num_trbs_remaining > 0));
R_UNLESS(&*trb != nullptr, xusb::ResultTransferRingFull());
trb->transfer.InitializeNormal(download_buffer + download_head, attempted_size);
if (td_size > 0) {
trb->transfer.chain_bit = 1;
trb->transfer.td_size = td_size;
}
if (attempted_size == 0) {
this->download_needs_zlp = false;
} else {
this->download_head+= attempted_size;
}
has_queued_any = true;
}
if (has_queued_any) {
trb->transfer.interrupt_on_completion = true;
trb->transfer.interrupt_on_short_packet = true;
trb.Release();
if (this->download_head == this->download_size && !this->download_needs_zlp) {
last_trb = &*trb;
}
this->state = State::DataPhaseReceive;
ep::out.RingDoorbell();
}
return ResultSuccess();
}
/* xusb::Gadget implementation */
Result FastbootGadget::ProcessTransferEventImpl(xusb::TransferEventTRB *event, xusb::TRBBorrow transfer) {
if (event->completion_code != 1 && event->completion_code != 13) { // success or short packet
print(SCREEN_LOG_LEVEL_DEBUG, "failing due to transfer completion with bad code %d\n", event->completion_code);
return xusb::ResultUnexpectedCompletionCode();
}
switch(this->state) {
case State::WaitingForHostCommand:
R_UNLESS(event->ep_id == ep::out.GetIndex(), xusb::ResultUnexpectedEndpoint());
R_UNLESS(&*transfer == this->last_trb, xusb::ResultUnexpectedTRB());
{
size_t received_size = transfer->transfer.transfer_length - event->trb_transfer_length;
this->command_buffer[received_size] = 0;
return this->impl.ProcessCommand(this->command_buffer);
}
case State::SendingResponse:
R_UNLESS(event->ep_id == ep::in.GetIndex(), xusb::ResultUnexpectedEndpoint());
R_UNLESS(&*transfer == this->last_trb, xusb::ResultUnexpectedTRB());
switch(this->response_disposition) {
case ResponseDisposition::ReadHostCommand:
return this->ReadHostCommand();
case ResponseDisposition::Download:
this->last_trb = nullptr;
return this->QueueTRBsForReceive();
case ResponseDisposition::Okay:
return this->SendResponse(ResponseDisposition::ReadHostCommand, ResponseToken::OKAY, "");
case ResponseDisposition::Continue:
this->state = State::Continuing;
return this->impl.Continue();
case ResponseDisposition::Reboot:
this->state = State::Reboot;
return ResultSuccess();
case ResponseDisposition::Chainload:
this->state = State::Chainload;
return ResultSuccess();
default:
return ResultSuccess();
}
case State::DataPhaseReceive:
R_UNLESS(event->ep_id == ep::out.GetIndex(), xusb::ResultUnexpectedEndpoint());
/* Release completed TRBs back to ring early so we can reuse them. */
transfer.Release();
if (&*transfer == last_trb) {
return this->SendResponse(ResponseDisposition::ReadHostCommand, ResponseToken::OKAY, "");
} else {
return this->QueueTRBsForReceive();
}
case State::DataPhaseTransmit:
/* TODO, if we ever implement a command that involves sending data. */
return ResultSuccess();
case State::Continuing:
return this->impl.Continue();
default:
return ResultSuccess();
}
}
void FastbootGadget::ProcessTransferEvent(xusb::TransferEventTRB *trb, xusb::TRBBorrow transfer) {
Result r = this->ProcessTransferEventImpl(trb, std::move(transfer));
if (r.IsFailure()) {
print(SCREEN_LOG_LEVEL_ERROR, "fastboot error while processing transfer event: 0x%x\n", r.GetValue());
this->state = State::UsbError;
this->usb_error = r;
}
}
Result FastbootGadget::HandleSetupRequest(usb::SetupPacket &packet) {
if (packet.bmRequestType == usb::SetupPacket::PackRequestType(
usb::SetupPacket::RequestDirection::DeviceToHost,
usb::SetupPacket::RequestType::Vendor,
usb::SetupPacket::RequestRecipient::Device) &&
packet.bRequest == descriptors::bMS_VendorCode) {
/* Retrieve MS OS 2.0 vendor-specific descriptor */
R_UNLESS(packet.wValue == 0, xusb::ResultMalformedSetupRequest());
R_UNLESS(packet.wIndex == usb::ms::MS_OS_20_DESCRIPTOR_INDEX, xusb::ResultMalformedSetupRequest());
uint16_t actual_length = sizeof(descriptors::ms_os20_descriptor);
if (actual_length > packet.wLength) {
actual_length = packet.wLength;
}
return xusb::control::SendData((void*) &descriptors::ms_os20_descriptor, actual_length);
}
return xusb::ResultUnknownSetupRequest();
}
Result FastbootGadget::GetDeviceDescriptor(uint8_t index, const usb::DeviceDescriptor **descriptor, uint16_t *length) {
R_UNLESS(index == 0, xusb::ResultInvalidDescriptorIndex());
*descriptor = &descriptors::device_descriptor;
*length = sizeof(descriptors::device_descriptor);
return ResultSuccess();
}
Result FastbootGadget::GetDeviceQualifierDescriptor(uint8_t index, const usb::DeviceQualifierDescriptor **descriptor, uint16_t *length) {
R_UNLESS(index == 0, xusb::ResultInvalidDescriptorIndex());
*descriptor = &descriptors::device_qualifier_descriptor;
*length = sizeof(descriptors::device_qualifier_descriptor);
return ResultSuccess();
}
Result FastbootGadget::GetConfigurationDescriptor(uint8_t index, const usb::ConfigurationDescriptor **descriptor, uint16_t *length) {
R_UNLESS(index == 0, xusb::ResultInvalidDescriptorIndex());
*descriptor = &descriptors::whole_configuration_descriptor.configuration_descriptor;
*length = sizeof(descriptors::whole_configuration_descriptor);
return ResultSuccess();
}
Result FastbootGadget::GetBOSDescriptor(const usb::BOSDescriptor **descriptor, uint16_t *length) {
*descriptor = &descriptors::bos_container.bos_descriptor;
*length = sizeof(descriptors::bos_container);
return ResultSuccess();
}
Result FastbootGadget::GetStringDescriptor(uint8_t index, uint16_t language, const usb::CommonDescriptor **descriptor, uint16_t *length) {
R_UNLESS(language == 0x0409 || (language == 0 && index == 0), xusb::ResultInvalidDescriptorIndex());
R_UNLESS(descriptors::sd_indexer.GetStringDescriptor(index, language, descriptor, length), xusb::ResultInvalidDescriptorIndex());
return ResultSuccess();
}
Result FastbootGadget::SetConfiguration(uint16_t configuration) {
R_UNLESS(configuration <= 1, xusb::ResultInvalidConfiguration());
xusb::endpoints.ClearNonControlEndpoints();
ep::out.Initialize(descriptors::whole_configuration_descriptor.endpoint_out_descriptor, out_ring, std::size(out_ring));
ep::in .Initialize(descriptors::whole_configuration_descriptor.endpoint_in_descriptor, in_ring, std::size(in_ring));
return ResultSuccess();
}
Result FastbootGadget::Deconfigure() {
ep::out.Disable();
ep::in.Disable();
return ResultSuccess();
}
void FastbootGadget::PostConfigure() {
this->state = State::Invalid;
ReadHostCommand();
}
} // namespace fastboot
} // namespace ams

View file

@ -0,0 +1,116 @@
/*
* 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 "xusb.h"
#include "fastboot.h"
#include "fastboot_impl.h"
#include<cstdarg>
namespace ams {
namespace fastboot {
class FastbootGadget : public xusb::Gadget {
public:
FastbootGadget(uint8_t *download_buffer, size_t download_buffer_size);
enum class ResponseDisposition {
ReadHostCommand,
/* After this response completes, begin receive data phase. */
Download,
/* After this response completes, send an empty OKAY response with
* ReadHostCommand disposition. */
Okay,
/* After this response completes, call impl.Continue(). */
Continue,
Reboot,
Chainload,
} response_disposition;
enum class ResponseToken {
OKAY,
INFO,
FAIL,
DATA,
};
fastboot_return Run();
Result SendResponse(ResponseDisposition disposition, ResponseToken token, const char *fmt, ...);
Result PrepareDownload(size_t size);
size_t GetMaxDownloadSize() const;
size_t GetLastDownloadSize() const;
void *GetLastDownloadBuffer() const;
private:
void FormatResponse(ResponseToken token, const char *fmt, std::va_list args);
Result ReadHostCommand();
Result QueueTRBsForReceive();
FastbootImpl impl;
enum class State {
Invalid,
WaitingForHostCommand,
SendingResponse,
DataPhaseReceive,
DataPhaseTransmit,
Continuing,
Exit,
UsbError,
Reboot,
Chainload,
} state;
Result usb_error;
uint8_t *download_buffer;
size_t download_buffer_size;
size_t download_head;
size_t download_size;
bool download_needs_zlp;
int download_active_trbs;
char command_buffer[65];
char response_buffer[65];
xusb::TRB *last_trb = nullptr;
private:
/* xusb::Gadget implementation */
Result ProcessTransferEventImpl(xusb::TransferEventTRB *event, xusb::TRBBorrow transfer);
virtual void ProcessTransferEvent(xusb::TransferEventTRB *event, xusb::TRBBorrow transfer) override;
virtual Result HandleSetupRequest(usb::SetupPacket &packet) override;
virtual Result GetDeviceDescriptor(uint8_t index, const usb::DeviceDescriptor **descriptor, uint16_t *length) override;
virtual Result GetDeviceQualifierDescriptor(uint8_t index, const usb::DeviceQualifierDescriptor **descriptor, uint16_t *length) override;
virtual Result GetConfigurationDescriptor(uint8_t index, const usb::ConfigurationDescriptor **descriptor, uint16_t *length) override;
virtual Result GetBOSDescriptor(const usb::BOSDescriptor **descriptor, uint16_t *length) override;
virtual Result GetStringDescriptor(uint8_t index, uint16_t language, const usb::CommonDescriptor **descriptor, uint16_t *length) override;
virtual Result SetConfiguration(uint16_t configuration) override;
virtual Result Deconfigure() override;
virtual void PostConfigure() override;
};
} // namespace fastboot
} // namespace ams

View file

@ -0,0 +1,229 @@
/*
* 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 "fastboot.h"
#include "fastboot_gadget.h"
extern "C" {
#include "../lib/log.h"
#include "../utils.h"
#include "../chainloader.h"
#include "../lib/fatfs/ff.h"
}
#include<array>
#include<stdio.h>
uint32_t crc32(uint32_t crc, const uint8_t *buf, size_t len) {
int k;
crc = ~crc;
while (len--) {
crc ^= *buf++;
for (k = 0; k < 8; k++)
crc = crc & 1 ? (crc >> 1) ^ 0xedb88320 : crc >> 1;
}
return ~crc;
}
struct android_boot_image_v2 {
// version 0
uint8_t magic[8];
uint32_t kernel_size;
uint32_t kernel_addr;
uint32_t ramdisk_size;
uint32_t ramdisk_addr;
uint32_t second_size;
uint32_t second_addr;
uint32_t tags_addr;
uint32_t page_size;
uint32_t header_version;
uint32_t os_version;
uint8_t name[16];
uint8_t cmdline[512];
uint32_t id[8];
uint8_t extra_cmdline[1024];
// version 1
uint32_t recovery_dtbo_size;
uint64_t recovery_dtbo_offset;
uint32_t header_size;
// version 2
uint32_t dtb_size;
uint64_t dtb_addr;
} __attribute__((packed));
namespace ams {
namespace fastboot {
using ResponseDisposition = FastbootGadget::ResponseDisposition;
using ResponseToken = FastbootGadget::ResponseToken;
FastbootImpl::FastbootImpl(FastbootGadget &gadget) : gadget(gadget) {
}
Result FastbootImpl::ProcessCommand(char *command_buffer) {
print(SCREEN_LOG_LEVEL_DEBUG, "got host command: '%s'\n", command_buffer);
const char *argument = nullptr;
for (size_t i = 0; command_buffer[i] != 0; i++) {
if (command_buffer[i] == ':') {
command_buffer[i] = 0;
argument = command_buffer + i + 1;
break;
}
}
if (!strcmp(command_buffer, "getvar")) {
return this->GetVar(argument);
} else if (!strcmp(command_buffer, "download")) {
return this->Download(argument);
} else if (!strcmp(command_buffer, "flash")) {
return this->Flash(argument);
} else if (!strcmp(command_buffer, "reboot")) {
return this->Reboot(argument);
} else if (!strcmp(command_buffer, "boot")) {
return this->Boot(argument);
} else if (!strcmp(command_buffer, "oem crc32")) {
return this->OemCRC32(argument);
} else {
return this->gadget.SendResponse(ResponseDisposition::ReadHostCommand, ResponseToken::FAIL, "unknown command: %s", command_buffer);
}
}
Result FastbootImpl::GetVar(const char *argument) {
if (strcmp(argument, "version") == 0) {
return this->gadget.SendResponse(ResponseDisposition::ReadHostCommand, ResponseToken::OKAY, "0.4");
} else if (strcmp(argument, "product") == 0) {
return this->gadget.SendResponse(ResponseDisposition::ReadHostCommand, ResponseToken::OKAY, "Fusée Fastboot");
} else if (strcmp(argument, "max-download-size") == 0) {
return this->gadget.SendResponse(ResponseDisposition::ReadHostCommand, ResponseToken::OKAY, "%08X", this->gadget.GetMaxDownloadSize());
} else {
return this->gadget.SendResponse(ResponseDisposition::ReadHostCommand, ResponseToken::FAIL, "unknown variable");
}
}
Result FastbootImpl::Download(const char *argument) {
bool parsed_ok = true;
uint32_t download_size = 0;
for (int i = 0; i < 8; i++) {
download_size<<= 4;
char ch = argument[i];
if (ch >= '0' && ch <= '9') {
download_size|= ch - '0';
} else if (ch >= 'a' && ch <= 'f') {
download_size|= (ch - 'a') + 0xa;
} else if (ch >= 'A' && ch <= 'F') {
download_size|= (ch - 'A') + 0xa;
} else {
parsed_ok = false;
break;
}
}
if (!parsed_ok) {
return this->gadget.SendResponse(ResponseDisposition::ReadHostCommand, ResponseToken::FAIL, "failed to parse download size");
}
R_TRY_CATCH(gadget.PrepareDownload(download_size)) {
R_CATCH(xusb::ResultDownloadTooLarge) {
return this->gadget.SendResponse(ResponseDisposition::ReadHostCommand, ResponseToken::FAIL, "download size too large");
}
} R_END_TRY_CATCH;
return this->gadget.SendResponse(ResponseDisposition::Download, ResponseToken::DATA, "%08X", download_size);
}
Result FastbootImpl::Flash(const char *argument) {
return this->gadget.SendResponse(ResponseDisposition::ReadHostCommand, ResponseToken::FAIL, "unknown partition");
}
Result FastbootImpl::Reboot(const char *argment) {
return this->gadget.SendResponse(ResponseDisposition::Reboot, ResponseToken::OKAY, "");
}
Result FastbootImpl::Boot(const char *argment) {
if (this->gadget.GetLastDownloadSize() == 0) {
return this->gadget.SendResponse(ResponseDisposition::ReadHostCommand, ResponseToken::FAIL, "no image has been downloaded");
}
/* Validate the boot image we have received. */
if (this->gadget.GetLastDownloadSize() < sizeof(android_boot_image_v2)) {
return this->gadget.SendResponse(ResponseDisposition::ReadHostCommand, ResponseToken::FAIL, "received boot image is too small");
}
android_boot_image_v2 *bootimg = (android_boot_image_v2*) this->gadget.GetLastDownloadBuffer();
if (memcmp(bootimg->magic, "ANDROID!", sizeof(bootimg->magic)) != 0) {
print(SCREEN_LOG_LEVEL_DEBUG, "invalid magic: '%s'\n", bootimg->magic);
return this->gadget.SendResponse(ResponseDisposition::ReadHostCommand, ResponseToken::FAIL, "received boot image has invalid magic");
}
if (bootimg->header_version != 2) {
print(SCREEN_LOG_LEVEL_DEBUG, "invalid version: %d\n", bootimg->header_version);
return this->gadget.SendResponse(ResponseDisposition::ReadHostCommand, ResponseToken::FAIL, "received boot image has invalid version");
}
if (bootimg->header_size != sizeof(*bootimg)) {
print(SCREEN_LOG_LEVEL_DEBUG, "invalid size: %d, expected %d\n", bootimg->header_size, sizeof(*bootimg));
return this->gadget.SendResponse(ResponseDisposition::ReadHostCommand, ResponseToken::FAIL, "received boot image has invalid size");
}
if (bootimg->ramdisk_size != 0 ||
bootimg->second_size != 0 ||
bootimg->dtb_size != 0) {
return this->gadget.SendResponse(ResponseDisposition::ReadHostCommand, ResponseToken::FAIL, "received boot image has unexpected ramdisk, second stage bootloader, or dtb");
}
/* Setup chainloader. */
print(SCREEN_LOG_LEVEL_DEBUG, "loading kernel to 0x%lx\n", bootimg->kernel_addr);
g_chainloader_num_entries = 1;
g_chainloader_entries[0].load_address = bootimg->kernel_addr;
g_chainloader_entries[0].src_address = (uintptr_t) this->gadget.GetLastDownloadBuffer() + bootimg->page_size;
g_chainloader_entries[0].size = bootimg->kernel_size;
g_chainloader_entries[0].num = 0;
g_chainloader_entrypoint = bootimg->kernel_addr;
return this->gadget.SendResponse(ResponseDisposition::Chainload, ResponseToken::OKAY, "");
}
Result FastbootImpl::OemCRC32(const char *argument) {
uint32_t crc = crc32(0, (const uint8_t*) this->gadget.GetLastDownloadBuffer(), this->gadget.GetLastDownloadSize());
return this->gadget.SendResponse(ResponseDisposition::Okay, ResponseToken::INFO, "%08lx", crc);
}
Result FastbootImpl::Continue() {
switch(this->current_action) {
default:
case Action::Invalid:
fatal_error("fastboot implementation has invalid current action");
}
}
} // namespace fastboot
} // namespace ams

View file

@ -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<stddef.h>
namespace ams {
namespace fastboot {
class FastbootGadget;
class FastbootImpl {
public:
FastbootImpl(FastbootGadget &gadget);
/* Implementation is allowed to modify the command buffer for string
parsing reasons. */
Result ProcessCommand(char *command_buffer);
Result Continue();
private:
FastbootGadget &gadget;
enum class Action {
Invalid,
} current_action;
union {
/* Actions */
};
/* Commands */
Result GetVar(const char *arg);
Result Download(const char *arg);
Result Flash(const char *arg);
Result Reboot(const char *arg);
Result Boot(const char *arg);
Result OemCRC32(const char *arg);
/* Continuations */
};
} // namespace fastboot
} // namespace ams

View file

@ -0,0 +1,31 @@
/*
* 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 "xusb_result.h"
namespace fastboot {
class FastbootInterface {
public:
virtual xusb::Result SendOkayResponse(const char *message="") = 0;
virtual xusb::Result SendInfoForOkayResponse(const char *message) = 0;
virtual xusb::Result SendInfoForFlasher(const char *message) = 0;
virtual xusb::Result SendFailResponse(const char *message) = 0;
};
} // namespace fastboot

View file

@ -0,0 +1,489 @@
/*
* 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<stdint.h>
#include<stddef.h>
#include<string.h>
namespace usb {
enum class DescriptorType : uint8_t {
DEVICE = 1,
CONFIGURATION = 2,
STRING = 3,
INTERFACE = 4,
ENDPOINT = 5,
DEVICE_QUALIFIER = 6,
OTHER_SPEED_CONFIGURATION = 7,
INTERFACE_POWER = 8,
OTG = 9,
DEBUG = 10,
INTERFACE_ASSOCIATION = 11,
BOS = 15,
DEVICE_CAPABILITY = 16,
SUPERSPEED_USB_ENDPOINT_COMPANION = 48,
SUPERSPEEDPLUS_ISOCHRONOUS_ENDPOINT_COMPANION = 49,
};
enum class EndpointFeatureSelector : uint8_t {
ENDPOINT_HALT = 0,
};
enum class DeviceCapabilityType : uint8_t {
WIRELESS_USB = 0x1,
USB_2_0_EXTENSION = 0x2,
SUPERSPEED_USB = 0x3,
CONTAINER_ID = 0x4,
PLATFORM = 0x5,
POWER_DELIVERY_CAPABILITY = 0x6,
BATTERY_INFO_CAPABILITY = 0x7,
PD_CONSUMER_PORT_CAPABILITY = 0x8,
PD_PROVIDER_PORT_CAPABILITY = 0x9,
SUPERSPEED_PLUS = 0xa,
PRECISION_TIME_MEASUREMENT = 0xb,
WIRELESS_USB_EXT = 0xc,
BILLBOARD = 0xd,
AUTHENTICATION = 0xe,
BILLBOARD_EX = 0xf,
CONFIGURATION_SUMMARY = 0x10,
};
enum class EndpointType : uint8_t {
Control = 0,
Isochronous = 1,
Bulk = 2,
Interrupt = 3,
};
struct CommonDescriptor {
uint8_t bLength;
DescriptorType bDescriptorType;
} __attribute__((packed));
struct DeviceDescriptor : public CommonDescriptor {
struct {
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;
} __attribute__((packed));
} __attribute__((packed));
struct DeviceQualifierDescriptor : public CommonDescriptor {
struct {
uint16_t bcdUSB;
uint8_t bDeviceClass;
uint8_t bDeviceSubClass;
uint8_t bDeviceProtocol;
uint8_t bMaxPacketSize0;
uint8_t bNumConfigurations;
uint8_t bReserved;
} __attribute__((packed));
} __attribute__((packed));
struct ConfigurationDescriptor : public CommonDescriptor {
struct {
uint16_t wTotalLength;
uint8_t bNumInterfaces;
uint8_t bConfigurationValue;
uint8_t iConfiguration;
uint8_t bmAttributes;
uint8_t bMaxPower;
} __attribute__((packed));
} __attribute__((packed));
static_assert(sizeof(ConfigurationDescriptor) == 9);
struct InterfaceDescriptor : public CommonDescriptor {
struct {
uint8_t bInterfaceNumber;
uint8_t bAlternateSetting;
uint8_t bNumEndpoints;
uint8_t bInterfaceClass;
uint8_t bInterfaceSubClass;
uint8_t bInterfaceProtocol;
uint8_t iInterface;
} __attribute__((packed));
} __attribute__((packed));
static_assert(sizeof(InterfaceDescriptor) == 9);
struct EndpointDescriptor : public CommonDescriptor {
struct {
uint8_t bEndpointAddress;
uint8_t bmAttributes;
uint16_t wMaxPacketSize;
uint8_t bInterval;
} __attribute__((packed));
EndpointType GetEndpointType() const {
return (EndpointType) (bmAttributes & 0b11);
}
} __attribute__((packed));
static_assert(sizeof(EndpointDescriptor) == 7);
namespace detail {
template<size_t N>
struct FixedString {
constexpr FixedString(char16_t const* s) {
for (size_t i = 0; i < N; i++) {
buffer[i] = s[i];
}
}
static const size_t length = N;
char16_t buffer[N + 1]{};
};
template<size_t N> FixedString(char16_t const (&)[N]) -> FixedString<N - 1>;
} // namespace detail
template <detail::FixedString string>
struct StringDescriptor : public CommonDescriptor {
inline constexpr StringDescriptor() : bString {} {
this->bLength = (string.length * 2) + 2;
this->bDescriptorType = DescriptorType::STRING;
for (size_t i = 0; i < string.length; i++) {
this->bString[i] = string.buffer[i];
}
}
char16_t bString[string.length];
} __attribute__((packed));
namespace detail {
template<size_t I, typename First, typename... T>
struct StringDescriptorIndexer : public First::template WithIndex<I>, public StringDescriptorIndexer<I + 1, T...> {
using ThisDescriptor = First::template WithIndex<I>;
using NextIndexer = StringDescriptorIndexer<I + 1, T...>;
bool GetStringDescriptor(uint8_t index, uint16_t language, const CommonDescriptor **descriptor, uint16_t *length) const {
if (index == I) {
*descriptor = &ThisDescriptor::_descriptor;
*length = sizeof(ThisDescriptor::_descriptor);
return true;
} else {
return NextIndexer::GetStringDescriptor(index, language, descriptor, length);
}
}
};
template<size_t I, typename First>
struct StringDescriptorIndexer<I, First> : public First::template WithIndex<I> {
using ThisDescriptor = First::template WithIndex<I>;
bool GetStringDescriptor(uint8_t index, uint16_t language, const CommonDescriptor **descriptor, uint16_t *length) const {
if (index == I) {
*descriptor = &ThisDescriptor::_descriptor;
*length = sizeof(ThisDescriptor::_descriptor);
return true;
} else {
return false;
}
}
};
} // namespace detail
template<typename... T>
using StringDescriptorIndexer = detail::StringDescriptorIndexer<0, T...>;
/* The wrapper type is here because we can't pass templates as template
* parameters directly. */
#define USB_DECLARE_STRING_DESCRIPTOR(id, english_value) struct SD_##id { \
template<size_t I> \
struct WithIndex { \
static constexpr ::usb::StringDescriptor<english_value> _descriptor = {}; \
static constexpr size_t id = I; \
}; \
}
struct BOSDescriptor : public CommonDescriptor {
struct {
uint16_t wTotalLength;
uint8_t bNumDeviceCaps;
} __attribute__((packed));
} __attribute__((packed));
struct DeviceCapabilityDescriptor : public CommonDescriptor {
struct {
DeviceCapabilityType bDevCapabilityType;
} __attribute__((packed));
} __attribute__((packed));
namespace caps {
struct USB_2_0_EXTENSION : public DeviceCapabilityDescriptor {
struct {
uint32_t bmAttributes;
} __attribute__((packed));
} __attribute__((packed));
struct SUPERSPEED : public DeviceCapabilityDescriptor {
struct {
uint8_t bmAttributes;
uint16_t wSpeedsSupported;
uint8_t bFunctionalitySupport;
uint8_t bU1DevExitLat;
uint16_t wU2DevExitLat;
} __attribute__((packed));
} __attribute__((packed));
template<typename PlatformCapability>
struct PLATFORM : public DeviceCapabilityDescriptor {
uint8_t bReserved;
uint8_t PlatformCapabilityUUID[16];
PlatformCapability platform_cap;
constexpr PLATFORM(PlatformCapability platform_cap) :
DeviceCapabilityDescriptor({
{
.bLength = sizeof(*this),
.bDescriptorType = usb::DescriptorType::DEVICE_CAPABILITY,
},
{
.bDevCapabilityType = usb::DeviceCapabilityType::PLATFORM
}
}),
bReserved(0),
platform_cap(platform_cap) {
static_assert(sizeof(PlatformCapability::UUID[0]) == 1);
static_assert(sizeof(PlatformCapability::UUID) == sizeof(PlatformCapabilityUUID));
for (size_t i = 0; i < sizeof(PlatformCapability::UUID); i++) {
PlatformCapabilityUUID[i] = PlatformCapability::UUID[i];
}
}
} __attribute__((packed));
template<typename T>
constexpr PLATFORM<T> make_platform_capability(T cap) {
return PLATFORM<T>(cap);
}
} // namespace caps
namespace ms {
inline const uint32_t NTDDI_WIN8_1 = 0x06030000;
inline const uint8_t MS_OS_20_DESCRIPTOR_INDEX = 0x7;
struct DescriptorSetInformation {
static inline const uint8_t UUID[16] = {0xDF, 0x60, 0xDD, 0xD8, 0x89, 0x45, 0xC7, 0x4C, 0x9C, 0xD2, 0x65, 0x9D, 0x9E, 0x64, 0x8A, 0x9F};
uint32_t dwWindowsVersion;
uint16_t wMSOSDescriptorSetTotalLength;
uint8_t bMS_VendorCode;
uint8_t bAltEnumCode;
} __attribute__((packed));
namespace os20 {
enum class DescriptorType : uint16_t {
SetHeaderDescriptor = 0,
SubsetHeaderConfiguration = 1,
SubsetHeaderFunction = 2,
FeatureCompatibleID = 3,
FeatureRegProperty = 4,
FeatureMinResumeTime = 5,
FeatureModelID = 6,
FeatureCCGPDevice = 7,
FeatureVendorRevision = 8
};
struct DescriptorSetHeader {
uint16_t wLength;
DescriptorType wDescriptorType;
uint32_t dwWindowsVersion;
uint16_t wTotalLength;
} __attribute__((packed));
/*
struct ConfigurationSubsetHeader {
uint16_t wLength;
uint16_t wDescriptorType;
uint8_t bConfigurationValue;
uint8_t bReserved;
uint16_t wTotalLength;
} __attribute__((packed));
struct FunctionSubsetHeader {
uint16_t wLength;
uint16_t wDescriptorType;
uint8_t bFirstInterface;
uint8_t bReserved;
uint16_t wSubsetLength;
} __attribute__((packed));
*/
struct FeatureCompatibleID {
uint16_t wLength = sizeof(*this);
DescriptorType wDescriptorType = DescriptorType::FeatureCompatibleID;
uint8_t CompatibleID[8] = {0};
uint8_t SubCompatibleID[8] = {0};
constexpr FeatureCompatibleID(const char CompatibleID[], const char SubCompatibleID[]) {
for (size_t i = 0; i < sizeof(this->CompatibleID) && CompatibleID[i] != '\0'; i++) {
this->CompatibleID[i] = CompatibleID[i];
}
for (size_t i = 0; i < sizeof(this->SubCompatibleID) && SubCompatibleID[i] != '\0'; i++) {
this->SubCompatibleID[i] = SubCompatibleID[i];
}
}
} __attribute__((packed));
enum PropertyDataType : uint16_t {
REG_SZ = 1,
REG_EXPAND_SZ = 2,
REG_BINARY = 3,
REG_DWORD_LITTLE_ENDIAN = 4,
REG_DWORD_BIG_ENDIAN = 5,
REG_LINK = 6,
REG_MULTI_SZ = 7,
};
template<PropertyDataType property_data_type, detail::FixedString property_name, detail::FixedString property_data>
struct FeatureRegistryProperty {
uint16_t wLength = sizeof(*this);
DescriptorType wDescriptorType = DescriptorType::FeatureRegProperty;
uint16_t wPropertyDataType = property_data_type;
uint16_t wPropertyNameLength = (property_name.length + 1) * 2;
char16_t PropertyName[property_name.length + 1] = {0};
uint16_t wPropertyDataLength = (property_data.length + 1) * 2;
char16_t PropertyData[property_data.length + 1] = {0};
constexpr FeatureRegistryProperty() {
for (size_t i = 0; i < property_name.length; i++) {
this->PropertyName[i] = property_name.buffer[i];
}
for (size_t i = 0; i < property_data.length; i++) {
this->PropertyData[i] = property_data.buffer[i];
}
}
} __attribute__((packed));
} // namespace os20
} // namespace ms
namespace detail {
template<typename First, typename... T>
struct PackedTuple {
First first;
PackedTuple<T...> remaining;
constexpr PackedTuple(First first, T... remaining) :
first(first),
remaining(remaining...) {
}
} __attribute__((packed));
template<typename First>
struct PackedTuple<First> {
First first;
constexpr PackedTuple(First first) : first(first) {
}
} __attribute__((packed));
} // namespace detail
template<typename... T>
struct BOSDescriptorContainer {
BOSDescriptor bos_descriptor;
detail::PackedTuple<T...> tuple;
constexpr BOSDescriptorContainer(T... members) : bos_descriptor {
{
.bLength = sizeof(BOSDescriptor),
.bDescriptorType = DescriptorType::BOS,
},
{
.wTotalLength = sizeof(*this),
.bNumDeviceCaps = sizeof...(T),
}
}, tuple(members...) {
}
} __attribute__((packed));
template<typename... T>
constexpr BOSDescriptorContainer<T...> make_bos_descriptor_container(T... members) {
return BOSDescriptorContainer<T...>(members...);
}
struct SetupPacket {
enum class StandardRequest : uint8_t {
GET_STATUS = 0,
CLEAR_FEATURE = 1,
// reserved
SET_FEATURE = 3,
// reserved
SET_ADDRESS = 5,
GET_DESCRIPTOR = 6,
SET_DESCRIPTOR = 7,
GET_CONFIGURATION = 8,
SET_CONFIGURATION = 9,
GET_INTERFACE = 10,
SET_INTERFACE = 11,
SYNCH_FRAME = 12
};
enum class RequestDirection {
HostToDevice = 0,
DeviceToHost = 1,
};
enum class RequestType {
Standard = 0,
Class = 1,
Vendor = 2,
Reserved = 3,
};
enum class RequestRecipient {
Device = 0,
Interface = 1,
Endpoint = 2,
Other = 3
};
static inline uint8_t PackRequestType(RequestDirection dir, RequestType type, RequestRecipient recipient) {
return ((uint8_t) dir << 7) |
((uint8_t) type << 5) |
((uint8_t) recipient << 0);
}
uint8_t bmRequestType;
uint8_t bRequest;
uint16_t wValue;
uint16_t wIndex;
uint16_t wLength;
} __attribute__((packed));
} // namespace usb

View file

@ -0,0 +1,600 @@
/*
* 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 "xusb.h"
#include "xusb_event_ring.h"
#include "xusb_endpoint.h"
#include "../reg/reg_apbdev_pmc.h"
#include "../reg/reg_car.h"
#include "../reg/reg_fuse.h"
#include "../reg/reg_xusb_padctl.h"
#include "../reg/reg_xusb_dev.h"
extern "C" {
#include "../timers.h"
#include "../lib/log.h"
#include "../utils.h"
#include "../mc.h"
}
#include<array>
namespace ams {
namespace xusb {
namespace impl {
namespace padctl {
void SetupPads(t210::CLK_RST_CONTROLLER::OscFreq osc_freq);
void SetupPllu(t210::CLK_RST_CONTROLLER::OscFreq osc_freq);
void SetupUTMIP(t210::CLK_RST_CONTROLLER::OscFreq osc_freq);
void ApplyFuseCalibration();
void DisablePd();
void SetupTracking(t210::CLK_RST_CONTROLLER::OscFreq osc_freq);
}
void ConfigureClockAndResetForDeviceMode();
EventRing event_ring;
TRB ep0_transfer_ring[16];
Gadget *current_gadget = nullptr;
}
void Initialize() {
const bool use_bootrom = false;
t210::CLK_RST_CONTROLLER
.CLK_OUT_ENB_W_0()
.CLK_ENB_XUSB.Enable()
.Commit()
.RST_DEVICES_W_0()
.SWR_XUSB_PADCTL_RST.Set(0)
.Commit();
udelay(2);
t210::XUSB_PADCTL
.USB2_PAD_MUX_0()
.USB2_OTG_PAD_PORT0.SetXUSB()
.USB2_BIAS_PAD.SetXUSB()
.Commit();
if (use_bootrom) {
((bool (*)()) (0x1104fd))();
} else {
impl::padctl::SetupPads(t210::CLK_RST_CONTROLLER.OSC_CTRL_0().OSC_FREQ.Get());
}
t210::XUSB_PADCTL
.USB2_PORT_CAP_0()
.PORT0_CAP.SetDeviceOnly()
.Commit()
.SS_PORT_MAP_0()
.PORT0_MAP.SetUSB2Port0()
.Commit();
t210::APBDEV_PMC
.USB_AO_0()
.VBUS_WAKEUP_PD_P0.Set(0)
.ID_PD_P0.Set(0)
.Commit();
udelay(1);
if (use_bootrom) {
((void (*)()) (0x110227))();
} else {
impl::ConfigureClockAndResetForDeviceMode();
}
impl::event_ring.Initialize();
endpoints[0].InitializeForControl(impl::ep0_transfer_ring, std::size(impl::ep0_transfer_ring));
t210::T_XUSB_DEV_XHCI
.ECPLO()
.ADDRLO.Set(endpoints.GetContextsForHardware())
.Commit()
.ECPHI()
.ADDRHI.Set(0)
.Commit();
endpoints[0].Reload();
t210::T_XUSB_DEV_XHCI
.CTRL()
.LSE.Enable()
.IE.Enable()
.Commit();
}
void EnableDevice(Gadget &gadget) {
impl::current_gadget = &gadget;
t210::XUSB_PADCTL
.ELPG_PROGRAM_0_0(0).Commit()
.ELPG_PROGRAM_1_0(0).Commit()
.USB2_VBUS_ID_0()
.VBUS_SOURCE_SELECT.SetOVERRIDE()
.ID_SOURCE_SELECT.SetOVERRIDE()
.Commit();
t210::T_XUSB_DEV_XHCI
.PORTHALT()
.HALT_LTSSM.Set(0)
.Commit()
.CTRL()
.ENABLE.Enable()
.Commit()
.CFG_DEV_FE()
.PORTREGSEL.SetHSFS() // PortSC accesses route to register for HS
.Commit()
.PORTSC()
.LWS.Set(1)
.PLS.SetRXDETECT()
.Commit()
.CFG_DEV_FE()
.PORTREGSEL.SetSS() // PortSC accesses route to register for SS
.Commit()
.PORTSC()
.LWS.Set(1)
.PLS.SetRXDETECT()
.Commit()
.CFG_DEV_FE()
.PORTREGSEL.SetINIT() // PortSC Accesses route to register based on current link speed
.Commit();
t210::XUSB_PADCTL
.USB2_VBUS_ID_0()
.VBUS_OVERRIDE.Set(1)
.ID_OVERRIDE.SetFLOAT()
.Commit();
}
void Process() {
impl::event_ring.Process();
}
void Finalize() {
mc_release_ahb_redirect();
}
Gadget *GetCurrentGadget() {
return impl::current_gadget;
}
namespace impl {
namespace padctl {
void SetupPads(t210::CLK_RST_CONTROLLER::OscFreq osc_freq) {
constexpr bool use_bootrom = false;
SetupPllu(osc_freq);
if (use_bootrom) {
((void (*)(t210::CLK_RST_CONTROLLER::OscFreq)) (0x11039b))(osc_freq);
((void (*)()) (0x110357))();
((int (*)()) (0x110327))();
((bool (*)(t210::CLK_RST_CONTROLLER::OscFreq)) (0x1102cb))(osc_freq);
} else {
SetupUTMIP(osc_freq);
ApplyFuseCalibration();
DisablePd();
SetupTracking(osc_freq);
}
udelay(30);
}
void SetupPllu(t210::CLK_RST_CONTROLLER::OscFreq osc_freq) {
struct {
uint8_t divn, divm, divp;
} pllu_config;
using OscFreq = t210::CLK_RST_CONTROLLER::OscFreq;
switch(osc_freq) {
case OscFreq::OSC13:
pllu_config = {37, 1, 1};
break;
case OscFreq::OSC16P8:
pllu_config = {28, 1, 1};
break;
case OscFreq::OSC19P2:
pllu_config = {25, 1, 1};
break;
case OscFreq::OSC38P4:
pllu_config = {25, 2, 1};
break;
case OscFreq::OSC12:
pllu_config = {40, 1, 1};
break;
case OscFreq::OSC48:
pllu_config = {40, 4, 1};
break;
case OscFreq::OSC26:
pllu_config = {37, 2, 1};
break;
default:
fatal_error("xusb initialization failed: invalid oscillator frequency");
break;
}
// reset PLLU
t210::CLK_RST_CONTROLLER
.PLLU_BASE_0()
.PLLU_ENABLE.Disable()
.PLLU_CLKENABLE_48M.Disable()
.PLLU_OVERRIDE.Set(1)
.PLLU_CLKENABLE_ICUSB.Disable()
.PLLU_CLKENABLE_HSIC.Disable()
.PLLU_CLKENABLE_USB.Disable()
.Commit();
udelay(100);
t210::CLK_RST_CONTROLLER
.PLLU_MISC_0()
.PLLU_EN_LCKDET.Enable()
.Commit()
.PLLU_BASE_0()
.PLLU_DIVM.Set(pllu_config.divm)
.PLLU_DIVN.Set(pllu_config.divn)
.PLLU_DIVP.Set(pllu_config.divp)
.PLLU_OVERRIDE.Set(1)
.PLLU_ENABLE.Enable()
.Commit();
int i = 0;
while (!t210::CLK_RST_CONTROLLER.PLLU_BASE_0().PLLU_LOCK && i < 100000) {
i++;
}
if (t210::CLK_RST_CONTROLLER.PLLU_BASE_0().PLLU_LOCK) {
print(SCREEN_LOG_LEVEL_DEBUG, "got PLLU lock in %d iterations\n", i);
} else {
fatal_error("xusb initialization failed: PLLU lock not acquired in %d iterations\n", i);
}
t210::CLK_RST_CONTROLLER
.PLLU_BASE_0()
.PLLU_CLKENABLE_USB.Enable()
.PLLU_CLKENABLE_HSIC.Enable()
.PLLU_CLKENABLE_48M.Enable()
.PLLU_CLKENABLE_ICUSB.Enable()
.Commit();
}
void SetupUTMIP(t210::CLK_RST_CONTROLLER::OscFreq osc_freq) {
t210::CLK_RST_CONTROLLER
.UTMIPLL_HW_PWRDN_CFG0_0()
.UTMIPLL_IDDQ_SWCTL.Set(1) // "IDDQ by software."
.UTMIPLL_IDDQ_OVERRIDE_VALUE.Set(0) // "PLL not in IDDQ mode... Override value used only when UTMIP_LL_IDDQ_SWCTL is set"
.Commit();
constexpr bool use_pllu = false; // works either way
struct {
uint8_t ndiv, mdiv;
} pll_config;
struct {
uint16_t enable_dly_count;
/* only used with PLLU reference source */
uint16_t stable_count;
/* only used with PLLU reference source */
uint16_t active_dly_count;
uint16_t xtal_freq_count;
} count_config;
using OscFreq = t210::CLK_RST_CONTROLLER::OscFreq;
switch(osc_freq) {
case OscFreq::OSC13:
pll_config = {74, 1};
count_config = {2, 51, 9, 127};
break;
case OscFreq::OSC16P8:
pll_config = {57, 1};
count_config = {3, 66, 11, 165};
break;
case OscFreq::OSC19P2:
pll_config = {50, 1};
count_config = {3, 75, 12, 188};
break;
case OscFreq::OSC38P4:
pll_config = {25, 1};
count_config = {5, 150, 24, 375};
break;
case OscFreq::OSC12:
pll_config = {80, 1};
count_config = {2, 47, 8, 118};
break;
case OscFreq::OSC48:
pll_config = {40, 2};
count_config = {6, 188, 31, 469};
break;
case OscFreq::OSC26:
pll_config = {74, 2};
count_config = {4, 102, 17, 254};
break;
default:
fatal_error("xusb initialization failed: invalid oscillator frequency");
break;
}
if (use_pllu) {
t210::CLK_RST_CONTROLLER
.UTMIP_PLL_CFG3_0()
.UTMIP_PLL_REF_SRC_SEL.Set(1)
.Commit();
/* 480 MHz * 30 / 15 = 960 MHz */
pll_config = {30, 15};
} else {
// TODECIDE: set PLL_REF_SRC_SEL? or assume correct from reset?
/* don't need to wait for PLLU to become stable if we're not using it */
count_config.stable_count = 0;
count_config.enable_dly_count = 0;
}
t210::CLK_RST_CONTROLLER
.UTMIP_PLL_CFG0_0()
.UTMIP_PLL_NDIV.Set(pll_config.ndiv)
.UTMIP_PLL_MDIV.Set(pll_config.mdiv)
.Commit()
.UTMIP_PLL_CFG2_0()
.UTMIP_PHY_XTAL_CLOCKEN.Set(1)
.UTMIP_PLLU_STABLE_COUNT.Set(count_config.stable_count)
.UTMIP_PLL_ACTIVE_DLY_COUNT.Set(count_config.active_dly_count)
.Commit()
.UTMIP_PLL_CFG1_0()
.UTMIP_PLLU_ENABLE_DLY_COUNT.Set(count_config.enable_dly_count)
.UTMIP_XTAL_FREQ_COUNT.Set(count_config.xtal_freq_count)
.UTMIP_FORCE_PLL_ACTIVE_POWERDOWN.Set(0)
.UTMIP_FORCE_PLL_ENABLE_POWERUP.Set(1)
.UTMIP_FORCE_PLL_ENABLE_POWERDOWN.Set(0)
.Commit();
for (int i = 0; i < 100 && !t210::CLK_RST_CONTROLLER.UTMIPLL_HW_PWRDN_CFG0_0().UTMIPLL_LOCK; i++) {
udelay(1);
}
if (t210::CLK_RST_CONTROLLER.UTMIPLL_HW_PWRDN_CFG0_0().UTMIPLL_LOCK) {
print(SCREEN_LOG_LEVEL_DEBUG, "got UTMIPLL lock\n");
print(SCREEN_LOG_LEVEL_DEBUG, "UTMIP_PLL_CFG0_0: 0x%08x\n", t210::CLK_RST_CONTROLLER.UTMIP_PLL_CFG0_0().read_value);
print(SCREEN_LOG_LEVEL_DEBUG, "UTMIP_PLL_CFG1_0: 0x%08x\n", t210::CLK_RST_CONTROLLER.UTMIP_PLL_CFG1_0().read_value);
print(SCREEN_LOG_LEVEL_DEBUG, "UTMIP_PLL_CFG2_0: 0x%08x\n", t210::CLK_RST_CONTROLLER.UTMIP_PLL_CFG2_0().read_value);
} else {
/* There is some issue where occasionally after a reboot-to-self we
* fail to get a UTMIP lock. The BootROM ignores this failure and
* continues on. It seems that in these cases it eventually starts
* working, but may take a few seconds to do so. */
print(SCREEN_LOG_LEVEL_ERROR, "failed to get UTMIP lock\n");
print(SCREEN_LOG_LEVEL_DEBUG, "UTMIP_PLL_CFG0_0: 0x%08x\n", t210::CLK_RST_CONTROLLER.UTMIP_PLL_CFG0_0().read_value);
print(SCREEN_LOG_LEVEL_DEBUG, "UTMIP_PLL_CFG1_0: 0x%08x\n", t210::CLK_RST_CONTROLLER.UTMIP_PLL_CFG1_0().read_value);
print(SCREEN_LOG_LEVEL_DEBUG, "UTMIP_PLL_CFG2_0: 0x%08x\n", t210::CLK_RST_CONTROLLER.UTMIP_PLL_CFG2_0().read_value);
}
t210::CLK_RST_CONTROLLER
.UTMIP_PLL_CFG2_0()
.UTMIP_FORCE_PD_SAMP_A_POWERDOWN.Set(0)
.UTMIP_FORCE_PD_SAMP_A_POWERUP.Set(1)
.UTMIP_FORCE_PD_SAMP_B_POWERDOWN.Set(0)
.UTMIP_FORCE_PD_SAMP_B_POWERUP.Set(1)
.UTMIP_FORCE_PD_SAMP_C_POWERDOWN.Set(0)
.UTMIP_FORCE_PD_SAMP_C_POWERUP.Set(1)
.UTMIP_FORCE_PD_SAMP_D_POWERDOWN.Set(0)
.UTMIP_FORCE_PD_SAMP_D_POWERUP.Set(1)
.Commit()
.UTMIP_PLL_CFG1_0()
.UTMIP_FORCE_PLL_ENABLE_POWERUP.Set(0)
.UTMIP_FORCE_PLL_ENABLE_POWERDOWN.Set(0)
.Commit()
.UTMIPLL_HW_PWRDN_CFG0_0()
.UTMIPLL_USE_LOCKDET.Set(1)
.UTMIPLL_CLK_ENABLE_SWCTL.Set(0)
.UTMIPLL_SEQ_ENABLE.Set(1)
.Commit();
udelay(2);
}
void ApplyFuseCalibration() {
auto fuse = t210::FUSE.SKU_USB_CALIB();
t210::XUSB_PADCTL
.USB2_OTG_PAD0_CTL_0_0()
.HS_CURR_LEVEL.Set(fuse.HS_CURR_LEVEL.Get())
.Commit()
.USB2_OTG_PAD0_CTL_1_0()
.TERM_RANGE_ADJ.Set(fuse.TERM_RANGE_ADJ.Get())
.RPD_CTRL.Set(t210::FUSE.USB_CALIB_EXT().RPD_CTRL.Get())
.Commit()
.USB2_BATTERY_CHRG_OTGPAD0_CTL1_0()
.VREG_FIX18.Set(0)
.VREG_LEV.Set(1)
.Commit();
}
void DisablePd() {
t210::XUSB_PADCTL
.USB2_OTG_PAD0_CTL_0_0()
.PD_ZI.Set(0)
.PD.Set(0)
.Commit()
.USB2_OTG_PAD0_CTL_1_0()
.PD_DR.Set(0)
.Commit()
.USB2_BATTERY_CHRG_OTGPAD0_CTL0_0()
.PD_CHG.Set(0)
.Commit()
.USB2_BIAS_PAD_CTL_0_0()
.PD.Set(0)
.Commit();
}
void SetupTracking(t210::CLK_RST_CONTROLLER::OscFreq osc_freq) {
uint8_t divisor;
using OscFreq = t210::CLK_RST_CONTROLLER::OscFreq;
switch(osc_freq) {
case OscFreq::OSC13:
divisor = 2;
break;
case OscFreq::OSC16P8:
divisor = 2;
break;
case OscFreq::OSC19P2:
divisor = 2;
break;
case OscFreq::OSC38P4:
divisor = 6;
break;
case OscFreq::OSC12:
divisor = 2;
break;
case OscFreq::OSC48:
divisor = 6;
break;
case OscFreq::OSC26:
divisor = 4;
break;
default:
fatal_error("xusb initialization failed: invalid oscillator frequency");
break;
}
t210::CLK_RST_CONTROLLER
.CLK_OUT_ENB_Y_0()
.CLK_ENB_USB2_TRK.Enable()
.Commit()
.CLK_SOURCE_USB2_HSIC_TRK_0()
.USB2_HSIC_TRK_CLK_DIVISOR.Set(divisor)
.Commit();
auto reg = t210::XUSB_PADCTL
.USB2_BIAS_PAD_CTL_1_0(0)
.TRK_DONE_RESET_TIMER.Set(10)
.TRK_START_TIMER.Set(30);
reg.PD_TRK.Set(0).Commit();
udelay(100);
reg.PD_TRK.Set(1).Commit();
udelay(3);
reg.PD_TRK.Set(0).Commit();
udelay(100);
t210::XUSB_PADCTL
.USB2_BIAS_PAD_CTL_1_0()
.PD_TRK.Set(1)
.Commit();
t210::CLK_RST_CONTROLLER
.CLK_OUT_ENB_Y_0()
.CLK_ENB_USB2_TRK.Disable()
.Commit();
}
} // namespace padctl
void ConfigureClockAndResetForDeviceMode() {
t210::CLK_RST_CONTROLLER
.PLLU_OUTA_0()
.PLLU_OUT1_RSTN.Set(1) // RESET_DISABLE
.Commit();
udelay(2);
t210::CLK_RST_CONTROLLER
.CLK_OUT_ENB_U_0()
.CLK_ENB_XUSB_DEV.Enable()
.Commit()
.CLK_SOURCE_XUSB_CORE_DEV_0()
.XUSB_CORE_DEV_CLK_SRC.SetPLLP_OUT0()
.XUSB_CORE_DEV_CLK_DIVISOR.Set(6)
.Commit();
udelay(2);
t210::CLK_RST_CONTROLLER
.CLK_SOURCE_XUSB_FS_0()
.XUSB_FS_CLK_SRC.SetFO_48M()
.Commit()
.CLK_OUT_ENB_W_0()
.CLK_ENB_XUSB_SS.Enable()
.Commit()
.CLK_SOURCE_XUSB_SS_0()
.XUSB_SS_CLK_SRC.SetHSIC_480()
.XUSB_SS_CLK_DIVISOR.Set(6)
.Commit()
.RST_DEVICES_W_0()
.SWR_XUSB_SS_RST.Set(0) // DISABLE
.Commit()
.RST_DEVICES_U_0()
.SWR_XUSB_DEV_RST.Set(0) // DISABLE
.Commit();
udelay(2);
mc_acquire_ahb_redirect();
t210::XUSB_DEV
.CONFIGURATION_0()
.EN_FPCI.Enable()
.Commit();
t210::T_XUSB_DEV
.CFG_1()
.IO_SPACE.Enable()
.MEMORY_SPACE.Enable()
.BUS_MASTER.Enable()
.Commit();
udelay(1);
t210::T_XUSB_DEV
.CFG_4(0)
.BASE_ADDRESS.Set(t210::XUSB_DEV_BASE)
.PREFETCHABLE.SetNOT()
.ADDRESS_TYPE.Set32_BIT()
.SPACE_TYPE.SetMEMORY()
.Commit();
t210::XUSB_DEV
.INTR_MASK_0()
.IP_INT_MASK.Set(1) // "IP (SATA/AZ) interrupt to MPCORE gated by mask."
.Commit();
}
} // namespace impl
Result xusb::Gadget::HandleSetupRequest(usb::SetupPacket &packet) {
return ResultUnknownSetupRequest();
}
} // namespace xusb
} // namespace ams

View file

@ -0,0 +1,57 @@
/*
* 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 "usb_types.h"
#include "xusb_trb.h"
#include "xusb_endpoint.h"
#include<vapours/results/xusb_gadget_results.hpp>
#include<stddef.h>
namespace ams {
namespace xusb {
class Gadget {
public:
virtual Result HandleSetupRequest(usb::SetupPacket &packet);
virtual Result GetDeviceDescriptor(uint8_t index, const usb::DeviceDescriptor **descriptor, uint16_t *length) = 0;
virtual Result GetDeviceQualifierDescriptor(uint8_t index, const usb::DeviceQualifierDescriptor **descriptor, uint16_t *length) = 0;
virtual Result GetConfigurationDescriptor(uint8_t index, const usb::ConfigurationDescriptor **descriptor, uint16_t *length) = 0;
virtual Result GetBOSDescriptor(const usb::BOSDescriptor **descriptor, uint16_t *length) = 0;
virtual Result GetStringDescriptor(uint8_t index, uint16_t language, const usb::CommonDescriptor **descriptor, uint16_t *length) = 0;
/* It is the implementor's responsibility to configure endpoints before this returns. */
virtual Result SetConfiguration(uint16_t configuration) = 0;
virtual void PostConfigure() = 0;
virtual Result Deconfigure() = 0;
virtual void ProcessTransferEvent(TransferEventTRB *event, TRBBorrow transfer) = 0;
};
void Initialize();
void EnableDevice(Gadget &gadget);
void Process();
void Finalize();
Gadget *GetCurrentGadget();
} // namespace xusb
} // namespace ams

View file

@ -0,0 +1,414 @@
/*
* 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 "xusb_control.h"
#include "usb_types.h"
#include "xusb.h"
#include "xusb_endpoint.h"
#include "../reg/reg_xusb_dev.h"
extern "C" {
#include "../timers.h"
#include "../lib/log.h"
}
#include<string.h>
#include<vapours/results/xusb_gadget_results.hpp>
namespace ams {
namespace xusb {
namespace control {
DeviceState device_state = DeviceState::Powered;
namespace {
enum class ControlEpState {
Idle,
DataInStage,
DataOutStage,
StatusStage,
} control_ep_state = ControlEpState::Idle;
TRB *control_ep_last_trb = nullptr;
uint16_t control_ep_seq_num = 0;
void ResetControlEndpoint() {
xusb::endpoints[0].Pause();
xusb::endpoints[0].ResetAndReloadTransferRing();
xusb::endpoints[0].ClearPause();
control_ep_state = ControlEpState::Idle;
control_ep_last_trb = nullptr;
}
void ChangeDeviceState(DeviceState new_state) {
// if we are transitioning in or out of configured state,
if ((device_state == DeviceState::Configured) != (new_state == DeviceState::Configured)) {
t210::T_XUSB_DEV_XHCI.CTRL().RUN.Set(new_state == DeviceState::Configured).Commit();
if (new_state == DeviceState::Configured) {
t210::T_XUSB_DEV_XHCI.ST().RC.Clear().Commit();
xusb::GetCurrentGadget()->PostConfigure();
}
}
switch(new_state) {
case DeviceState::Default:
device_state = DeviceState::Default;
print(SCREEN_LOG_LEVEL_DEBUG, "entering default state, resetting control endpoint state...\n");
ResetControlEndpoint();
break;
case DeviceState::Address:
device_state = DeviceState::Address;
break;
case DeviceState::Configured:
device_state = DeviceState::Configured;
break;
default:
print(SCREEN_LOG_LEVEL_ERROR, "ChangeDeviceState(%d) state unknown\n", (int) new_state);
break;
}
}
} // anonymous namespace
Result SendData(void *data, size_t size) {
TRBBorrow trb;
R_TRY(xusb::endpoints[0].EnqueueTRB(&trb));
trb->transfer.InitializeDataStage(true, (void*) data, size);
trb->transfer.interrupt_on_completion = true;
trb->transfer.interrupt_on_short_packet = true;
trb.Release();
udelay(1000);
xusb::endpoints[0].RingDoorbell(control_ep_seq_num);
control_ep_state = ControlEpState::DataInStage;
control_ep_last_trb = &*trb;
return ResultSuccess();
}
Result SendStatus(bool direction) {
TRBBorrow trb;
R_TRY(xusb::endpoints[0].EnqueueTRB(&trb));
trb->transfer.InitializeStatusStage(direction);
trb->transfer.interrupt_on_completion = true;
trb->transfer.interrupt_on_short_packet = true;
trb.Release();
udelay(1000);
xusb::endpoints[0].RingDoorbell(control_ep_seq_num);
control_ep_state = ControlEpState::StatusStage;
control_ep_last_trb = &*trb;
return ResultSuccess();
}
namespace impl {
Result SetAddress(uint16_t addr) {
print(SCREEN_LOG_LEVEL_DEBUG, "got SET_ADDRESS request(%d)\n", addr);
R_UNLESS(addr <= 127, ams::xusb::ResultInvalidAddress());
t210::T_XUSB_DEV_XHCI
.CTRL()
.DEVADR.Set(addr)
.Commit();
xusb::endpoints.SetEP0DeviceAddress(addr);
ChangeDeviceState(DeviceState::Address);
return SendStatus(true);
}
Result GetDescriptor(usb::DescriptorType type, uint8_t index, uint16_t language_id, uint16_t length) {
print(SCREEN_LOG_LEVEL_DEBUG, "got GET_DESCRIPTOR request(%d, %d, %d, %d)\n", type, index, language_id, length);
uint16_t actual_length = 0;
const usb::CommonDescriptor *descriptor = nullptr;
switch(type) {
case usb::DescriptorType::DEVICE:
R_UNLESS(language_id == 0, xusb::ResultMalformedSetupRequest());
R_TRY(xusb::GetCurrentGadget()->GetDeviceDescriptor(index, (const usb::DeviceDescriptor**) &descriptor, &actual_length));
break;
case usb::DescriptorType::CONFIGURATION:
R_UNLESS(language_id == 0, xusb::ResultMalformedSetupRequest());
R_TRY(xusb::GetCurrentGadget()->GetConfigurationDescriptor(index, (const usb::ConfigurationDescriptor**) &descriptor, &actual_length));
break;
case usb::DescriptorType::BOS:
R_UNLESS(index == 0, xusb::ResultInvalidDescriptorIndex());
R_UNLESS(language_id == 0, xusb::ResultMalformedSetupRequest());
R_TRY(xusb::GetCurrentGadget()->GetBOSDescriptor((const usb::BOSDescriptor**) &descriptor, &actual_length));
break;
case usb::DescriptorType::STRING:
R_TRY(xusb::GetCurrentGadget()->GetStringDescriptor(index, language_id, &descriptor, &actual_length));
break;
case usb::DescriptorType::DEVICE_QUALIFIER:
R_UNLESS(language_id == 0, xusb::ResultMalformedSetupRequest());
R_TRY(xusb::GetCurrentGadget()->GetDeviceQualifierDescriptor(index, (const usb::DeviceQualifierDescriptor**) &descriptor, &actual_length));
break;
default:
return xusb::ResultUnknownDescriptorType();
}
if (actual_length > length) {
actual_length = length;
}
return SendData((void*) descriptor, actual_length);
}
Result SetConfiguration(uint16_t config) {
print(SCREEN_LOG_LEVEL_DEBUG, "got SET_CONFIGURATION request(%d)\n", config);
R_UNLESS(device_state == DeviceState::Address || device_state == DeviceState::Configured, xusb::ResultInvalidDeviceState());
// if we are already configured, deconfigure first.
if (device_state == DeviceState::Configured) {
R_TRY(xusb::GetCurrentGadget()->Deconfigure());
ChangeDeviceState(DeviceState::Address);
}
// if we need to reconfigure, do it.
if (config != 0) {
R_TRY(xusb::GetCurrentGadget()->SetConfiguration(config));
ChangeDeviceState(DeviceState::Configured);
}
return SendStatus(true);
}
Result ClearEndpointHaltFeature(uint8_t index) {
xusb::endpoints[EpAddr::Decode(index)].ClearHalt();
return SendStatus(true);
}
} // namespace impl
Result ProcessEP0TransferEventImpl(TransferEventTRB *event, TRBBorrow transfer) {
if (event->completion_code == 1 || event->completion_code == 13) {
/* Success or short packet. */
} else if (event->completion_code == 223) { // sequence number error
/* Sequence number error; skip to latest packet. */
control_ep_state = ControlEpState::Idle;
} else {
/* Error. */
control_ep_state = ControlEpState::Idle;
}
if (&*transfer != control_ep_last_trb) {
/* This can happen when we skip packets. */
print(SCREEN_LOG_LEVEL_ERROR, "got transfer event for wrong TRB (got %p, expected %p)\n", &*transfer, control_ep_last_trb);
return xusb::ResultUnexpectedTRB();
} else {
control_ep_last_trb = nullptr;
switch(control_ep_state) {
case ControlEpState::Idle:
return xusb::ResultInvalidControlEndpointState();
case ControlEpState::DataInStage:
return SendStatus(false); // OUT
case ControlEpState::DataOutStage:
return SendStatus(false); // IN
case ControlEpState::StatusStage:
control_ep_state = ControlEpState::Idle;
return ResultSuccess();
default:
return ResultSuccess();
}
}
}
void ProcessEP0TransferEvent(TransferEventTRB *event, TRBBorrow transfer) {
Result r = ProcessEP0TransferEventImpl(event, std::move(transfer));
if (r.IsFailure()) {
print(SCREEN_LOG_LEVEL_DEBUG, "failed to handle ep0 transfer event: 0x%x\n", r.GetValue());
xusb::endpoints[0].Halt();
}
}
Result ProcessSetupPacketEventImpl(SetupPacketEventTRB *trb) {
R_UNLESS(trb->seq_num != 0xfffe, xusb::ResultInvalidSetupPacketSequenceNumber());
R_UNLESS(trb->seq_num != 0xffff, xusb::ResultInvalidSetupPacketSequenceNumber());
R_UNLESS(device_state >= DeviceState::Default && device_state <= DeviceState::Configured, xusb::ResultInvalidDeviceState());
R_UNLESS(control_ep_state == ControlEpState::Idle, xusb::ResultControlEndpointBusy());
control_ep_seq_num = trb->seq_num;
xusb::endpoints[0].ClearHalt();
/* Let gadget try to service request first. */
do {
R_TRY_CATCH(xusb::GetCurrentGadget()->HandleSetupRequest(trb->packet)) {
R_CATCH(xusb::ResultUnknownSetupRequest) { break; }
} R_END_TRY_CATCH;
return ResultSuccess();
} while (0);
/* If gadget did not recognize the request, handle it ourselves. */
switch((usb::SetupPacket::StandardRequest) trb->packet.bRequest) {
case usb::SetupPacket::StandardRequest::CLEAR_FEATURE:
if (trb->packet.bmRequestType == usb::SetupPacket::PackRequestType(
usb::SetupPacket::RequestDirection::HostToDevice,
usb::SetupPacket::RequestType::Standard,
usb::SetupPacket::RequestRecipient::Endpoint) &&
trb->packet.wValue == (uint8_t) usb::EndpointFeatureSelector::ENDPOINT_HALT) {
R_UNLESS(trb->packet.wLength == 0, xusb::ResultMalformedSetupRequest());
return impl::ClearEndpointHaltFeature(trb->packet.wIndex);
} else {
return xusb::ResultUnknownSetupRequest();
}
case usb::SetupPacket::StandardRequest::SET_ADDRESS:
if (trb->packet.bmRequestType != usb::SetupPacket::PackRequestType(
usb::SetupPacket::RequestDirection::HostToDevice,
usb::SetupPacket::RequestType::Standard,
usb::SetupPacket::RequestRecipient::Device) ||
trb->packet.wIndex != 0 ||
trb->packet.wLength != 0) {
return xusb::ResultMalformedSetupRequest();
}
if (device_state == DeviceState::Configured) {
return xusb::ResultInvalidDeviceState();
}
return impl::SetAddress(trb->packet.wValue);
case usb::SetupPacket::StandardRequest::GET_DESCRIPTOR:
if (trb->packet.bmRequestType != usb::SetupPacket::PackRequestType(
usb::SetupPacket::RequestDirection::DeviceToHost,
usb::SetupPacket::RequestType::Standard,
usb::SetupPacket::RequestRecipient::Device)) {
return xusb::ResultMalformedSetupRequest();
}
return impl::GetDescriptor(
(usb::DescriptorType) (trb->packet.wValue >> 8),
trb->packet.wValue & 0xff,
trb->packet.wIndex,
trb->packet.wLength);
case usb::SetupPacket::StandardRequest::SET_CONFIGURATION:
if (trb->packet.bmRequestType != usb::SetupPacket::PackRequestType(
usb::SetupPacket::RequestDirection::HostToDevice,
usb::SetupPacket::RequestType::Standard,
usb::SetupPacket::RequestRecipient::Device) ||
trb->packet.wIndex != 0 ||
trb->packet.wLength != 0) {
return xusb::ResultMalformedSetupRequest();
}
return impl::SetConfiguration(trb->packet.wValue);
default:
return xusb::ResultUnknownSetupRequest();
}
}
void ProcessSetupPacketEvent(SetupPacketEventTRB *trb) {
Result r = ProcessSetupPacketEventImpl(trb);
if (r.IsFailure()) {
print(SCREEN_LOG_LEVEL_WARNING, "error 0x%x while handling setup request\n", r.GetValue());
print(SCREEN_LOG_LEVEL_WARNING, " bmRequestType: %d\n", trb->packet.bmRequestType);
print(SCREEN_LOG_LEVEL_WARNING, " bRequest: %d\n", trb->packet.bRequest);
print(SCREEN_LOG_LEVEL_WARNING, " wValue: %d\n", trb->packet.wValue);
print(SCREEN_LOG_LEVEL_WARNING, " wIndex: %d\n", trb->packet.wIndex);
print(SCREEN_LOG_LEVEL_WARNING, " wLenth: %d\n", trb->packet.wLength);
xusb::endpoints[0].Halt();
}
}
void ProcessPortStatusChangeEvent(AbstractTRB *trb) {
auto portsc = t210::T_XUSB_DEV_XHCI.PORTSC();
auto porthalt = t210::T_XUSB_DEV_XHCI.PORTHALT();
print(SCREEN_LOG_LEVEL_DEBUG, "port status changed (0x%08x)\n", portsc.read_value);
if (portsc.CSC) { // connect status change
int port_speed = portsc.PS.Get();
print(SCREEN_LOG_LEVEL_DEBUG, "connect status changed (port speed %d)\n", port_speed);
portsc.CSC.Clear().Commit();
}
if (portsc.PRC.Get()) { // port reset change
print(SCREEN_LOG_LEVEL_DEBUG, "port reset changed (%d)\n", portsc.PR.Get());
if (!portsc.PR.Get()) {
ChangeDeviceState(DeviceState::Default);
}
portsc.PRC.Clear().Commit();
}
if (portsc.WRC) { // warm port reset change
t210::T_XUSB_DEV_XHCI.PORTHALT().HALT_LTSSM.Set(0).Commit();
print(SCREEN_LOG_LEVEL_DEBUG, "warm port reset changed (%d)\n", portsc.WPR.Get());
portsc.WRC.Clear().Commit();
}
if (porthalt.STCHG_REQ) {
print(SCREEN_LOG_LEVEL_DEBUG, "porthalt STCHG_REQ\n");
porthalt.HALT_LTSSM.Set(0).Commit();
}
if (portsc.PLC) { // port link state change
print(SCREEN_LOG_LEVEL_DEBUG, "port link state changed (%d)\n", portsc.PLS.Get());
if (portsc.PLS.Get() == 3) { // U3
ChangeDeviceState(DeviceState::Default);
}
portsc.PLC.Clear().Commit();
}
if (portsc.CEC) { // port configuration link error
print(SCREEN_LOG_LEVEL_DEBUG, "port configuration link error\n");
portsc.CEC.Clear().Commit();
}
}
} // namespace control
} // namespace xusb
} // namespace ams

View file

@ -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 "xusb_trb.h"
#include "xusb_endpoint.h"
namespace ams {
namespace xusb {
namespace control {
enum class DeviceState {
Powered,
Default,
Address,
Configured,
};
extern DeviceState state;
Result SendData(void *data, size_t size);
Result SendStatus(bool direction);
void ProcessEP0TransferEvent(TransferEventTRB *event, TRBBorrow transfer);
void ProcessSetupPacketEvent(SetupPacketEventTRB *trb);
void ProcessPortStatusChangeEvent(AbstractTRB *trb);
} // namespace control
} // namespace xusb
} // namespace ams

View file

@ -0,0 +1,368 @@
/*
* 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 "xusb_endpoint.h"
#include "xusb.h"
#include "xusb_control.h"
extern "C" {
#include "../lib/log.h"
}
#include<string.h>
namespace ams {
namespace xusb {
void EndpointContext::InitializeForControl() {
memset((void*) this, 0, sizeof(*this));
this->ep_state = 1;
this->cerr = 3;
this->ep_type = 4;
this->max_packet_size = 64;
this->dcs = 1;
this->average_trb_length = 8;
this->cec = 3;
this->seqnum = 0;
}
void EndpointContext::Initialize(const usb::EndpointDescriptor &desc) {
memset((void*) this, 0, sizeof(*this));
this->ep_state = 1;
if (desc.GetEndpointType() != usb::EndpointType::Control && (desc.bEndpointAddress & 0x80)) {
/* In endpoint types are offset by 4. */
this->ep_type = (uint8_t) desc.GetEndpointType() + 4;
} else {
this->ep_type = (uint8_t) desc.GetEndpointType();
}
this->cerr = 3;
this->max_packet_size = desc.wMaxPacketSize;
this->max_burst_size = 0;
this->cec = 3;
}
void EndpointAccessor::RingDoorbell() const {
t210::T_XUSB_DEV_XHCI.DB()
.TARGET.Set(this->no)
.Commit();
}
void EndpointAccessor::RingDoorbell(uint16_t stream_id) const {
t210::T_XUSB_DEV_XHCI.DB()
.STREAMID.Set(stream_id)
.TARGET.Set(this->no)
.Commit();
}
void EndpointAccessor::InitializeForControl(TRB *ring, size_t ring_size) const {
/* Initialize hardware endpoint context for control endpont. */
this->GetContext().InitializeForControl();
/* Initialize transfer ring. */
this->GetRingManager().Initialize(this->GetContext(), ring, ring_size);
}
void EndpointAccessor::Initialize(const usb::EndpointDescriptor &desc, TRB *ring, size_t ring_size) const {
/* Initialize hardawre endpoint context for regular endpoint. */
this->GetContext().Initialize(desc);
/* Initialize transfer ring. */
this->GetRingManager().Initialize(this->GetContext(), ring, ring_size);
this->Reload();
t210::T_XUSB_DEV_XHCI.EP_PAUSE()[this->no].Set(0).Commit();
t210::T_XUSB_DEV_XHCI.EP_HALT()[this->no].Set(0).Commit();
}
void EndpointAccessor::Disable() const {
this->GetContext().ep_state = 0;
this->Reload();
this->ClearPause();
this->ClearHalt();
auto stopped = t210::T_XUSB_DEV_XHCI.EP_STOPPED();
if (stopped[this->no]) {
stopped[this->no].Clear().Commit();
}
}
Result EndpointAccessor::TransferNormal(void *data, size_t size, TRB **trb_out) const {
TRBBorrow trb;
R_TRY(this->EnqueueTRB(&trb));
trb->transfer.InitializeNormal(data, size);
trb->transfer.interrupt_on_completion = true;
trb->transfer.interrupt_on_short_packet = true;
trb.Release();
this->RingDoorbell();
*trb_out = &*trb;
return ResultSuccess();
}
void EndpointAccessor::Halt() const {
auto ep_halt = t210::T_XUSB_DEV_XHCI.EP_HALT();
if (ep_halt[this->no]) {
return;
}
(ep_halt[this->no] = 1).Commit();
/* BootROM polls EP_HALT register, but TRM says to poll STCHG instead. */
while (!t210::T_XUSB_DEV_XHCI.EP_STCHG()[this->no]) {
}
t210::T_XUSB_DEV_XHCI.EP_STCHG()[this->no].Clear().Commit();
}
void EndpointAccessor::ClearHalt() const {
auto ep_halt = t210::T_XUSB_DEV_XHCI.EP_HALT();
if (!ep_halt[this->no]) {
return;
}
(ep_halt[this->no] = 0).Commit();
/* Bootrom polls EP_HALT register, but TRM says to poll STCHG instead. */
while (!t210::T_XUSB_DEV_XHCI.EP_STCHG()[this->no]) {
}
t210::T_XUSB_DEV_XHCI.EP_STCHG()[this->no].Clear().Commit();
}
void EndpointAccessor::Pause() const {
auto ep_pause = t210::T_XUSB_DEV_XHCI.EP_PAUSE();
if (ep_pause[this->no]) {
return;
}
(ep_pause[this->no] = 1).Commit();
/* Bootrom polls EP_PAUSE register, but TRM says to poll STCHG instead. */
while (!t210::T_XUSB_DEV_XHCI.EP_STCHG()[this->no]) {
}
t210::T_XUSB_DEV_XHCI.EP_STCHG()[this->no].Clear().Commit();
}
void EndpointAccessor::ClearPause() const {
auto ep_pause = t210::T_XUSB_DEV_XHCI.EP_PAUSE();
if (!ep_pause[this->no]) {
return;
}
(ep_pause[this->no] = 0).Commit();
/* Bootrom polls EP_PAUSE register, but TRM says to poll STCHG instead. */
while (!t210::T_XUSB_DEV_XHCI.EP_STCHG()[this->no]) {
}
t210::T_XUSB_DEV_XHCI.EP_STCHG()[this->no].Clear().Commit();
}
void EndpointAccessor::Reload() const {
auto ep_reload = t210::T_XUSB_DEV_XHCI.EP_RELOAD();
ep_reload[this->no] = 1;
ep_reload.Commit();
while (t210::T_XUSB_DEV_XHCI.EP_RELOAD()[this->no]) {
}
}
TRBBorrow EndpointAccessor::AcceptCompletedTRB(TransferEventTRB *trb) const {
return TRBBorrow(&this->GetRingManager(), trb->GetSourceTRB());
}
void EndpointAccessor::ResetAndReloadTransferRing() const {
this->GetRingManager().Reset(this->GetContext());
this->Reload();
}
void EndpointAccessor::DumpTransferRingForDebug() const {
this->GetRingManager().DumpTransferRingForDebug();
}
TRBBorrow::TRBBorrow(TRBBorrow &&other) :
ring(other.ring),
trb(other.trb),
state(other.state),
cycle_state(other.cycle_state) {
other.state = State::Empty;
}
TRBBorrow &TRBBorrow::operator=(TRBBorrow &&other) {
this->Release();
this->ring = other.ring;
this->trb = other.trb;
this->state = other.state;
this->cycle_state = other.cycle_state;
other.state = State::Empty;
return *this;
}
void TRBBorrow::Release() {
if (this->state == State::BorrowForEnqueue) {
this->trb->abstract.SetCycle(this->cycle_state);
this->state = State::Empty;
} else if (this->state == State::BorrowForRecycle) {
this->ring->RecycleTRB(this->trb);
this->state = State::Empty;
}
}
void EndpointRingManager::Initialize(EndpointContext &ep_context, TRB *ring, size_t ring_size) {
this->ring = ring;
this->ring_size = ring_size;
this->Reset(ep_context);
}
void EndpointRingManager::Reset(EndpointContext &ep_context) {
memset(this->ring, 0, sizeof(TRB) * this->ring_size);
this->producer_cycle_state = true;
this->enqueue = &this->ring[0];
this->dequeue = &this->ring[0];
ep_context.dcs = this->producer_cycle_state;
ep_context.tr_dequeue_ptr = ((uint64_t) &this->ring[0]) >> 4;
}
size_t EndpointRingManager::GetFreeTRBCount() {
/* Last TRB is reserved for link. */
size_t adjusted_ring_size = (this->ring_size - 1);
if (this->ring_full) {
return 0;
}
return adjusted_ring_size - ((this->enqueue + adjusted_ring_size - this->dequeue) % adjusted_ring_size);
}
TRB *EndpointRingManager::EnqueueTRB(bool chain) {
if (ring_full) {
return nullptr;
}
TRB *trb = this->enqueue++;
if (this->enqueue == this->ring + (this->ring_size - 1)) {
this->enqueue->link.Initialize(&this->ring[0]);
this->enqueue->link.SetToggleCycle(true);
this->enqueue->link.SetChain(chain);
this->enqueue->abstract.SetCycle(this->producer_cycle_state);
this->enqueue = &this->ring[0];
this->producer_cycle_state = !this->producer_cycle_state;
}
if (this->enqueue == this->dequeue) {
this->ring_full = true;
}
trb->abstract.Clear();
return trb;
}
void EndpointRingManager::RecycleTRB(TRB *trb) {
if (trb + 1 == &this->ring[this->ring_size-1]) {
this->dequeue = &this->ring[0];
} else {
this->dequeue = trb + 1;
}
this->ring_full = false;
}
void EndpointRingManager::DebugState() {
ScreenLogLevel ll = (ScreenLogLevel) (SCREEN_LOG_LEVEL_DEBUG | SCREEN_LOG_LEVEL_NO_PREFIX);
print(ll, "%8s", "address ");
for (size_t i = 0; i < this->ring_size; i++) {
TRB *ptr = &this->ring[i];
print(ll, "%x", ((uint32_t) ptr >> 4) & 0xf);
}
print(ll, "\n%8s", "cycle ");
for (size_t i = 0; i < this->ring_size; i++) {
TRB *ptr = &this->ring[i];
print(ll, "%d", ptr->abstract.GetCycle());
}
print(ll, "\n%8s", "deq/enq ");
for (size_t i = 0; i < this->ring_size; i++) {
TRB *ptr = &this->ring[i];
if (this->dequeue == ptr && this->enqueue == ptr) {
print(ll, "!");
} else if (this->dequeue == ptr) {
print(ll, "D");
} else if (this->enqueue == ptr) {
print(ll, "E");
} else if (ptr == &this->ring[this->ring_size-1]) {
print(ll, "L");
} else {
print(ll, " ");
}
}
print(ll, "\n%8s", "ioc ");
for (size_t i = 0; i < this->ring_size; i++) {
TRB *ptr = &this->ring[i];
print(ll, "%d", ptr->transfer.interrupt_on_completion);
}
print(ll, "\n");
}
void EndpointRingManager::DumpTransferRingForDebug() {
ScreenLogLevel ll = (ScreenLogLevel) (SCREEN_LOG_LEVEL_DEBUG | SCREEN_LOG_LEVEL_NO_PREFIX);
print(ll, " enq deq type \n");
for (TRB *ptr = &this->ring[0]; ptr < &this->ring[this->ring_size]; ptr++) {
print(ll, " %3s %3s %3d %08x %08x %08x %08x\n",
ptr == this->enqueue ? "[E]" : "",
ptr == this->dequeue ? "[D]" : "",
ptr->transfer.trb_type,
ptr->abstract.field0,
ptr->abstract.field1,
ptr->abstract.field2,
ptr->abstract.field3);
}
print(ll, "pcs: %d\n", this->producer_cycle_state);
print(ll, "full: %s\n", this->ring_full ? "yes" : "no");
}
void Endpoints::ClearNonControlEndpoints() {
memset(&contexts[2], 0, sizeof(contexts) - 2*sizeof(contexts[0]));
}
void Endpoints::SetEP0DeviceAddress(uint16_t addr) {
this->contexts[0].device_address = addr;
}
Endpoints endpoints __attribute__((section(".dram")));
} // namespace xusb
} // namespace ams

View file

@ -0,0 +1,346 @@
/*
* 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 "xusb_trb.h"
#include "../reg/reg_xusb_dev.h"
#include<vapours/results/xusb_gadget_results.hpp>
#include<stddef.h>
namespace ams {
namespace xusb {
struct EndpointContext {
uint32_t ep_state:3;
uint32_t :5;
uint32_t multi:2;
uint32_t max_p_streams:5;
uint32_t lsa:1;
uint32_t interval:8;
uint32_t :8;
uint32_t :1;
uint32_t cerr:2;
uint32_t ep_type:3;
uint32_t :1;
uint32_t hid:1;
uint32_t max_burst_size:8;
uint32_t max_packet_size:16;
uint64_t dcs:1;
uint64_t :3;
uint64_t tr_dequeue_ptr:60;
uint32_t average_trb_length:16;
uint32_t max_esit_payload:16;
uint32_t event_data_transfer_length_accumulator:24;
uint32_t :1;
uint32_t ptd:1;
uint32_t sxs:1;
uint32_t seqnum:5;
uint32_t cprog:8;
uint32_t s_byte:7;
uint32_t tp:2;
uint32_t rec:1;
uint32_t cec:2;
uint32_t ced:1;
uint32_t hsp:1;
uint32_t rty:1;
uint32_t std:1;
uint32_t status:8;
uint32_t data_offset:17;
uint32_t :4;
uint32_t lpa:1;
uint32_t num_trb:5;
uint32_t num_p:5;
uint32_t scratchpad1;
uint32_t scratchpad2;
uint32_t cping:8;
uint32_t sping:8;
uint32_t tc:2;
uint32_t ns:1;
uint32_t ro:1;
uint32_t tlm:1;
uint32_t dlm:1;
uint32_t hsp2:1;
uint32_t rty2:1;
uint32_t stop_reclaim_request:8;
uint32_t device_address:8;
uint32_t hub_address:8;
uint32_t root_port_number:8;
uint32_t slot_id:8;
uint32_t routing_string:20;
uint32_t speed:4;
uint32_t lpu:1;
uint32_t myy:1;
uint32_t hub:1;
uint32_t dci:5;
uint32_t tt_hub_slot_id:8;
uint32_t tt_port_num:8;
uint32_t ssf:4;
uint32_t sps:2;
uint32_t interrupt_target:10;
uint64_t frz:1;
uint64_t end:1;
uint64_t elm:1;
uint64_t mrk:1;
uint64_t endpoint_link:60;
void InitializeForControl();
void Initialize(const usb::EndpointDescriptor &desc);
} __attribute__((aligned(64)));
enum Dir {
Out,
In,
};
struct EpAddr {
static inline EpAddr Decode(uint8_t addr) {
return EpAddr {
.no = (uint8_t) (addr & 0b1111),
.direction = (addr >> 7) ? Dir::In : Dir::Out,
};
}
uint8_t no;
Dir direction;
};
class EndpointRingManager {
public:
void Initialize(EndpointContext &ep_context, TRB *ring, size_t ring_size);
void Reset(EndpointContext &ep_context);
inline size_t GetRingUsableCount() {
return this->ring_size - 1;
}
size_t GetFreeTRBCount();
void DumpTransferRingForDebug(); // long form
void DebugState();
TRB *EnqueueTRB(bool chain);
void RecycleTRB(TRB *trb);
inline bool GetProducerCycleState() {
return this->producer_cycle_state;
}
private:
TRB *ring = nullptr;
size_t ring_size = 0;
bool ring_full = false;
bool producer_cycle_state = true;
TRB *dequeue = nullptr;
TRB *enqueue = nullptr;
};
/* TRB ownership:
Initially, all TRBs are owned by the transfer ring manager.
By calling EnqueueTRB(), you are borrowing a TRB from the ring.
By calling Release() or allowing TRBBorrow to be destructed, the borrow is transferred to the hardware.
When the hardware is finished with the TRB and interrupts for completion, the borrow is transferred back to the software.
By calling Release() or allowing TRBRecycleHelper to be destructed, the TRB is recycled to the transfer ring manager.
*/
class EndpointAccessor;
class TRBBorrow {
public:
inline TRBBorrow() :
state(State::Empty) {
}
private:
friend class EndpointAccessor;
inline TRBBorrow(EndpointRingManager *ring, TRB *trb) :
ring(ring),
trb(trb),
state(State::BorrowForRecycle) {
}
inline TRBBorrow(EndpointRingManager *ring, TRB *trb, bool cycle_state) :
ring(ring),
trb(trb),
state(State::BorrowForEnqueue),
cycle_state(cycle_state) {
}
public:
inline ~TRBBorrow() {
Release();
}
TRBBorrow(TRBBorrow &) = delete;
TRBBorrow(TRBBorrow &&);
TRBBorrow &operator=(TRBBorrow &) = delete;
TRBBorrow &operator=(TRBBorrow &&);
void Release();
inline TRB &operator*() {
return *this->trb;
}
inline TRB *operator->() {
return this->trb;
}
private:
EndpointRingManager *ring;
TRB *trb;
enum class State {
Empty,
BorrowForEnqueue,
BorrowForRecycle
} state;
bool cycle_state;
};
class Endpoints;
class EndpointAccessor {
public:
constexpr inline EndpointAccessor(uint8_t ep_index, Endpoints &eps) :
no(ep_index),
endpoints(eps) {
}
void InitializeForControl(TRB *ring, size_t ring_size) const;
void Initialize(const usb::EndpointDescriptor &desc, TRB *ring, size_t ring_size) const;
void Disable() const;
inline size_t GetRingUsableCount() const {
return this->GetRingManager().GetRingUsableCount();
}
inline size_t GetFreeTRBCount() const {
return this->GetRingManager().GetFreeTRBCount();
}
inline Result EnqueueTRB(TRBBorrow *borrow, bool chain=false) const {
// it is important to call this before Enqueue
bool cycle_state = this->GetRingManager().GetProducerCycleState();
TRB *trb = this->GetRingManager().EnqueueTRB(chain);
R_UNLESS(trb != nullptr, xusb::ResultTransferRingFull());
*borrow = TRBBorrow(
&this->GetRingManager(),
trb,
cycle_state);
return ResultSuccess();
}
Result TransferNormal(void *buffer, size_t size, TRB **trb_out) const;
void Halt() const;
void ClearHalt() const;
void Pause() const;
void ClearPause() const;
void Reload() const;
void RingDoorbell() const;
void RingDoorbell(uint16_t stream_id) const;
TRBBorrow AcceptCompletedTRB(TransferEventTRB *trb) const;
inline uint8_t GetIndex() const {
return this->no;
}
void ResetAndReloadTransferRing() const;
void DumpTransferRingForDebug() const;
private:
inline EndpointContext &GetContext() const;
inline EndpointRingManager &GetRingManager() const;
void Initialize(TRB *ring, size_t ring_size);
uint8_t no;
Endpoints &endpoints;
};
class Endpoints {
public:
static const size_t NR_EPS = 32;
private:
friend class EndpointAccessor;
// structure-of-arrays instead of array-of-structures
// to improve alignment.
EndpointRingManager rings[NR_EPS];
EndpointContext contexts[NR_EPS];
public:
void ClearNonControlEndpoints();
void SetEP0DeviceAddress(uint16_t addr);
constexpr inline EndpointAccessor operator[](EpAddr addr) {
return EndpointAccessor(addr.no * 2 + (addr.direction == Dir::In ? 1 : 0), *this);
};
constexpr inline EndpointAccessor operator[](int no) {
return EndpointAccessor(no, *this);
};
inline uintptr_t GetContextsForHardware() {
return (uintptr_t) contexts;
}
};
inline EndpointContext &EndpointAccessor::GetContext() const {
return this->endpoints.contexts[this->no];
}
inline EndpointRingManager &EndpointAccessor::GetRingManager() const {
return this->endpoints.rings[this->no];
}
extern Endpoints endpoints;
static_assert(sizeof(EndpointContext) == 64, "Endpoint context should be 64 bytes");
} // namespace xusb
} // namespace ams

View file

@ -0,0 +1,120 @@
/*
* 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 "xusb_event_ring.h"
#include "xusb.h"
#include "xusb_control.h"
#include "xusb_endpoint.h"
#include "../reg/reg_xusb_dev.h"
extern "C" {
#include "../lib/log.h"
#include "../utils.h"
}
#include<array>
#include<string.h>
namespace ams {
namespace xusb {
void EventRing::Initialize() {
memset((void*) &storage[0], 0, sizeof(storage));
if (((uintptr_t) &storage[0] & 0b1111) != 0) {
fatal_error("xusb initialization failed: event ring storage is misaligned\n");
}
this->consumer_dequeue_ptr = &this->storage[0];
this->consumer_cycle_state = true;
t210::T_XUSB_DEV_XHCI
.ERST0BALO(0).ADDRLO.Set((uint32_t) &this->storage[0]).Commit()
.ERST0BAHI(0).ADDRHI.Set(0).Commit()
.ERST1BALO(0).ADDRLO.Set((uint32_t) &this->storage[0x10]).Commit()
.ERST1BAHI(0).ADDRHI.Set(0).Commit()
.ERSTSZ(0)
.ERST0SZ.Set(0x10)
.ERST1SZ.Set(0x10)
.Commit()
.EREPLO(0)
.ADDRLO.Set((uint32_t) &this->storage[0])
.ECS.Set(this->consumer_cycle_state)
.Commit()
.EREPHI(0).ADDRHI.Set(0).Commit()
.ERDPLO(0).ADDRLO.Set((uint32_t) &this->storage[0]).Commit()
.ERDPHI(0).ADDRHI.Set(0).Commit();
}
void EventRing::Process() {
auto st = t210::T_XUSB_DEV_XHCI.ST();
if (st.IP) {
/* Clear interrupt pending. */
st.IP.Clear().Commit();
}
while (this->consumer_dequeue_ptr->abstract.GetCycle() == this->consumer_cycle_state) {
switch(this->consumer_dequeue_ptr->abstract.GetType()) {
case TRBType::SetupPacketEvent:
control::ProcessSetupPacketEvent(&this->consumer_dequeue_ptr->setup_packet_event);
break;
case TRBType::PortStatusChangeEvent:
control::ProcessPortStatusChangeEvent(&this->consumer_dequeue_ptr->abstract);
break;
case TRBType::TransferEvent: {
TransferEventTRB *te = &this->consumer_dequeue_ptr->transfer_event;
TRBBorrow completed_transfer = xusb::endpoints[te->ep_id].AcceptCompletedTRB(te);
if (te->ep_id == 0) {
xusb::control::ProcessEP0TransferEvent(te, std::move(completed_transfer));
} else {
xusb::GetCurrentGadget()->ProcessTransferEvent(te, std::move(completed_transfer));
}
break; }
default:
print(SCREEN_LOG_LEVEL_WARNING, "got unrecognized TRB type on event ring: %d\n", (int) this->consumer_dequeue_ptr->abstract.GetType());
break;
}
if ((size_t) (this->consumer_dequeue_ptr - &this->storage[0]) == std::size(this->storage) - 1) {
this->consumer_dequeue_ptr = &this->storage[0];
this->consumer_cycle_state = !this->consumer_cycle_state;
} else {
this->consumer_dequeue_ptr++;
}
/* Some situations cause the event ring to continue filling up while
we're processing it, such as if we are submitting transfers and
they are completing before we fully yield out to the main
loop. */
t210::T_XUSB_DEV_XHCI.ERDPLO()
.ADDRLO.Set((uint32_t) this->consumer_dequeue_ptr)
.Commit();
}
t210::T_XUSB_DEV_XHCI.ERDPLO()
.EHB.Clear()
.ADDRLO.Set((uint32_t) this->consumer_dequeue_ptr)
.Commit();
}
} // namespace xusb
} // namespace ams

View file

@ -0,0 +1,38 @@
/*
* 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 "xusb_trb.h"
namespace ams {
namespace xusb {
class EventRing {
public:
void Initialize();
void Process();
private:
TRB storage[0x20];
TRB *consumer_dequeue_ptr;
bool consumer_cycle_state;
} __attribute__((aligned(16)));
} // namespace xusb
} // namespace ams

View file

@ -0,0 +1,287 @@
/*
* 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 "xusb.h"
#include "xusb_control.h"
#include "xusb_endpoint.h"
#include<array>
extern "C" {
#include "../lib/log.h"
#include "../utils.h"
#include "../btn.h"
}
namespace ams {
namespace xusb {
namespace test {
USB_DECLARE_STRING_DESCRIPTOR(langid, u"\x0409"); // English (US)
USB_DECLARE_STRING_DESCRIPTOR(manu, u"Test Manufacturer");
USB_DECLARE_STRING_DESCRIPTOR(product, u"Test Product");
USB_DECLARE_STRING_DESCRIPTOR(serial, u"Test Serial Number");
USB_DECLARE_STRING_DESCRIPTOR(configuration, u"Test Configuration");
USB_DECLARE_STRING_DESCRIPTOR(interface, u"Test Interface");
static constexpr usb::StringDescriptorIndexer<SD_langid,
SD_manu,
SD_product,
SD_serial,
SD_configuration,
SD_interface> sd_indexer;
static xusb::TRB in_ring[5];
static xusb::TRB out_ring[5];
static char buffer[513] __attribute__((section(".dram"))) = {0};
class XusbTestGadget : public Gadget {
public:
static constexpr usb::DeviceDescriptor device_descriptor = {
{
.bLength = sizeof(usb::DeviceDescriptor),
.bDescriptorType = usb::DescriptorType::DEVICE,
},
{
.bcdUSB = 0x210,
.bDeviceClass = 0,
.bDeviceSubClass = 0,
.bDeviceProtocol = 0,
.bMaxPacketSize0 = 64,
.idVendor = 0x1209,
.idProduct = 0x8b00,
.bcdDevice = 0x100,
.iManufacturer = sd_indexer.manu,
.iProduct = sd_indexer.product,
.iSerialNumber = sd_indexer.serial,
.bNumConfigurations = 1,
}
};
static constexpr usb::DeviceQualifierDescriptor device_qualifier_descriptor = {
{
.bLength = sizeof(usb::DeviceQualifierDescriptor),
.bDescriptorType = usb::DescriptorType::DEVICE_QUALIFIER,
},
{
.bcdUSB = 0x210,
.bDeviceClass = 0,
.bDeviceSubClass = 0,
.bDeviceProtocol = 0,
.bMaxPacketSize0 = 64,
.bNumConfigurations = 1,
.bReserved = 0,
}
};
static const struct WholeConfigurationDescriptor {
usb::ConfigurationDescriptor configuration_descriptor = {
{
.bLength = sizeof(usb::ConfigurationDescriptor),
.bDescriptorType = usb::DescriptorType::CONFIGURATION,
},
{
.wTotalLength = sizeof(WholeConfigurationDescriptor),
.bNumInterfaces = 1,
.bConfigurationValue = 1,
.iConfiguration = 4,
.bmAttributes = 0x80,
.bMaxPower = 0,
}
};
usb::InterfaceDescriptor interface_descriptor {
{
.bLength = sizeof(usb::InterfaceDescriptor),
.bDescriptorType = usb::DescriptorType::INTERFACE,
},
{
.bInterfaceNumber = 0,
.bAlternateSetting = 0,
.bNumEndpoints = 2,
.bInterfaceClass = 0xff,
.bInterfaceSubClass = 0xff,
.bInterfaceProtocol = 0xff,
.iInterface = 5,
}
};
usb::EndpointDescriptor endpoint_in_descriptor {
{
.bLength = sizeof(usb::EndpointDescriptor),
.bDescriptorType = usb::DescriptorType::ENDPOINT,
},
{
.bEndpointAddress = 0x81,
.bmAttributes = 0x2,
.wMaxPacketSize = 512,
.bInterval = 1,
}
};
usb::EndpointDescriptor endpoint_out_descriptor {
{
.bLength = sizeof(usb::EndpointDescriptor),
.bDescriptorType = usb::DescriptorType::ENDPOINT,
},
{
.bEndpointAddress = 0x01,
.bmAttributes = 0x2,
.wMaxPacketSize = 512,
.bInterval = 1,
}
};
} __attribute__((packed)) whole_configuration_descriptor;
virtual Result GetDeviceDescriptor(uint8_t index, const usb::DeviceDescriptor **descriptor, uint16_t *length) override {
R_UNLESS(index == 0, xusb::ResultInvalidDescriptorIndex());
*descriptor = &device_descriptor;
*length = sizeof(device_descriptor);
return ResultSuccess();
}
virtual Result GetDeviceQualifierDescriptor(uint8_t index, const usb::DeviceQualifierDescriptor **descriptor, uint16_t *length) override {
R_UNLESS(index == 0, xusb::ResultInvalidDescriptorIndex());
*descriptor = &device_qualifier_descriptor;
*length = sizeof(device_qualifier_descriptor);
return ResultSuccess();
}
virtual Result GetConfigurationDescriptor(uint8_t index, const usb::ConfigurationDescriptor **descriptor, uint16_t *length) override {
R_UNLESS(index == 0, xusb::ResultInvalidDescriptorIndex());
*descriptor = &whole_configuration_descriptor.configuration_descriptor;
*length = sizeof(whole_configuration_descriptor);
return ResultSuccess();
}
virtual Result GetBOSDescriptor(const usb::BOSDescriptor **descriptor, uint16_t *length) override {
return ResultUnknownDescriptorType();
}
virtual Result GetStringDescriptor(uint8_t index, uint16_t language, const usb::CommonDescriptor **descriptor, uint16_t *length) override {
R_UNLESS(language == 0x0409 || (language == 0 && index == 0), xusb::ResultInvalidDescriptorIndex());
R_UNLESS(sd_indexer.GetStringDescriptor(index, language, descriptor, length), xusb::ResultInvalidDescriptorIndex());
return ResultSuccess();
}
static constexpr auto out = xusb::endpoints[xusb::EpAddr {1, xusb::Dir::Out}];
static constexpr auto in = xusb::endpoints[xusb::EpAddr {1, xusb::Dir::In}];
virtual Result SetConfiguration(uint16_t configuration) override {
R_UNLESS(configuration <= 1, xusb::ResultInvalidConfiguration());
xusb::endpoints.ClearNonControlEndpoints();
out.Initialize(whole_configuration_descriptor.endpoint_out_descriptor, out_ring, std::size(out_ring));
in.Initialize(whole_configuration_descriptor.endpoint_in_descriptor, in_ring, std::size(in_ring));
print(SCREEN_LOG_LEVEL_DEBUG, "configured and enabled endpoints!\n");
return ResultSuccess();
}
virtual Result Deconfigure() override {
out.Disable();
in.Disable();
return ResultSuccess();
}
TRB *last_out_trb = nullptr;
TRB *last_in_trb = nullptr;
Result SubmitOutTRB() {
TRBBorrow trb;
R_TRY(out.EnqueueTRB(&trb));
print(SCREEN_LOG_LEVEL_DEBUG, "submitting out trb %p\n", &*trb);
trb->transfer.InitializeNormal((void*) buffer, sizeof(buffer)-1);
trb->transfer.interrupt_on_completion = true;
trb->transfer.interrupt_on_short_packet = true;
trb.Release();
last_out_trb = &*trb;
out.RingDoorbell();
return ResultSuccess();
}
Result SubmitInTRB(size_t length) {
TRBBorrow trb;
R_TRY(in.EnqueueTRB(&trb));
print(SCREEN_LOG_LEVEL_DEBUG, "submitting in trb %p\n", &*trb);
trb->transfer.InitializeNormal((void*) buffer, length);
trb->transfer.interrupt_on_completion = true;
trb->transfer.interrupt_on_short_packet = true;
trb.Release();
last_in_trb = &*trb;
in.RingDoorbell();
return ResultSuccess();
}
virtual void PostConfigure() override {
SubmitOutTRB();
}
virtual void ProcessTransferEvent(TransferEventTRB *event, TRBBorrow transfer) override {
print(SCREEN_LOG_LEVEL_DEBUG, "gadget got transfer event for ep %d\n", event->ep_id);
if (event->ep_id == out.GetIndex()) {
if (&*transfer == last_out_trb) {
print(SCREEN_LOG_LEVEL_DEBUG, " out transfer completed: %d bytes remain\n", event->trb_transfer_length);
buffer[transfer->transfer.transfer_length - event->trb_transfer_length] = 0;
print(SCREEN_LOG_LEVEL_DEBUG, " \"%s\"\n", buffer);
SubmitInTRB(transfer->transfer.transfer_length - event->trb_transfer_length);
} else {
print(SCREEN_LOG_LEVEL_DEBUG, " got completion for unexpected OUT trb (got %p, expected %p)\n", &*transfer, last_out_trb);
}
} else if (event->ep_id == in.GetIndex()) {
if (&*transfer == last_in_trb) {
SubmitOutTRB();
} else {
print(SCREEN_LOG_LEVEL_DEBUG, " got completion for unexpected IN trb (got %p, expected %p)\n", &*transfer, last_out_trb);
}
}
}
};
static XusbTestGadget test_gadget;
Gadget &GetTestGadget() {
return test_gadget;
}
} // namespace test
} // namespace xusb
} // namespace ams

View file

@ -0,0 +1,29 @@
/*
* 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 "xusb.h"
namespace xusb {
namespace test {
Gadget &GetTestGadget();
} // namespace test
} // namespace xusb

View file

@ -0,0 +1,185 @@
/*
* 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 "usb_types.h"
#include<stdint.h>
#include<string.h>
namespace ams {
namespace xusb {
enum class TRBType : uint8_t {
// transfer TRBs
Normal = 1,
SetupStage = 2,
DataStage = 3,
StatusStage = 4,
Isoch = 5,
Link = 6,
EventData = 7,
NoOp = 8,
// event TRBs
TransferEvent = 32,
PortStatusChangeEvent = 34,
SetupPacketEvent = 63, // vendor defined
};
union TRB;
struct AbstractTRB {
uint32_t field0;
uint32_t field1;
uint32_t field2;
uint32_t field3;
inline void Clear() {
// leave cycle bit so hardware doesn't accidentlly consume this TRB
this->field0 = 0;
this->field1 = 0;
this->field2 = 0;
this->field3&= 1;
}
inline bool GetCycle() {
return this->field3 & 1;
}
inline void SetCycle(bool cycle) {
this->field3&= ~1;
this->field3|= cycle;
}
inline TRBType GetType() {
return (TRBType) ((this->field3 >> 10) & 0b111111);
}
inline void SetType(TRBType type) {
this->field3&= ~(0b111111 << 10);
this->field3|= (((uint32_t) type) & 0b111111) << 10;
}
} __attribute__((aligned(16)));
struct LinkTRB : protected AbstractTRB {
inline void Initialize(TRB *link) {
AbstractTRB::SetType(TRBType::Link);
this->field0 = (uint32_t) link;
}
inline void SetToggleCycle(bool tc) {
this->field3&= ~2;
this->field3|= tc << 1;
}
inline void SetChain(bool chain) {
this->field3&= ~0x10;
this->field3|= chain << 4;
}
} __attribute__((aligned(16)));
struct TransferTRB {
uint32_t data_ptr_lo;
uint32_t data_ptr_hi;
uint32_t transfer_length:17;
uint32_t td_size:5;
uint32_t interrupter_target:10;
uint32_t :0;
uint32_t cycle:1;
uint32_t evaluate_next_trb:1;
uint32_t interrupt_on_short_packet:1;
uint32_t no_snoop:1;
uint32_t chain_bit:1;
uint32_t interrupt_on_completion:1;
uint32_t immediate_data:1;
uint32_t :2; // reserved
uint32_t block_event_interrupt:1;
uint32_t trb_type:6;
uint32_t dir:1;
uint32_t :15;
inline void InitializeNoOp() {
this->trb_type = (uint32_t) TRBType::NoOp;
}
inline void InitializeNormal(void *data, size_t length) {
this->data_ptr_lo = (uint32_t) data;
this->transfer_length = length;
this->trb_type = (uint32_t) TRBType::Normal;
}
inline void InitializeDataStage(bool dir, void *data, size_t length) {
this->data_ptr_lo = (uint32_t) data;
this->transfer_length = length;
this->trb_type = (uint32_t) TRBType::DataStage;
this->dir = dir;
}
// see Table 4-7 on page 213 of XHCI spec
inline void InitializeStatusStage(bool dir) {
this->trb_type = (uint32_t) TRBType::StatusStage;
this->dir = dir;
}
} __attribute__((aligned(16)));
struct TransferEventTRB {
uint32_t trb_ptr_lo;
uint32_t trb_ptr_hi;
uint32_t trb_transfer_length:24;
uint32_t completion_code:8;
uint32_t cycle:1;
uint32_t :1;
uint32_t ed:1;
uint32_t :7;
uint32_t trb_type:6;
uint32_t ep_id:5;
inline TRB *GetSourceTRB() {
return (TRB*) trb_ptr_lo;
}
} __attribute__((aligned(16)));
struct SetupPacketEventTRB {
usb::SetupPacket packet;
uint16_t seq_num;
uint16_t field2;
uint32_t field3;
inline bool GetEventDataFlag() {
return (field3 & 0b100) != 0;
}
} __attribute__((aligned(16)));
union TRB {
public:
AbstractTRB abstract;
LinkTRB link;
TransferTRB transfer;
TransferEventTRB transfer_event;
SetupPacketEventTRB setup_packet_event;
} __attribute__((aligned(16)));
static_assert(sizeof(TRB) == 16, "TRB should be 16 bytes");
} // namespace xusb
} // namespace ams

View file

@ -27,6 +27,7 @@
#include "lib/log.h"
#include "lib/vsprintf.h"
#include "display/video_fb.h"
#include "fastboot/fastboot.h"
extern void (*__program_exit_callback)(int rc);
@ -43,7 +44,10 @@ static char g_bct0_buffer[BCTO_MAX_SIZE] __attribute__((section(".dram"))) = {0}
"debugmode = 1\n"\
"debugmode_user = 0\n"\
"disable_user_exception_handlers = 0\n"\
"[stratosphere]\n"
"[stratosphere]\n" \
"[fastboot]\n" \
"force_enable = 0\n" \
"button_timeout_ms = 0\n"
static const char *load_config(void) {
if (!read_from_file(g_bct0_buffer, BCTO_MAX_SIZE, "atmosphere/config/BCT.ini")) {
@ -130,8 +134,18 @@ int main(void) {
/* Assert that our configuration is sane. */
stage2_validate_config(&bct0);
/* Load the loader payload into DRAM. */
stage2_load(&bct0);
/* Try to enter fastboot, if we are configured to. */
switch(fastboot_enter(&bct0)) {
case FASTBOOT_INVALID:
case FASTBOOT_SKIPPED:
case FASTBOOT_LOAD_STAGE2:
/* Load the loader payload into DRAM. */
stage2_load(&bct0);
break;
case FASTBOOT_CHAINLOAD:
print(SCREEN_LOG_LEVEL_DEBUG, "fastboot: chainloading\n");
break;
}
/* Setup argument data. */
strcpy(g_chainloader_arg_data, bct0.stage2_path);

View file

@ -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 "reg_util.h"
namespace t210 {
const struct APBDEV_PMC {
static const uintptr_t base_addr = 0x7000e400;
using Peripheral = APBDEV_PMC;
BEGIN_DEFINE_REGISTER(USB_AO_0, uint32_t, 0xf0)
DEFINE_RW_FIELD(ID_PD_P0, 3)
DEFINE_RW_FIELD(VBUS_WAKEUP_PD_P0, 2)
END_DEFINE_REGISTER(USB_AO_0)
} APBDEV_PMC;
} // namespace t210

View file

@ -0,0 +1,221 @@
/*
* 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 "reg_util.h"
namespace t210 {
const struct CLK_RST_CONTROLLER {
static const uintptr_t base_addr = 0x60006000;
using Peripheral = CLK_RST_CONTROLLER;
enum class OscFreq {
OSC13 = 0,
OSC16P8 = 1,
OSC19P2 = 4,
OSC38P4 = 5,
OSC12 = 8,
OSC48 = 9,
OSC26 = 12,
};
BEGIN_DEFINE_REGISTER(RST_DEVICES_U_0, uint32_t, 0xc)
DEFINE_RW_FIELD(SWR_XUSB_DEV_RST, 31)
DEFINE_RW_FIELD(SWR_XUSB_HOST_RST, 25)
END_DEFINE_REGISTER(RST_DEVICES_U_0)
BEGIN_DEFINE_REGISTER(CLK_OUT_ENB_U_0, uint32_t, 0x18)
DEFINE_RW_FIELD(CLK_ENB_XUSB_DEV, 31)
DEFINE_RW_FIELD(CLK_ENB_XUSB_HOST, 25)
END_DEFINE_REGISTER(CLK_OUT_ENB_U_0)
BEGIN_DEFINE_REGISTER(OSC_CTRL_0, uint32_t, 0x50)
DEFINE_RW_FIELD(OSC_FREQ, 28, 31, OscFreq)
END_DEFINE_REGISTER(OSC_CTRL_0)
BEGIN_DEFINE_REGISTER(PLLU_BASE_0, uint32_t, 0xc0)
DEFINE_RW_FIELD(PLLU_BYPASS, 31)
DEFINE_RW_FIELD(PLLU_ENABLE, 30)
DEFINE_RW_FIELD(PLLU_REF_DIS, 29)
DEFINE_RO_FIELD(PLLU_LOCK, 27)
DEFINE_RW_FIELD(PLLU_CLKENABLE_48M, 25)
DEFINE_RW_FIELD(PLLU_OVERRIDE, 24)
DEFINE_RW_FIELD(PLLU_CLKENABLE_ICUSB, 23)
DEFINE_RW_FIELD(PLLU_CLKENABLE_HSIC, 22)
DEFINE_RW_FIELD(PLLU_CLKENABLE_USB, 21)
DEFINE_RW_FIELD(PLLU_DIVP, 16, 20)
DEFINE_RW_FIELD(PLLU_DIVN, 8, 15)
DEFINE_RW_FIELD(PLLU_DIVM, 0, 7)
END_DEFINE_REGISTER(PLLU_BASE_0)
BEGIN_DEFINE_REGISTER(PLLU_OUTA_0, uint32_t, 0xc4)
DEFINE_RW_FIELD(PLLU_OUT2_RATIO, 24, 21)
DEFINE_RW_FIELD(PLLU_OUT2_OVRRIDE, 18)
DEFINE_RW_FIELD(PLLU_OUT2_CLKEN, 17)
DEFINE_RW_FIELD(PLLU_OUT2_RSTEN, 16)
DEFINE_RW_FIELD(PLLU_OUT1_RATIO, 8, 15)
DEFINE_RW_FIELD(PLLU_OUT1_OVRRIDE, 2)
DEFINE_RW_FIELD(PLLU_OUT1_CLKEN, 1)
DEFINE_RW_FIELD(PLLU_OUT1_RSTN, 0)
END_DEFINE_REGISTER(PLLU_OUTA_0)
BEGIN_DEFINE_REGISTER(PLLU_MISC_0, uint32_t, 0xcc)
DEFINE_RW_FIELD(PLLU_IDDQ, 31)
DEFINE_RO_FIELD(PLLU_FREQLOCK, 30)
DEFINE_RW_FIELD(PLLU_EN_LCKDET, 29)
BEGIN_DEFINE_RW_SYMBOLIC_FIELD(PLLU_PTS, 27, 28)
DEFINE_RW_SYMBOLIC_VALUE(DISABLE, 0)
DEFINE_RW_SYMBOLIC_VALUE(VCO, 1)
DEFINE_RW_SYMBOLIC_VALUE(FO, 2)
DEFINE_RW_SYMBOLIC_VALUE(FO_ICUSB, 3)
END_DEFINE_SYMBOLIC_FIELD(PLLU_PTS)
DEFINE_RW_FIELD(PLLU_KCP, 25, 26)
DEFINE_RW_FIELD(PLLU_KVCO, 24)
DEFINE_RW_FIELD(PLLU_SETUP, 0, 23)
END_DEFINE_REGISTER(PLLU_MISC_0)
BEGIN_DEFINE_REGISTER(CLK_OUT_ENB_Y_0, uint32_t, 0x298)
DEFINE_RW_FIELD(CLK_ENB_USB2_TRK, 18)
END_DEFINE_REGISTER(CLK_OUT_ENB_Y_0)
BEGIN_DEFINE_REGISTER(RST_DEVICES_W_0, uint32_t, 0x35c)
DEFINE_RW_FIELD(SWR_XUSB_SS_RST, 28)
DEFINE_RW_FIELD(SWR_DVFS_RST, 27)
DEFINE_RW_FIELD(SWR_ENTROPY_RST, 21)
DEFINE_RW_FIELD(SWR_XUSB_PADCTL_RST, 14)
DEFINE_RW_FIELD(SWR_RESERVED10_RST, 13)
DEFINE_RW_FIELD(SWR_RESERVED9_RST, 12)
// SWR_RESERVED8_RST conspicuously missing from TRM
DEFINE_RW_FIELD(SWR_RESERVED7_RST, 10)
DEFINE_RW_FIELD(SWR_RESERVED6_RST, 9)
DEFINE_RW_FIELD(SWR_CEC_RST, 8)
DEFINE_RW_FIELD(SWR_RESERVED5_RST, 7)
DEFINE_RW_FIELD(SWR_RESERVED4_RST, 6)
DEFINE_RW_FIELD(SWR_RESERVED3_RST, 5)
DEFINE_RW_FIELD(SWR_RESERVED2_RST, 4)
DEFINE_RW_FIELD(SWR_RESERVED1_RST, 3)
DEFINE_RW_FIELD(SWR_RESERVED0_RST, 2)
DEFINE_RW_FIELD(SWR_SATACOLD_RST, 1)
END_DEFINE_REGISTER(RST_DEVICES_W_0)
BEGIN_DEFINE_REGISTER(CLK_OUT_ENB_W_0, uint32_t, 0x364)
DEFINE_RW_FIELD(CLK_ENB_XUSB_SS, 28)
DEFINE_RW_FIELD(CLK_ENB_XUSB, 15)
END_DEFINE_REGISTER(CLK_OUT_ENB_W_0)
BEGIN_DEFINE_REGISTER(UTMIP_PLL_CFG0_0, uint32_t, 0x480)
DEFINE_RW_FIELD(UTMIP_PLL_VREG_CTRL, 27, 28)
DEFINE_RW_FIELD(UTMIP_PLL_KCP, 25, 26)
DEFINE_RW_FIELD(UTMIP_PLL_NDIV, 16, 23)
DEFINE_RW_FIELD(UTMIP_PLL_MDIV, 8, 15)
DEFINE_RW_FIELD(UTMIP_PLL_LOCK_OVR, 2)
DEFINE_RW_FIELD(UTMIP_PLL_RESERVED2, 1)
DEFINE_RW_FIELD(UTMIP_PLL_EN_LCKDET, 0)
END_DEFINE_REGISTER(UTMIP_PLL_CFG0_0)
BEGIN_DEFINE_REGISTER(UTMIP_PLL_CFG1_0, uint32_t, 0x484)
DEFINE_RW_FIELD(UTMIP_PLLU_ENABLE_DLY_COUNT, 27, 31)
DEFINE_RW_FIELD(UTMIP_PLL_RSVD, 18, 26)
DEFINE_RW_FIELD(UTMIP_FORCE_PLLU_POWERUP, 17)
DEFINE_RW_FIELD(UTMIP_FORCE_PLLU_POWERDOWN, 16)
DEFINE_RW_FIELD(UTMIP_FORCE_PLL_ENABLE_POWERUP, 15)
DEFINE_RW_FIELD(UTMIP_FORCE_PLL_ENABLE_POWERDOWN, 14)
DEFINE_RW_FIELD(UTMIP_FORCE_PLL_ACTIVE_POWERUP, 13)
DEFINE_RW_FIELD(UTMIP_FORCE_PLL_ACTIVE_POWERDOWN, 12)
DEFINE_RW_FIELD(UTMIP_XTAL_FREQ_COUNT, 0, 11)
END_DEFINE_REGISTER(UTMIP_PLL_CFG1_0)
BEGIN_DEFINE_REGISTER(UTMIP_PLL_CFG2_0, uint32_t, 0x488)
DEFINE_RW_FIELD(UTMIP_PHY_XTAL_CLOCKEN, 30)
DEFINE_RW_FIELD(UTMIP_FORCE_PD_RESERVED, 26, 29)
DEFINE_RW_FIELD(UTMIP_FORCE_PD_SAMP_D_POWERUP, 25)
DEFINE_RW_FIELD(UTMIP_FORCE_PD_SAMP_D_POWERDOWN, 24)
DEFINE_RW_FIELD(UTMIP_PLL_ACTIVE_DLY_COUNT, 18, 23)
DEFINE_RW_FIELD(UTMIP_PLLU_STABLE_COUNT, 6, 17)
DEFINE_RW_FIELD(UTMIP_FORCE_PD_SAMP_C_POWERUP, 5)
DEFINE_RW_FIELD(UTMIP_FORCE_PD_SAMP_C_POWERDOWN, 4)
DEFINE_RW_FIELD(UTMIP_FORCE_PD_SAMP_B_POWERUP, 3)
DEFINE_RW_FIELD(UTMIP_FORCE_PD_SAMP_B_POWERDOWN, 2)
DEFINE_RW_FIELD(UTMIP_FORCE_PD_SAMP_A_POWERUP, 1)
DEFINE_RW_FIELD(UTMIP_FORCE_PD_SAMP_A_POWERDOWN, 0)
END_DEFINE_REGISTER(UTMIP_PLL_CFG2_0)
BEGIN_DEFINE_REGISTER(UTMIP_PLL_CFG3_0, uint32_t, 0x4c0)
DEFINE_RW_FIELD(UTMIP_PLL_REF_SRC_SEL, 26)
DEFINE_RW_FIELD(UTMIP_PLL_REF_DIS, 25)
DEFINE_RW_FIELD(UTMIP_PLL_PTS, 24)
DEFINE_RW_FIELD(UTMIP_PLL_SETUP, 0, 23) // "Debug control bits."
END_DEFINE_REGISTER(UTMIP_PLL_CFG3_0)
BEGIN_DEFINE_REGISTER(UTMIPLL_HW_PWRDN_CFG0_0, uint32_t, 0x52c)
DEFINE_RO_FIELD(UTMIPLL_LOCK, 31)
DEFINE_RO_FIELD(UTMIPLL_SEQ_STATE, 26, 27)
DEFINE_RW_FIELD(UTMIPLL_SEQ_START_STATE, 25)
DEFINE_RW_FIELD(UTMIPLL_SEQ_ENABLE, 24)
DEFINE_RW_FIELD(UTMIPLL_IDDQ_PD_INCLUDE, 7)
DEFINE_RW_FIELD(UTMIPLL_USE_LOCKDET, 6)
DEFINE_RW_FIELD(UTMIPLL_SEQ_RESET_INPUT_VALUE, 5)
DEFINE_RW_FIELD(UTMIPLL_SEQ_IN_SWCTL, 4)
DEFINE_RW_FIELD(UTMIPLL_CLK_ENABLE_OVERRIDE_VALUE, 3)
DEFINE_RW_FIELD(UTMIPLL_CLK_ENABLE_SWCTL, 2)
DEFINE_RW_FIELD(UTMIPLL_IDDQ_OVERRIDE_VALUE, 1)
DEFINE_RW_FIELD(UTMIPLL_IDDQ_SWCTL, 0)
END_DEFINE_REGISTER(UTMIPLL_HW_PWRDN_CFG0_0)
BEGIN_DEFINE_REGISTER(CLK_SOURCE_XUSB_FS_0, uint32_t, 0x608)
BEGIN_DEFINE_RW_SYMBOLIC_FIELD(XUSB_FS_CLK_SRC, 29, 31)
DEFINE_RW_SYMBOLIC_VALUE(CLK_M, 0)
DEFINE_RW_SYMBOLIC_VALUE(FO_48M, 2)
DEFINE_RW_SYMBOLIC_VALUE(PLLP_OUT0, 4)
DEFINE_RW_SYMBOLIC_VALUE(HSIC_480, 6)
END_DEFINE_SYMBOLIC_FIELD(XUSB_FS_CLK_SRC)
DEFINE_RW_FIELD(XUSB_FS_CLK_DIVISOR, 0, 7)
END_DEFINE_REGISTER(CLK_SOURCE_XUSB_FS_0)
BEGIN_DEFINE_REGISTER(CLK_SOURCE_XUSB_CORE_DEV_0, uint32_t, 0x60c)
BEGIN_DEFINE_RW_SYMBOLIC_FIELD(XUSB_CORE_DEV_CLK_SRC, 29, 31)
DEFINE_RW_SYMBOLIC_VALUE(CLK_M, 0)
DEFINE_RW_SYMBOLIC_VALUE(PLLP_OUT0, 1)
DEFINE_RW_SYMBOLIC_VALUE(PLLREFE_CLKOUT, 5)
END_DEFINE_SYMBOLIC_FIELD(XUSB_CORE_DEV_CLK_SRC)
DEFINE_RW_FIELD(XUSB_CORE_DEV_CLK_DIVISOR, 0, 7)
END_DEFINE_REGISTER(CLK_SOURCE_XUSB_CORE_DEV_0)
BEGIN_DEFINE_REGISTER(CLK_SOURCE_XUSB_SS_0, uint32_t, 0x610)
BEGIN_DEFINE_RW_SYMBOLIC_FIELD(XUSB_SS_CLK_SRC, 29, 31)
DEFINE_RW_SYMBOLIC_VALUE(CLK_M, 0)
DEFINE_RW_SYMBOLIC_VALUE(PLLREFE_CLKOUT, 1)
DEFINE_RW_SYMBOLIC_VALUE(CLK_S, 2)
DEFINE_RW_SYMBOLIC_VALUE(HSIC_480, 3)
END_DEFINE_SYMBOLIC_FIELD(XUSB_SS_CLK_SRC)
DEFINE_RW_FIELD(XUSB_HS_HSICP_CLK_SEL, 26)
DEFINE_RW_FIELD(XUSB_HS_CLK_BYPASS_SWITCH, 25)
DEFINE_RW_FIELD(XUSB_SS_CLK_BYPASS_SWITCH, 24)
DEFINE_RW_FIELD(XUSB_SS_CLK_DIVISOR, 0, 7)
END_DEFINE_REGISTER(CLK_SOURCE_XUSB_SS_0)
BEGIN_DEFINE_REGISTER(CLK_SOURCE_USB2_HSIC_TRK_0, uint32_t, 0x6cc)
DEFINE_RW_FIELD(USB2_HSIC_TRK_CLK_DIVISOR, 0, 7)
END_DEFINE_REGISTER(CLK_SOURCE_USB2_HSIC_TRK_0)
} CLK_RST_CONTROLLER;
} // namespace t210

View file

@ -0,0 +1,38 @@
/*
* 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 "reg_util.h"
namespace t210 {
const struct FUSE {
static const uintptr_t base_addr = 0x7000f800;
using Peripheral = FUSE;
BEGIN_DEFINE_REGISTER(SKU_USB_CALIB, uint32_t, 0x1f0)
DEFINE_RO_FIELD(TERM_RANGE_ADJ, 7, 10) // TODO: check?
DEFINE_RO_FIELD(HS_CURR_LEVEL, 0, 5) // TODO: check?
END_DEFINE_REGISTER(SKU_USB_CALIB)
BEGIN_DEFINE_REGISTER(USB_CALIB_EXT, uint32_t, 0x350)
DEFINE_RO_FIELD(RPD_CTRL, 0, 5)
END_DEFINE_REGISTER(USB_CALIB_EXT)
} FUSE;
} // namespace t210

View file

@ -0,0 +1,495 @@
/*
* 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<stdint.h>
template<typename Peripheral, typename _Base, uintptr_t _offset>
class RegisterAccessor {
public:
using Base = _Base;
static const uintptr_t offset = _offset;
__attribute__((always_inline)) RegisterAccessor(const Peripheral &p) :
peripheral(p),
ptr(reinterpret_cast<volatile Base *>(p.base_addr + _offset)),
read_value(*ptr),
write_value(read_value) {
}
__attribute__((always_inline)) RegisterAccessor(const Peripheral &p, Base value) :
peripheral(p),
ptr(reinterpret_cast<volatile Base *>(p.base_addr + _offset)),
read_value(value),
write_value(value) {
}
__attribute__((always_inline)) const Peripheral &Commit() {
*ptr = write_value;
return peripheral;
}
const Peripheral &peripheral;
volatile Base *ptr;
Base read_value;
Base write_value;
};
struct rw1c_marker {};
template<typename Peripheral, typename _Base>
class BitArrayRegisterAccessor {
public:
using Base = _Base;
__attribute__((always_inline)) BitArrayRegisterAccessor(const Peripheral &p, uintptr_t offset) :
peripheral(p),
ptr(reinterpret_cast<volatile Base *>(p.base_addr + offset)),
read_value(*ptr),
write_value(read_value) {
}
__attribute__((always_inline)) BitArrayRegisterAccessor(const Peripheral &p, uintptr_t offset, Base value) :
peripheral(p),
ptr(reinterpret_cast<volatile Base *>(p.base_addr + offset)),
read_value(value),
write_value(value) {
}
__attribute__((always_inline)) BitArrayRegisterAccessor(const Peripheral &p, uintptr_t offset, rw1c_marker _marker) :
peripheral(p),
ptr(reinterpret_cast<volatile Base *>(p.base_addr + offset)),
read_value(*ptr),
write_value(0) {
}
class BitAccessor {
public:
__attribute__((always_inline)) BitAccessor(BitArrayRegisterAccessor &reg, int idx) :
reg(reg),
idx(idx) {
}
__attribute__((always_inline)) bool Get() const {
return (reg.read_value >> idx) & 1;
}
__attribute__((always_inline)) BitArrayRegisterAccessor &Set(bool value) {
reg.write_value&= ~(1 << idx);
reg.write_value|= (value << idx);
return reg;
}
__attribute__((always_inline)) explicit operator bool() const {
return Get();
}
__attribute__((always_inline)) BitArrayRegisterAccessor &operator=(bool value) {
return Set(value);
}
__attribute__((always_inline)) BitArrayRegisterAccessor &Clear() {
return Set(1);
}
private:
BitArrayRegisterAccessor &reg;
int idx;
};
__attribute__((always_inline)) BitAccessor operator[](int idx) {
return BitAccessor(*this, idx);
}
__attribute__((always_inline)) const Peripheral &Commit() {
*ptr = write_value;
read_value = write_value;
return peripheral;
}
const Peripheral &peripheral;
volatile Base *ptr;
Base read_value;
Base write_value;
};
// [low, high] is an inclusive range, since that's the way these things are written in the TRM
template<typename RegAcc, uint8_t low, uint8_t high = low, typename Type = typename RegAcc::Base, uint8_t internal_shift = 0>
class RegisterFieldAccessorBase {
public:
__attribute__((always_inline)) RegisterFieldAccessorBase(RegAcc &acc) : acc(acc) { }
protected:
__attribute__((always_inline)) Type Get() {
return Type(((this->acc.read_value & mask) >> low) << internal_shift);
}
__attribute__((always_inline)) RegAcc &Set(Type value) {
this->acc.write_value&= ~mask;
this->acc.write_value|= ((typename RegAcc::Base(value) >> internal_shift) << low) & mask;
return acc;
}
public:
static const typename RegAcc::Base mask = ((typename RegAcc::Base(2) << (high-low)) - 1) << low;
protected:
using FieldType = Type;
using RegisterAccessor = RegAcc;
RegAcc &acc;
};
// base accessors
template<typename RegAcc, uint8_t low, uint8_t high = low, typename Type = typename RegAcc::Base, uint8_t internal_shift = 0>
class RegisterRWFieldAccessorBase : public RegisterFieldAccessorBase<RegAcc, low, high, Type, internal_shift> {
public:
__attribute__((always_inline)) RegisterRWFieldAccessorBase(RegAcc &acc) : RegisterFieldAccessorBase<RegAcc, low, high, Type, internal_shift>(acc) {
}
__attribute__((always_inline)) Type Get() {
return RegisterFieldAccessorBase<RegAcc, low, high, Type, internal_shift>::Get();
}
__attribute__((always_inline)) explicit operator Type() {
return Get();
}
__attribute__((always_inline)) RegAcc &Set(Type value) {
return RegisterFieldAccessorBase<RegAcc, low, high, Type, internal_shift>::Set(value);
}
};
template<typename RegAcc, uint8_t low, uint8_t high = low, typename Type = typename RegAcc::Base, uint8_t internal_shift = 0>
class RegisterROFieldAccessorBase : public RegisterFieldAccessorBase<RegAcc, low, high, Type, internal_shift> {
public:
__attribute__((always_inline)) RegisterROFieldAccessorBase(RegAcc &acc) : RegisterFieldAccessorBase<RegAcc, low, high, Type, internal_shift>(acc) {
}
__attribute__((always_inline)) Type Get() {
return RegisterFieldAccessorBase<RegAcc, low, high, Type, internal_shift>::Get();
}
__attribute__((always_inline)) explicit operator Type() {
return Get();
}
};
template<typename RegAcc, uint8_t low, uint8_t high = low, typename Type = typename RegAcc::Base, uint8_t internal_shift = 0>
class RegisterWOFieldAccessorBase : public RegisterFieldAccessorBase<RegAcc, low, high, Type, internal_shift> {
public:
__attribute__((always_inline)) RegisterWOFieldAccessorBase(RegAcc &acc) : RegisterFieldAccessorBase<RegAcc, low, high, Type, internal_shift>(acc) {
}
__attribute__((always_inline)) RegAcc &Set(Type value) {
return RegisterFieldAccessorBase<RegAcc, low, high, Type, internal_shift>::Set(value);
}
};
// generic accessors
template<typename RegAcc, uint8_t low, uint8_t high = low, typename Type = typename RegAcc::Base, uint8_t internal_shift = 0>
class RegisterRWFieldAccessor : public RegisterRWFieldAccessorBase<RegAcc, low, high, Type, internal_shift> {
public:
__attribute__((always_inline)) RegisterRWFieldAccessor(RegAcc &acc) : RegisterRWFieldAccessorBase<RegAcc, low, high, Type, internal_shift>(acc) {
}
};
template<typename RegAcc, uint8_t low, uint8_t high = low, typename Type = typename RegAcc::Base, uint8_t internal_shift = 0>
class RegisterROFieldAccessor : public RegisterROFieldAccessorBase<RegAcc, low, high, Type, internal_shift> {
public:
__attribute__((always_inline)) RegisterROFieldAccessor(RegAcc &acc) : RegisterROFieldAccessorBase<RegAcc, low, high, Type, internal_shift>(acc) {
}
};
template<typename RegAcc, uint8_t low, uint8_t high = low, typename Type = typename RegAcc::Base, uint8_t internal_shift = 0>
class RegisterWOFieldAccessor : public RegisterWOFieldAccessorBase<RegAcc, low, high, Type, internal_shift> {
public:
__attribute__((always_inline)) RegisterWOFieldAccessor(RegAcc &acc) : RegisterWOFieldAccessorBase<RegAcc, low, high, Type, internal_shift>(acc) {
}
};
// unusual accessors
template<typename RegAcc, uint8_t low>
class RegisterRW1CFieldAccessor : public RegisterFieldAccessorBase<RegAcc, low, low, bool, 0> {
public:
__attribute__((always_inline)) RegisterRW1CFieldAccessor(RegAcc &acc) : RegisterFieldAccessorBase<RegAcc, low, low, bool, 0>(acc) {
acc.write_value&= ~RegisterFieldAccessorBase<RegAcc, low, low, bool>::mask;
}
__attribute__((always_inline)) bool Get() {
return RegisterFieldAccessorBase<RegAcc, low, low, bool>::Get();
}
__attribute__((always_inline)) operator bool() {
return RegisterFieldAccessorBase<RegAcc, low, low, bool>::Get();
}
__attribute__((always_inline)) RegAcc &Clear() {
return RegisterFieldAccessorBase<RegAcc, low, low, bool>::Set(true);
}
};
// templated-specialized accessors
template<typename RegAcc, uint8_t low>
class RegisterRWFieldAccessor<RegAcc, low, low> : public RegisterRWFieldAccessorBase<RegAcc, low, low, bool> {
public:
__attribute__((always_inline)) RegisterRWFieldAccessor(RegAcc &acc) : RegisterRWFieldAccessorBase<RegAcc, low, low, bool>(acc) {
}
__attribute__((always_inline)) RegAcc &Disable() {
return RegisterFieldAccessorBase<RegAcc, low, low, bool>::Set(false);
}
__attribute__((always_inline)) RegAcc &Enable() {
return RegisterFieldAccessorBase<RegAcc, low, low, bool>::Set(true);
}
__attribute__((always_inline)) operator bool() {
return RegisterFieldAccessorBase<RegAcc, low, low, bool>::Get();
}
};
template<typename RegAcc, uint8_t low>
class RegisterROFieldAccessor<RegAcc, low, low> : public RegisterROFieldAccessorBase<RegAcc, low, low, bool> {
public:
__attribute__((always_inline)) RegisterROFieldAccessor(RegAcc &acc) : RegisterROFieldAccessorBase<RegAcc, low, low, bool>(acc) {
}
__attribute__((always_inline)) operator bool() {
return RegisterROFieldAccessorBase<RegAcc, low, low, bool>::Get();
}
};
#define BEGIN_DEFINE_REGISTER(name, type, offset) \
class name##_Accessor : public RegisterAccessor<Peripheral, type, offset> { \
public: \
using RegSelf = name##_Accessor; \
__attribute__((always_inline)) name##_Accessor(const Peripheral &p) : RegisterAccessor(p) {} \
__attribute__((always_inline)) name##_Accessor(const Peripheral &p, type value) : RegisterAccessor(p, value) {}
#define BEGIN_DEFINE_WO_REGISTER(name, type, offset) \
class name##_Accessor : public RegisterAccessor<Peripheral, type, offset> { \
public: \
using RegSelf = name##_Accessor; \
__attribute__((always_inline)) name##_Accessor(const Peripheral &p) : RegisterAccessor(p, 0) {}
#define DEFINE_RW_FIELD(name, ...) \
RegisterRWFieldAccessor<RegSelf, __VA_ARGS__> name = RegisterRWFieldAccessor<RegSelf, __VA_ARGS__>(*this);
#define DEFINE_RO_FIELD(name, ...) \
RegisterROFieldAccessor<RegSelf, __VA_ARGS__> name = RegisterROFieldAccessor<RegSelf, __VA_ARGS__>(*this);
#define DEFINE_WO_FIELD(name, ...) \
RegisterWOFieldAccessor<RegSelf, __VA_ARGS__> name = RegisterWOFieldAccessor<RegSelf, __VA_ARGS__>(*this);
#define DEFINE_RW1C_FIELD(name, ...) \
RegisterRW1CFieldAccessor<RegSelf, __VA_ARGS__> name = RegisterRW1CFieldAccessor<RegSelf, __VA_ARGS__>(*this);
#define BEGIN_DEFINE_RO_SYMBOLIC_FIELD(name, low, high) \
struct name##_Accessor : public RegisterROFieldAccessor<RegSelf, low, high, RegSelf::Base> {\
using SymSelf = name##_Accessor; \
__attribute__((always_inline)) name##_Accessor(RegSelf &acc) : RegisterROFieldAccessor(acc) { }
#define BEGIN_DEFINE_RW_SYMBOLIC_FIELD(name, low, high) \
struct name##_Accessor : public RegisterRWFieldAccessor<RegSelf, low, high, RegSelf::Base> {\
using SymSelf = name##_Accessor; \
__attribute__((always_inline)) name##_Accessor(RegSelf &acc) : RegisterRWFieldAccessor(acc) { }
#define DEFINE_RO_SYMBOLIC_VALUE(name, value) \
__attribute__((always_inline)) bool Is##name() { \
return Get() == typename SymSelf::FieldType(value); \
}
#define DEFINE_RW_SYMBOLIC_VALUE(name, value) \
__attribute__((always_inline)) RegisterAccessor &Set##name() {\
return Set(typename SymSelf::FieldType(value)); \
} \
__attribute__((always_inline)) bool Is##name() { \
return Get() == typename SymSelf::FieldType(value); \
}
#define END_DEFINE_SYMBOLIC_FIELD(name) \
} name = name##_Accessor(*this);
#define END_DEFINE_REGISTER(name) \
}; \
__attribute__((always_inline)) name##_Accessor name() const { \
return name##_Accessor(*this); \
} \
__attribute__((always_inline)) name##_Accessor name(name##_Accessor::Base value) const { \
return name##_Accessor(*this, value); \
}
#define END_DEFINE_WO_REGISTER(name) \
}; \
__attribute__((always_inline)) name##_Accessor name() const { \
return name##_Accessor(*this); \
}
#define DEFINE_BIT_ARRAY_REGISTER(name, type, offset) \
__attribute__((always_inline)) BitArrayRegisterAccessor<Peripheral, type> name() const { \
return BitArrayRegisterAccessor<Peripheral, type>(*this, offset); \
} \
__attribute__((always_inline)) BitArrayRegisterAccessor<Peripheral, type> name(type value) const { \
return BitArrayRegisterAccessor<Peripheral, type>(*this, offset, value); \
}
#define DEFINE_RW1C_BIT_ARRAY_REGISTER(name, type, offset) \
__attribute__((always_inline)) BitArrayRegisterAccessor<Peripheral, type> name() const { \
return BitArrayRegisterAccessor<Peripheral, type>(*this, offset, rw1c_marker {}); \
}
#if (0)
/*
This can look a little obtuse, so here's an example of how it's meant to be used:
*/
namespace t210 {
const struct T_XUSB_DEV_XHCI {
/* This field needs to have this name and this type, since it is used by the *_DEFINE_REGISTER macros. */
/* However, it is allowed to be non-static, in case you have multiple instances of the same kind of peripheral. */
static const uintptr_t base_addr = 0x7000d000;
/* This using needs to have this name, since it is used by the macros. */
using Peripheral = T_XUSB_DEV_XHCI;
/* You can use your own types with register fields, as long as they can be casted to and from integer types. */
enum class LinkSpeed {
/* ... */
};
/* BEGIN_DEFINE_REGISTER(name, base type, offset from periphal base)
Defines a register. */
BEGIN_DEFINE_REGISTER(EREPLO, uint32_t, 0x28)
/* DEFINE_RW_FIELD(name, lo, [hi], [type], [internal shift])
Internal shift is used for fields that store values without low bits.
Here, we want to be able to access a pointer directly, but the low 4 bits
belong to a different field, so the internal shift automatically shifts
reads and writes for us. */
DEFINE_RW_FIELD(ADDRLO, 4, 31, uintptr_t, 4)
DEFINE_RW_FIELD(SEGI, 1)
DEFINE_RW_FIELD(ECS, 0)
END_DEFINE_REGISTER(EREPLO)
BEGIN_DEFINE_REGISTER(PORTSC, uint32_t, 0x3c)
/* DEFINE_RO_FIELD(name, lo, [hi], [base type], [internal shift])
Same as DEFINE_RW_FIELD except it creates a different accessor type
that does not have methods for writing. */
DEFINE_RO_FIELD(WPR, 30)
DEFINE_RO_FIELD(RSVD4, 28, 29)
/* DEFINE_RW1C_FIELD(name, lo)
Defines a bit that is cleared when 1 is written to it, so we
need to be sure to not write it back if we don't mean to. Will not
write back unless you call .Clear() on it.
Forced to be a single-bit boolean type. */
DEFINE_RW1C_FIELD(CEC, 23)
DEFINE_RW1C_FIELD(PLC, 22)
DEFINE_RW1C_FIELD(PRC, 21)
DEFINE_RO_FIELD(RSVD3, 20)
DEFINE_RW1C_FIELD(WRC, 19)
DEFINE_RW1C_FIELD(CSC, 17)
DEFINE_RW_FIELD(LWS, 16)
DEFINE_RO_FIELD(RSVD2, 15)
/* BEGIN_DEFINE_RO_SYMBOLIC_FIELD(name, lo, hi)
Defines an enumerated field without writer accessor methods. */
BEGIN_DEFINE_RO_SYMBOLIC_FIELD(PS, 10, 13)
/* Defines an enumerated value without writer accessor methods. */
DEFINE_RO_SYMBOLIC_VALUE(UNDEFINED, 0)
DEFINE_RO_SYMBOLIC_VALUE(FS, 1)
DEFINE_RO_SYMBOLIC_VALUE(LS, 2)
DEFINE_RO_SYMBOLIC_VALUE(HS, 3)
DEFINE_RO_SYMBOLIC_VALUE(SS, 4)
END_DEFINE_SYMBOLIC_FIELD(PS)
DEFINE_RO_FIELD(LANE_POLARITY_VALUE, 9)
BEGIN_DEFINE_RW_SYMBOLIC_FIELD(PLS, 5, 8)
DEFINE_RW_SYMBOLIC_VALUE(U0, 0)
DEFINE_RW_SYMBOLIC_VALUE(U1, 1)
DEFINE_RW_SYMBOLIC_VALUE(U2, 2)
DEFINE_RW_SYMBOLIC_VALUE(U3, 3)
DEFINE_RW_SYMBOLIC_VALUE(DISABLED, 4)
DEFINE_RW_SYMBOLIC_VALUE(RXDETECT, 5)
DEFINE_RW_SYMBOLIC_VALUE(INACTIVE, 6)
DEFINE_RW_SYMBOLIC_VALUE(POLLING, 7)
DEFINE_RW_SYMBOLIC_VALUE(RECOVERY, 8)
DEFINE_RW_SYMBOLIC_VALUE(HOTRESET, 9)
DEFINE_RW_SYMBOLIC_VALUE(COMPLIANCE, 0xa)
DEFINE_RW_SYMBOLIC_VALUE(LOOPBACK, 0xb)
DEFINE_RW_SYMBOLIC_VALUE(RESUME, 0xf)
END_DEFINE_SYMBOLIC_FIELD(PLS)
DEFINE_RO_FIELD(PR, 4)
DEFINE_RW_FIELD(LANE_POLARITY_OVRD_VALUE, 3)
DEFINE_RW_FIELD(LANE_POLARITY_OVRD, 2)
DEFINE_RW_FIELD(PED, 1)
DEFINE_RW_FIELD(CCS, 0)
END_DEFINE_REGISTER(PORTSC)
} T_XUSB_DEV_XHCI;
void chaining_example() {
t210::CLK_RST_CONTROLLER
.CLK_OUT_ENB_W_0()
.CLK_ENB_XUSB().Enable()
.Commit()
.RST_DEVICES_W_0()
.SWR_XUSB_SS_RST().Set(0)
.Commit();
t210::XUSB_PADCTL
.USB2_PAD_MUX_0()
.USB2_OTG_PAD_PORT0().SetXUSB()
.USB2_BIAS_PAD().SetXUSB()
.Commit();
}
void rw1c_example() {
auto portsc = t210::XUSB_DEV.PORTSC();
if (portsc.CEC) {
portsc.CEC.Clear();
}
portsc.PED.Set(true);
portsc.Commit();
}
uintptr_t internal_shift_example() {
t210::XUSB_DEV
.EREPLO()
.ADDRLO.Set((uintptr_t) &internal_shift_example)
.Commit();
return t210::XUSB_DEV.EREPLO().ADDRLO.Get();
}
void no_read_example() {
t210::XUSB_DEV
.EREPLO(0) // does not read from register- instead assumes it contains 0
.ADDRLO.Set((uintptr_t) &no_read_example)
.Commit();
}
#endif

View file

@ -0,0 +1,222 @@
/*
* 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 "reg_util.h"
namespace t210 {
static const uintptr_t XUSB_DEV_BASE = 0x700d0000;
const struct T_XUSB_DEV_XHCI {
static const uintptr_t base_addr = XUSB_DEV_BASE;
using Peripheral = T_XUSB_DEV_XHCI;
BEGIN_DEFINE_WO_REGISTER(DB, uint32_t, 0x4)
DEFINE_WO_FIELD(STREAMID, 16, 31)
DEFINE_WO_FIELD(TARGET, 8, 15)
END_DEFINE_WO_REGISTER(DB)
BEGIN_DEFINE_REGISTER(ERSTSZ, uint32_t, 0x8)
DEFINE_RW_FIELD(ERST1SZ, 16, 31)
DEFINE_RW_FIELD(ERST0SZ, 0, 15)
END_DEFINE_REGISTER(ERSTSZ)
BEGIN_DEFINE_REGISTER(ERST0BALO, uint32_t, 0x10)
DEFINE_RW_FIELD(ADDRLO, 4, 31, uintptr_t, 4)
END_DEFINE_REGISTER(ERST0BALO)
BEGIN_DEFINE_REGISTER(ERST0BAHI, uint32_t, 0x14)
DEFINE_RW_FIELD(ADDRHI, 0, 31)
END_DEFINE_REGISTER(ERST0BAHI)
BEGIN_DEFINE_REGISTER(ERST1BALO, uint32_t, 0x18)
DEFINE_RW_FIELD(ADDRLO, 4, 31, uintptr_t, 4)
END_DEFINE_REGISTER(ERST1BALO)
BEGIN_DEFINE_REGISTER(ERST1BAHI, uint32_t, 0x1c)
DEFINE_RW_FIELD(ADDRHI, 0, 31)
END_DEFINE_REGISTER(ERST1BAHI)
BEGIN_DEFINE_REGISTER(ERDPLO, uint32_t, 0x20)
DEFINE_RW_FIELD(ADDRLO, 4, 31, uintptr_t, 4)
DEFINE_RW1C_FIELD(EHB, 3)
END_DEFINE_REGISTER(ERDPLO)
BEGIN_DEFINE_REGISTER(ERDPHI, uint32_t, 0x24)
DEFINE_RW_FIELD(ADDRHI, 0, 31)
END_DEFINE_REGISTER(ERDPHI)
BEGIN_DEFINE_REGISTER(EREPLO, uint32_t, 0x28)
DEFINE_RW_FIELD(ADDRLO, 4, 31, uintptr_t, 4)
DEFINE_RW_FIELD(SEGI, 1)
DEFINE_RW_FIELD(ECS, 0)
END_DEFINE_REGISTER(EREPLO)
BEGIN_DEFINE_REGISTER(EREPHI, uint32_t, 0x2c)
DEFINE_RW_FIELD(ADDRHI, 0, 31)
END_DEFINE_REGISTER(EREPHI)
BEGIN_DEFINE_REGISTER(CTRL, uint32_t, 0x30)
DEFINE_RW_FIELD(ENABLE, 31)
DEFINE_RW_FIELD(DEVADR, 24, 30)
DEFINE_RW_FIELD(RSVD0, 8, 23)
DEFINE_RW_FIELD(EWE, 7)
DEFINE_RW_FIELD(SMI_DSE, 6)
DEFINE_RW_FIELD(SMI_EVT, 5)
DEFINE_RW_FIELD(IE, 4)
DEFINE_RW_FIELD(RSVD1, 2, 3)
DEFINE_RW_FIELD(LSE, 1)
DEFINE_RW_FIELD(RUN, 0)
END_DEFINE_REGISTER(CTRL)
BEGIN_DEFINE_REGISTER(ST, uint32_t, 0x34)
DEFINE_RO_FIELD(RSVD1, 6, 1)
DEFINE_RW1C_FIELD(DSE, 5)
DEFINE_RW1C_FIELD(IP, 4)
DEFINE_RO_FIELD(RSVD0, 1, 3)
DEFINE_RW1C_FIELD(RC, 0)
END_DEFINE_REGISTER(ST)
BEGIN_DEFINE_REGISTER(PORTSC, uint32_t, 0x3c)
DEFINE_RW_FIELD(WPR, 30)
DEFINE_RW_FIELD(RSVD4, 28, 29)
DEFINE_RW1C_FIELD(CEC, 23)
DEFINE_RW1C_FIELD(PLC, 22)
DEFINE_RW1C_FIELD(PRC, 21)
DEFINE_RW_FIELD(RSVD3, 20)
DEFINE_RW1C_FIELD(WRC, 19)
DEFINE_RW1C_FIELD(CSC, 17)
DEFINE_RW_FIELD(LWS, 16)
DEFINE_RW_FIELD(RSVD2, 15)
BEGIN_DEFINE_RO_SYMBOLIC_FIELD(PS, 10, 13)
DEFINE_RO_SYMBOLIC_VALUE(UNDEFINED, 0)
DEFINE_RO_SYMBOLIC_VALUE(FS, 1)
DEFINE_RO_SYMBOLIC_VALUE(LS, 2)
DEFINE_RO_SYMBOLIC_VALUE(HS, 3)
DEFINE_RO_SYMBOLIC_VALUE(SS, 4)
END_DEFINE_SYMBOLIC_FIELD(PS)
DEFINE_RO_FIELD(LANE_POLARITY_VALUE, 9)
BEGIN_DEFINE_RW_SYMBOLIC_FIELD(PLS, 5, 8)
DEFINE_RW_SYMBOLIC_VALUE(U0, 0)
DEFINE_RW_SYMBOLIC_VALUE(U1, 1)
DEFINE_RW_SYMBOLIC_VALUE(U2, 2)
DEFINE_RW_SYMBOLIC_VALUE(U3, 3)
DEFINE_RW_SYMBOLIC_VALUE(DISABLED, 4)
DEFINE_RW_SYMBOLIC_VALUE(RXDETECT, 5)
DEFINE_RW_SYMBOLIC_VALUE(INACTIVE, 6)
DEFINE_RW_SYMBOLIC_VALUE(POLLING, 7)
DEFINE_RW_SYMBOLIC_VALUE(RECOVERY, 8)
DEFINE_RW_SYMBOLIC_VALUE(HOTRESET, 9)
DEFINE_RW_SYMBOLIC_VALUE(COMPLIANCE, 0xa)
DEFINE_RW_SYMBOLIC_VALUE(LOOPBACK, 0xb)
DEFINE_RW_SYMBOLIC_VALUE(RESUME, 0xf)
END_DEFINE_SYMBOLIC_FIELD(PLS)
DEFINE_RO_FIELD(PR, 4)
DEFINE_RW_FIELD(LANE_POLARITY_OVRD_VALUE, 3)
DEFINE_RW_FIELD(LANE_POLARITY_OVRD, 2)
DEFINE_RW1C_FIELD(PED, 1) // TODO: is this rw1c?
DEFINE_RW_FIELD(CCS, 0)
END_DEFINE_REGISTER(PORTSC)
BEGIN_DEFINE_REGISTER(ECPLO, uint32_t, 0x40)
DEFINE_RW_FIELD(ADDRLO, 6, 31, uintptr_t, 6)
END_DEFINE_REGISTER(ECPLO)
BEGIN_DEFINE_REGISTER(ECPHI, uint32_t, 0x44)
DEFINE_RW_FIELD(ADDRHI, 0, 31)
END_DEFINE_REGISTER(ECPHI)
DEFINE_BIT_ARRAY_REGISTER(EP_HALT, uint32_t, 0x50)
DEFINE_BIT_ARRAY_REGISTER(EP_PAUSE, uint32_t, 0x54)
DEFINE_BIT_ARRAY_REGISTER(EP_RELOAD, uint32_t, 0x58)
DEFINE_RW1C_BIT_ARRAY_REGISTER(EP_STCHG, uint32_t, 0x5c)
BEGIN_DEFINE_REGISTER(PORTHALT, uint32_t, 0x6c)
// TRM labels both bit 26 and bit 20 as STCHG_REQ, but Linux kernel sources disambiguate it.
// https://patchwork.kernel.org/patch/11129617/
//DEFINE_RW_FIELD(???, 26)
DEFINE_RW_FIELD(STCHG_PME_EN, 25)
DEFINE_RW_FIELD(STCHG_INTR_EN, 24)
DEFINE_RW1C_FIELD(STCHG_REQ, 20)
DEFINE_RW_FIELD(STCHG_STATE, 16, 19)
DEFINE_RW1C_FIELD(HALT_REJECT, 1) // TODO: RW1C semantics
DEFINE_RW_FIELD(HALT_LTSSM, 0)
END_DEFINE_REGISTER(PORTHALT)
DEFINE_RW1C_BIT_ARRAY_REGISTER(EP_STOPPED, uint32_t, 0x78)
BEGIN_DEFINE_REGISTER(CFG_DEV_FE, uint32_t, 0x85c)
// "Note: Do not change the values in bits 31:2"
DEFINE_RW_FIELD(INFINITE_SS_RETRY, 29)
DEFINE_RW_FIELD(EN_PRIME_EVENT, 28)
DEFINE_RW_FIELD(FEATURE_LPM, 27)
DEFINE_RW_FIELD(EN_STALL_EVENT, 26)
DEFINE_RW_FIELD(PORTDISCON_RST_HW, 25)
DEFINE_RW_FIELD(CTX_RESTORE, 24)
DEFINE_RW_FIELD(MFCOUNT_MIN, 4, 23)
DEFINE_RW_FIELD(PORTRST_HW, 3)
DEFINE_RW_FIELD(SEQNUM_INIT, 2)
BEGIN_DEFINE_RW_SYMBOLIC_FIELD(PORTREGSEL, 0, 1)
DEFINE_RW_SYMBOLIC_VALUE(INIT, 0)
DEFINE_RW_SYMBOLIC_VALUE(SS, 1)
DEFINE_RW_SYMBOLIC_VALUE(HSFS, 2)
END_DEFINE_SYMBOLIC_FIELD(PORTREGSEL)
END_DEFINE_REGISTER(CFG_DEV_FE)
} T_XUSB_DEV_XHCI;
const struct T_XUSB_DEV {
static const uintptr_t base_addr = XUSB_DEV_BASE + 0x8000;
using Peripheral = T_XUSB_DEV;
BEGIN_DEFINE_REGISTER(CFG_1, uint32_t, 0x4)
DEFINE_RW_FIELD(IO_SPACE, 0)
DEFINE_RW_FIELD(MEMORY_SPACE, 1)
DEFINE_RW_FIELD(BUS_MASTER, 2)
END_DEFINE_REGISTER(CFG_1)
BEGIN_DEFINE_REGISTER(CFG_4, uint32_t, 0x10)
DEFINE_RW_FIELD(BASE_ADDRESS, 15, 31, uintptr_t, 15)
BEGIN_DEFINE_RW_SYMBOLIC_FIELD(PREFETCHABLE, 3, 3)
DEFINE_RW_SYMBOLIC_VALUE(NOT, 0)
DEFINE_RW_SYMBOLIC_VALUE(MERGABLE, 1)
END_DEFINE_SYMBOLIC_FIELD(PREFETCHABLE)
BEGIN_DEFINE_RW_SYMBOLIC_FIELD(ADDRESS_TYPE, 1, 2)
DEFINE_RW_SYMBOLIC_VALUE(32_BIT, 0)
DEFINE_RW_SYMBOLIC_VALUE(64_BIT, 2)
END_DEFINE_SYMBOLIC_FIELD(ADDRESS_TYPE)
BEGIN_DEFINE_RW_SYMBOLIC_FIELD(SPACE_TYPE, 0, 0)
DEFINE_RW_SYMBOLIC_VALUE(MEMORY, 0)
DEFINE_RW_SYMBOLIC_VALUE(IO, 1)
END_DEFINE_SYMBOLIC_FIELD(SPACE_TYPE)
END_DEFINE_REGISTER(CFG_4)
} T_XUSB_DEV;
const struct XUSB_DEV {
static const uintptr_t base_addr = XUSB_DEV_BASE + 0x9000;
using Peripheral = XUSB_DEV;
BEGIN_DEFINE_REGISTER(CONFIGURATION_0, uint32_t, 0x180)
DEFINE_RW_FIELD(EN_FPCI, 0)
END_DEFINE_REGISTER(CONFIGURATION_0)
BEGIN_DEFINE_REGISTER(INTR_MASK_0, uint32_t, 0x188)
DEFINE_RW_FIELD(IP_INT_MASK, 16)
DEFINE_RW_FIELD(MSI_MASK, 8)
DEFINE_RW_FIELD(INT_MASK, 0)
END_DEFINE_REGISTER(INTR_MASK_0)
} XUSB_DEV;
} // namespace t210

View file

@ -0,0 +1,211 @@
/*
* 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 "reg_util.h"
namespace t210 {
const struct XUSB_PADCTL {
static const uintptr_t base_addr = 0x7009f000;
using Peripheral = XUSB_PADCTL;
BEGIN_DEFINE_REGISTER(USB2_PAD_MUX_0, uint32_t, 0x4)
BEGIN_DEFINE_RW_SYMBOLIC_FIELD(USB2_OTG_PAD_PORT0, 0, 1)
DEFINE_RW_SYMBOLIC_VALUE(SNPS, 0)
DEFINE_RW_SYMBOLIC_VALUE(XUSB, 1)
DEFINE_RW_SYMBOLIC_VALUE(UART, 2)
END_DEFINE_SYMBOLIC_FIELD(USB2_OTG_PAD_PORT0)
BEGIN_DEFINE_RW_SYMBOLIC_FIELD(USB2_BIAS_PAD, 18, 19)
DEFINE_RW_SYMBOLIC_VALUE(SNPS, 0)
DEFINE_RW_SYMBOLIC_VALUE(XUSB, 1)
DEFINE_RW_SYMBOLIC_VALUE(UART, 2)
END_DEFINE_SYMBOLIC_FIELD(USB2_BIAS_PAD)
END_DEFINE_REGISTER(USB2_PAD_MUX_0)
BEGIN_DEFINE_REGISTER(USB2_PORT_CAP_0, uint32_t, 0x8)
BEGIN_DEFINE_RW_SYMBOLIC_FIELD(PORT0_CAP, 0, 1)
DEFINE_RW_SYMBOLIC_VALUE(Disabled, 0)
DEFINE_RW_SYMBOLIC_VALUE(HostOnly, 1)
DEFINE_RW_SYMBOLIC_VALUE(DeviceOnly, 2)
DEFINE_RW_SYMBOLIC_VALUE(OtgCap, 3)
END_DEFINE_SYMBOLIC_FIELD(PORT0_CAP)
END_DEFINE_REGISTER(USB2_PORT_CAP_0)
BEGIN_DEFINE_REGISTER(SS_PORT_MAP_0, uint32_t, 0x14)
BEGIN_DEFINE_RW_SYMBOLIC_FIELD(PORT0_MAP, 0, 3)
DEFINE_RW_SYMBOLIC_VALUE(USB2Port0, 0)
DEFINE_RW_SYMBOLIC_VALUE(USB2Port1, 1)
DEFINE_RW_SYMBOLIC_VALUE(USB2Port2, 2)
DEFINE_RW_SYMBOLIC_VALUE(USB2Port3, 3)
DEFINE_RW_SYMBOLIC_VALUE(InitDisabled, 7)
END_DEFINE_SYMBOLIC_FIELD(PORT0_MAP)
END_DEFINE_REGISTER(SS_PORT_MAP_0)
BEGIN_DEFINE_REGISTER(ELPG_PROGRAM_0_0, uint32_t, 0x20)
END_DEFINE_REGISTER(ELPG_PROGRAM_0_0)
BEGIN_DEFINE_REGISTER(ELPG_PROGRAM_1_0, uint32_t, 0x24)
END_DEFINE_REGISTER(ELPG_PROGRAM_1_0)
BEGIN_DEFINE_REGISTER(USB2_BATTERY_CHRG_OTGPAD0_CTL0_0, uint32_t, 0x80)
DEFINE_RW_FIELD(GENERATE_SRP, 31)
DEFINE_RW_FIELD(SRP_INTR_EN, 30)
DEFINE_RW_FIELD(SRP_DETECTED, 29)
DEFINE_RW_FIELD(SRP_DETECT_EN, 28)
DEFINE_RW_FIELD(DCD_INTR_EN, 27)
DEFINE_RW_FIELD(DCD_DETECTED, 26)
DEFINE_RW_FIELD(ZIN_FILTER_EN, 25)
DEFINE_RW_FIELD(ZIN_CHNG_INTR_EN, 24)
DEFINE_RW_FIELD(ZIN_ST_CHNG, 23)
DEFINE_RO_FIELD(ZIN, 22)
DEFINE_RW_FIELD(ZIP_FILTER_EN, 21)
DEFINE_RW_FIELD(ZIP_CHNG_INTER_EN, 20)
DEFINE_RW_FIELD(ZIP_ST_CHNG, 19)
DEFINE_RO_FIELD(ZIP, 18)
DEFINE_RW_FIELD(OP_I_SRC_EN, 13)
DEFINE_RW_FIELD(ON_SRC_EN, 12)
DEFINE_RW_FIELD(ON_SINK_EN, 11)
DEFINE_RW_FIELD(OP_SRV_EN, 10)
DEFINE_RW_FIELD(OP_SINK_EN, 9)
DEFINE_RW_FIELD(VDAT_DET_FILTER_EN, 8)
DEFINE_RW_FIELD(VDAT_DET_CHNG_INTER_EN, 7)
DEFINE_RW_FIELD(VDAT_DET_ST_CHNG, 6)
DEFINE_RO_FIELD(VDAT_DET, 5)
DEFINE_RW_FIELD(VDCD_DET_FILTER_EN, 4)
DEFINE_RW_FIELD(VDCD_DET_CHNG_INTR_EN, 3)
DEFINE_RW_FIELD(VDCD_DET_ST_CHNG, 2)
DEFINE_RO_FIELD(VDCD_DET, 1)
DEFINE_RW_FIELD(PD_CHG, 0)
END_DEFINE_REGISTER(USB2_BATTERY_CHRG_OTGPAD0_CTL0_0)
BEGIN_DEFINE_REGISTER(USB2_BATTERY_CHRG_OTGPAD0_CTL1_0, uint32_t, 0x84)
DEFINE_RW_FIELD(USBON_RPU_OVRD_VAL, 23)
DEFINE_RW_FIELD(USBON_RPU_OVRD, 22)
DEFINE_RW_FIELD(USBON_RPD_OVRD_VAL, 21)
DEFINE_RW_FIELD(USBON_RPD_OVRD, 20)
DEFINE_RW_FIELD(USBOP_RPU_OVRD_VAL, 19)
DEFINE_RW_FIELD(USBOP_RPU_OVRD, 18)
DEFINE_RW_FIELD(USBOP_RPD_OVRD_VAL, 17)
DEFINE_RW_FIELD(USBOP_RPD_OVRD, 16)
DEFINE_RW_FIELD(VREG_DYN_DLY, 9, 10)
DEFINE_RW_FIELD(VREG_LEV, 7, 8)
DEFINE_RW_FIELD(VREG_FIX18, 6)
DEFINE_RW_FIELD(DIV_DET_EN, 4)
DEFINE_RO_FIELD(VOP_DIV2P7_DET, 3)
DEFINE_RO_FIELD(VOP_DIV2P0_DET, 2)
DEFINE_RO_FIELD(VON_DIV2P7_DET, 1)
DEFINE_RO_FIELD(VON_DIV2P0_DET, 0)
END_DEFINE_REGISTER(USB2_BATTERY_CHRG_OTGPAD0_CTL1_0)
BEGIN_DEFINE_REGISTER(USB2_OTG_PAD0_CTL_0_0, uint32_t, 0x88)
DEFINE_RW_FIELD(PD_ZI, 29)
DEFINE_RW_FIELD(PD2_OVRD_EN, 28)
DEFINE_RW_FIELD(PD2, 27)
DEFINE_RW_FIELD(PD, 26)
DEFINE_RW_FIELD(TERM_SEL, 25)
DEFINE_RW_FIELD(LS_FSLEW, 21, 24)
DEFINE_RW_FIELD(LS_RSLEW, 17, 20)
DEFINE_RW_FIELD(FS_FSLEW, 13, 16)
DEFINE_RW_FIELD(FS_RSLEW, 9, 12)
DEFINE_RW_FIELD(HS_SLEW, 6, 8)
DEFINE_RW_FIELD(HS_CURR_LEVEL, 0, 5)
END_DEFINE_REGISTER(USB2_OTG_PAD0_CTL_0_0)
BEGIN_DEFINE_REGISTER(USB2_OTG_PAD0_CTL_1_0, uint32_t, 0x8c)
DEFINE_RW_FIELD(RPD_CTRL, 26, 30)
DEFINE_RO_FIELD(RPU_STATUS_HIGH, 25)
DEFINE_RW_FIELD(RPU_SWITCH_LOW, 24)
DEFINE_RW_FIELD(RPU_SWITCH_OVRD, 23)
DEFINE_RW_FIELD(HS_LOOPBACK_OVRD_VAL, 22)
DEFINE_RW_FIELD(HS_LOOPBACK_OVRD_EN, 21)
DEFINE_RW_FIELD(PTERM_RANGE_ADJ, 17, 20)
DEFINE_RW_FIELD(PD_DISC_OVRD_VAL, 16)
DEFINE_RW_FIELD(PD_CHRP_OVRD_VAL, 15)
DEFINE_RW_FIELD(RPU_RANGE_ADJ, 13, 14)
DEFINE_RW_FIELD(HS_COUP_EN, 11, 12)
DEFINE_RW_FIELD(SPARE, 7, 10)
DEFINE_RW_FIELD(TERM_RANGE_ADJ, 3, 6)
DEFINE_RW_FIELD(PD_DR, 2)
DEFINE_RW_FIELD(PD_DISC_OVRD, 1)
DEFINE_RW_FIELD(PD_CHRP_OVRD, 0)
END_DEFINE_REGISTER(USB2_OTG_PAD0_CTL_1_0)
BEGIN_DEFINE_REGISTER(USB2_BIAS_PAD_CTL_0_0, uint32_t, 0x284)
DEFINE_RW_FIELD(TRK_POWER_ENA, 29)
DEFINE_RW_FIELD(SPARE, 25, 28)
DEFINE_RW_FIELD(CHG_DIV, 21, 24)
DEFINE_RW_FIELD(TEMP_COEF, 18, 20)
DEFINE_RW_FIELD(VREF_CTRL, 15, 17)
DEFINE_RW_FIELD(ADJRPU, 12, 14)
DEFINE_RW_FIELD(PD, 11)
DEFINE_RW_FIELD(TERM_OFFSET, 8, 10)
DEFINE_RW_FIELD(HS_CHIRP_LEVEL, 6, 7)
DEFINE_RW_FIELD(HS_DISCON_LEVEL, 3, 5)
DEFINE_RW_FIELD(HS_SQUELCH_LEVEL, 0, 2)
END_DEFINE_REGISTER(USB2_BIAS_PAD_CTL_0_0)
BEGIN_DEFINE_REGISTER(USB2_BIAS_PAD_CTL_1_0, uint32_t, 0x288)
DEFINE_RW_FIELD(FORCE_TRK_CLK_EN, 30)
DEFINE_RW_FIELD(TRK_SW_OVRF, 29)
DEFINE_RO_FIELD(TRK_DONE, 28)
DEFINE_RW_FIELD(TRK_START, 27)
DEFINE_RW_FIELD(PD_TRK, 26)
DEFINE_RW_FIELD(TRK_DONE_RESET_TIMER, 19, 25)
DEFINE_RW_FIELD(TRK_START_TIMER, 12, 18)
DEFINE_RO_FIELD(PCTRL, 6, 11)
DEFINE_RO_FIELD(TCTRL, 0, 5)
END_DEFINE_REGISTER(USB2_BIAS_PAD_CTL_1_0)
BEGIN_DEFINE_REGISTER(USB2_VBUS_ID_0, uint32_t, 0xc60)
DEFINE_RW_FIELD(VBUS_WAKEUP_CHNG_INTR_EN, 24)
DEFINE_RW_FIELD(VBUS_WAKEUP_ST_CHNG, 23)
DEFINE_RO_FIELD(VBUS_WAKEUP, 22)
BEGIN_DEFINE_RW_SYMBOLIC_FIELD(ID_OVERRIDE, 18, 21)
DEFINE_RW_SYMBOLIC_VALUE(GND, 0)
DEFINE_RW_SYMBOLIC_VALUE(A, 4)
DEFINE_RW_SYMBOLIC_VALUE(B, 2)
DEFINE_RW_SYMBOLIC_VALUE(C, 1)
DEFINE_RW_SYMBOLIC_VALUE(FLOAT, 8)
END_DEFINE_SYMBOLIC_FIELD(ID_OVERRIDE)
BEGIN_DEFINE_RW_SYMBOLIC_FIELD(ID_SOURCE_SELECT, 16, 17)
DEFINE_RW_SYMBOLIC_VALUE(VGPIO, 0)
DEFINE_RW_SYMBOLIC_VALUE(OVERRIDE, 1)
END_DEFINE_SYMBOLIC_FIELD(ID_SOURCE_SELECT)
DEFINE_RW_FIELD(VBUS_WAKEUP_OVERRIDE, 15)
DEFINE_RW_FIELD(VBUS_OVERRIDE, 14)
BEGIN_DEFINE_RW_SYMBOLIC_FIELD(VBUS_SOURCE_SELECT, 12, 13)
DEFINE_RW_SYMBOLIC_VALUE(VGPIO, 0)
DEFINE_RW_SYMBOLIC_VALUE(OVERRIDE, 1)
END_DEFINE_SYMBOLIC_FIELD(VBUS_SOURCE_SELECT)
DEFINE_RW_FIELD(IDDIG_CHNG_INTR_EN, 11)
DEFINE_RW_FIELD(IDDIG_ST_CHNG, 10)
DEFINE_RO_FIELD(IDDIG_C, 9)
DEFINE_RO_FIELD(IDDIG_B, 8)
DEFINE_RO_FIELD(IDDIG_A, 7)
DEFINE_RO_FIELD(IDDIG, 6)
DEFINE_RW_FIELD(VBUS_VALID_CHNG_INTR_EN, 5)
DEFINE_RW_FIELD(VBUS_VALID_ST_CHNG, 4)
DEFINE_RO_FIELD(VBUS_VALID, 3)
DEFINE_RW_FIELD(OTG_VBUS_SESS_VLD_CHNG_INTR_EN, 2)
DEFINE_RW_FIELD(OTG_VBUS_SESS_VLD_ST_CHNG, 1)
DEFINE_RO_FIELD(OTG_VBUS_SESS_VLD, 0)
END_DEFINE_REGISTER(USB2_VBUS_ID_0)
} XUSB_PADCTL;
} // namespace t210

View file

@ -22,6 +22,8 @@
#include <stdint.h>
#include <string.h>
#ifndef BIT
#define BIT(n) (1u << (n))
#define BITL(n) (1ull << (n))
#define MASK(n) (BIT(n) - 1)
@ -29,6 +31,8 @@
#define MASK2(a,b) (MASK(a) & ~MASK(b))
#define MASK2L(a,b) (MASKL(a) & ~MASKL(b))
#endif
#define MAKE_REG32(a) (*(volatile uint32_t *)(a))
#define ALIGN(m) __attribute__((aligned(m)))
@ -108,12 +112,13 @@ static inline bool overlaps_a(const void *as, const void *ae, const void *bs, co
static inline bool check_32bit_address_range_in_program(uintptr_t addr, size_t size) {
extern uint8_t __chainloader_start__[], __chainloader_end__[];
extern uint8_t __stack_bottom__[], __stack_top__[];
extern uint8_t __dram_start__[], __dram_end__[];
extern uint8_t __start__[], __end__[];
uint8_t *start = (uint8_t *)addr, *end = start + size;
return overlaps_a(start, end, __chainloader_start__, __chainloader_end__) ||
overlaps_a(start, end, __stack_bottom__, __stack_top__) ||
overlaps_a(start, end, (void *)0xC0000000, (void *)0xC03C0000) || /* framebuffer */
overlaps_a(start, end, __dram_start__, __dram_end__) ||
overlaps_a(start, end, __start__, __end__);
}

View file

@ -63,3 +63,4 @@
/* Unofficial. */
#include <vapours/results/exosphere_results.hpp>
#include <vapours/results/xusb_gadget_results.hpp>

View file

@ -0,0 +1,47 @@
/*
* 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::xusb {
/* Please note: These results are all custom, and not official. */
R_DEFINE_NAMESPACE_RESULT_MODULE(445);
/* Result 1-1000 reserved for Atmosphere. */
/* USB protocol-level results. */
R_DEFINE_ERROR_RESULT(InvalidDeviceState, 1);
R_DEFINE_ERROR_RESULT(MalformedSetupRequest, 2);
R_DEFINE_ERROR_RESULT(UnknownSetupRequest, 3);
R_DEFINE_ERROR_RESULT(UnknownDescriptorType, 4);
R_DEFINE_ERROR_RESULT(InvalidDescriptorIndex, 5);
R_DEFINE_ERROR_RESULT(InvalidAddress, 6);
R_DEFINE_ERROR_RESULT(ControlEndpointBusy, 7);
R_DEFINE_ERROR_RESULT(InvalidSetupPacketSequenceNumber, 8);
R_DEFINE_ERROR_RESULT(InvalidControlEndpointState, 9);
R_DEFINE_ERROR_RESULT(InvalidConfiguration, 10);
R_DEFINE_ERROR_RESULT(TransferRingFull, 11);
/* Gadget-level results. */
R_DEFINE_ERROR_RESULT(UnexpectedCompletionCode, 101);
R_DEFINE_ERROR_RESULT(UnexpectedEndpoint, 102);
R_DEFINE_ERROR_RESULT(UnexpectedTRB, 103);
R_DEFINE_ERROR_RESULT(DownloadTooLarge, 104);
}