diff --git a/common/memory_map.h b/common/memory_map.h
index 372c7c1..16272c9 100644
--- a/common/memory_map.h
+++ b/common/memory_map.h
@@ -86,6 +86,14 @@
#define NX_BIS_CACHE_ADDR 0xFEE00000
#define NX_BIS_CACHE_SZ 0x8800
+// USB buffers.
+#define USBD_ADDR 0xFEF00000
+#define USB_DESCRIPTOR_ADDR 0xFEF40000
+#define USB_EP_CONTROL_BUF_ADDR 0xFEF80000
+#define USB_EP_BULK_IN_BUF_ADDR 0xFF000000
+#define USB_EP_BULK_OUT_BUF_ADDR 0xFF800000
+#define USB_EP_BULK_OUT_MAX_XFER 0x800000
+
// #define EXT_PAYLOAD_ADDR 0xC0000000
// #define RCM_PAYLOAD_ADDR (EXT_PAYLOAD_ADDR + ALIGN(PATCHED_RELOC_SZ, 0x10))
// #define COREBOOT_ADDR (0xD0000000 - rom_size)
diff --git a/nyx/nyx_gui/usb/usb_t210.h b/nyx/nyx_gui/usb/usb_t210.h
new file mode 100644
index 0000000..f36900e
--- /dev/null
+++ b/nyx/nyx_gui/usb/usb_t210.h
@@ -0,0 +1,172 @@
+/*
+ * USB driver for Tegra X1
+ *
+ * Copyright (c) 2019 CTCaer
+ *
+ * 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 .
+ */
+
+#ifndef _USB_T210_H_
+#define _USB_T210_H_
+
+#include "../utils/types.h"
+
+/* General USB registers */
+#define USB1_IF_USB_SUSP_CTRL 0x400
+#define SUSP_CTRL_USB_WAKE_ON_CNNT_EN_DEV (1 << 3)
+#define SUSP_CTRL_USB_WAKE_ON_DISCON_EN_DEV (1 << 4)
+#define SUSP_CTRL_USB_PHY_CLK_VALID (1 << 7)
+#define SUSP_CTRL_UTMIP_RESET (1 << 11)
+#define SUSP_CTRL_UTMIP_PHY_ENB (1 << 12)
+#define SUSP_CTRL_UTMIP_UTMIP_SUSPL1_SET (1 << 25)
+#define USB1_IF_USB_PHY_VBUS_SENSORS 0x404
+#define USB1_UTMIP_XCVR_CFG0 0x808
+#define USB1_UTMIP_BIAS_CFG0 0x80C
+#define USB1_UTMIP_HSRX_CFG0 0x810
+#define USB1_UTMIP_HSRX_CFG1 0x814
+#define USB1_UTMIP_TX_CFG0 0x820
+#define USB1_UTMIP_MISC_CFG1 0x828
+#define USB1_UTMIP_DEBOUNCE_CFG0 0x82C
+#define USB1_UTMIP_SPARE_CFG0 0x834
+#define USB1_UTMIP_XCVR_CFG1 0x838
+#define USB1_UTMIP_BIAS_CFG1 0x83C
+#define USB1_UTMIP_BIAS_CFG2 0x850
+#define USB1_UTMIP_XCVR_CFG2 0x854
+#define USB1_UTMIP_XCVR_CFG3 0x858
+
+/* USB Queue Head Descriptor */
+#define USB2_QH_USB2D_QH_EP_BASE (USB_BASE + 0x1000)
+#define USB_QHD_EP_CAP_IOS_ENABLE (1 << 15)
+#define USB_QHD_EP_CAP_MAX_PKT_LEN_MASK 0x7FF
+#define USB_QHD_EP_CAP_ZERO_LEN_TERM_DIS (1 << 29)
+#define USB_QHD_EP_CAP_MULTI_NON_ISO (0 << 30)
+#define USB_QHD_EP_CAP_MULTI_1 (1 << 30)
+#define USB_QHD_EP_CAP_MULTI_2 (2 << 30)
+#define USB_QHD_EP_CAP_MULTI_3 (3 << 30)
+
+#define USB_QHD_TOKEN_XFER_ERROR (1 << 3)
+#define USB_QHD_TOKEN_BUFFER_ERROR (1 << 5)
+#define USB_QHD_TOKEN_HALTED (1 << 6)
+#define USB_QHD_TOKEN_ACTIVE (1 << 7)
+#define USB_QHD_TOKEN_MULT_OVERR_MASK (2 << 10)
+#define USB_QHD_TOKEN_IRQ_ON_COMPLETE (1 << 15)
+#define USB_QHD_TOKEN_TOTAL_BYTES_SHIFT 16
+
+/* USB_OTG/USB_1 controllers register bits */
+#define USB2D_PORTSC1_SUSP (1 << 7)
+
+#define USB2D_USBCMD_RUN (1 << 0)
+#define USB2D_USBCMD_RESET (1 << 1)
+#define USB2D_USBCMD_ITC_MASK (0xFF << 16)
+
+#define USB2D_USBSTS_UI (1 << 0)
+#define USB2D_USBSTS_UEI (1 << 1)
+#define USB2D_USBSTS_PCI (1 << 2)
+#define USB2D_USBSTS_FRI (1 << 3)
+#define USB2D_USBSTS_SEI (1 << 4)
+#define USB2D_USBSTS_AAI (1 << 5)
+#define USB2D_USBSTS_URI (1 << 6)
+#define USB2D_USBSTS_SRI (1 << 7)
+#define USB2D_USBSTS_SLI (1 << 8)
+
+#define USB2D_USBMODE_CM_MASK (3 << 0)
+#define USB2D_USBMODE_CM_IDLE 0
+#define USB2D_USBMODE_CM_RSVD 1
+#define USB2D_USBMODE_CM_DEVICE 2
+#define USB2D_USBMODE_CM_HOST 3
+
+#define USB2D_ENDPT_STATUS_RX_OFFSET (1 << 0)
+#define USB2D_ENDPT_STATUS_TX_OFFSET (1 << 16)
+
+#define USB2D_ENDPTCTRL_RX_EP_STALL (1 << 0)
+#define USB2D_ENDPTCTRL_RX_EP_TYPE_CTRL (0 << 2)
+#define USB2D_ENDPTCTRL_RX_EP_TYPE_ISO (1 << 2)
+#define USB2D_ENDPTCTRL_RX_EP_TYPE_BULK (2 << 2)
+#define USB2D_ENDPTCTRL_RX_EP_TYPE_INTR (3 << 2)
+#define USB2D_ENDPTCTRL_RX_EP_TYPE_MASK (3 << 2)
+#define USB2D_ENDPTCTRL_RX_EP_INHIBIT (1 << 5)
+#define USB2D_ENDPTCTRL_RX_EP_RESET (1 << 6)
+#define USB2D_ENDPTCTRL_RX_EP_ENABLE (1 << 7)
+#define USB2D_ENDPTCTRL_TX_EP_STALL (1 << 16)
+#define USB2D_ENDPTCTRL_TX_EP_TYPE_CTRL (0 << 18)
+#define USB2D_ENDPTCTRL_TX_EP_TYPE_ISO (1 << 18)
+#define USB2D_ENDPTCTRL_TX_EP_TYPE_BULK (2 << 18)
+#define USB2D_ENDPTCTRL_TX_EP_TYPE_INTR (3 << 18)
+#define USB2D_ENDPTCTRL_TX_EP_TYPE_MASK (3 << 18)
+#define USB2D_ENDPTCTRL_TX_EP_INHIBIT (1 << 21)
+#define USB2D_ENDPTCTRL_TX_EP_RESET (1 << 22)
+#define USB2D_ENDPTCTRL_TX_EP_ENABLE (1 << 23)
+
+#define USB2D_HOSTPC1_DEVLC_ASUS (1 << 17)
+#define USB2D_HOSTPC1_DEVLC_PHCD (1 << 22)
+#define USB2D_HOSTPC1_DEVLC_PSPD_MASK (3 << 25)
+
+#define USB2D_OTGSC_USB_ID_PULLUP (1 << 5)
+#define USB2D_OTGSC_USB_IRQ_STS_MASK (0x7F << 16)
+
+/* USB_OTG/USB_1 controllers registers */
+typedef struct _t210_usb2d_t
+{
+ vu32 id;
+ vu32 unk0;
+ vu32 hw_host;
+ vu32 hw_device;
+ vu32 hw_txbuf;
+ vu32 hw_rxbuf;
+ vu32 unk1[26];
+ vu32 gptimer0ld;
+ vu32 gptimer0ctrl;
+ vu32 gptimer1ld;
+ vu32 gptimer1ctrl;
+ vu32 unk2[28];
+ vu16 caplength;
+ vu16 hciversion;
+ vu32 hcsparams;
+ vu32 hccparams;
+ vu32 unk3[5];
+ vu32 dciversion;
+ vu32 dccparams;
+ vu32 extsts;
+ vu32 usbextintr;
+ vu32 usbcmd;
+ vu32 usbsts;
+ vu32 usbintr;
+ vu32 frindex;
+ vu32 unk4;
+ vu32 periodiclistbase;
+ vu32 asynclistaddr;
+ vu32 asyncttsts;
+ vu32 burstsize;
+ vu32 txfilltuning;
+ vu32 unk6;
+ vu32 icusb_ctrl;
+ vu32 ulpi_viewport;
+ vu32 rsvd0[4];
+ vu32 portsc1;
+ vu32 rsvd1[15];
+ vu32 hostpc1_devlc;
+ vu32 rsvd2[15];
+ vu32 otgsc;
+ vu32 usbmode;
+ vu32 unk10;
+ vu32 endptnak;
+ vu32 endptnak_enable;
+ vu32 endptsetupstat;
+ vu32 endptprime;
+ vu32 endptflush;
+ vu32 endptstatus;
+ vu32 endptcomplete;
+ vu32 endptctrl[16];
+} t210_usb2d_t;
+
+#endif
diff --git a/nyx/nyx_gui/usb/usbd.c b/nyx/nyx_gui/usb/usbd.c
new file mode 100644
index 0000000..15ec431
--- /dev/null
+++ b/nyx/nyx_gui/usb/usbd.c
@@ -0,0 +1,1672 @@
+/*
+ * USB Device driver for Tegra X1
+ *
+ * Copyright (c) 2019 CTCaer
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#include
+#include
+
+#include "usbd.h"
+#include "usb_descriptors.h"
+#include "usb_t210.h"
+
+#include "../gfx/gfx.h"
+#include "../soc/bpmp.h"
+#include "../soc/clock.h"
+#include "../soc/fuse.h"
+#include "../soc/gpio.h"
+#include "../soc/pinmux.h"
+#include "../soc/pmc.h"
+#include "../soc/t210.h"
+#include "../utils/btn.h"
+#include "../utils/util.h"
+
+#include "../../../common/memory_map.h"
+
+typedef enum
+{
+ USB_HW_EP0 = 0,
+ USB_HW_EP1 = 1
+} usb_hw_ep_t;
+
+typedef enum
+{
+ USB_EP_ADDR_CTRL_OUT = 0x00,
+ USB_EP_ADDR_CTRL_IN = 0x80,
+ USB_EP_ADDR_BULK_OUT = 0x01,
+ USB_EP_ADDR_BULK_IN = 0x81,
+} usb_ep_addr_t;
+
+typedef enum
+{
+ USB_EP_CFG_RESET = 0,
+ USB_EP_CFG_STALL = 1
+} usb_ep_cfg_t;
+
+typedef enum
+{
+ USB_EP_STATUS_IDLE = 0,
+ USB_EP_STATUS_ACTIVE = 1,
+ USB_EP_STATUS_ERROR = 2,
+ USB_EP_STATUS_NO_CONFIG = 3,
+ USB_EP_STATUS_STALLED = 4,
+ USB_EP_STATUS_DISABLED = 5
+} usb_ep_status_t;
+
+typedef enum {
+ USB_SETUP_RECIPIENT_DEVICE = 0,
+ USB_SETUP_RECIPIENT_INTERFACE = 1,
+ USB_SETUP_RECIPIENT_ENDPOINT = 2,
+ USB_SETUP_RECIPIENT_OTHER = 3,
+
+ USB_SETUP_TYPE_STANDARD = 0x00,
+ USB_SETUP_TYPE_CLASS = 0x20,
+ USB_SETUP_TYPE_VENDOR = 0x40,
+ USB_SETUP_TYPE_RESERVED = 0x60,
+
+ USB_SETUP_HOST_TO_DEVICE = 0x00,
+ USB_SETUP_DEVICE_TO_HOST = 0x80,
+} usb_setup_req_type_t;
+
+typedef enum {
+ USB_REQUEST_GET_STATUS = 0,
+ USB_REQUEST_CLEAR_FEATURE = 1,
+ USB_REQUEST_SET_FEATURE = 3,
+ USB_REQUEST_SET_ADDRESS = 5,
+ USB_REQUEST_GET_DESCRIPTOR = 6,
+ USB_REQUEST_SET_DESCRIPTOR = 7,
+ USB_REQUEST_GET_CONFIGURATION = 8,
+ USB_REQUEST_SET_CONFIGURATION = 9,
+ USB_REQUEST_GET_INTERFACE = 10,
+ USB_REQUEST_SET_INTERFACE = 11,
+ USB_REQUEST_SYNCH_FRAME = 12,
+
+ USB_REQUEST_GET_MS_DESCRIPTOR = 0x99,
+
+ USB_REQUEST_BULK_GET_MAX_LUN = 0xFE,
+ USB_REQUEST_BULK_RESET = 0xFF
+} usb_standard_req_t;
+
+typedef enum {
+ USB_FEATURE_ENDPOINT_HALT = 0,
+ USB_FEATURE_DEVICE_REMOTE_WAKEUP = 1,
+ USB_FEATURE_TEST_MODE = 2,
+} usb_get_status_req_t;
+
+typedef enum {
+ USB_STATUS_EP_OK = 0,
+ USB_STATUS_EP_HALTED = 1,
+
+ USB_STATUS_DEV_SELF_POWERED = 1,
+ USB_STATUS_DEV_REMOTE_WAKE = 2,
+} usb_set_clear_feature_req_t;
+
+typedef enum {
+ USB_XFER_DIR_OUT = 0,
+ USB_XFER_DIR_IN = 1,
+} usb_xfer_dir_t;
+
+typedef enum {
+ USB_SPEED_LOW = 0,
+ USB_SPEED_FULL = 1,
+ USB_SPEED_HIGH = 2,
+ USB_SPEED_SUPER = 3,
+} usb_speed_t;
+
+typedef enum {
+ USB_XFER_TYPE_CONTROL = 0,
+ USB_XFER_TYPE_ISOCHRONOUS = 1,
+ USB_XFER_TYPE_BULK = 2,
+ USB_XFER_TYPE_INTERRUPT = 3,
+} usb_xfer_type_t;
+
+typedef struct _dTD_t
+{
+ vu32 next_dTD;
+ vu32 info;
+ vu32 pages[5];
+ vu32 reserved;
+} dTD_t;
+
+typedef struct _dQH_t
+{
+ vu32 ep_capabilities;
+ vu32 curr_dTD_ptr;
+ vu32 next_dTD_ptr;
+ vu32 token;
+ vu32 buffers[5]; // hmmm.
+ vu32 reserved;
+ vu32 setup[2];
+ vu32 gap[4];
+} dQH_t;
+
+typedef struct _usbd_t
+{
+ volatile dTD_t dtds[4 * 4]; // 4 dTD per endpoint.
+ volatile dQH_t *qhs;
+ int ep_configured[4];
+ int ep_bytes_requested[4];
+} usbd_t;
+
+typedef struct _usb_ctrl_setup_t
+{
+ u8 bmRequestType;
+ u8 bRequest;
+ u16 wValue;
+ u16 wIndex;
+ u16 wLength;
+} usb_ctrl_setup_t;
+
+typedef struct _usbd_controller_t
+{
+ u32 port_speed;
+ t210_usb2d_t *regs;
+ usb_ctrl_setup_t control_setup;
+ usb_desc_t *desc;
+ usb_gadget_type type;
+ u8 configuration_set;
+ u8 usb_phy_ready;
+ u8 configuration;
+ u8 interface;
+ u8 max_lun;
+ u8 max_lun_set;
+ u8 bulk_reset_req;
+ u8 hid_report_sent;
+ bool charger_detect;
+} usbd_controller_t;
+
+u8 usb_serial_string_descriptor[26] =
+{
+ 26, 0x03,
+ 'C', 0x00, '7', 0x00, 'C', 0x00, '0', 0x00,
+ '9', 0x00, '2', 0x00, '4', 0x00, '2', 0x00, 'F', 0x00, '7', 0x00, '0', 0x00, '3', 0x00
+};
+
+u8 usb_lang_id_string_descriptor[] =
+{
+ 4, 3,
+ 0x09, 0x04
+};
+
+usbd_t *usbdaemon;
+
+usbd_controller_t *usbd_otg;
+usbd_controller_t usbd_usb_otg_controller_ctxt;
+
+bool usb_init_done = false;
+
+u8 *usb_ep0_ctrl_buf = (u8 *)USB_EP_CONTROL_BUF_ADDR;
+
+static int _usbd_reset_usb_otg_phy_device_mode()
+{
+ usbd_otg->usb_phy_ready = 0;
+
+ // Clear UTMIP reset.
+ USB(USB1_IF_USB_SUSP_CTRL) &= ~SUSP_CTRL_UTMIP_RESET;
+
+ // Wait for PHY clock to get validated.
+ u32 retries = 100000; // 200ms timeout.
+ while (!(USB(USB1_IF_USB_SUSP_CTRL) & SUSP_CTRL_USB_PHY_CLK_VALID))
+ {
+ retries--;
+ if (!retries)
+ return 1;
+ usleep(1);
+ }
+ usbd_otg->usb_phy_ready = 1;
+
+ // Clear all device addresses, enabled setup requests and transmit events.
+ usbd_otg->regs->periodiclistbase = 0;
+ usbd_otg->regs->endptsetupstat = usbd_otg->regs->endptsetupstat;
+ usbd_otg->regs->endptcomplete = usbd_otg->regs->endptcomplete;
+
+ // Stop device controller.
+ usbd_otg->regs->usbcmd &= ~USB2D_USBCMD_RUN;
+
+ // Set controller mode to idle.
+ usbd_otg->regs->usbmode &= ~USB2D_USBMODE_CM_MASK;
+
+ // Reset the controller.
+ usbd_otg->regs->usbcmd |= USB2D_USBCMD_RESET;
+
+ // Wait for the reset to complete.
+ retries = 100000; // 200ms timeout.
+ while (usbd_otg->regs->usbcmd & USB2D_USBCMD_RESET)
+ {
+ retries--;
+ if (!retries)
+ return 2;
+ usleep(1);
+ }
+
+ // Wait for PHY clock to get validated after reset.
+ retries = 100000; // 200ms timeout.
+ while (!(USB(USB1_IF_USB_SUSP_CTRL) & SUSP_CTRL_USB_PHY_CLK_VALID))
+ {
+ retries--;
+ if (!retries)
+ return 3;
+ usleep(1);
+ }
+
+ // Set controller to Device mode.
+ usbd_otg->regs->usbmode = (usbd_otg->regs->usbmode & ~USB2D_USBMODE_CM_MASK) | USB2D_USBMODE_CM_DEVICE;
+
+ // Wait for the selected mode to be enabled.
+ retries = 100000; // 200ms timeout.
+ while ((usbd_otg->regs->usbmode & USB2D_USBMODE_CM_MASK) != USB2D_USBMODE_CM_DEVICE)
+ {
+ retries--;
+ if (!retries)
+ return 4;
+ usleep(1);
+ }
+
+ // Disable all interrupts.
+ usbd_otg->regs->usbintr = 0;
+
+ // Set the ID pullup and disable all OTGSC interrupts.
+ usbd_otg->regs->otgsc = USB2D_OTGSC_USB_ID_PULLUP;
+
+ // Clear all relevant interrupt statuses.
+ usbd_otg->regs->usbsts =
+ USB2D_USBSTS_UI | USB2D_USBSTS_UEI | USB2D_USBSTS_PCI |
+ USB2D_USBSTS_FRI | USB2D_USBSTS_SEI | USB2D_USBSTS_AAI |
+ USB2D_USBSTS_URI | USB2D_USBSTS_SRI | USB2D_USBSTS_SLI;
+
+ // Disable and clear all OTGSC interrupts.
+ usbd_otg->regs->otgsc = USB2D_OTGSC_USB_IRQ_STS_MASK;
+
+ // Clear EP0, EP1, EP2 setup requests.
+ usbd_otg->regs->endptsetupstat = 7; //TODO: Shouldn't this be endptsetupstat = endptsetupstat?
+
+ // Set all interrupts to immediate.
+ usbd_otg->regs->usbcmd &= ~USB2D_USBCMD_ITC_MASK;
+
+ return 0;
+}
+
+static void _usb_charger_detect()
+{
+ // Charger detect init.
+ usbd_otg->charger_detect = 0;
+ bool charger_detect_enable = FUSE(FUSE_RESERVED_SW) & 0x10; // Disabled on Switch production.
+ if (charger_detect_enable)
+ {
+ usbd_otg->charger_detect |= 1;
+ // Configure detect pin.
+ PINMUX_AUX(PINMUX_AUX_LCD_GPIO1) &= ~(PINMUX_PARKED | PINMUX_TRISTATE | PINMUX_PULL_MASK);
+ gpio_config(GPIO_PORT_V, GPIO_PIN_3, GPIO_MODE_GPIO);
+
+ // Configure charger pin.
+ PINMUX_AUX(PINMUX_AUX_USB_VBUS_EN0) &=
+ ~(PINMUX_INPUT_ENABLE | PINMUX_PARKED | PINMUX_TRISTATE | PINMUX_PULL_MASK);
+ gpio_config(GPIO_PORT_CC, GPIO_PIN_5, GPIO_MODE_GPIO);
+ gpio_output_enable(GPIO_PORT_CC, GPIO_PIN_5, GPIO_OUTPUT_ENABLE);
+
+ // Enable charger.
+ if (gpio_read(GPIO_PORT_V, GPIO_PIN_3))
+ {
+ usbd_otg->charger_detect |= 2;
+ gpio_write(GPIO_PORT_CC, GPIO_PIN_5, GPIO_HIGH);
+ usbd_otg->charger_detect |= 0x100;
+ USB(USB1_UTMIP_BAT_CHRG_CFG0) = BAT_CHRG_CFG0_OP_SRC_EN; // Clears UTMIP_PD_CHRG and enables charger detect.
+ usleep(5000);
+ }
+ }
+}
+
+int usb_device_init()
+{
+ if (usb_init_done)
+ return 0;
+
+ // Configure PLLU.
+ CLOCK(CLK_RST_CONTROLLER_PLLU_MISC) = CLOCK(CLK_RST_CONTROLLER_PLLU_MISC) | 0x20000000; // Disable reference clock.
+ u32 pllu_cfg = (((((CLOCK(CLK_RST_CONTROLLER_PLLU_BASE) >> 8 << 8) | 2) & 0xFFFF00FF) | ((0x19 << 8) & 0xFFFF)) & 0xFFE0FFFF) | (1<< 16) | 0x1000000;
+ CLOCK(CLK_RST_CONTROLLER_PLLU_BASE) = pllu_cfg;
+ CLOCK(CLK_RST_CONTROLLER_PLLU_BASE) = pllu_cfg | 0x40000000; // Enable.
+
+ // Wait for PLL to stabilize.
+ u32 timeout = (u32)TMR(TIMERUS_CNTR_1US) + 1300;
+ while (!(CLOCK(CLK_RST_CONTROLLER_PLLU_BASE) & (1 << 27))) // PLL_LOCK.
+ if ((u32)TMR(TIMERUS_CNTR_1US) > timeout)
+ break;
+ usleep(10);
+
+ // Enable PLLU USB/HSIC/ICUSB/48M.
+ CLOCK(CLK_RST_CONTROLLER_PLLU_BASE) = CLOCK(CLK_RST_CONTROLLER_PLLU_BASE) | 0x2600000 | 0x800000;
+
+ // Enable USBD clock.
+ CLOCK(CLK_RST_CONTROLLER_CLK_ENB_L_SET) = (1 << 22);
+ usleep(2);
+ CLOCK(CLK_RST_CONTROLLER_RST_DEV_L_SET) = (1 << 22);
+ usleep(2);
+ CLOCK(CLK_RST_CONTROLLER_RST_DEV_L_CLR) = (1 << 22);
+ usleep(2);
+
+ // Clear XUSB_PADCTL reset
+ CLOCK(CLK_RST_CONTROLLER_RST_DEV_W_CLR) = (1 << 14);
+
+ // Enable USB PHY and reset for programming.
+ u32 usb_susp_ctrl = USB(USB1_IF_USB_SUSP_CTRL);
+ USB(USB1_IF_USB_SUSP_CTRL) = usb_susp_ctrl | SUSP_CTRL_UTMIP_RESET;
+ USB(USB1_IF_USB_SUSP_CTRL) = usb_susp_ctrl | SUSP_CTRL_UTMIP_PHY_ENB | SUSP_CTRL_UTMIP_RESET;
+
+ // Disable UTMIPLL IDDQ.
+ CLOCK(CLK_RST_CONTROLLER_UTMIPLL_HW_PWRDN_CFG0) &= 0xFFFFFFFD;
+ usleep(10);
+
+ // Disable crystal clock.
+ USB(USB1_UTMIP_MISC_CFG1) &= 0xBFFFFFFF;
+ CLOCK(CLK_RST_CONTROLLER_UTMIP_PLL_CFG2) &= 0xBFFFFFFF;
+
+ // Set B_SESS_VLD.
+ USB(USB1_IF_USB_PHY_VBUS_SENSORS) |= 0x1000;
+ USB(USB1_IF_USB_PHY_VBUS_SENSORS) |= 0x800;
+
+ // Set UTMIPLL dividers and enable it.
+ CLOCK(CLK_RST_CONTROLLER_UTMIP_PLL_CFG0) = (CLOCK(CLK_RST_CONTROLLER_UTMIP_PLL_CFG0) & 0xFF0000FF) | 0x190000 | 0x100;
+ CLOCK(CLK_RST_CONTROLLER_UTMIP_PLL_CFG2) = (CLOCK(CLK_RST_CONTROLLER_UTMIP_PLL_CFG2) & 0xFF00003F) | 0x600000; // Set delay count for 38.4Mhz osc crystal.
+ CLOCK(CLK_RST_CONTROLLER_UTMIP_PLL_CFG1) = ((CLOCK(CLK_RST_CONTROLLER_UTMIP_PLL_CFG1) & 0x7FFF000) | 0x8000 | 0x177) & 0xFFFFAFFF;
+
+ // Wait for UTMIPLL to stabilize.
+ u32 retries = 10; // Wait 20us
+ while (!(CLOCK(CLK_RST_CONTROLLER_UTMIPLL_HW_PWRDN_CFG0) & 0x80000000) && retries)
+ {
+ usleep(1);
+ retries--;
+ }
+
+ // Configure UTMIP Transceiver Cells.
+ u32 fuse_usb_calib = FUSE(FUSE_USB_CALIB);
+ USB(USB1_UTMIP_XCVR_CFG0) = (((USB(USB1_UTMIP_XCVR_CFG0) & 0xFFFFFFF0) | (fuse_usb_calib & 0xF)) & 0xFE3FFFFF) | ((fuse_usb_calib & 0x3F) << 25 >> 29 << 22);
+ USB(USB1_UTMIP_XCVR_CFG1) = (USB(USB1_UTMIP_XCVR_CFG1) & 0xFFC3FFFF) | ((fuse_usb_calib << 21) >> 28 << 18);
+ USB(USB1_UTMIP_XCVR_CFG3) = (USB(USB1_UTMIP_XCVR_CFG3) & 0xFFFFC1FF) | ((FUSE(FUSE_USB_CALIB_EXT) & 0x1F) << 9);
+ USB(USB1_UTMIP_XCVR_CFG0) &= 0xFFDFFFFF;
+ USB(USB1_UTMIP_XCVR_CFG2) = (USB(USB1_UTMIP_XCVR_CFG2) & 0xFFFFF1FF) | 0x400;
+ usleep(1);
+
+ // Configure misc UTMIP.
+ USB(USB1_UTMIP_DEBOUNCE_CFG0) = (USB(USB1_UTMIP_DEBOUNCE_CFG0) & 0xFFFF0000) | 0xBB80;
+ USB(USB1_UTMIP_BIAS_CFG1) = (USB(USB1_UTMIP_BIAS_CFG1) & 0xFFFFC0FF) | 0x100; // when osc is 38.4KHz
+
+ //USB(USB1_UTMIP_SPARE_CFG0) &= 0xFFFFFEE7; unpatched0
+ USB(USB1_UTMIP_BIAS_CFG2) |= 2; //patched0 - UTMIP_HSSQUELCH_LEVEL_NEW: 2.
+ USB(USB1_UTMIP_SPARE_CFG0) &= 0xFFFFFE67; //patched0 - FUSE_HS_IREF_CAP_CFG
+ USB(USB1_UTMIP_TX_CFG0) |= 0x80000;
+
+ //USB(USB1_UTMIP_HSRX_CFG0) = (USB(USB1_UTMIP_HSRX_CFG0) & 0xFFF003FF) | 0x88000 | 0x4000; unpatched1
+ USB(USB1_UTMIP_HSRX_CFG0) = (USB(USB1_UTMIP_HSRX_CFG0) & 0xF0F003FF) | 0x88000 | 0x4000; //patched1 - reset UTMIP_PCOUNT_UPDN_DIV: From 1 to 0.
+ USB(USB1_UTMIP_BIAS_CFG2) &= 0xFFFFFFF8; //patched1 - UTMIP_HSSQUELCH_LEVEL_NEW: 0
+
+ USB(USB1_UTMIP_HSRX_CFG1) = (USB(USB1_UTMIP_HSRX_CFG1) & 0xFFFFFFC1) | 0x12;
+ USB(USB1_UTMIP_MISC_CFG1) |= 0x40000000;
+
+ // Enable crystal clock.
+ CLOCK(CLK_RST_CONTROLLER_UTMIP_PLL_CFG2) |= 0x40000000;
+ // Enable USB2 tracking.
+ CLOCK(CLK_RST_CONTROLLER_CLK_OUT_ENB_Y) |= 0x40000;
+ CLOCK(CLK_RST_CONTROLLER_CLK_SOURCE_USB2_HSIC_TRK) = (CLOCK(CLK_RST_CONTROLLER_CLK_SOURCE_USB2_HSIC_TRK) & 0xFFFFFF00) | 6; // Set trank divisor to 4.
+
+ USB(USB1_UTMIP_BIAS_CFG1) = (USB(USB1_UTMIP_BIAS_CFG1) & 0xFFC03F07) | 0x78000 | 0x50; // Set delays.
+ USB(USB1_UTMIP_BIAS_CFG0) &= 0xFFFFFBFF; // Disable Power down bias circuit.
+ usleep(1);
+
+ // Force PDTRK input into power up.
+ USB(USB1_UTMIP_BIAS_CFG1) = (USB(USB1_UTMIP_BIAS_CFG1) & 0xFFFFFFFE) | 2;
+ usleep(100);
+
+ // TRK cycle done. Force PDTRK input into power down.
+ USB(USB1_UTMIP_BIAS_CFG1) = (USB(USB1_UTMIP_BIAS_CFG1) & 0xFF7FFFFF) | 1;
+ usleep(3);
+
+ // Force PDTRK input into power up.
+ USB(USB1_UTMIP_BIAS_CFG1) = USB(USB1_UTMIP_BIAS_CFG1) & 0xFFFFFFFE;
+ usleep(100);
+
+ // TRK cycle done. Force PDTRK input into power down.
+ USB(USB1_UTMIP_BIAS_CFG1) = (USB(USB1_UTMIP_BIAS_CFG1) & 0xFF7FFFFF) | 1;
+
+ // Disable USB2_TRK clock and configure UTMIP misc.
+ CLOCK(CLK_RST_CONTROLLER_CLK_OUT_ENB_Y) &= 0xFFFBFFFF;
+ CLOCK(CLK_RST_CONTROLLER_UTMIP_PLL_CFG2) = (CLOCK(CLK_RST_CONTROLLER_UTMIP_PLL_CFG2) & 0xFEFFFFEA) | 0x2000000 | 0x28 | 2;
+ usleep(1);
+
+ USB(USB1_UTMIP_BIAS_CFG0) &= 0xFF3FF7FF;
+ usleep(1);
+
+ // Clear power downs on UTMIP ID and VBUS wake up, PD, PD2, PDZI, PDCHRP, PDDR.
+ PMC(APBDEV_PMC_USB_AO) &= 0xFFFFFFF3; // UTMIP ID and VBUS wake up.
+ usleep(1);
+ USB(USB1_UTMIP_XCVR_CFG0) &= 0xFFFFBFFF; // UTMIP_FORCE_PD_POWERDOWN.
+ usleep(1);
+ USB(USB1_UTMIP_XCVR_CFG0) &= 0xFFFEFFFF; // UTMIP_FORCE_PD2_POWERDOWN.
+ usleep(1);
+ USB(USB1_UTMIP_XCVR_CFG0) &= 0xFFFBFFFF; // UTMIP_FORCE_PDZI_POWERDOWN.
+ usleep(1);
+ USB(USB1_UTMIP_XCVR_CFG1) &= 0xFFFFFFFB; // UTMIP_FORCE_PDCHRP_POWERDOWN.
+ usleep(1);
+ USB(USB1_UTMIP_XCVR_CFG1) &= 0xFFFFFFEF; // UTMIP_FORCE_PDDR_POWERDOWN.
+ usleep(1);
+
+ // AHB USB performance cfg.
+ AHB_GIZMO(AHB_GIZMO_AHB_MEM) |= AHB_MEM_ENB_FAST_REARBITRATE;
+ AHB_GIZMO(AHB_GIZMO_USB) |= AHB_GIZMO_USB_IMMEDIATE;
+ AHB_GIZMO(AHB_ARBITRATION_PRIORITY_CTRL) |= ARBITRATION_PRIORITY_CTRL_ENB_FAST_REARBITRATE;
+ AHB_GIZMO(AHB_AHB_MEM_PREFETCH_CFG1) =
+ MEM_PREFETCH_ENABLE | (MEM_PREFETCH_AHB_MST_USB << 26) | (12 << 21) | 0x1000; // addr boundary 64KB
+
+ // Set software and hardware context storage and clear it.
+ usbdaemon = (usbd_t *)USBD_ADDR; // Depends on USB_TD_BUFFER_PAGE_SIZE aligned address.
+ usbd_otg = &usbd_usb_otg_controller_ctxt;
+ memset(usbd_otg, 0, sizeof(usbd_controller_t));
+ memset(usbdaemon, 0, sizeof(usbd_t));
+
+ usbd_otg->regs = (t210_usb2d_t *)USB_OTG_BASE;
+ usbd_otg->usb_phy_ready = 0;
+
+ // Initialize USB PHY on the USB_OTG Controller (#1) in Device mode.
+ int result = _usbd_reset_usb_otg_phy_device_mode();
+ usbd_otg->configuration_set = 0;
+
+ _usb_charger_detect();
+
+ if (!result)
+ usb_init_done = true;
+
+ return result;
+}
+
+static void _usb_device_power_down()
+{
+ // Enable PHY low power suspend.
+ usbd_otg->regs->hostpc1_devlc |= USB2D_HOSTPC1_DEVLC_PHCD;
+ // Do not use any controller regs after the above!
+ // A reset or clear of the PHCD suspend bit must happen.
+
+ // Power down OTG and Bias circuits.
+ USB(USB1_UTMIP_BIAS_CFG0) |= (1 << 11) | (1 << 10); // UTMIP_OTGPD, UTMIP_BIASPD.
+
+ // Power down ID detectors.
+ USB(USB1_UTMIP_BIAS_CFG0) |= (1 << 23) | (1 << 22); //UTMIP_IDPD_SEL, UTMIP_IDPD_VAL.
+
+ if (usbd_otg->charger_detect)
+ {
+ USB(USB1_UTMIP_BAT_CHRG_CFG0) = 1; //UTMIP_PD_CHRG
+ usbd_otg->charger_detect = 0;
+ }
+
+ // Power down the UTMIP transceivers.
+ // UTMIP_FORCE_PDZI_POWERDOWN, UTMIP_FORCE_PD2_POWERDOWN, UTMIP_FORCE_PD_POWERDOWN.
+ USB(USB1_UTMIP_XCVR_CFG0) |= (1 << 18) | (1 << 16) |(1 << 14);
+ // UTMIP_FORCE_PDDR_POWERDOWN, UTMIP_FORCE_PDCHRP_POWERDOWN, UTMIP_FORCE_PDDISC_POWERDOWN.
+ USB(USB1_UTMIP_XCVR_CFG1) |= (1 << 4) | (1 << 2) | (1 << 0);
+
+ // Keep UTMIP in reset.
+ USB(USB1_IF_USB_SUSP_CTRL) |= SUSP_CTRL_UTMIP_RESET;
+
+ // Power down PD trunk.
+ USB(USB1_UTMIP_BIAS_CFG1) |= (1 << 0); //UTMIP_FORCE_PDTRK_POWERDOWN.
+
+ // Force UTMIP_PLL power down.
+ CLOCK(CLK_RST_CONTROLLER_UTMIP_PLL_CFG1) |= (1 << 14); // UTMIP_FORCE_PLL_ENABLE_POWERDOWN.
+ CLOCK(CLK_RST_CONTROLLER_UTMIP_PLL_CFG1) |= (1 << 12); // UTMIP_FORCE_PLL_ACTIVE_POWERDOWN.
+ CLOCK(CLK_RST_CONTROLLER_UTMIP_PLL_CFG2) |= (1 << 4) | (1 << 0); // UTMIP_FORCE_PD_SAMP_A/C_POWERDOWN.
+ CLOCK(CLK_RST_CONTROLLER_UTMIP_PLL_CFG1) |= (1 << 16); // UTMIP_FORCE_PLLU_POWERDOWN.
+
+ // Disable crystal clock.
+ USB(USB1_UTMIP_MISC_CFG1) &= 0xBFFFFFFF;
+
+ // Enable UTMIPLL IDDQ.
+ CLOCK(CLK_RST_CONTROLLER_UTMIPLL_HW_PWRDN_CFG0) |= 2;
+
+ // Set XUSB_PADCTL reset
+ CLOCK(CLK_RST_CONTROLLER_RST_DEV_W_SET) = (1 << 14);
+
+ // Disable USBD clock.
+ CLOCK(CLK_RST_CONTROLLER_CLK_ENB_L_CLR) = (1 << 22);
+
+ // Completely disable PLLU.
+ CLOCK(CLK_RST_CONTROLLER_PLLU_BASE) &= ~0x2E00000; // Disable PLLU USB/HSIC/ICUSB/48M.
+ CLOCK(CLK_RST_CONTROLLER_PLLU_BASE) &= ~0x40000000; // Disable PLLU.
+ CLOCK(CLK_RST_CONTROLLER_PLLU_MISC) &= ~0x20000000; // Enable reference clock.
+
+ usb_init_done = false;
+}
+
+static void _usbd_stall_reset_ep1(usb_xfer_dir_t direction, usb_ep_cfg_t stall)
+{
+ stall &= 1;
+ if (direction == USB_XFER_DIR_IN)
+ {
+ usbd_otg->regs->endptctrl[1] = (usbd_otg->regs->endptctrl[1] & ~USB2D_ENDPTCTRL_TX_EP_STALL) | ((u32)stall << 16);
+ if (!stall)
+ usbd_otg->regs->endptctrl[1] |= USB2D_ENDPTCTRL_TX_EP_RESET;
+ }
+ else
+ {
+ usbd_otg->regs->endptctrl[1] = (usbd_otg->regs->endptctrl[1] & ~USB2D_ENDPTCTRL_RX_EP_STALL) | stall;
+ if (!stall)
+ usbd_otg->regs->endptctrl[1] |= USB2D_ENDPTCTRL_RX_EP_RESET;
+ }
+}
+
+void usbd_end(bool reset_ep, bool only_controller)
+{
+ if (reset_ep)
+ {
+ usbd_flush_endpoint(USB_EP_ALL);
+ _usbd_stall_reset_ep1(0, USB_EP_CFG_RESET); // EP1 Bulk IN.
+ _usbd_stall_reset_ep1(1, USB_EP_CFG_RESET); // EP1 Bulk OUT.
+ //TODO: what about EP0 simultaneous in/out reset.
+
+ usbd_otg->configuration = 0;
+ usbd_otg->interface = 0;
+ usbd_otg->configuration_set = 0;
+ usbd_otg->max_lun_set = 0;
+ }
+
+ // Stop device controller.
+ usbd_otg->regs->usbcmd &= ~USB2D_USBCMD_RUN;
+
+ // Enable PHY auto low power suspend.
+ usbd_otg->regs->hostpc1_devlc |= USB2D_HOSTPC1_DEVLC_ASUS;
+
+ if (!only_controller)
+ _usb_device_power_down();
+}
+
+void usb_device_stall_ep1_bulk_out()
+{
+ _usbd_stall_reset_ep1(USB_XFER_DIR_OUT, USB_EP_CFG_STALL);
+}
+
+void usb_device_stall_ep1_bulk_in()
+{
+ _usbd_stall_reset_ep1(USB_XFER_DIR_IN, USB_EP_CFG_STALL);
+}
+
+int usbd_get_max_pkt_length(int endpoint)
+{
+ switch (endpoint)
+ {
+ case USB_EP_CTRL_OUT:
+ case USB_EP_CTRL_IN:
+ return 64;
+ case USB_EP_BULK_OUT:
+ case USB_EP_BULK_IN:
+ if (usbd_otg->port_speed == 2)
+ return 512;
+ else
+ return 64;
+ default:
+ return 64;
+ }
+}
+
+static void _usbd_initialize_ep_ctrl(u32 endpoint)
+{
+ usb_hw_ep_t actual_ep = (endpoint & 2) >> 1;
+ usb_xfer_dir_t direction = endpoint & 1;
+
+ memset((void *)&usbdaemon->qhs[endpoint], 0, sizeof(dQH_t));
+
+ if (!endpoint)
+ usbdaemon->qhs[endpoint].ep_capabilities = USB_QHD_EP_CAP_IOS_ENABLE;
+
+ usbdaemon->qhs[endpoint].next_dTD_ptr = 1; // TERMINATE_SET
+
+ u32 max_packet_len = usbd_get_max_pkt_length(endpoint) & USB_QHD_EP_CAP_MAX_PKT_LEN_MASK;
+ usbdaemon->qhs[endpoint].ep_capabilities |= max_packet_len << 16;
+
+ if (direction == USB_XFER_DIR_IN)
+ {
+ u32 endpoint_type = usbd_otg->regs->endptctrl[actual_ep] & ~USB2D_ENDPTCTRL_TX_EP_TYPE_MASK;
+ if (actual_ep)
+ endpoint_type |= usbd_otg->type ? USB2D_ENDPTCTRL_TX_EP_TYPE_INTR : USB2D_ENDPTCTRL_TX_EP_TYPE_BULK;
+ else
+ endpoint_type |= USB2D_ENDPTCTRL_TX_EP_TYPE_CTRL;
+
+ usbd_otg->regs->endptctrl[actual_ep] = endpoint_type;
+
+ usbd_otg->regs->endptctrl[actual_ep] &= ~USB2D_ENDPTCTRL_TX_EP_STALL;
+
+ if (actual_ep == USB_HW_EP1)
+ usbd_otg->regs->endptctrl[1] |= USB2D_ENDPTCTRL_TX_EP_RESET;
+
+ usbd_otg->regs->endptctrl[actual_ep] |= USB2D_ENDPTCTRL_TX_EP_ENABLE;
+ }
+ else // EP Bulk OUT.
+ {
+ u32 endpoint_type = usbd_otg->regs->endptctrl[actual_ep] & ~USB2D_ENDPTCTRL_RX_EP_TYPE_MASK;
+ if (actual_ep)
+ {
+ endpoint_type |= usbd_otg->type ? USB2D_ENDPTCTRL_RX_EP_TYPE_INTR : USB2D_ENDPTCTRL_RX_EP_TYPE_BULK;
+ }
+ else
+ endpoint_type |= USB2D_ENDPTCTRL_RX_EP_TYPE_CTRL;
+
+ usbd_otg->regs->endptctrl[actual_ep] = endpoint_type;
+ usbd_otg->regs->endptctrl[actual_ep] &= ~USB2D_ENDPTCTRL_RX_EP_STALL;
+
+ if (actual_ep == USB_HW_EP1)
+ usbd_otg->regs->endptctrl[1] |= USB2D_ENDPTCTRL_RX_EP_RESET;
+
+ usbd_otg->regs->endptctrl[actual_ep] |= USB2D_ENDPTCTRL_RX_EP_ENABLE;
+ }
+}
+
+static int _usbd_initialize_ep0()
+{
+ memset((void *)usbdaemon->qhs, 0, sizeof(dQH_t) * 4); // Clear all used EP queue heads.
+ memset(usbdaemon, 0, 0x80);
+
+ usbd_otg->regs->asynclistaddr = (u32)usbdaemon->qhs;
+
+ _usbd_initialize_ep_ctrl(USB_EP_CTRL_OUT);
+ _usbd_initialize_ep_ctrl(USB_EP_CTRL_IN);
+
+ // Disable Auto Low Power.
+ usbd_otg->regs->hostpc1_devlc &= ~USB2D_HOSTPC1_DEVLC_ASUS;
+
+ // Initiate an attach event.
+ usbd_otg->regs->usbcmd |= USB2D_USBCMD_RUN;
+
+ u32 retries = 100000; // 200ms timeout.
+ while (!(usbd_otg->regs->usbcmd & USB2D_USBCMD_RUN))
+ {
+ retries--;
+ if (!retries)
+ return 3;
+ usleep(1);
+ }
+
+ return 0;
+}
+
+// static void _disable_usb_wdt4()
+// {
+// if (TIMER_WDT4_STATUS & 1)// active
+// {
+// TIMER_TMR0_TMR_PTV &= 0x7FFFFFFF; // Disable timer
+// TIMER_WDT4_UNLOCK_PATTERN = 0xC45A; // Alow writes to disable counter bit.
+// TIMER_WDT4_COMMAND |= 2; // Disable counter
+// TIMER_TMR0_TMR_PCR |= 0x40000000;// INTR_CLR
+// }
+// }
+
+int usbd_flush_endpoint(u32 endpoint)
+{
+
+ usb_hw_ep_t actual_ep = (endpoint & 2) >> 1;
+ usb_xfer_dir_t direction = endpoint & 1;
+ u32 reg_mask = endpoint;
+
+ // Flash all endpoints or 1.
+ if (endpoint != USB_EP_ALL)
+ {
+ if (direction == USB_XFER_DIR_IN)
+ reg_mask = USB2D_ENDPT_STATUS_TX_OFFSET << actual_ep;
+ else
+ reg_mask = USB2D_ENDPT_STATUS_RX_OFFSET << actual_ep;
+ }
+ usbd_otg->regs->endptflush = reg_mask;
+
+ u32 retries = 100000; // 200ms timeout.
+ while (usbd_otg->regs->endptflush & reg_mask)
+ {
+ retries--;
+ if (!retries)
+ return 3;
+ usleep(1);
+ }
+
+ // Wait for the endpoint to finish all transactions (buffer not ready).
+ retries = 100000; // 200ms timeout.
+ while (usbd_otg->regs->endptstatus & reg_mask)
+ {
+ retries--;
+ if (!retries)
+ return 3;
+ usleep(1);
+ }
+
+ // Wait for the endpoint to clear the primed status.
+ retries = 100000; // 200ms timeout.
+ while (usbd_otg->regs->endptprime & reg_mask)
+ {
+ retries--;
+ if (!retries)
+ return 3;
+ usleep(1);
+ }
+
+ return 0;
+}
+
+static void _usbd_mark_ep_complete(u32 endpoint)
+{
+ u32 complete_bit;
+ usb_hw_ep_t actual_ep = (endpoint & 2) >> 1;
+ usb_xfer_dir_t direction = endpoint & 1;
+
+ usbd_flush_endpoint(endpoint);
+ memset((void *)&usbdaemon->dtds[endpoint * 4], 0, sizeof(dTD_t) * 4);
+ memset((void *)&usbdaemon->qhs[endpoint], 0, sizeof(dQH_t));
+ usbdaemon->ep_configured[endpoint] = 0;
+ usbdaemon->ep_bytes_requested[endpoint] = 0;
+
+ if (direction == USB_XFER_DIR_IN)
+ complete_bit = USB2D_ENDPT_STATUS_TX_OFFSET << actual_ep;
+ else
+ complete_bit = USB2D_ENDPT_STATUS_RX_OFFSET << actual_ep;
+
+ usbd_otg->regs->endptcomplete |= complete_bit;
+}
+
+static usb_ep_status_t _usbd_get_ep_status(usb_ep_t endpoint)
+{
+ bool status;
+ u32 reg_val;
+ u32 reg_mask;
+ u32 actual_ep = (endpoint & 2) >> 1;
+ usb_xfer_dir_t direction = endpoint & 1;
+
+ if (direction == USB_XFER_DIR_IN)
+ reg_mask = USB2D_ENDPT_STATUS_TX_OFFSET << actual_ep;
+ else
+ reg_mask = USB2D_ENDPT_STATUS_RX_OFFSET << actual_ep;
+
+ if (actual_ep == USB_HW_EP1)
+ reg_val = usbd_otg->regs->endptctrl[1];
+ else
+ reg_val = usbd_otg->regs->endptctrl[0];
+
+ // Check stalled status.
+ if (direction == USB_XFER_DIR_IN)
+ status = reg_val & USB2D_ENDPTCTRL_TX_EP_STALL;
+ else
+ status = reg_val & USB2D_ENDPTCTRL_RX_EP_STALL;
+
+ if (status)
+ return USB_EP_STATUS_STALLED;
+
+ // Check enabled status.
+ if (direction == USB_XFER_DIR_IN)
+ status = reg_val & USB2D_ENDPTCTRL_TX_EP_ENABLE;
+ else
+ status = reg_val & USB2D_ENDPTCTRL_RX_EP_ENABLE;
+
+ if (!status)
+ return USB_EP_STATUS_DISABLED;
+
+ // CHeck qHD error status.
+ u32 token_error_mask = USB_QHD_TOKEN_HALTED | USB_QHD_TOKEN_BUFFER_ERROR | USB_QHD_TOKEN_XFER_ERROR;
+ if (usbdaemon->qhs[endpoint].token & token_error_mask)
+ return USB_EP_STATUS_ERROR;
+
+ // Check if endpoint has a request or a ready buffer.
+ if ((usbd_otg->regs->endptprime & reg_mask) || (usbd_otg->regs->endptstatus & reg_mask))
+ return USB_EP_STATUS_ACTIVE; // RX/TX active.
+
+ // Return idle or not configured status.
+ if (!usbdaemon->ep_configured[endpoint])
+ return USB_EP_STATUS_NO_CONFIG;
+
+ return USB_EP_STATUS_IDLE;
+}
+
+static int _usbd_ep_operation(usb_ep_t endpoint, u8 *buf, u32 len, bool sync)
+{
+ if (!buf)
+ len = 0;
+
+ u32 prime_bit;
+ usb_hw_ep_t actual_ep = (endpoint & 2) >> 1;
+ usb_xfer_dir_t direction = endpoint & 1;
+ u32 length_left = len;
+ u32 dtd_ep_idx = endpoint * 4;
+
+ _usbd_mark_ep_complete(endpoint);
+
+ if (endpoint == USB_EP_CTRL_OUT)
+ usbdaemon->qhs[endpoint].ep_capabilities = USB_QHD_EP_CAP_IOS_ENABLE;
+
+ u32 max_packet_len = usbd_get_max_pkt_length(endpoint) & USB_QHD_EP_CAP_MAX_PKT_LEN_MASK;
+ usbdaemon->qhs[endpoint].ep_capabilities |= (max_packet_len << 16) | USB_QHD_EP_CAP_ZERO_LEN_TERM_DIS;
+ usbdaemon->qhs[endpoint].next_dTD_ptr = 0; // Clear terminate bit.
+ //usbdaemon->qhs[endpoint].ep_capabilities |= USB_QHD_TOKEN_IRQ_ON_COMPLETE;
+
+ usbdaemon->ep_configured[endpoint] = 1;
+ usbdaemon->ep_bytes_requested[endpoint] = len;
+
+ // Configure dTD.
+ u32 dtd_idx = 0;
+ do
+ {
+ if (dtd_idx)
+ usbdaemon->dtds[dtd_ep_idx + dtd_idx - 1].next_dTD = (u32)&usbdaemon->dtds[dtd_ep_idx + dtd_idx];
+
+ u32 dtd_size = MIN(length_left, USB_TD_BUFFER_MAX_SIZE); // 16KB max per dTD.
+ usbdaemon->dtds[dtd_ep_idx + dtd_idx].info = (dtd_size << 16) | USB_QHD_TOKEN_ACTIVE;
+ // usbdaemon->dtds[dtd_ep_idx + dtd_idx].info |= USB_QHD_TOKEN_IRQ_ON_COMPLETE;
+
+ // Set buffers addresses to all page pointers.
+ u32 dt_buffer_offset = dtd_idx * USB_TD_BUFFER_MAX_SIZE;
+ for (u32 i = 0; i < 4; i++)
+ usbdaemon->dtds[dtd_ep_idx + dtd_idx].pages[i] =
+ (u32)&buf[dt_buffer_offset + (USB_TD_BUFFER_PAGE_SIZE * i)];
+
+ //usbdaemon->dtds[dtd_ep_idx + dtd_idx].pages[5] =
+ // (u32)&buf[dt_buffer_offset + (USB_TD_BUFFER_PAGE_SIZE * 4)]; // Last buffer. Unused.
+
+ length_left -= dtd_size;
+ if (length_left)
+ dtd_idx++;
+ }
+ while (length_left);
+
+ // Last dTD, terminate it.
+ usbdaemon->dtds[dtd_ep_idx + dtd_idx].next_dTD = 1;
+
+ // Set first dTD address to queue head next dTD.
+ usbdaemon->qhs[endpoint].next_dTD_ptr |= (u32)&usbdaemon->dtds[dtd_ep_idx] & 0xFFFFFFE0;
+
+ // Flush AHB prefetcher.
+ AHB_GIZMO(AHB_AHB_MEM_PREFETCH_CFG1) &= ~MEM_PREFETCH_ENABLE;
+ AHB_GIZMO(AHB_AHB_MEM_PREFETCH_CFG1) |= MEM_PREFETCH_ENABLE;
+
+ if (direction == USB_XFER_DIR_IN)
+ {
+ prime_bit = USB2D_ENDPT_STATUS_TX_OFFSET << actual_ep;
+ bpmp_mmu_maintenance(BPMP_MMU_MAINT_CLN_INV_WAY, false);
+ }
+ else
+ prime_bit = USB2D_ENDPT_STATUS_RX_OFFSET << actual_ep;
+
+ // Prime endpoint.
+ usbd_otg->regs->endptprime |= prime_bit; // USB2_CONTROLLER_USB2D_ENDPTPRIME.
+
+ int res = 0;
+ usb_ep_status_t ep_status;
+ if (sync)
+ {
+ ep_status = _usbd_get_ep_status(endpoint);
+ if (ep_status == USB_EP_STATUS_ACTIVE)
+ {
+ u32 retries = 1000000; // Timeout 2s.
+ while (retries)
+ {
+ ep_status = _usbd_get_ep_status(endpoint);
+ if (ep_status != USB_EP_STATUS_ACTIVE)
+ {
+ if (ep_status == USB_EP_STATUS_DISABLED)
+ res = 28;
+ goto out;
+ }
+ retries--;
+ usleep(1);
+ }
+ res = 3;
+ }
+ else if (ep_status == USB_EP_STATUS_DISABLED)
+ res = 28;
+out:
+ if (res)
+ _usbd_mark_ep_complete(endpoint);
+ else if (_usbd_get_ep_status(endpoint) != USB_EP_STATUS_IDLE)
+ res = 26;
+ }
+
+ if (direction == USB_XFER_DIR_OUT)
+ bpmp_mmu_maintenance(BPMP_MMU_MAINT_CLN_INV_WAY, false);
+
+ return res;
+}
+
+static int _usbd_ep_ack(usb_ep_t ep)
+{
+ return _usbd_ep_operation(ep, NULL, 0, true);
+}
+
+static void _usbd_set_ep0_stall()
+{
+ // EP Control endpoints must be always stalled together.
+ usbd_otg->regs->endptctrl[0] =
+ USB2D_ENDPTCTRL_TX_EP_ENABLE | USB2D_ENDPTCTRL_TX_EP_STALL |
+ USB2D_ENDPTCTRL_RX_EP_ENABLE | USB2D_ENDPTCTRL_RX_EP_STALL;
+}
+
+void usbd_set_ep_stall(u32 endpoint, int ep_stall)
+{
+ usb_hw_ep_t actual_ep = (endpoint & 2) >> 1;
+ usb_xfer_dir_t direction = endpoint & 1;
+
+ if (ep_stall)
+ {
+ if (direction == USB_XFER_DIR_IN)
+ usbd_otg->regs->endptctrl[actual_ep] |= USB2D_ENDPTCTRL_TX_EP_STALL; // Stall EP Bulk IN.
+ else
+ usbd_otg->regs->endptctrl[actual_ep] |= USB2D_ENDPTCTRL_RX_EP_STALL; // Stall EP Bulk OUT.
+ }
+ else
+ {
+ if (direction == USB_XFER_DIR_IN)
+ usbd_otg->regs->endptctrl[actual_ep] &= ~USB2D_ENDPTCTRL_TX_EP_STALL; // Clear stall EP Bulk IN.
+ else
+ usbd_otg->regs->endptctrl[actual_ep] &= ~USB2D_ENDPTCTRL_RX_EP_STALL; // Clear stall EP Bulk OUT.
+ }
+}
+
+static void _usbd_handle_get_class_request(bool *transmit_data, u8 *descriptor, int *size, int *ep_stall)
+{
+ u8 _bRequest = usbd_otg->control_setup.bRequest;
+ u16 _wIndex = usbd_otg->control_setup.wIndex;
+ u16 _wValue = usbd_otg->control_setup.wValue;
+ u16 _wLength = usbd_otg->control_setup.wLength;
+
+ bool valid_interface = _wIndex == usbd_otg->interface;
+ bool valid_len = _bRequest == USB_REQUEST_BULK_GET_MAX_LUN ? 1 : 0;
+
+ if (!valid_interface || _wValue != 0 || _wLength != valid_len)
+ {
+ *ep_stall = 1;
+ return;
+ }
+
+ switch (_bRequest)
+ {
+ case USB_REQUEST_BULK_RESET:
+ _usbd_ep_ack(USB_EP_CTRL_IN);
+ usbd_otg->bulk_reset_req = 1;
+ break; // DELAYED_STATUS;
+ case USB_REQUEST_BULK_GET_MAX_LUN:
+ *transmit_data = 1;
+ descriptor[0] = usbd_otg->max_lun; // Set 0 LUN for 1 drive supported.
+ usbd_otg->max_lun_set = 1;
+ break;
+ default:
+ *ep_stall = 1;
+ break;
+ }
+}
+
+static void _usbd_handle_get_descriptor(bool *transmit_data, void **descriptor, int *size, int *ep_stall)
+{
+ u8 descriptor_type = usbd_otg->control_setup.wValue >> 8;
+ u8 descriptor_subtype = usbd_otg->control_setup.wValue & 0xFF;
+
+ switch (descriptor_type)
+ {
+ case USB_DESCRIPTOR_DEVICE:
+ {
+/*
+ u32 soc_rev = APB_MISC(APB_MISC_GP_HIDREV);
+ usb_device_descriptor.idProduct = (soc_rev >> 8) & 0xFF; // chip_id.
+ usb_device_descriptor.idProduct |= ((soc_rev << 4) | (FUSE(FUSE_SKU_INFO) & 0xF)) << 8; // HIDFAM.
+ usb_device_descriptor.bcdDevice = (soc_rev >> 16) & 0xF; // MINORREV.
+ usb_device_descriptor.bcdDevice |= ((soc_rev >> 4) & 0xF) << 8; // MAJORREV.
+*/
+ *descriptor = usbd_otg->desc->dev;
+ *size = usbd_otg->desc->dev->bLength;
+ *transmit_data = 1;
+ return;
+ }
+ case USB_DESCRIPTOR_CONFIGURATION:
+ if (usbd_otg->type == USB_GADGET_UMS)
+ {
+ if (usbd_otg->port_speed == 2) // High speed. 512 bytes.
+ {
+ usbd_otg->desc->cfg->endpoint[0].wMaxPacketSize = 0x200;
+ usbd_otg->desc->cfg->endpoint[1].wMaxPacketSize = 0x200;
+ }
+ else // Full speed. 64 bytes.
+ {
+ usbd_otg->desc->cfg->endpoint[0].wMaxPacketSize = 0x40;
+ usbd_otg->desc->cfg->endpoint[1].wMaxPacketSize = 0x40;
+ }
+ }
+ else
+ {
+ usb_cfg_hid_descr_t *tmp = (usb_cfg_hid_descr_t *)usbd_otg->desc->cfg;
+ if (usbd_otg->port_speed == 2) // High speed. 512 bytes.
+ {
+ tmp->endpoint[0].wMaxPacketSize = 0x200;
+ tmp->endpoint[1].wMaxPacketSize = 0x200;
+ }
+ else // Full speed. 64 bytes.
+ {
+ tmp->endpoint[0].wMaxPacketSize = 0x40;
+ tmp->endpoint[1].wMaxPacketSize = 0x40;
+ }
+ }
+ *descriptor = usbd_otg->desc->cfg;
+ *size = usbd_otg->desc->cfg->config.wTotalLength;
+ *transmit_data = 1;
+ return;
+ case USB_DESCRIPTOR_STRING:
+ switch (descriptor_subtype)
+ {
+ case 1:
+ *descriptor = usbd_otg->desc->vendor;
+ *size = usbd_otg->desc->vendor[0];
+ break;
+ case 2:
+ *descriptor = usbd_otg->desc->product;
+ *size = usbd_otg->desc->product[0];
+ break;
+ case 3:
+ *descriptor = usb_serial_string_descriptor;
+ *size = usb_serial_string_descriptor[0];
+ break;
+ case 0xEE:
+ *descriptor = usbd_otg->desc->ms_os;
+ *size = usbd_otg->desc->ms_os->bLength;
+ break;
+ default:
+ *descriptor = usb_lang_id_string_descriptor;
+ *size = 4;
+ break;
+ }
+ *transmit_data = 1;
+ return;
+ case USB_DESCRIPTOR_DEVICE_QUALIFIER:
+ if (!usbd_otg->desc->dev_qual)
+ goto exit;
+ *descriptor = usbd_otg->desc->dev_qual;
+ *size = usbd_otg->desc->dev_qual->bLength;
+ *transmit_data = 1;
+ return;
+ case USB_DESCRIPTOR_OTHER_SPEED_CONFIGURATION:
+ if (!usbd_otg->desc->cfg_other)
+ goto exit;
+ if (usbd_otg->port_speed == 2)
+ {
+ usbd_otg->desc->cfg_other->endpoint[0].wMaxPacketSize = 0x40;
+ usbd_otg->desc->cfg_other->endpoint[1].wMaxPacketSize = 0x40;
+ }
+ else
+ {
+ usbd_otg->desc->cfg_other->endpoint[0].wMaxPacketSize = 0x200;
+ usbd_otg->desc->cfg_other->endpoint[1].wMaxPacketSize = 0x200;
+ }
+ if ((usbd_otg->charger_detect & 1) && (usbd_otg->charger_detect & 2))
+ usbd_otg->desc->cfg_other->config.bMaxPower = 500 / 2;
+ *descriptor = usbd_otg->desc->cfg_other;
+ *size = usbd_otg->desc->cfg_other->config.wTotalLength;
+ *transmit_data = 1;
+ return;
+ case USB_DESCRIPTOR_DEVICE_BINARY_OBJECT:
+ *descriptor = usbd_otg->desc->dev_bot;
+ *size = usbd_otg->desc->dev_bot->wTotalLength;
+ *transmit_data = 1;
+ return;
+ default:
+ *transmit_data = 0;
+ *ep_stall = 1;
+ return;
+ }
+exit:
+ *transmit_data = 0;
+ *ep_stall = 1;
+ return;
+}
+
+static int _usbd_handle_set_request(int *ep_stall)
+{
+ int ret = 0;
+ u8 bRequest = usbd_otg->control_setup.bRequest;
+ if (bRequest == USB_REQUEST_SET_ADDRESS)
+ {
+ ret = _usbd_ep_ack(USB_EP_CTRL_IN);
+
+ // Set USB address for device mode.
+ if (!ret)
+ usbd_otg->regs->periodiclistbase = (usbd_otg->regs->periodiclistbase & 0x1FFFFFF) | ((usbd_otg->control_setup.wValue & 0xFF) << 25);
+ }
+ else if (bRequest == USB_REQUEST_SET_CONFIGURATION)
+ {
+ ret = _usbd_ep_ack(USB_EP_CTRL_IN);
+ if (!ret)
+ {
+ usbd_otg->configuration = usbd_otg->control_setup.wValue;
+ _usbd_initialize_ep_ctrl(USB_EP_BULK_OUT);
+ _usbd_initialize_ep_ctrl(USB_EP_BULK_IN);
+ usbd_otg->configuration_set = 1;
+ }
+ }
+ else
+ *ep_stall = 1;
+
+ return ret;
+}
+
+static int _usbd_handle_ep0_control_transfer()
+{
+ int direction;
+
+ int ret = 0;
+ bool transmit_data = 0;
+ u8 *descriptor = NULL;
+ u8 *descriptor_buf = (u8 *)USB_DESCRIPTOR_ADDR;
+ int size = 0;
+ int ep_stall = 0;
+
+ u8 _bmRequestType = usbd_otg->control_setup.bmRequestType;
+ u8 _bRequest = usbd_otg->control_setup.bRequest;
+ u16 _wValue = usbd_otg->control_setup.wValue;
+ u16 _wIndex = usbd_otg->control_setup.wIndex;
+ u16 _wLength = usbd_otg->control_setup.wLength;
+
+ //gfx_printf("%02X %02X %04X %04X %04X\n", _bmRequestType, _bRequest, _wValue, _wIndex, _wLength);
+
+ switch (_bmRequestType)
+ {
+ case (USB_SETUP_HOST_TO_DEVICE | USB_SETUP_RECIPIENT_DEVICE | USB_SETUP_TYPE_STANDARD):
+ ret = _usbd_handle_set_request(&ep_stall);
+ break;
+
+ case (USB_SETUP_HOST_TO_DEVICE | USB_SETUP_RECIPIENT_INTERFACE | USB_SETUP_TYPE_STANDARD):
+ ret = _usbd_ep_ack(USB_EP_CTRL_IN);
+ if (!ret)
+ usbd_otg->interface = _wValue;
+ break;
+
+ case (USB_SETUP_HOST_TO_DEVICE | USB_SETUP_RECIPIENT_ENDPOINT | USB_SETUP_TYPE_STANDARD):
+ switch (_bRequest)
+ {
+ case USB_REQUEST_CLEAR_FEATURE:
+ case USB_REQUEST_SET_FEATURE:
+ if ((_wValue & 0xFF) == USB_FEATURE_ENDPOINT_HALT)
+ {
+ switch (_wIndex) // endpoint
+ {
+ case USB_EP_ADDR_CTRL_OUT:
+ direction = 2;
+ break;
+ case USB_EP_ADDR_CTRL_IN:
+ direction = 3;
+ break;
+ case USB_EP_ADDR_BULK_OUT:
+ direction = 0;
+ break;
+ case USB_EP_ADDR_BULK_IN:
+ direction = 1;
+ break;
+ default:
+ _usbd_stall_reset_ep1(3, USB_EP_CFG_STALL);
+ goto out;
+ }
+
+ if (_bRequest == USB_REQUEST_CLEAR_FEATURE)
+ _usbd_stall_reset_ep1(direction, USB_EP_CFG_RESET);
+ else
+ _usbd_stall_reset_ep1(direction, USB_EP_CFG_STALL);
+
+ ret = _usbd_ep_ack(USB_EP_CTRL_IN);
+ }
+ else
+ _usbd_stall_reset_ep1(3, USB_EP_CFG_STALL);
+
+ break;
+ default:
+ ep_stall = 1;
+ break;
+ }
+ break;
+ case (USB_SETUP_HOST_TO_DEVICE | USB_SETUP_RECIPIENT_INTERFACE | USB_SETUP_TYPE_CLASS):
+ _usbd_handle_get_class_request(&transmit_data, descriptor, &size, &ep_stall);
+ break;
+ case (USB_SETUP_DEVICE_TO_HOST | USB_SETUP_RECIPIENT_DEVICE | USB_SETUP_TYPE_STANDARD):
+ switch (_bRequest)
+ {
+ case USB_REQUEST_GET_STATUS:
+ descriptor = (u8 *)descriptor_buf;
+ descriptor[0] = USB_STATUS_DEV_SELF_POWERED;
+ descriptor[1] = 0; // No support for remove wake up.
+ transmit_data = 1;
+ size = 2;
+ break;
+ case USB_REQUEST_GET_DESCRIPTOR:
+ _usbd_handle_get_descriptor(&transmit_data, (void **)&descriptor, &size, &ep_stall);
+ break;
+ case USB_REQUEST_GET_CONFIGURATION:
+ descriptor = (u8 *)&usbd_otg->configuration;
+ size = _wLength;
+ transmit_data = 1;
+ break;
+ default:
+ ep_stall = 1;
+ break;
+ }
+ break;
+
+ case (USB_SETUP_DEVICE_TO_HOST | USB_SETUP_RECIPIENT_INTERFACE | USB_SETUP_TYPE_STANDARD):
+ if (_bRequest == USB_REQUEST_GET_INTERFACE)
+ {
+ descriptor = (void *)&usbd_otg->interface;
+ }
+ else if (_bRequest == USB_REQUEST_GET_STATUS)
+ {
+ descriptor = (void *)&descriptor_buf;
+ memset(descriptor, 0, _wLength);
+ }
+ else if (_bRequest == USB_REQUEST_GET_DESCRIPTOR && (_wValue >> 8) == USB_DESCRIPTOR_HID_REPORT && usbd_otg->type > USB_GADGET_UMS)
+ {
+ if (usbd_otg->type == USB_GADGET_HID_GAMEPAD)
+ {
+ descriptor = (u8 *)&hid_report_descriptor_jc;
+ _wLength = sizeof(hid_report_descriptor_jc);
+ }
+ else // USB_GADGET_HID_TOUCHPAD
+ {
+ descriptor = (u8 *)&hid_report_descriptor_touch;
+ _wLength = sizeof(hid_report_descriptor_touch);
+ }
+
+ usbd_otg->hid_report_sent = 1;
+ }
+ else
+ {
+ ep_stall = 1;
+ break;
+ }
+
+ size = _wLength;
+ transmit_data = 1;
+ break;
+
+ case (USB_SETUP_DEVICE_TO_HOST | USB_SETUP_RECIPIENT_ENDPOINT | USB_SETUP_TYPE_STANDARD):
+ if (_bRequest == USB_REQUEST_GET_STATUS)
+ {
+ int ep_req;
+ switch (_wIndex)
+ {
+ case 0:
+ ep_req = 0;
+ break;
+ case 1:
+ ep_req = 2;
+ break;
+ case 0x80:
+ ep_req = 1;
+ break;
+ case 0x81:
+ ep_req = 3;
+ break;
+ default:
+ _usbd_stall_reset_ep1(3, USB_EP_CFG_STALL);
+ goto out;
+ }
+
+ size = _wLength;
+ descriptor = (u8 *)&descriptor_buf;
+ memset(descriptor, 0, size);
+
+ if (_usbd_get_ep_status(ep_req) == USB_EP_STATUS_STALLED)
+ descriptor[0] = USB_STATUS_EP_HALTED;
+ else
+ descriptor[0] = USB_STATUS_EP_OK;
+
+ transmit_data = 1;
+ }
+ else
+ _usbd_stall_reset_ep1(3, USB_EP_CFG_STALL);
+ break;
+
+ case (USB_SETUP_DEVICE_TO_HOST | USB_SETUP_RECIPIENT_INTERFACE | USB_SETUP_TYPE_CLASS):
+ descriptor = (u8 *)&descriptor_buf;
+ memset(descriptor, 0, _wLength);
+
+ _usbd_handle_get_class_request(&transmit_data, descriptor, &size, &ep_stall);
+ size = _wLength;
+ break;
+ case (USB_SETUP_DEVICE_TO_HOST | USB_SETUP_RECIPIENT_INTERFACE | USB_SETUP_TYPE_VENDOR):
+ case (USB_SETUP_DEVICE_TO_HOST | USB_SETUP_RECIPIENT_DEVICE | USB_SETUP_TYPE_VENDOR):
+ if (_bRequest == USB_REQUEST_GET_MS_DESCRIPTOR)
+ {
+ switch (_wIndex)
+ {
+ case USB_DESCRIPTOR_MS_COMPAT_ID:
+ descriptor = (u8 *)usbd_otg->desc->ms_cid;
+ size = usbd_otg->desc->ms_cid->dLength;
+ transmit_data = 1;
+ break;
+ case USB_DESCRIPTOR_MS_EXTENDED_PROPERTIES:
+ descriptor = (u8 *)usbd_otg->desc->mx_ext;
+ size = usbd_otg->desc->mx_ext->dLength;
+ transmit_data = 1;
+ break;
+ default:
+ ep_stall = 1;
+ break;
+ }
+ }
+ else
+ ep_stall = 1;
+ break;
+
+ default:
+ ep_stall = 1;
+ break;
+ }
+
+ // Transmit data to HOST if any.
+ if (transmit_data)
+ {
+ memcpy(usb_ep0_ctrl_buf, descriptor, size);
+
+ if (_wLength < size)
+ size = _wLength;
+ ret = _usbd_ep_operation(USB_EP_CTRL_IN, usb_ep0_ctrl_buf, size, true);
+ if (!ret)
+ ret = _usbd_ep_ack(USB_EP_CTRL_OUT);
+ }
+
+out:
+ if (ep_stall)
+ _usbd_set_ep0_stall();
+
+ return ret;
+}
+
+static int _usbd_ep0_initialize()
+{
+ bool enter = false;
+ if (usbd_otg->configuration_set)
+ enter = true;
+ else
+ {
+ usbdaemon->qhs = (volatile dQH_t *)USB2_QH_USB2D_QH_EP_BASE;
+
+ if (!_usbd_initialize_ep0())
+ enter = true;
+ }
+
+ if (enter)
+ {
+ usbd_otg->configuration_set = 0;
+ usbd_otg->max_lun_set = 0;
+
+ // Timeout if cable or communication isn't started in 1.5 minutes.
+ u32 timer = get_tmr_ms() + 90000;
+ while (true)
+ {
+ u32 usb_status_irqs = usbd_otg->regs->usbsts;
+
+ // Clear all interrupt statuses.
+ usbd_otg->regs->usbsts = usb_status_irqs;
+
+ // Check if a reset was received.
+ if (usb_status_irqs & USB2D_USBSTS_URI)
+ {
+ //_disable_usb_wdt4();
+
+ // Clear all device addresses, enabled setup requests, transmit events and flush all endpoints.
+ usbd_otg->regs->periodiclistbase = 0;
+ usbd_otg->regs->endptsetupstat = usbd_otg->regs->endptsetupstat;
+ usbd_otg->regs->endptcomplete = usbd_otg->regs->endptcomplete;
+ usbd_flush_endpoint(USB_EP_ALL);
+ }
+
+ // Check if port change happened.
+ if (usb_status_irqs & USB2D_USBSTS_PCI)
+ usbd_otg->port_speed = (usbd_otg->regs->hostpc1_devlc & USB2D_HOSTPC1_DEVLC_PSPD_MASK) >> 25;
+
+ // Acknowledge setup request for EP0 and copy its configuration.
+ u32 ep0_setup_req = usbd_otg->regs->endptsetupstat;
+ if (ep0_setup_req & 1)
+ {
+ usbd_otg->regs->endptsetupstat = ep0_setup_req;
+ memcpy(&usbd_otg->control_setup, (void *)usbdaemon->qhs->setup, 8);
+ if (_usbd_handle_ep0_control_transfer())
+ break;
+ }
+ if (usbd_otg->configuration_set)
+ return 0;
+
+ if (timer < get_tmr_ms() || btn_read_vol() == (BTN_VOL_UP | BTN_VOL_DOWN))
+ return 2;
+ }
+ }
+
+ return 3;
+}
+
+int usb_device_ep0_initialize(usb_gadget_type type)
+{
+ switch (type)
+ {
+ case USB_GADGET_UMS:
+ usbd_otg->desc = &usb_gadget_ums_descriptors;
+ break;
+ case USB_GADGET_HID_GAMEPAD:
+ usbd_otg->desc = &usb_gadget_hid_jc_descriptors;
+ break;
+ case USB_GADGET_HID_TOUCHPAD:
+ usbd_otg->desc = &usb_gadget_hid_touch_descriptors;
+ break;
+ }
+
+ usbd_otg->type = type;
+
+ int result = _usbd_ep0_initialize();
+ if (result)
+ result = 8;
+ return result;
+}
+
+int usbd_handle_ep0_pending_control_transfer()
+{
+ // Acknowledge setup request for EP0 and copy its configuration.
+ u32 ep0_setup_req = usbd_otg->regs->endptsetupstat;
+ if (ep0_setup_req & 1)
+ {
+ usbd_otg->regs->endptsetupstat = ep0_setup_req;
+ memcpy(&usbd_otg->control_setup, (void *)usbdaemon->qhs->setup, 8);
+ _usbd_handle_ep0_control_transfer();
+ memset(usb_ep0_ctrl_buf, 0, USB_TD_BUFFER_PAGE_SIZE);
+ }
+
+ if (usbd_otg->bulk_reset_req)
+ {
+ usbd_otg->bulk_reset_req = 0;
+ return 1;
+ }
+
+ return 0;
+}
+
+static usb_ep_status_t _usbd_get_ep1_status(usb_xfer_dir_t dir)
+{
+ usb_ep_t ep;
+ if (dir == USB_XFER_DIR_OUT)
+ ep = USB_EP_BULK_OUT;
+ else
+ ep = USB_EP_BULK_IN;
+ return _usbd_get_ep_status(ep);
+}
+
+int usb_device_read_ep1_out(u8 *buf, u32 len, u32 *bytes_read, bool sync)
+{
+ if (len > USB_EP_BUFFER_MAX_SIZE)
+ len = USB_EP_BUFFER_MAX_SIZE;
+
+ int result = _usbd_ep_operation(USB_EP_BULK_OUT, buf, len, sync);
+
+ if (sync && bytes_read)
+ {
+ if (result)
+ *bytes_read = 0;
+ else
+ *bytes_read = len;
+ }
+
+ return result;
+}
+
+int usb_device_read_ep1_out_big_reads(u8 *buf, u32 len, u32 *bytes_read)
+{
+ if (len > USB_EP_BULK_OUT_MAX_XFER)
+ len = USB_EP_BULK_OUT_MAX_XFER;
+
+ int result;
+ u32 bytes = 0;
+ *bytes_read = 0;
+ u8 *buf_curr = buf;
+
+ while (len)
+ {
+ u32 len_ep = MIN(len, USB_EP_BUFFER_MAX_SIZE);
+
+ result = usb_device_read_ep1_out(buf_curr, len_ep, &bytes, true);
+ if (!result)
+ {
+ len -= len_ep;
+ buf_curr += len_ep;
+ *bytes_read = *bytes_read + bytes;
+ }
+ else
+ break;
+ }
+
+ return result;
+}
+
+static int _usbd_get_ep1_out_bytes_read()
+{
+ if (_usbd_get_ep_status(2) != USB_EP_STATUS_IDLE)
+ return 0;
+ else
+ return (usbdaemon->ep_bytes_requested[2] - (usbdaemon->qhs[2].token >> 16));
+}
+
+int usb_device_ep1_out_reading_finish(u32 *pending_bytes)
+{
+ usb_ep_status_t ep_status;
+ do
+ {
+ ep_status = _usbd_get_ep1_status(USB_XFER_DIR_OUT);
+ if ((ep_status == USB_EP_STATUS_IDLE) || (ep_status == USB_EP_STATUS_DISABLED))
+ break;
+
+ usbd_handle_ep0_pending_control_transfer();
+ }
+ while ((ep_status == USB_EP_STATUS_ACTIVE) || (ep_status == USB_EP_STATUS_STALLED));
+
+ *pending_bytes = _usbd_get_ep1_out_bytes_read();
+
+ if (ep_status == USB_EP_STATUS_IDLE)
+ return 0;
+ else if (ep_status == USB_EP_STATUS_DISABLED)
+ return 28;
+ else
+ return 26;
+}
+
+int usb_device_write_ep1_in(u8 *buf, u32 len, u32 *bytes_written, bool sync)
+{
+ if (len > USB_EP_BUFFER_MAX_SIZE)
+ len = USB_EP_BUFFER_MAX_SIZE;
+
+ int result = _usbd_ep_operation(USB_EP_BULK_IN, buf, len, sync);
+
+ if (sync && bytes_written)
+ {
+ if (result)
+ *bytes_written = 0;
+ else
+ *bytes_written = len;
+ }
+
+ return result;
+}
+
+static int _usbd_get_ep1_in_bytes_written()
+{
+ if (_usbd_get_ep_status(3) != USB_EP_STATUS_IDLE)
+ return 0;
+ else
+ return (usbdaemon->ep_bytes_requested[3] - (usbdaemon->qhs[3].token >> 16));
+}
+
+int usb_device_ep1_in_writing_finish(u32 *pending_bytes)
+{
+ usb_ep_status_t ep_status;
+ do
+ {
+ ep_status = _usbd_get_ep1_status(USB_XFER_DIR_IN);
+ if ((ep_status == USB_EP_STATUS_IDLE) || (ep_status == USB_EP_STATUS_DISABLED))
+ break;
+
+ usbd_handle_ep0_pending_control_transfer();
+ }
+ while ((ep_status == USB_EP_STATUS_ACTIVE) || (ep_status == USB_EP_STATUS_STALLED));
+
+ *pending_bytes = _usbd_get_ep1_in_bytes_written();
+
+ if (ep_status == USB_EP_STATUS_IDLE)
+ return 0;
+ else if (ep_status == USB_EP_STATUS_DISABLED)
+ return 28;
+
+ usb_device_stall_ep1_bulk_out();
+ return 26;
+}
+
+bool usb_device_get_suspended()
+{
+ u32 suspended = usbd_otg->regs->portsc1 & USB2D_PORTSC1_SUSP;
+ return (suspended ? true : false);
+}
+
+u32 usb_device_get_port_status()
+{
+ return (usbd_otg->regs->portsc1);
+}
+
+bool usb_device_get_max_lun(u8 max_lun)
+{
+ // Timeout if get MAX_LUN request doesn't happen in 10s.
+ u32 timer = get_tmr_ms() + 10000;
+
+ usbd_otg->max_lun = max_lun;
+
+ while (!usbd_otg->max_lun_set)
+ {
+ usbd_handle_ep0_pending_control_transfer();
+ if (timer < get_tmr_ms() || btn_read_vol() == (BTN_VOL_UP | BTN_VOL_DOWN))
+ return true;
+ }
+
+ return false;
+}
+
+bool usb_device_get_hid_report()
+{
+ // Timeout if get GET_HID_REPORT request doesn't happen in 10s.
+ u32 timer = get_tmr_ms() + 10000;
+
+ while (!usbd_otg->hid_report_sent)
+ {
+ usbd_handle_ep0_pending_control_transfer();
+ if (timer < get_tmr_ms() || btn_read_vol() == (BTN_VOL_UP | BTN_VOL_DOWN))
+ return true;
+ }
+
+ return false;
+}
diff --git a/nyx/nyx_gui/usb/usbd.h b/nyx/nyx_gui/usb/usbd.h
new file mode 100644
index 0000000..ca33895
--- /dev/null
+++ b/nyx/nyx_gui/usb/usbd.h
@@ -0,0 +1,84 @@
+/*
+ * USB Device driver for Tegra X1
+ *
+ * Copyright (c) 2019 CTCaer
+ *
+ * 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 .
+ */
+
+#ifndef _USB_H_
+#define _USB_H_
+
+#include "../utils/types.h"
+
+#define USB_TD_BUFFER_PAGE_SIZE 0x1000
+#define USB_TD_BUFFER_MAX_SIZE (USB_TD_BUFFER_PAGE_SIZE * 4)
+//#define USB_HW_BUFFER_5_PAGES 0x5000
+#define USB_EP_BUFFER_1_TD (USB_TD_BUFFER_MAX_SIZE)
+#define USB_EP_BUFFER_2_TD (USB_TD_BUFFER_MAX_SIZE * 2)
+#define USB_EP_BUFFER_4_TD (USB_TD_BUFFER_MAX_SIZE * 4)
+#define USB_EP_BUFFER_MAX_SIZE (USB_EP_BUFFER_4_TD)
+
+typedef struct _usb_ctxt_t
+{
+ u32 type;
+ u32 partition;
+ u32 offset;
+ u32 sectors;
+ u32 ro;
+ void (*system_maintenance)(bool);
+ void *label;
+ void (*set_text)(void *, const char *);
+} usb_ctxt_t;
+
+typedef enum _usb_hid_type
+{
+ USB_HID_GAMEPAD,
+ USB_HID_TOUCHPAD
+} usb_hid_type;
+
+typedef enum _usb_gadget_type
+{
+ USB_GADGET_UMS,
+ USB_GADGET_HID_GAMEPAD,
+ USB_GADGET_HID_TOUCHPAD
+} usb_gadget_type;
+
+typedef enum
+{
+ USB_EP_CTRL_OUT = 0, // EP0.
+ USB_EP_CTRL_IN = 1, // EP0.
+ USB_EP_BULK_OUT = 2, // EP1.
+ USB_EP_BULK_IN = 3, // EP1.
+ USB_EP_ALL = 0xFFFFFFFF
+} usb_ep_t;
+
+int usbd_flush_endpoint(u32 ep);
+void usbd_set_ep_stall(u32 endpoint, int ep_stall);
+int usbd_get_max_pkt_length(int endpoint);
+int usbd_handle_ep0_pending_control_transfer();
+void usbd_end(bool reset_ep, bool only_controller);
+int usb_device_init();
+int usb_device_ep0_initialize(usb_gadget_type type);
+int usb_device_read_ep1_out(u8 *buf, u32 len, u32 *bytes_read, bool sync);
+int usb_device_read_ep1_out_big_reads(u8 *buf, u32 len, u32 *bytes_read);
+int usb_device_ep1_out_reading_finish(u32 *pending_bytes);
+int usb_device_write_ep1_in(u8 *buf, u32 len, u32 *bytes_written, bool sync);
+int usb_device_ep1_in_writing_finish(u32 *pending_bytes);
+bool usb_device_get_suspended();
+
+bool usb_device_get_max_lun(u8 max_lun);
+bool usb_device_get_hid_report();
+u32 usb_device_get_port_status();
+
+#endif
\ No newline at end of file