mirror of
https://github.com/Atmosphere-NX/Atmosphere.git
synced 2025-04-22 04:24:48 +00:00
fusee-primary: add fastboot usb gadget
This commit is contained in:
parent
d29baa337e
commit
ebc3de045a
34 changed files with 5444 additions and 9 deletions
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
76
fusee/fusee-primary/src/fastboot/fastboot.cpp
Normal file
76
fusee/fusee-primary/src/fastboot/fastboot.cpp
Normal 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;
|
||||
}
|
36
fusee/fusee-primary/src/fastboot/fastboot.h
Normal file
36
fusee/fusee-primary/src/fastboot/fastboot.h
Normal 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
|
192
fusee/fusee-primary/src/fastboot/fastboot_descriptors.inc
Normal file
192
fusee/fusee-primary/src/fastboot/fastboot_descriptors.inc
Normal 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
|
402
fusee/fusee-primary/src/fastboot/fastboot_gadget.cpp
Normal file
402
fusee/fusee-primary/src/fastboot/fastboot_gadget.cpp
Normal 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
|
116
fusee/fusee-primary/src/fastboot/fastboot_gadget.h
Normal file
116
fusee/fusee-primary/src/fastboot/fastboot_gadget.h
Normal 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
|
229
fusee/fusee-primary/src/fastboot/fastboot_impl.cpp
Normal file
229
fusee/fusee-primary/src/fastboot/fastboot_impl.cpp
Normal 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
|
60
fusee/fusee-primary/src/fastboot/fastboot_impl.h
Normal file
60
fusee/fusee-primary/src/fastboot/fastboot_impl.h
Normal 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
|
31
fusee/fusee-primary/src/fastboot/fastboot_interface.h
Normal file
31
fusee/fusee-primary/src/fastboot/fastboot_interface.h
Normal 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
|
489
fusee/fusee-primary/src/fastboot/usb_types.h
Normal file
489
fusee/fusee-primary/src/fastboot/usb_types.h
Normal 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
|
600
fusee/fusee-primary/src/fastboot/xusb.cpp
Normal file
600
fusee/fusee-primary/src/fastboot/xusb.cpp
Normal 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
|
57
fusee/fusee-primary/src/fastboot/xusb.h
Normal file
57
fusee/fusee-primary/src/fastboot/xusb.h
Normal 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
|
414
fusee/fusee-primary/src/fastboot/xusb_control.cpp
Normal file
414
fusee/fusee-primary/src/fastboot/xusb_control.cpp
Normal 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
|
48
fusee/fusee-primary/src/fastboot/xusb_control.h
Normal file
48
fusee/fusee-primary/src/fastboot/xusb_control.h
Normal 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
|
368
fusee/fusee-primary/src/fastboot/xusb_endpoint.cpp
Normal file
368
fusee/fusee-primary/src/fastboot/xusb_endpoint.cpp
Normal 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
|
346
fusee/fusee-primary/src/fastboot/xusb_endpoint.h
Normal file
346
fusee/fusee-primary/src/fastboot/xusb_endpoint.h
Normal 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
|
120
fusee/fusee-primary/src/fastboot/xusb_event_ring.cpp
Normal file
120
fusee/fusee-primary/src/fastboot/xusb_event_ring.cpp
Normal 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
|
38
fusee/fusee-primary/src/fastboot/xusb_event_ring.h
Normal file
38
fusee/fusee-primary/src/fastboot/xusb_event_ring.h
Normal 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
|
287
fusee/fusee-primary/src/fastboot/xusb_test_gadget.cpp
Normal file
287
fusee/fusee-primary/src/fastboot/xusb_test_gadget.cpp
Normal 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
|
29
fusee/fusee-primary/src/fastboot/xusb_test_gadget.h
Normal file
29
fusee/fusee-primary/src/fastboot/xusb_test_gadget.h
Normal 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
|
185
fusee/fusee-primary/src/fastboot/xusb_trb.h
Normal file
185
fusee/fusee-primary/src/fastboot/xusb_trb.h
Normal 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
|
|
@ -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);
|
||||
|
|
33
fusee/fusee-primary/src/reg/reg_apbdev_pmc.h
Normal file
33
fusee/fusee-primary/src/reg/reg_apbdev_pmc.h
Normal 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
|
221
fusee/fusee-primary/src/reg/reg_car.h
Normal file
221
fusee/fusee-primary/src/reg/reg_car.h
Normal 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
|
38
fusee/fusee-primary/src/reg/reg_fuse.h
Normal file
38
fusee/fusee-primary/src/reg/reg_fuse.h
Normal 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
|
495
fusee/fusee-primary/src/reg/reg_util.h
Normal file
495
fusee/fusee-primary/src/reg/reg_util.h
Normal 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 ®, 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 ®
|
||||
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
|
222
fusee/fusee-primary/src/reg/reg_xusb_dev.h
Normal file
222
fusee/fusee-primary/src/reg/reg_xusb_dev.h
Normal 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
|
211
fusee/fusee-primary/src/reg/reg_xusb_padctl.h
Normal file
211
fusee/fusee-primary/src/reg/reg_xusb_padctl.h
Normal 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
|
|
@ -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__);
|
||||
}
|
||||
|
||||
|
|
|
@ -63,3 +63,4 @@
|
|||
|
||||
/* Unofficial. */
|
||||
#include <vapours/results/exosphere_results.hpp>
|
||||
#include <vapours/results/xusb_gadget_results.hpp>
|
||||
|
|
|
@ -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);
|
||||
|
||||
}
|
Loading…
Add table
Reference in a new issue