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