diff --git a/firmware/application/baseband_api.cpp b/firmware/application/baseband_api.cpp index 173e277de..4daf524dc 100644 --- a/firmware/application/baseband_api.cpp +++ b/firmware/application/baseband_api.cpp @@ -32,10 +32,6 @@ #include "core_control.hpp" -/* Set true to enable additional checks to ensure - * M4 and M0 are synchronized before passing messages. */ -static constexpr bool enforce_core_sync = true; - /* Set true to enable check for baseband messages getting stuck. * This implies the baseband thread is not dequeuing and has probably stalled. * NB: This check adds a small amout of overhead to the message sending code @@ -474,7 +470,7 @@ bool is_image_running() { return baseband_image_running; } -void run_image(const spi_flash::image_tag_t image_tag) { +void run_image(const spi_flash::image_tag_t image_tag, bool enforce_core_sync) { if (baseband_image_running) { chDbgPanic("BBRunning"); } @@ -487,7 +483,7 @@ void run_image(const spi_flash::image_tag_t image_tag) { creg::m4txevent::enable(); - if constexpr (enforce_core_sync) { + if (enforce_core_sync) { // Wait up to 3 seconds for baseband to start handling events. auto count = 3'000u; while (!shared_memory.baseband_ready && --count) @@ -498,7 +494,7 @@ void run_image(const spi_flash::image_tag_t image_tag) { } } -void run_prepared_image(const uint32_t m4_code) { +void run_prepared_image(const uint32_t m4_code, bool enforce_core_sync) { if (baseband_image_running) { chDbgPanic("BBRunning"); } @@ -511,7 +507,7 @@ void run_prepared_image(const uint32_t m4_code) { creg::m4txevent::enable(); - if constexpr (enforce_core_sync) { + if (enforce_core_sync) { // Wait up to 3 seconds for baseband to start handling events. auto count = 3'000u; while (!shared_memory.baseband_ready && --count) diff --git a/firmware/application/baseband_api.hpp b/firmware/application/baseband_api.hpp index 4953cae6d..e192168f3 100644 --- a/firmware/application/baseband_api.hpp +++ b/firmware/application/baseband_api.hpp @@ -131,8 +131,8 @@ void request_beep_stop(); void request_audio_beep(uint32_t freq, uint32_t sample_rate, uint32_t duration_ms); bool is_image_running(); -void run_image(const portapack::spi_flash::image_tag_t image_tag); -void run_prepared_image(const uint32_t m4_code); +void run_image(const portapack::spi_flash::image_tag_t image_tag, bool enforce_core_sync = true); +void run_prepared_image(const uint32_t m4_code, bool enforce_core_sync = true); void shutdown(); void spectrum_streaming_start(); diff --git a/firmware/application/external/external.cmake b/firmware/application/external/external.cmake index 9a300561b..6f928e948 100644 --- a/firmware/application/external/external.cmake +++ b/firmware/application/external/external.cmake @@ -407,6 +407,10 @@ set(EXTCPPSRC external/aprs_tx/main.cpp external/aprs_tx/ui_aprs_tx.cpp + #sd over usb + external/sdusb/main.cpp + external/sdusb/ui_sd_over_usb.cpp + ) set(EXTAPPLIST @@ -505,13 +509,5 @@ set(EXTAPPLIST ais_rx aprs_rx aprs_tx + sdusb ) - -# sdusb has type conflicts with PRALINE (HackRF Pro) - add only for non-PRALINE builds -if(NOT BOARD STREQUAL "PRALINE") - list(APPEND EXTCPPSRC - external/sdusb/main.cpp - external/sdusb/ui_sd_over_usb.cpp - ) - list(APPEND EXTAPPLIST sdusb) -endif() diff --git a/firmware/application/external/sdusb/ui_sd_over_usb.cpp b/firmware/application/external/sdusb/ui_sd_over_usb.cpp index 5b11a2291..e4832824e 100644 --- a/firmware/application/external/sdusb/ui_sd_over_usb.cpp +++ b/firmware/application/external/sdusb/ui_sd_over_usb.cpp @@ -46,7 +46,7 @@ SdOverUsbView::SdOverUsbView(NavigationView& nav) sdcStop(&SDCD1); portapack::shutdown(true, false); - baseband::run_prepared_image(portapack::memory::map::m4_code.base()); + baseband::run_prepared_image(portapack::memory::map::m4_code.base(), false); m0_halt(); /* will not return*/ }; diff --git a/firmware/baseband/CMakeLists.txt b/firmware/baseband/CMakeLists.txt index abc2ac5fa..6fc29cf44 100644 --- a/firmware/baseband/CMakeLists.txt +++ b/firmware/baseband/CMakeLists.txt @@ -748,13 +748,6 @@ set(MODE_CPPSRC ) DeclareTargets(PRTT rtty_tx) -### SD over USB - -set(MODE_INCDIR - ${HACKRF_PATH}/firmware - ${HACKRF_PATH}/firmware/common - ${HACKRF_PATH}/firmware/libopencm3/include -) ### Morse TX @@ -794,6 +787,15 @@ set(MODE_CPPSRC ) DeclareTargets(PTET tetra_rx) +### SD over USB + +set(MODE_INCDIR + ${HACKRF_PATH}/firmware + ${HACKRF_PATH}/firmware/common + ${HACKRF_PATH}/firmware/libopencm3/include + ${HACKRF_PATH}/firmware/hackrf_usb + sd_over_usb/ +) set(MODE_CPPSRC sd_over_usb/proc_sd_over_usb.cpp @@ -802,7 +804,9 @@ set(MODE_CPPSRC sd_over_usb/diskio.c sd_over_usb/sd_over_usb.c sd_over_usb/usb_descriptor.c - sd_over_usb/hackrf_core.c + sd_over_usb/usb_api_transceiver.c + sd_over_usb/cpu_clock.c + sd_over_usb/pins.c ${HACKRF_PATH}/firmware/common/adc.c ${HACKRF_PATH}/firmware/common/selftest.c @@ -829,6 +833,15 @@ set(MODE_CPPSRC ${HACKRF_PATH}/firmware/common/rffc5071_spi.c ${HACKRF_PATH}/firmware/common/rffc5071.c ${HACKRF_PATH}/firmware/common/gpdma.c + ${HACKRF_PATH}/firmware/common/clock_gen.c + ${HACKRF_PATH}/firmware/common/cpld_jtag.c + ${HACKRF_PATH}/firmware/common/leds.c + ${HACKRF_PATH}/firmware/common/power.c + ${HACKRF_PATH}/firmware/common/clock_io.c + ${HACKRF_PATH}/firmware/common/rom_iap.c + ${HACKRF_PATH}/firmware/common/w25q80bv.c + ${HACKRF_PATH}/firmware/common/w25q80bv_target.c + ${HACKRF_PATH}/firmware/libopencm3/lib/cm3/nvic.c ${HACKRF_PATH}/firmware/libopencm3/lib/cm3/sync.c @@ -836,10 +849,7 @@ set(MODE_CPPSRC ${HACKRF_PATH}/firmware/libopencm3/lib/lpc43xx/timer.c ${HACKRF_PATH}/firmware/libopencm3/lib/lpc43xx/i2c.c ) -# sd_over_usb has type conflicts with PRALINE (HackRF Pro) - disable for now -if(NOT BOARD STREQUAL "PRALINE") DeclareTargets(PUSB sd_over_usb) -endif() ### HackRF "factory" firmware diff --git a/firmware/baseband/sd_over_usb/cpu_clock.c b/firmware/baseband/sd_over_usb/cpu_clock.c new file mode 100644 index 000000000..1f615a78d --- /dev/null +++ b/firmware/baseband/sd_over_usb/cpu_clock.c @@ -0,0 +1,263 @@ +/* + * Copyright 2026 Great Scott Gadgets + * + * This file is part of HackRF. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that 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; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#include "cpu_clock.h" + +#include + +#include +#if defined(IS_JAWBREAKER) || defined(IS_HACKRF_ONE) || defined(IS_PRALINE) +#include +#endif + +#include "delay.h" +#include "i2c_bus.h" +#include "i2c_lpc.h" +#include "si5351c.h" + +/* We start with the CPU clock at 96MHz */ +unsigned int cpu_clock_mhz = 96; + +/* +Configure PLL1 (Main MCU Clock) to max speed (204MHz). +Note: PLL1 clock is used by M4/M0 core, Peripheral, APB1. +This function shall be called after cpu_clock_init(). +*/ +static void cpu_clock_pll1_max_speed(void) { + uint32_t reg_val; + + /* This function implements the sequence recommended in: + * UM10503 Rev 2.4 (Aug 2018), section 13.2.1.1, page 167. */ + + /* 1. Select the IRC as BASE_M4_CLK source. */ + reg_val = CGU_BASE_M4_CLK; + reg_val &= ~CGU_BASE_M4_CLK_CLK_SEL_MASK; + reg_val |= CGU_BASE_M4_CLK_CLK_SEL(CGU_SRC_IRC) | CGU_BASE_M4_CLK_AUTOBLOCK(1); + CGU_BASE_M4_CLK = reg_val; + + /* CPU is now at 12MHz */ + cpu_clock_mhz = 12; + + /* 2. Enable the crystal oscillator. */ + CGU_XTAL_OSC_CTRL &= ~CGU_XTAL_OSC_CTRL_ENABLE_MASK; + + /* 3. Wait 250us. */ + delay_us(250); + + /* 4. Set the AUTOBLOCK bit. */ + CGU_PLL1_CTRL |= CGU_PLL1_CTRL_AUTOBLOCK(1); + + /* 5. Reconfigure PLL1 to produce the final output frequency, with the + * crystal oscillator as clock source. */ + reg_val = CGU_PLL1_CTRL; + // clang-format off + reg_val &= ~( CGU_PLL1_CTRL_CLK_SEL_MASK | + CGU_PLL1_CTRL_PD_MASK | + CGU_PLL1_CTRL_FBSEL_MASK | + CGU_PLL1_CTRL_BYPASS_MASK | + CGU_PLL1_CTRL_DIRECT_MASK | + CGU_PLL1_CTRL_PSEL_MASK | + CGU_PLL1_CTRL_MSEL_MASK | + CGU_PLL1_CTRL_NSEL_MASK ); + /* Set PLL1 up to 12MHz * 17 = 204MHz. + * Direct mode: FCLKOUT = FCCO = M*(FCLKIN/N) */ + reg_val |= CGU_PLL1_CTRL_CLK_SEL(CGU_SRC_XTAL) | + CGU_PLL1_CTRL_PSEL(0) | + CGU_PLL1_CTRL_NSEL(0) | + CGU_PLL1_CTRL_MSEL(16) | + CGU_PLL1_CTRL_FBSEL(0) | + CGU_PLL1_CTRL_DIRECT(1); + // clang-format on + CGU_PLL1_CTRL = reg_val; + + /* 6. Wait for PLL1 to lock. */ + while (!(CGU_PLL1_STAT & CGU_PLL1_STAT_LOCK_MASK)) { + } + + /* 7. Set the PLL1 P-divider to divide by 2 (DIRECT=0, PSEL=0). */ + CGU_PLL1_CTRL &= ~CGU_PLL1_CTRL_DIRECT_MASK; + + /* 8. Select PLL1 as BASE_M4_CLK source. */ + reg_val = CGU_BASE_M4_CLK; + reg_val &= ~CGU_BASE_M4_CLK_CLK_SEL_MASK; + reg_val |= CGU_BASE_M4_CLK_CLK_SEL(CGU_SRC_PLL1); + CGU_BASE_M4_CLK = reg_val; + + /* CPU is now at 102MHz */ + cpu_clock_mhz = 102; + + /* 9. Wait 50us. */ + delay_us(50); + + /* 10. Set the PLL1 P-divider to direct output mode (DIRECT=1). */ + CGU_PLL1_CTRL |= CGU_PLL1_CTRL_DIRECT_MASK; + + /* CPU is now at 204MHz */ + cpu_clock_mhz = 204; +} + +/* clock startup for LPC4320 configure PLL1 to max speed (204MHz). +Note: PLL1 clock is used by M4/M0 core, Peripheral, APB1. */ +void cpu_clock_init(void) { + /* use IRC as clock source for APB1 (including I2C0) */ + CGU_BASE_APB1_CLK = CGU_BASE_APB1_CLK_CLK_SEL(CGU_SRC_IRC); + + /* use IRC as clock source for APB3 */ + CGU_BASE_APB3_CLK = CGU_BASE_APB3_CLK_CLK_SEL(CGU_SRC_IRC); + + // FIXME disable I2C + /* Kick I2C0 down to 400kHz when we switch over to APB1 clock = 204MHz */ + i2c_bus_start(si5351c.bus, &i2c_config_fast_clock); + + /* + * 12MHz clock is entering LPC XTAL1/OSC input now. + * On HackRF One and Jawbreaker, there is a 12 MHz crystal at the LPC. + * Set up PLL1 to run from XTAL1 input. + */ + + // FIXME a lot of the details here should be in a CGU driver + + /* set xtal oscillator to low frequency mode */ + CGU_XTAL_OSC_CTRL &= ~CGU_XTAL_OSC_CTRL_HF_MASK; + + cpu_clock_pll1_max_speed(); + + /* use XTAL_OSC as clock source for APB1 */ + CGU_BASE_APB1_CLK = + CGU_BASE_APB1_CLK_AUTOBLOCK(1) | CGU_BASE_APB1_CLK_CLK_SEL(CGU_SRC_XTAL); + + /* use XTAL_OSC as clock source for APB3 */ + CGU_BASE_APB3_CLK = + CGU_BASE_APB3_CLK_AUTOBLOCK(1) | CGU_BASE_APB3_CLK_CLK_SEL(CGU_SRC_XTAL); + + /* use XTAL_OSC as clock source for PLL0USB */ + CGU_PLL0USB_CTRL = CGU_PLL0USB_CTRL_PD(1) | CGU_PLL0USB_CTRL_AUTOBLOCK(1) | + CGU_PLL0USB_CTRL_CLK_SEL(CGU_SRC_XTAL); + while (CGU_PLL0USB_STAT & CGU_PLL0USB_STAT_LOCK_MASK) { + } + + /* configure PLL0USB to produce 480 MHz clock from 12 MHz XTAL_OSC */ + /* Values from User Manual v1.4 Table 94, for 12MHz oscillator. */ + CGU_PLL0USB_MDIV = 0x06167FFA; + CGU_PLL0USB_NP_DIV = 0x00302062; + CGU_PLL0USB_CTRL |= + (CGU_PLL0USB_CTRL_PD(1) | CGU_PLL0USB_CTRL_DIRECTI(1) | + CGU_PLL0USB_CTRL_DIRECTO(1) | CGU_PLL0USB_CTRL_CLKEN(1)); + + /* power on PLL0USB and wait until stable */ + CGU_PLL0USB_CTRL &= ~CGU_PLL0USB_CTRL_PD_MASK; + while (!(CGU_PLL0USB_STAT & CGU_PLL0USB_STAT_LOCK_MASK)) { + } + + /* use PLL0USB as clock source for USB0 */ + CGU_BASE_USB0_CLK = CGU_BASE_USB0_CLK_AUTOBLOCK(1) | + CGU_BASE_USB0_CLK_CLK_SEL(CGU_SRC_PLL0USB); + + /* Switch peripheral clock over to use PLL1 (204MHz) */ + CGU_BASE_PERIPH_CLK = CGU_BASE_PERIPH_CLK_AUTOBLOCK(1) | + CGU_BASE_PERIPH_CLK_CLK_SEL(CGU_SRC_PLL1); + + /* Switch APB1 clock over to use PLL1 (204MHz) */ + CGU_BASE_APB1_CLK = + CGU_BASE_APB1_CLK_AUTOBLOCK(1) | CGU_BASE_APB1_CLK_CLK_SEL(CGU_SRC_PLL1); + + /* Switch APB3 clock over to use PLL1 (204MHz) */ + CGU_BASE_APB3_CLK = + CGU_BASE_APB3_CLK_AUTOBLOCK(1) | CGU_BASE_APB3_CLK_CLK_SEL(CGU_SRC_PLL1); + + CGU_BASE_SSP0_CLK = + CGU_BASE_SSP0_CLK_AUTOBLOCK(1) | CGU_BASE_SSP0_CLK_CLK_SEL(CGU_SRC_PLL1); + + CGU_BASE_SSP1_CLK = + CGU_BASE_SSP1_CLK_AUTOBLOCK(1) | CGU_BASE_SSP1_CLK_CLK_SEL(CGU_SRC_PLL1); + +#ifdef IS_NOT_RAD1O + if (IS_NOT_RAD1O) { + /* Disable unused clocks */ + /* Start with PLLs */ + CGU_PLL0AUDIO_CTRL = CGU_PLL0AUDIO_CTRL_PD(1); + + /* Dividers */ + CGU_IDIVA_CTRL = CGU_IDIVA_CTRL_PD(1); + CGU_IDIVB_CTRL = CGU_IDIVB_CTRL_PD(1); + CGU_IDIVC_CTRL = CGU_IDIVC_CTRL_PD(1); + CGU_IDIVD_CTRL = CGU_IDIVD_CTRL_PD(1); + CGU_IDIVE_CTRL = CGU_IDIVE_CTRL_PD(1); + + /* Base clocks */ + CGU_BASE_SPIFI_CLK = + CGU_BASE_SPIFI_CLK_PD(1); /* SPIFI is only used at boot */ + CGU_BASE_USB1_CLK = + CGU_BASE_USB1_CLK_PD(1); /* USB1 is not exposed on HackRF */ + CGU_BASE_PHY_RX_CLK = CGU_BASE_PHY_RX_CLK_PD(1); + CGU_BASE_PHY_TX_CLK = CGU_BASE_PHY_TX_CLK_PD(1); + CGU_BASE_LCD_CLK = CGU_BASE_LCD_CLK_PD(1); + CGU_BASE_VADC_CLK = CGU_BASE_VADC_CLK_PD(1); + CGU_BASE_SDIO_CLK = CGU_BASE_SDIO_CLK_PD(0) | CGU_BASE_SDIO_CLK_AUTOBLOCK(1) | CGU_BASE_SDIO_CLK_CLK_SEL(0x09); + CGU_BASE_UART0_CLK = CGU_BASE_UART0_CLK_PD(1); + CGU_BASE_UART1_CLK = CGU_BASE_UART1_CLK_PD(1); + CGU_BASE_UART2_CLK = CGU_BASE_UART2_CLK_PD(1); + CGU_BASE_UART3_CLK = CGU_BASE_UART3_CLK_PD(1); + CGU_BASE_OUT_CLK = CGU_BASE_OUT_CLK_PD(1); + CGU_BASE_AUDIO_CLK = CGU_BASE_AUDIO_CLK_PD(1); + CGU_BASE_CGU_OUT0_CLK = CGU_BASE_CGU_OUT0_CLK_PD(1); + CGU_BASE_CGU_OUT1_CLK = CGU_BASE_CGU_OUT1_CLK_PD(1); + + /* Disable unused peripheral clocks */ + CCU1_CLK_APB1_CAN1_CFG = 0; + CCU1_CLK_APB1_I2S_CFG = 0; + CCU1_CLK_APB1_MOTOCONPWM_CFG = 0; + // CCU1_CLK_APB3_ADC0_CFG = 0; + CCU1_CLK_APB3_ADC1_CFG = 0; + CCU1_CLK_APB3_CAN0_CFG = 0; + CCU1_CLK_APB3_DAC_CFG = 0; + // CCU1_CLK_M4_DMA_CFG = 0; + CCU1_CLK_M4_EMC_CFG = 0; + CCU1_CLK_M4_EMCDIV_CFG = 0; + CCU1_CLK_M4_ETHERNET_CFG = 0; + CCU1_CLK_M4_LCD_CFG = 0; + CCU1_CLK_M4_QEI_CFG = 0; + CCU1_CLK_M4_RITIMER_CFG = 0; + // CCU1_CLK_M4_SCT_CFG = 0; + // CCU1_CLK_M4_SDIO_CFG = 1; + CCU1_CLK_M4_SPIFI_CFG = 0; + CCU1_CLK_M4_TIMER0_CFG = 0; + // CCU1_CLK_M4_TIMER1_CFG = 0; + // CCU1_CLK_M4_TIMER2_CFG = 0; + CCU1_CLK_M4_TIMER3_CFG = 0; + CCU1_CLK_M4_UART1_CFG = 0; + CCU1_CLK_M4_USART0_CFG = 0; + CCU1_CLK_M4_USART2_CFG = 0; + CCU1_CLK_M4_USART3_CFG = 0; + CCU1_CLK_M4_USB1_CFG = 0; + CCU1_CLK_M4_VADC_CFG = 0; + // CCU1_CLK_SPIFI_CFG = 0; + // CCU1_CLK_USB1_CFG = 0; + // CCU1_CLK_VADC_CFG = 0; + // CCU2_CLK_APB0_UART1_CFG = 0; + // CCU2_CLK_APB0_USART0_CFG = 0; + // CCU2_CLK_APB2_USART2_CFG = 0; + // CCU2_CLK_APB2_USART3_CFG = 0; + // CCU2_CLK_APLL_CFG = 0; + // CCU2_CLK_SDIO_CFG = 0; + } +#endif +} diff --git a/firmware/baseband/sd_over_usb/flashsize.h b/firmware/baseband/sd_over_usb/flashsize.h new file mode 100644 index 000000000..6f70f09be --- /dev/null +++ b/firmware/baseband/sd_over_usb/flashsize.h @@ -0,0 +1 @@ +#pragma once diff --git a/firmware/baseband/sd_over_usb/hackrf_core.c b/firmware/baseband/sd_over_usb/hackrf_core.c deleted file mode 100644 index 9145a4a50..000000000 --- a/firmware/baseband/sd_over_usb/hackrf_core.c +++ /dev/null @@ -1,1085 +0,0 @@ -/* - * Copyright 2012-2022 Great Scott Gadgets - * Copyright 2012 Jared Boone - * Copyright 2013 Benjamin Vernoux - * - * This file is part of HackRF. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * This program is distributed in the hope that 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; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, - * Boston, MA 02110-1301, USA. - */ - -#include "hackrf_core.h" -#include "hackrf_ui.h" -#include "sgpio.h" -#include "si5351c.h" -#include "spi_ssp.h" -#include "max283x.h" -#include "max5864.h" -#include "max5864_target.h" -#include "w25q80bv.h" -#include "w25q80bv_target.h" -#include "i2c_bus.h" -#include "i2c_lpc.h" -#include "cpld_jtag.h" -#include "platform_detect.h" -#include "platform_gpio.h" -#include "platform_scu.h" -#include "fixed_point.h" -#include "clkin.h" -#include "cpu_clock.h" -#include "usb_type.h" -#include -#include -#include -#include - -#ifdef HACKRF_ONE -#include "portapack.h" -#endif - -#include "gpio_lpc.h" - -/* Referenced by the hackrf common delay.c. The Mayhem firmware already runs - * the CPU at 204MHz when this baseband image is started. */ -unsigned int cpu_clock_mhz = 204; - -/* GPIO Output PinMux */ -static struct gpio gpio_led[] = { - GPIO(2, 1), - GPIO(2, 2), - GPIO(2, 8), -#ifdef RAD1O - GPIO(5, 26), -#endif -}; - -// clang-format off -static struct gpio gpio_1v8_enable = GPIO(3, 6); - -/* MAX283x GPIO (XCVR_CTL) PinMux */ -static struct gpio gpio_max283x_select = GPIO(0, 15); - -/* MAX5864 SPI chip select (AD_CS) GPIO PinMux */ -static struct gpio gpio_max5864_select = GPIO(2, 7); - -/* RFFC5071 GPIO serial interface PinMux */ -// #ifdef RAD1O -// static struct gpio gpio_rffc5072_select = GPIO(2, 13); -// static struct gpio gpio_rffc5072_clock = GPIO(5, 6); -// static struct gpio gpio_rffc5072_data = GPIO(3, 3); -// static struct gpio gpio_rffc5072_reset = GPIO(2, 14); -// #endif - -/* RF supply (VAA) control */ -#ifdef HACKRF_ONE -static struct gpio gpio_vaa_disable = GPIO(2, 9); -#endif -#ifdef RAD1O -static struct gpio gpio_vaa_enable = GPIO(2, 9); -#endif - -static struct gpio gpio_w25q80bv_hold = GPIO(1, 14); -static struct gpio gpio_w25q80bv_wp = GPIO(1, 15); - -/* RF switch control */ -#ifdef HACKRF_ONE -static struct gpio gpio_hp = GPIO(2, 0); -static struct gpio gpio_lp = GPIO(2, 10); -static struct gpio gpio_tx_mix_bp = GPIO(2, 11); -static struct gpio gpio_no_mix_bypass = GPIO(1, 0); -static struct gpio gpio_rx_mix_bp = GPIO(2, 12); -static struct gpio gpio_tx_amp = GPIO(2, 15); -static struct gpio gpio_tx = GPIO(5, 15); -static struct gpio gpio_mix_bypass = GPIO(5, 16); -static struct gpio gpio_rx = GPIO(5, 5); -static struct gpio gpio_no_tx_amp_pwr = GPIO(3, 5); -static struct gpio gpio_amp_bypass = GPIO(0, 14); -static struct gpio gpio_rx_amp = GPIO(1, 11); -static struct gpio gpio_no_rx_amp_pwr = GPIO(1, 12); -#endif -#ifdef RAD1O -static struct gpio gpio_tx_rx_n = GPIO(1, 11); -static struct gpio gpio_tx_rx = GPIO(0, 14); -static struct gpio gpio_by_mix = GPIO(1, 12); -static struct gpio gpio_by_mix_n = GPIO(2, 10); -static struct gpio gpio_by_amp = GPIO(1, 0); -static struct gpio gpio_by_amp_n = GPIO(5, 5); -static struct gpio gpio_mixer_en = GPIO(5, 16); -static struct gpio gpio_low_high_filt = GPIO(2, 11); -static struct gpio gpio_low_high_filt_n = GPIO(2, 12); -static struct gpio gpio_tx_amp = GPIO(2, 15); -static struct gpio gpio_rx_lna = GPIO(5, 15); -#endif - -/* CPLD JTAG interface GPIO pins */ -static struct gpio gpio_cpld_tdo = GPIO(5, 18); -static struct gpio gpio_cpld_tck = GPIO(3, 0); -#if (defined HACKRF_ONE || defined RAD1O) -static struct gpio gpio_cpld_tms = GPIO(3, 4); -static struct gpio gpio_cpld_tdi = GPIO(3, 1); -#else -static struct gpio gpio_cpld_tms = GPIO(3, 1); -static struct gpio gpio_cpld_tdi = GPIO(3, 4); -#endif - -#ifdef HACKRF_ONE -static struct gpio gpio_cpld_pp_tms = GPIO(1, 1); -static struct gpio gpio_cpld_pp_tdo = GPIO(1, 8); -#endif - -/* other CPLD interface GPIO pins */ -static struct gpio gpio_hw_sync_enable = GPIO(5, 12); -static struct gpio gpio_q_invert = GPIO(0, 13); - -/* HackRF One r9 */ -#ifdef HACKRF_ONE -static struct gpio gpio_h1r9_rx = GPIO(0, 7); -static struct gpio gpio_h1r9_1v8_enable = GPIO(2, 9); -static struct gpio gpio_h1r9_vaa_disable = GPIO(3, 6); -static struct gpio gpio_h1r9_hw_sync_enable = GPIO(5, 5); -#endif -// clang-format on - -/* i2c0, i2c1, and i2c_config_fast_clock are defined in - * hackrf/firmware/common/i2c_bus.c and i2c_lpc.c; use those instead of - * redefining them here to avoid multiple-definition link errors. */ - -si5351c_driver_t clock_gen = { - .bus = &i2c0, - .i2c_address = 0x60, -}; - -ssp_config_t ssp_config_max283x = { - /* FIXME speed up once everything is working reliably */ - /* - // Freq About 0.0498MHz / 49.8KHz => Freq = PCLK / (CPSDVSR * [SCR+1]) with PCLK=PLL1=204MHz - const uint8_t serial_clock_rate = 32; - const uint8_t clock_prescale_rate = 128; - */ - // Freq About 4.857MHz => Freq = PCLK / (CPSDVSR * [SCR+1]) with PCLK=PLL1=204MHz - .data_bits = SSP_DATA_16BITS, - .serial_clock_rate = 21, - .clock_prescale_rate = 2, - .gpio_select = &gpio_max283x_select, -}; - -ssp_config_t ssp_config_max5864 = { - /* FIXME speed up once everything is working reliably */ - /* - // Freq About 0.0498MHz / 49.8KHz => Freq = PCLK / (CPSDVSR * [SCR+1]) with PCLK=PLL1=204MHz - const uint8_t serial_clock_rate = 32; - const uint8_t clock_prescale_rate = 128; - */ - // Freq About 4.857MHz => Freq = PCLK / (CPSDVSR * [SCR+1]) with PCLK=PLL1=204MHz - .data_bits = SSP_DATA_8BITS, - .serial_clock_rate = 21, - .clock_prescale_rate = 2, - .gpio_select = &gpio_max5864_select, -}; - -/* spi_bus_ssp1 is defined in hackrf/firmware/common/spi_bus.c. We update - * its .config at runtime in clock_init() to point at ssp_config_max5864. */ - -max283x_driver_t max283x = {}; - -max5864_driver_t max5864 = { - .bus = &spi_bus_ssp1, - .target_init = max5864_target_init, -}; - -ssp_config_t ssp_config_w25q80bv = { - .data_bits = SSP_DATA_8BITS, - .serial_clock_rate = 2, - .clock_prescale_rate = 2, -}; - -/* spi_bus_ssp0 is defined in hackrf/firmware/common/spi_bus.c. We update - * its .config at runtime in clock_init() to point at ssp_config_w25q80bv. */ - -w25q80bv_driver_t spi_flash = { - .bus = &spi_bus_ssp0, - .gpio_hold = &gpio_w25q80bv_hold, - .gpio_wp = &gpio_w25q80bv_wp, - .target_init = w25q80bv_target_init, -}; - -/* sgpio_config and rf_path are defined in hackrf/firmware/common/sgpio.c and - * rf_path.c. Their gpio_* fields are populated at runtime in pin_setup(). */ - -jtag_gpio_t jtag_gpio_cpld = { - .gpio_tms = &gpio_cpld_tms, - .gpio_tck = &gpio_cpld_tck, - .gpio_tdi = &gpio_cpld_tdi, - .gpio_tdo = &gpio_cpld_tdo, -#ifdef HACKRF_ONE - .gpio_pp_tms = &gpio_cpld_pp_tms, - .gpio_pp_tdo = &gpio_cpld_pp_tdo, -#endif -}; - -jtag_t jtag_cpld = { - .gpio = &jtag_gpio_cpld, -}; - -void delay(uint32_t duration) { - uint32_t i; - - for (i = 0; i < duration; i++) { - __asm__("nop"); - } -} - -void delay_us_at_mhz(uint32_t us, uint32_t mhz) { - // The loop below takes 3 cycles per iteration. - uint32_t loop_iterations = (us * mhz) / 3; - asm volatile( - "start%=:\n" - " subs %[ITERATIONS], #1\n" // 1 cycle - " bpl start%=\n" // 2 cycles - : - : [ITERATIONS] "r"(loop_iterations)); -} - -/* GCD algo from wikipedia */ -/* http://en.wikipedia.org/wiki/Greatest_common_divisor */ -static uint32_t gcd(uint32_t u, uint32_t v) { - int s; - - if (!u || !v) { - return u | v; - } - - for (s = 0; !((u | v) & 1); s++) { - u >>= 1; - v >>= 1; - } - - while (!(u & 1)) { - u >>= 1; - } - - do { - while (!(v & 1)) { - v >>= 1; - } - - if (u > v) { - uint32_t t; - t = v; - v = u; - u = t; - } - - v = v - u; - } while (v); - - return u << s; -} - -bool sample_rate_frac_set(uint32_t rate_num, uint32_t rate_denom) { - const uint64_t VCO_FREQ = 800 * 1000 * 1000; /* 800 MHz */ - uint32_t MSx_P1, MSx_P2, MSx_P3; - uint32_t a, b, c; - uint32_t rem; - - hackrf_ui()->set_sample_rate(rate_num / 2); - - /* Find best config */ - a = (VCO_FREQ * rate_denom) / rate_num; - - rem = (VCO_FREQ * rate_denom) - (a * rate_num); - - if (!rem) { - /* Integer mode */ - b = 0; - c = 1; - } else { - /* Fractional */ - uint32_t g = gcd(rem, rate_num); - rem /= g; - rate_num /= g; - - if (rate_num < (1 << 20)) { - /* Perfect match */ - b = rem; - c = rate_num; - } else { - /* Approximate */ - c = (1 << 20) - 1; - b = ((uint64_t)c * (uint64_t)rem) / rate_num; - - g = gcd(b, c); - b /= g; - c /= g; - } - } - - bool streaming = sgpio_cpld_stream_is_enabled(&sgpio_config); - - if (streaming) { - sgpio_cpld_stream_disable(&sgpio_config); - } - - /* Can we enable integer mode ? */ - if (a & 0x1 || b) { - si5351c_set_int_mode(&clock_gen, 0, 0); - } else { - si5351c_set_int_mode(&clock_gen, 0, 1); - } - - /* Final MS values */ - MSx_P1 = 128 * a + (128 * b / c) - 512; - MSx_P2 = (128 * b) % c; - MSx_P3 = c; - - if (detected_platform() == BOARD_ID_HACKRF1_R9) { - /* - * On HackRF One r9 all sample clocks are externally derived - * from MS1/CLK1 operating at twice the sample rate. - */ - si5351c_configure_multisynth(&clock_gen, 1, MSx_P1, MSx_P2, MSx_P3, 0); - } else { - /* - * On other platforms the clock generator produces three - * different sample clocks, all derived from multisynth 0. - */ - /* MS0/CLK0 is the source for the MAX5864/CPLD (CODEC_CLK). */ - si5351c_configure_multisynth(&clock_gen, 0, MSx_P1, MSx_P2, MSx_P3, 1); - - /* MS0/CLK1 is the source for the CPLD (CODEC_X2_CLK). */ - si5351c_configure_multisynth(&clock_gen, 1, 0, 0, 0, 0); // p1 doesn't matter - - /* MS0/CLK2 is the source for SGPIO (CODEC_X2_CLK) */ - si5351c_configure_multisynth(&clock_gen, 2, 0, 0, 0, 0); // p1 doesn't matter - } - - if (streaming) { - sgpio_cpld_stream_enable(&sgpio_config); - } - - return true; -} - -/* - * Configure clock generator to produce sample clock in units of 1/(2**24) Hz. - * Can be called with program=false for a dry run that returns the resultant - * frequency without actually configuring the clock generator. - */ -fp_40_24_t sample_rate_set(const fp_40_24_t sample_rate, const bool program) { - const fp_40_24_t vco = 800 * FP_ONE_MHZ; - uint64_t p1, p2, p3; - uint64_t n, d, q; - fp_40_24_t remainder, resultant_rate; - - fp_40_24_t rate = sample_rate * 2; - - p1 = ((128 * vco) / rate) - 512; - if (vco % rate) { - n = (128 * vco) - (rate * (p1 + 512)); - d = rate / FP_ONE_HZ; - n += (d / 2); - p2 = n / d; - p3 = 1 << 24; - - unsigned int shift = p2 ? __builtin_ctzll(p2) : 24; - p2 >>= shift; - p3 >>= shift; - - const uint64_t p3_max = 0xfffff; - if (p3 > p3_max) { - p2 *= p3_max; - p2 += (p3 / 2); - p2 /= p3; - p3 = p3_max; - } - - if (p2 >= p3) { - p1++; - p2 = 0; - } - } else { - p2 = 0; - } - - if (p1 > 0x3fe00) { - p1 = 0x3fe00; - p2 = 0; - } - - if (p2 == 0) { - p3 = 1; - n = (vco * 128); - d = (p1 + 512); - n += (d / 2); - resultant_rate = n / d; - } else { - const uint64_t vco_hz = vco / FP_ONE_HZ; - n = p3 * vco_hz * 128; - d = p3 * (p1 + 512) + p2; - const uint64_t rate_hz = n / d; - remainder = (n - (d * rate_hz)) * FP_ONE_HZ; - remainder += (d / 2); - q = remainder / d; - resultant_rate = (rate_hz * FP_ONE_HZ) + q; - } - - resultant_rate = (resultant_rate + 1) / 2; - - if (!program) { - return resultant_rate; - } - - bool streaming = sgpio_cpld_stream_is_enabled(&sgpio_config); - - if (streaming) { - sgpio_cpld_stream_disable(&sgpio_config); - } - - if (p1 & 0x1 || p2) { - si5351c_set_int_mode(&clock_gen, 0, 0); - } else { - si5351c_set_int_mode(&clock_gen, 0, 1); - } - - if (detected_platform() == BOARD_ID_HACKRF1_R9) { - si5351c_configure_multisynth(&clock_gen, 1, p1, p2, p3, 0); - } else { - si5351c_configure_multisynth(&clock_gen, 0, p1, p2, p3, 1); - si5351c_configure_multisynth(&clock_gen, 1, 0, 0, 0, 0); - si5351c_configure_multisynth(&clock_gen, 2, 0, 0, 0, 0); - } - - if (streaming) { - sgpio_cpld_stream_enable(&sgpio_config); - } - - return resultant_rate; -} - -bool baseband_filter_bandwidth_set(const uint32_t bandwidth_hz) { - uint32_t bandwidth_hz_real; - bandwidth_hz_real = max283x_set_lpf_bandwidth(&max283x, MAX283x_MODE_RX, bandwidth_hz); - - if (bandwidth_hz_real) { - hackrf_ui()->set_filter_bw(bandwidth_hz_real); - } - - return bandwidth_hz_real != 0; -} - -/* -Configure PLL1 (Main MCU Clock) to max speed (204MHz). -Note: PLL1 clock is used by M4/M0 core, Peripheral, APB1. -This function shall be called after cpu_clock_init(). -*/ -static void cpu_clock_pll1_max_speed(void) { - uint32_t reg_val; - - /* This function implements the sequence recommended in: - * UM10503 Rev 2.4 (Aug 2018), section 13.2.1.1, page 167. */ - - /* 1. Select the IRC as BASE_M4_CLK source. */ - reg_val = CGU_BASE_M4_CLK; - reg_val &= ~CGU_BASE_M4_CLK_CLK_SEL_MASK; - reg_val |= CGU_BASE_M4_CLK_CLK_SEL(CGU_SRC_IRC) | CGU_BASE_M4_CLK_AUTOBLOCK(1); - CGU_BASE_M4_CLK = reg_val; - - /* CPU is now at 12MHz */ - cpu_clock_mhz = 12; - - /* 2. Enable the crystal oscillator. */ - CGU_XTAL_OSC_CTRL &= ~CGU_XTAL_OSC_CTRL_ENABLE_MASK; - - /* 3. Wait 250us. */ - delay_us_at_mhz(250, 12); - - /* 4. Set the AUTOBLOCK bit. */ - CGU_PLL1_CTRL |= CGU_PLL1_CTRL_AUTOBLOCK(1); - - /* 5. Reconfigure PLL1 to produce the final output frequency, with the - * crystal oscillator as clock source. */ - reg_val = CGU_PLL1_CTRL; - // clang-format off - reg_val &= ~( CGU_PLL1_CTRL_CLK_SEL_MASK | - CGU_PLL1_CTRL_PD_MASK | - CGU_PLL1_CTRL_FBSEL_MASK | - CGU_PLL1_CTRL_BYPASS_MASK | - CGU_PLL1_CTRL_DIRECT_MASK | - CGU_PLL1_CTRL_PSEL_MASK | - CGU_PLL1_CTRL_MSEL_MASK | - CGU_PLL1_CTRL_NSEL_MASK ); - /* Set PLL1 up to 12MHz * 17 = 204MHz. - * Direct mode: FCLKOUT = FCCO = M*(FCLKIN/N) */ - reg_val |= CGU_PLL1_CTRL_CLK_SEL(CGU_SRC_XTAL) | - CGU_PLL1_CTRL_PSEL(0) | - CGU_PLL1_CTRL_NSEL(0) | - CGU_PLL1_CTRL_MSEL(16) | - CGU_PLL1_CTRL_FBSEL(0) | - CGU_PLL1_CTRL_DIRECT(1); - // clang-format on - CGU_PLL1_CTRL = reg_val; - - /* 6. Wait for PLL1 to lock. */ - while (!(CGU_PLL1_STAT & CGU_PLL1_STAT_LOCK_MASK)) { - } - - /* 7. Set the PLL1 P-divider to divide by 2 (DIRECT=0, PSEL=0). */ - CGU_PLL1_CTRL &= ~CGU_PLL1_CTRL_DIRECT_MASK; - - /* 8. Select PLL1 as BASE_M4_CLK source. */ - reg_val = CGU_BASE_M4_CLK; - reg_val &= ~CGU_BASE_M4_CLK_CLK_SEL_MASK; - reg_val |= CGU_BASE_M4_CLK_CLK_SEL(CGU_SRC_PLL1); - CGU_BASE_M4_CLK = reg_val; - - /* CPU is now at 102MHz */ - cpu_clock_mhz = 102; - - /* 9. Wait 50us. */ - delay_us_at_mhz(50, 102); - - /* 10. Set the PLL1 P-divider to direct output mode (DIRECT=1). */ - CGU_PLL1_CTRL |= CGU_PLL1_CTRL_DIRECT_MASK; - - /* CPU is now at 204MHz */ - cpu_clock_mhz = 204; -} - -/* clock startup for LPC4320 configure PLL1 to max speed (204MHz). -Note: PLL1 clock is used by M4/M0 core, Peripheral, APB1. */ -void cpu_clock_init(void) { - /* use IRC as clock source for APB1 (including I2C0) */ - CGU_BASE_APB1_CLK = CGU_BASE_APB1_CLK_CLK_SEL(CGU_SRC_IRC); - - /* use IRC as clock source for APB3 */ - CGU_BASE_APB3_CLK = CGU_BASE_APB3_CLK_CLK_SEL(CGU_SRC_IRC); - - i2c_bus_start(clock_gen.bus, &i2c_config_fast_clock); - - si5351c_init(&clock_gen); - si5351c_disable_all_outputs(&clock_gen); - si5351c_disable_oeb_pin_control(&clock_gen); - si5351c_power_down_all_clocks(&clock_gen); - si5351c_set_crystal_configuration(&clock_gen); - si5351c_enable_xo_and_ms_fanout(&clock_gen); - si5351c_configure_pll_multisynth(&clock_gen, SI5351C_INPUT_XTAL); - - /* - * Clocks on HackRF One r9: - * CLK0 -> MAX5864/CPLD/SGPIO (sample clocks) - * CLK1 -> RFFC5072/MAX2839 - * CLK2 -> External Clock Output/LPC43xx (power down at boot) - * - * Clocks on other platforms: - * CLK0 -> MAX5864/CPLD - * CLK1 -> CPLD - * CLK2 -> SGPIO - * CLK3 -> External Clock Output (power down at boot) - * CLK4 -> RFFC5072 (MAX2837 on rad1o) - * CLK5 -> MAX2837 (MAX2871 on rad1o) - * CLK6 -> none - * CLK7 -> LPC43xx (uses a 12MHz crystal by default) - */ - - if (detected_platform() == BOARD_ID_HACKRF1_R9) { - /* MS0/CLK0 is the reference for both RFFC5071 and MAX2839. */ - si5351c_configure_multisynth( - &clock_gen, - 0, - 20 * 128 - 512, - 0, - 1, - 0); /* 800/20 = 40MHz */ - } else { - /* MS4/CLK4 is the source for the RFFC5071 mixer (MAX2837 on rad1o). */ - si5351c_configure_multisynth( - &clock_gen, - 4, - 20 * 128 - 512, - 0, - 1, - 0); /* 800/20 = 40MHz */ - /* MS5/CLK5 is the source for the MAX2837 clock input (MAX2871 on rad1o). */ - si5351c_configure_multisynth( - &clock_gen, - 5, - 20 * 128 - 512, - 0, - 1, - 0); /* 800/20 = 40MHz */ - } - - /* MS6/CLK6 is unused. */ - /* MS7/CLK7 is unused. */ - - /* Set to 10 MHz, the common rate between Jawbreaker and HackRF One. */ - sample_rate_set(10000000 * (fp_40_24_t)FP_ONE_HZ, true); - - si5351c_configure_clock_control(&clock_gen); - si5351c_change_input(&clock_gen, SI5351C_INPUT_XTAL); - // soft reset - si5351c_reset_plls(&clock_gen, SI5351C_PLL_MASK_BOTH); - si5351c_enable_clock_outputs(&clock_gen); - - // FIXME disable I2C - /* Kick I2C0 down to 400kHz when we switch over to APB1 clock = 204MHz */ - i2c_bus_start(clock_gen.bus, &i2c_config_fast_clock); - - /* - * 12MHz clock is entering LPC XTAL1/OSC input now. - * On HackRF One and Jawbreaker, there is a 12 MHz crystal at the LPC. - * Set up PLL1 to run from XTAL1 input. - */ - - // FIXME a lot of the details here should be in a CGU driver - - /* set xtal oscillator to low frequency mode */ - CGU_XTAL_OSC_CTRL &= ~CGU_XTAL_OSC_CTRL_HF_MASK; - - cpu_clock_pll1_max_speed(); - - /* use XTAL_OSC as clock source for APB1 */ - CGU_BASE_APB1_CLK = - CGU_BASE_APB1_CLK_AUTOBLOCK(1) | CGU_BASE_APB1_CLK_CLK_SEL(CGU_SRC_XTAL); - - /* use XTAL_OSC as clock source for APB3 */ - CGU_BASE_APB3_CLK = - CGU_BASE_APB3_CLK_AUTOBLOCK(1) | CGU_BASE_APB3_CLK_CLK_SEL(CGU_SRC_XTAL); - - /* use XTAL_OSC as clock source for PLL0USB */ - CGU_PLL0USB_CTRL = CGU_PLL0USB_CTRL_PD(1) | CGU_PLL0USB_CTRL_AUTOBLOCK(1) | - CGU_PLL0USB_CTRL_CLK_SEL(CGU_SRC_XTAL); - while (CGU_PLL0USB_STAT & CGU_PLL0USB_STAT_LOCK_MASK) { - } - - /* configure PLL0USB to produce 480 MHz clock from 12 MHz XTAL_OSC */ - /* Values from User Manual v1.4 Table 94, for 12MHz oscillator. */ - CGU_PLL0USB_MDIV = 0x06167FFA; - CGU_PLL0USB_NP_DIV = 0x00302062; - CGU_PLL0USB_CTRL |= - (CGU_PLL0USB_CTRL_PD(1) | CGU_PLL0USB_CTRL_DIRECTI(1) | - CGU_PLL0USB_CTRL_DIRECTO(1) | CGU_PLL0USB_CTRL_CLKEN(1)); - - /* power on PLL0USB and wait until stable */ - CGU_PLL0USB_CTRL &= ~CGU_PLL0USB_CTRL_PD_MASK; - while (!(CGU_PLL0USB_STAT & CGU_PLL0USB_STAT_LOCK_MASK)) { - } - - /* use PLL0USB as clock source for USB0 */ - CGU_BASE_USB0_CLK = CGU_BASE_USB0_CLK_AUTOBLOCK(1) | - CGU_BASE_USB0_CLK_CLK_SEL(CGU_SRC_PLL0USB); - - /* Switch peripheral clock over to use PLL1 (204MHz) */ - CGU_BASE_PERIPH_CLK = CGU_BASE_PERIPH_CLK_AUTOBLOCK(1) | - CGU_BASE_PERIPH_CLK_CLK_SEL(CGU_SRC_PLL1); - - /* Switch APB1 clock over to use PLL1 (204MHz) */ - CGU_BASE_APB1_CLK = - CGU_BASE_APB1_CLK_AUTOBLOCK(1) | CGU_BASE_APB1_CLK_CLK_SEL(CGU_SRC_PLL1); - - /* Switch APB3 clock over to use PLL1 (204MHz) */ - CGU_BASE_APB3_CLK = - CGU_BASE_APB3_CLK_AUTOBLOCK(1) | CGU_BASE_APB3_CLK_CLK_SEL(CGU_SRC_PLL1); - - CGU_BASE_SSP0_CLK = - CGU_BASE_SSP0_CLK_AUTOBLOCK(1) | CGU_BASE_SSP0_CLK_CLK_SEL(CGU_SRC_PLL1); - - CGU_BASE_SSP1_CLK = - CGU_BASE_SSP1_CLK_AUTOBLOCK(1) | CGU_BASE_SSP1_CLK_CLK_SEL(CGU_SRC_PLL1); - -#if (defined JAWBREAKER || defined HACKRF_ONE) - /* Disable unused clocks */ - /* Start with PLLs */ - CGU_PLL0AUDIO_CTRL = CGU_PLL0AUDIO_CTRL_PD(1); - - /* Dividers */ - CGU_IDIVA_CTRL = CGU_IDIVA_CTRL_PD(1); - CGU_IDIVB_CTRL = CGU_IDIVB_CTRL_PD(1); - CGU_IDIVC_CTRL = CGU_IDIVC_CTRL_PD(1); - CGU_IDIVD_CTRL = CGU_IDIVD_CTRL_PD(1); - CGU_IDIVE_CTRL = CGU_IDIVE_CTRL_PD(1); - - /* Base clocks */ - CGU_BASE_SPIFI_CLK = CGU_BASE_SPIFI_CLK_PD(1); /* SPIFI is only used at boot */ - CGU_BASE_USB1_CLK = CGU_BASE_USB1_CLK_PD(1); /* USB1 is not exposed on HackRF */ - CGU_BASE_PHY_RX_CLK = CGU_BASE_PHY_RX_CLK_PD(1); - CGU_BASE_PHY_TX_CLK = CGU_BASE_PHY_TX_CLK_PD(1); - CGU_BASE_LCD_CLK = CGU_BASE_LCD_CLK_PD(1); - CGU_BASE_VADC_CLK = CGU_BASE_VADC_CLK_PD(1); - // CGU_BASE_SDIO_CLK = CGU_BASE_SDIO_CLK_PD(1); // << BREAKS - CGU_BASE_UART0_CLK = CGU_BASE_UART0_CLK_PD(1); - CGU_BASE_UART1_CLK = CGU_BASE_UART1_CLK_PD(1); - CGU_BASE_UART2_CLK = CGU_BASE_UART2_CLK_PD(1); - CGU_BASE_UART3_CLK = CGU_BASE_UART3_CLK_PD(1); - CGU_BASE_OUT_CLK = CGU_BASE_OUT_CLK_PD(1); - CGU_BASE_AUDIO_CLK = CGU_BASE_AUDIO_CLK_PD(1); - CGU_BASE_CGU_OUT0_CLK = CGU_BASE_CGU_OUT0_CLK_PD(1); - CGU_BASE_CGU_OUT1_CLK = CGU_BASE_CGU_OUT1_CLK_PD(1); - - /* Disable unused peripheral clocks */ - CCU1_CLK_APB1_CAN1_CFG = 0; - CCU1_CLK_APB1_I2S_CFG = 0; - CCU1_CLK_APB1_MOTOCONPWM_CFG = 0; - CCU1_CLK_APB3_ADC0_CFG = 0; - CCU1_CLK_APB3_ADC1_CFG = 0; - CCU1_CLK_APB3_CAN0_CFG = 0; - CCU1_CLK_APB3_DAC_CFG = 0; - // CCU1_CLK_M4_DMA_CFG = 0; - CCU1_CLK_M4_EMC_CFG = 0; - CCU1_CLK_M4_EMCDIV_CFG = 0; - CCU1_CLK_M4_ETHERNET_CFG = 0; - CCU1_CLK_M4_LCD_CFG = 0; - CCU1_CLK_M4_QEI_CFG = 0; - CCU1_CLK_M4_RITIMER_CFG = 0; - // CCU1_CLK_M4_SCT_CFG = 0; - // CCU1_CLK_M4_SDIO_CFG = 0; // << BREAKS - CCU1_CLK_M4_SPIFI_CFG = 0; - CCU1_CLK_M4_TIMER0_CFG = 0; - // CCU1_CLK_M4_TIMER1_CFG = 0; - // CCU1_CLK_M4_TIMER2_CFG = 0; - CCU1_CLK_M4_TIMER3_CFG = 0; - CCU1_CLK_M4_UART1_CFG = 0; - CCU1_CLK_M4_USART0_CFG = 0; - CCU1_CLK_M4_USART2_CFG = 0; - CCU1_CLK_M4_USART3_CFG = 0; - CCU1_CLK_M4_USB1_CFG = 0; - CCU1_CLK_M4_VADC_CFG = 0; - // CCU1_CLK_SPIFI_CFG = 0; - // CCU1_CLK_USB1_CFG = 0; - // CCU1_CLK_VADC_CFG = 0; - // CCU2_CLK_APB0_UART1_CFG = 0; - // CCU2_CLK_APB0_USART0_CFG = 0; - // CCU2_CLK_APB2_USART2_CFG = 0; - // CCU2_CLK_APB2_USART3_CFG = 0; - // CCU2_CLK_APLL_CFG = 0; - // CCU2_CLK_SDIO_CFG = 0; -#endif -} - -clock_source_t activate_best_clock_source(void) { -#ifdef HACKRF_ONE - /* Ensure PortaPack reference oscillator is off while checking for external clock input. */ - if (portapack_present()) { - portapack_reference_oscillator(false); - } -#endif - - clock_source_t source = CLOCK_SOURCE_HACKRF; - - /* Check for external clock input. */ - if (si5351c_clkin_signal_valid(&clock_gen)) { - source = CLOCK_SOURCE_EXTERNAL; - } else { -#ifdef HACKRF_ONE - /* Enable PortaPack reference oscillator (if present), and check for valid clock. */ - if (portapack_present()) { - portapack_reference_oscillator(true); - delay(510000); /* loop iterations @ 204MHz for >10ms for oscillator to enable. */ - if (si5351c_clkin_signal_valid(&clock_gen)) { - source = CLOCK_SOURCE_PORTAPACK; - } else { - portapack_reference_oscillator(false); - } - } -#endif - /* No external or PortaPack clock was found. Use HackRF Si5351C crystal. */ - } - - si5351c_change_input( - &clock_gen, - (source == CLOCK_SOURCE_HACKRF) ? SI5351C_INPUT_XTAL : SI5351C_INPUT_CLKIN); - hackrf_ui()->set_clock_source(source); - return source; -} - -void ssp1_set_mode_max283x(void) { - spi_bus_start(&spi_bus_ssp1, &ssp_config_max283x); -} - -void ssp1_set_mode_max5864(void) { - spi_bus_start(max5864.bus, &ssp_config_max5864); -} - -void pin_setup(void) { - const platform_scu_t* scu = platform_scu(); - - /* spi_bus_ssp0/1 are defined in hackrf/firmware/common/spi_bus.c with no - * .config. Patch in our SSP configs and SPI driver entry points here. */ - spi_bus_ssp0.config = &ssp_config_w25q80bv; - spi_bus_ssp0.start = spi_ssp_start; - spi_bus_ssp0.stop = spi_ssp_stop; - spi_bus_ssp0.transfer = spi_ssp_transfer; - spi_bus_ssp0.transfer_gather = spi_ssp_transfer_gather; - - spi_bus_ssp1.config = &ssp_config_max5864; - spi_bus_ssp1.start = spi_ssp_start; - spi_bus_ssp1.stop = spi_ssp_stop; - spi_bus_ssp1.transfer = spi_ssp_transfer; - spi_bus_ssp1.transfer_gather = spi_ssp_transfer_gather; - - /* sgpio_config / rf_path are defined in common/sgpio.c and rf_path.c - * with minimal initializers. Populate the gpio_* fields here. */ - sgpio_config.gpio_q_invert = &gpio_q_invert; - sgpio_config.gpio_trigger_enable = &gpio_hw_sync_enable; - sgpio_config.slice_mode_multislice = true; - -#ifdef HACKRF_ONE - rf_path.gpio_hp = &gpio_hp; - rf_path.gpio_lp = &gpio_lp; - rf_path.gpio_tx_mix_bp = &gpio_tx_mix_bp; - rf_path.gpio_no_mix_bypass = &gpio_no_mix_bypass; - rf_path.gpio_rx_mix_bp = &gpio_rx_mix_bp; - rf_path.gpio_tx_amp = &gpio_tx_amp; - rf_path.gpio_tx = &gpio_tx; - rf_path.gpio_mix_bypass = &gpio_mix_bypass; - rf_path.gpio_rx = &gpio_rx; - rf_path.gpio_no_tx_amp_pwr = &gpio_no_tx_amp_pwr; - rf_path.gpio_amp_bypass = &gpio_amp_bypass; - rf_path.gpio_rx_amp = &gpio_rx_amp; - rf_path.gpio_no_rx_amp_pwr = &gpio_no_rx_amp_pwr; -#endif -#ifdef RAD1O - rf_path.gpio_tx_rx_n = &gpio_tx_rx_n; - rf_path.gpio_tx_rx = &gpio_tx_rx; - rf_path.gpio_by_mix = &gpio_by_mix; - rf_path.gpio_by_mix_n = &gpio_by_mix_n; - rf_path.gpio_by_amp = &gpio_by_amp; - rf_path.gpio_by_amp_n = &gpio_by_amp_n; - rf_path.gpio_mixer_en = &gpio_mixer_en; - rf_path.gpio_low_high_filt = &gpio_low_high_filt; - rf_path.gpio_low_high_filt_n = &gpio_low_high_filt_n; - rf_path.gpio_tx_amp = &gpio_tx_amp; - rf_path.gpio_rx_lna = &gpio_rx_lna; -#endif - - /* Configure all GPIO as Input (safe state) */ - // gpio_init(); - - /* TDI and TMS pull-ups are required in all JTAG-compliant devices. - * - * The HackRF CPLD is always present, so let the CPLD pull up its TDI and TMS. - * - * The PortaPack may not be present, so pull up the PortaPack TMS pin from the - * microcontroller. - * - * TCK is recommended to be held low, so use microcontroller pull-down. - * - * TDO is undriven except when in Shift-IR or Shift-DR phases. - * Use the microcontroller to pull down to keep from floating. - * - * LPC43xx pull-up and pull-down resistors are approximately 53K. - */ -#ifdef HACKRF_ONE - scu_pinmux(scu->PINMUX_PP_TMS, SCU_GPIO_PUP | SCU_CONF_FUNCTION0); - scu_pinmux(scu->PINMUX_PP_TDO, SCU_GPIO_PDN | SCU_CONF_FUNCTION0); -#endif - scu_pinmux(scu->PINMUX_CPLD_TMS, SCU_GPIO_NOPULL | SCU_CONF_FUNCTION0); - scu_pinmux(scu->PINMUX_CPLD_TDI, SCU_GPIO_NOPULL | SCU_CONF_FUNCTION0); - scu_pinmux(scu->PINMUX_CPLD_TDO, SCU_GPIO_PDN | SCU_CONF_FUNCTION4); - scu_pinmux(scu->PINMUX_CPLD_TCK, SCU_GPIO_PDN | SCU_CONF_FUNCTION0); - - /* Configure SCU Pin Mux as GPIO */ - scu_pinmux(scu->PINMUX_LED1, SCU_GPIO_NOPULL); - scu_pinmux(scu->PINMUX_LED2, SCU_GPIO_NOPULL); - scu_pinmux(scu->PINMUX_LED3, SCU_GPIO_NOPULL); -#ifdef RAD1O - scu_pinmux(scu->PINMUX_LED4, SCU_GPIO_NOPULL | SCU_CONF_FUNCTION4); -#endif - - /* Configure USB indicators */ -#ifdef JAWBREAKER - scu_pinmux(scu->PINMUX_USB_LED0, SCU_CONF_FUNCTION3); - scu_pinmux(scu->PINMUX_USB_LED1, SCU_CONF_FUNCTION3); -#endif - - gpio_output(&gpio_led[0]); - gpio_output(&gpio_led[1]); - gpio_output(&gpio_led[2]); -#ifdef RAD1O - gpio_output(&gpio_led[3]); -#endif - - disable_1v8_power(); - if (detected_platform() == BOARD_ID_HACKRF1_R9) { -#ifdef HACKRF_ONE - gpio_output(&gpio_h1r9_1v8_enable); - scu_pinmux(scu->H1R9_EN1V8, SCU_GPIO_FAST | SCU_CONF_FUNCTION0); -#endif - } else { - gpio_output(&gpio_1v8_enable); - scu_pinmux(scu->PINMUX_EN1V8, SCU_GPIO_FAST | SCU_CONF_FUNCTION0); - } - -#ifdef HACKRF_ONE - /* Safe state: start with VAA turned off: */ - disable_rf_power(); - - /* Configure RF power supply (VAA) switch control signal as output */ - if (detected_platform() == BOARD_ID_HACKRF1_R9) { - gpio_output(&gpio_h1r9_vaa_disable); - } else { - gpio_output(&gpio_vaa_disable); - } -#endif - -#ifdef RAD1O - /* Safe state: start with VAA turned off: */ - disable_rf_power(); - - /* Configure RF power supply (VAA) switch control signal as output */ - gpio_output(&gpio_vaa_enable); - - /* Disable unused clock outputs. They generate noise. */ - scu_pinmux(CLK0, SCU_CLK_IN | SCU_CONF_FUNCTION7); - scu_pinmux(CLK2, SCU_CLK_IN | SCU_CONF_FUNCTION7); - - scu_pinmux(scu->PINMUX_GPIO3_10, SCU_GPIO_PDN | SCU_CONF_FUNCTION0); - scu_pinmux(scu->PINMUX_GPIO3_11, SCU_GPIO_PDN | SCU_CONF_FUNCTION0); - -#endif - - /* enable input on SCL and SDA pins */ - SCU_SFSI2C0 = SCU_I2C0_NOMINAL; - - spi_bus_start(&spi_bus_ssp1, &ssp_config_max283x); - - mixer_bus_setup(&mixer); - -#ifdef HACKRF_ONE - if (detected_platform() == BOARD_ID_HACKRF1_R9) { - rf_path.gpio_rx = &gpio_h1r9_rx; - sgpio_config.gpio_trigger_enable = &gpio_h1r9_hw_sync_enable; - } -#endif - rf_path_pin_setup(&rf_path); - - /* Configure external clock in */ - scu_pinmux(scu->PINMUX_GP_CLKIN, SCU_CLK_IN | SCU_CONF_FUNCTION1); - - sgpio_configure_pin_functions(&sgpio_config); -} - -void enable_1v8_power(void) { - if (detected_platform() == BOARD_ID_HACKRF1_R9) { -#ifdef HACKRF_ONE - gpio_set(&gpio_h1r9_1v8_enable); -#endif - } else { - gpio_set(&gpio_1v8_enable); - } -} - -void disable_1v8_power(void) { - if (detected_platform() == BOARD_ID_HACKRF1_R9) { -#ifdef HACKRF_ONE - gpio_clear(&gpio_h1r9_1v8_enable); -#endif - } else { - gpio_clear(&gpio_1v8_enable); - } -} - -#ifdef HACKRF_ONE -void enable_rf_power(void) { - uint32_t i; - - /* many short pulses to avoid one big voltage glitch */ - for (i = 0; i < 1000; i++) { - if (detected_platform() == BOARD_ID_HACKRF1_R9) { - gpio_set(&gpio_h1r9_vaa_disable); - gpio_clear(&gpio_h1r9_vaa_disable); - } else { - gpio_set(&gpio_vaa_disable); - gpio_clear(&gpio_vaa_disable); - } - } -} - -void disable_rf_power(void) { - if (detected_platform() == BOARD_ID_HACKRF1_R9) { - gpio_set(&gpio_h1r9_vaa_disable); - } else { - gpio_set(&gpio_vaa_disable); - } -} -#endif - -#ifdef RAD1O -void enable_rf_power(void) { - gpio_set(&gpio_vaa_enable); - - /* Let the voltage stabilize */ - delay(1000000); -} - -void disable_rf_power(void) { - gpio_clear(&gpio_vaa_enable); -} -#endif - -void led_on(const led_t led) { - gpio_set(&gpio_led[led]); -} - -void led_off(const led_t led) { - gpio_clear(&gpio_led[led]); -} - -void led_toggle(const led_t led) { - gpio_toggle(&gpio_led[led]); -} - -void set_leds(const uint8_t state) { - int num_leds = 3; -#ifdef RAD1O - num_leds = 4; -#endif - for (int i = 0; i < num_leds; i++) { - gpio_write(&gpio_led[i], ((state >> i) & 1) == 1); - } -} - -void trigger_enable(bool hw_sync_mode) { - gpio_write(sgpio_config.gpio_trigger_enable, hw_sync_mode == 1); -} - -void halt_and_flash(const uint32_t duration) { - /* blink LED1, LED2, and LED3 */ - while (1) { - led_on(LED1); - led_on(LED2); - led_on(LED3); - delay(duration); - led_off(LED1); - led_off(LED2); - led_off(LED3); - delay(duration); - } -} - -/* Upstream commit 85dfacf6 ("Universalize: firmware/hackrf_usb") wired - * usb_endpoint_control_out.setup_complete to transceiver_usb_setup_complete - * (defined in hackrf_usb/usb_api_transceiver.c). That function dispatches - * transceiver-specific SETUP packets and falls through to usb_setup_complete - * for everything else. sd_over_usb doesn't link the transceiver API, so - * provide a shim that always delegates to the standard handler. Without - * this, EP0 SETUP packets get a no-op completion and enumeration times out - * (host reports "device descriptor read/64, error -110"). */ -void usb_setup_complete(usb_endpoint_t* const endpoint); -void transceiver_usb_setup_complete(usb_endpoint_t* const endpoint); -void transceiver_usb_setup_complete(usb_endpoint_t* const endpoint) { - usb_setup_complete(endpoint); -} diff --git a/firmware/baseband/sd_over_usb/pins.c b/firmware/baseband/sd_over_usb/pins.c new file mode 100644 index 000000000..c4d2dfb54 --- /dev/null +++ b/firmware/baseband/sd_over_usb/pins.c @@ -0,0 +1,254 @@ +/* + * Copyright 2012-2026 Great Scott Gadgets + * Copyright 2012 Jared Boone + * Copyright 2013 Benjamin Vernoux + * + * This file is part of HackRF. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that 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; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#include "pins.h" + +#include + +#include "gpio.h" +#include "leds.h" +#include "platform_detect.h" +#include "platform_gpio.h" +#include "platform_scu.h" +#include "power.h" +#ifdef IS_PRALINE +#include "clock_io.h" +#endif + +void pins_shutdown(void) { + /* Configure all GPIO as Input (safe state) */ + // gpio_init(); + + /* Detect Platform */ + const platform_gpio_t* gpio = platform_gpio(); + const platform_scu_t* scu = platform_scu(); + + /* TDI and TMS pull-ups are required in all JTAG-compliant devices. + * + * The HackRF CPLD is always present, so let the CPLD pull up its TDI and TMS. + * + * The PortaPack may not be present, so pull up the PortaPack TMS pin from the + * microcontroller. + * + * TCK is recommended to be held low, so use microcontroller pull-down. + * + * TDO is undriven except when in Shift-IR or Shift-DR phases. + * Use the microcontroller to pull down to keep from floating. + * + * LPC43xx pull-up and pull-down resistors are approximately 53K. + */ +#ifdef IS_EXPANSION_COMPATIBLE + if (IS_EXPANSION_COMPATIBLE) { + scu_pinmux(scu->PINMUX_PP_TMS, SCU_GPIO_PUP | SCU_CONF_FUNCTION0); + scu_pinmux(scu->PINMUX_PP_TDO, SCU_GPIO_PDN | SCU_CONF_FUNCTION0); + } +#endif + scu_pinmux(scu->PINMUX_CPLD_TCK, SCU_GPIO_PDN | SCU_CONF_FUNCTION0); +#ifdef IS_NOT_PRALINE + if (IS_NOT_PRALINE) { + scu_pinmux(scu->PINMUX_CPLD_TMS, SCU_GPIO_NOPULL | SCU_CONF_FUNCTION0); + scu_pinmux(scu->PINMUX_CPLD_TDI, SCU_GPIO_NOPULL | SCU_CONF_FUNCTION0); + scu_pinmux(scu->PINMUX_CPLD_TDO, SCU_GPIO_PDN | SCU_CONF_FUNCTION4); + } +#endif + + /* Configure SCU Pin Mux as GPIO */ + scu_pinmux(scu->PINMUX_LED1, SCU_GPIO_NOPULL); + scu_pinmux(scu->PINMUX_LED2, SCU_GPIO_NOPULL); + scu_pinmux(scu->PINMUX_LED3, SCU_GPIO_NOPULL); +#ifdef IS_RAD1O + if (IS_RAD1O) { + scu_pinmux(scu->PINMUX_LED4, SCU_GPIO_NOPULL | SCU_CONF_FUNCTION4); + } +#endif +#ifdef IS_PRALINE + if (IS_PRALINE) { + scu_pinmux(scu->PINMUX_LED4, SCU_GPIO_NOPULL | SCU_CONF_FUNCTION0); + } +#endif + + /* Configure USB indicators */ +#ifdef IS_JAWBREAKER + if (IS_JAWBREAKER) { + scu_pinmux(scu->PINMUX_USB_LED0, SCU_CONF_FUNCTION3); + scu_pinmux(scu->PINMUX_USB_LED1, SCU_CONF_FUNCTION3); + } +#endif + +#ifdef IS_PRALINE + if (IS_PRALINE) { + // disable_1v2_power(); + // disable_3v3aux_power(); + // gpio_output(gpio->gpio_1v2_enable); + // gpio_output(gpio->gpio_3v3aux_enable_n); + // scu_pinmux(scu->PINMUX_EN1V2, SCU_GPIO_FAST | SCU_CONF_FUNCTION0); + // scu_pinmux(scu->PINMUX_EN3V3_AUX_N, SCU_GPIO_FAST | SCU_CONF_FUNCTION4); + } +#endif + +#ifdef IS_NOT_PRALINE + if (IS_NOT_PRALINE) { + disable_1v8_power(); +#ifdef IS_H1_R9 + if (IS_H1_R9) { + gpio_output(gpio->h1r9_1v8_enable); + scu_pinmux(scu->H1R9_EN1V8, SCU_GPIO_FAST | SCU_CONF_FUNCTION0); + } +#endif +#ifdef IS_NOT_H1_R9 + if (IS_NOT_H1_R9) { + gpio_output(gpio->gpio_1v8_enable); + scu_pinmux(scu->PINMUX_EN1V8, SCU_GPIO_FAST | SCU_CONF_FUNCTION0); + } +#endif + } +#endif + +#ifdef IS_H1_OR_PRALINE + if (IS_H1_OR_PRALINE) { + /* Safe state: start with VAA turned off: */ + disable_rf_power(); + + /* Configure RF power supply (VAA) switch control signal as output */ +#ifdef IS_H1_R9 + if (IS_H1_R9) { + gpio_output(gpio->h1r9_vaa_disable); + } +#endif +#ifdef IS_NOT_H1_R9 + if (IS_NOT_H1_R9) { + gpio_output(gpio->vaa_disable); + } +#endif + } +#endif + +#ifdef IS_RAD1O + if (IS_RAD1O) { + /* Safe state: start with VAA turned off: */ + disable_rf_power(); + + /* Configure RF power supply (VAA) switch control signal as output */ + gpio_output(gpio->vaa_enable); + + /* Disable unused clock outputs. They generate noise. */ + scu_pinmux(CLK0, SCU_CLK_IN | SCU_CONF_FUNCTION7); + scu_pinmux(CLK2, SCU_CLK_IN | SCU_CONF_FUNCTION7); + + scu_pinmux(scu->PINMUX_GPIO3_10, SCU_GPIO_PDN | SCU_CONF_FUNCTION0); + scu_pinmux(scu->PINMUX_GPIO3_11, SCU_GPIO_PDN | SCU_CONF_FUNCTION0); + } +#endif + +#ifdef IS_PRALINE + if (IS_PRALINE) { + scu_pinmux(scu->P2_CTRL0, scu->P2_CTRL0_PINCFG); + scu_pinmux(scu->P2_CTRL1, scu->P2_CTRL1_PINCFG); + scu_pinmux(scu->P1_CTRL0, scu->P1_CTRL0_PINCFG); + scu_pinmux(scu->P1_CTRL1, scu->P1_CTRL1_PINCFG); + scu_pinmux(scu->P1_CTRL2, scu->P1_CTRL2_PINCFG); + scu_pinmux(scu->CLKIN_CTRL, scu->CLKIN_CTRL_PINCFG); + scu_pinmux(scu->AA_EN, scu->AA_EN_PINCFG); + scu_pinmux(scu->TRIGGER_IN, scu->TRIGGER_IN_PINCFG); + scu_pinmux(scu->TRIGGER_OUT, scu->TRIGGER_OUT_PINCFG); + scu_pinmux(scu->PPS_OUT, scu->PPS_OUT_PINCFG); + scu_pinmux(scu->SCT_CLK, scu->SCT_CLK_PINCFG); + + scu_pinmux(scu->PINMUX_FPGA_CRESET, SCU_GPIO_PDN | SCU_CONF_FUNCTION0); + scu_pinmux(scu->PINMUX_FPGA_CDONE, SCU_GPIO_PDN | SCU_CONF_FUNCTION4); + scu_pinmux(scu->PINMUX_FPGA_SPI_CS, SCU_GPIO_PDN | SCU_CONF_FUNCTION0); + + scu_pinmux(scu->SSP1_CIPO, SCU_GPIO_PDN | SCU_CONF_FUNCTION0); + scu_pinmux(scu->SSP1_COPI, SCU_GPIO_PDN | SCU_CONF_FUNCTION0); + scu_pinmux(scu->SSP1_SCK, SCU_GPIO_PDN | SCU_CONF_FUNCTION2); + + scu_pinmux(scu->XCVR_ENABLE, SCU_GPIO_PDN | SCU_CONF_FUNCTION4); + scu_pinmux(scu->XCVR_RXENABLE, SCU_GPIO_PDN | SCU_CONF_FUNCTION4); + scu_pinmux(scu->XCVR_CS, SCU_GPIO_PDN | SCU_CONF_FUNCTION4); + scu_pinmux(scu->XCVR_RXHP, SCU_GPIO_PDN | SCU_CONF_FUNCTION4); + scu_pinmux(scu->XCVR_LD, SCU_GPIO_PDN | SCU_CONF_FUNCTION0); + + scu_pinmux(scu->MIXER_LD, SCU_GPIO_PDN | SCU_CONF_FUNCTION4); + scu_pinmux(scu->MIXER_SCLK, SCU_GPIO_PDN | SCU_CONF_FUNCTION4); + scu_pinmux(scu->MIXER_SDATA, SCU_GPIO_PDN | SCU_CONF_FUNCTION0); + scu_pinmux(scu->MIXER_ENX, SCU_GPIO_PDN | SCU_CONF_FUNCTION0); + scu_pinmux(scu->MIXER_RESETX, SCU_GPIO_PDN | SCU_CONF_FUNCTION0); + scu_pinmux(scu->MIXER_ENBL, SCU_GPIO_PDN | SCU_CONF_FUNCTION0); + + scu_pinmux(scu->AD_CS, SCU_GPIO_PDN | SCU_CONF_FUNCTION4); + + p2_ctrl_set(P2_SIGNAL_CLK3); + p1_ctrl_set(P1_SIGNAL_CLKIN); + clkin_ctrl_set(CLKIN_SIGNAL_P1); + + gpio_output(gpio->p2_ctrl0); + gpio_output(gpio->p2_ctrl1); + gpio_output(gpio->p1_ctrl0); + gpio_output(gpio->p1_ctrl1); + gpio_output(gpio->p1_ctrl2); + gpio_output(gpio->clkin_ctrl); + gpio_output(gpio->pps_out); + gpio_input(gpio->trigger_in); + gpio_input(gpio->trigger_out); + gpio_clear(gpio->fpga_cfg_spi_cs); + gpio_output(gpio->fpga_cfg_spi_cs); + gpio_clear(gpio->fpga_cfg_creset); + gpio_output(gpio->fpga_cfg_creset); + gpio_input(gpio->fpga_cfg_cdone); + gpio_input(gpio->max5864_select); + } +#endif + + /* enable input on SCL and SDA pins */ + SCU_SFSI2C0 = SCU_I2C0_NOMINAL; +} + +/* Run after pins_shutdown() and prior to enabling power supplies. */ +void pins_setup(void) { + /* Detect Platform */ + const platform_gpio_t* gpio = platform_gpio(); + const platform_scu_t* scu = platform_scu(); + + /* Configure LEDs */ + led_off(0); + led_off(1); + led_off(2); +#ifdef IS_FOUR_LEDS + if (IS_FOUR_LEDS) { + led_off(3); + } +#endif + + gpio_output(gpio->led[0]); + gpio_output(gpio->led[1]); + gpio_output(gpio->led[2]); + +#ifdef IS_FOUR_LEDS + if (IS_FOUR_LEDS) { + gpio_output(gpio->led[3]); + } +#endif + + /* Configure external clock in */ + scu_pinmux(scu->PINMUX_GP_CLKIN, SCU_CLK_IN | SCU_CONF_FUNCTION1); +} diff --git a/firmware/baseband/sd_over_usb/proc_sd_over_usb.cpp b/firmware/baseband/sd_over_usb/proc_sd_over_usb.cpp index 563f6cd41..0372030af 100644 --- a/firmware/baseband/sd_over_usb/proc_sd_over_usb.cpp +++ b/firmware/baseband/sd_over_usb/proc_sd_over_usb.cpp @@ -44,11 +44,11 @@ CH_IRQ_HANDLER(Vector60) { } int main() { + start_usb(); + sdcStart(&SDCD1, nullptr); if (sdcConnect(&SDCD1) == CH_FAILED) chDbgPanic("no sd card #1"); - start_usb(); - while (true) { usb_transfer(); } diff --git a/firmware/baseband/sd_over_usb/scsi.c b/firmware/baseband/sd_over_usb/scsi.c index 7b4339fd9..2cf6b9d1e 100644 --- a/firmware/baseband/sd_over_usb/scsi.c +++ b/firmware/baseband/sd_over_usb/scsi.c @@ -23,14 +23,35 @@ #include "scsi.h" #include "diskio.h" #include "gpio_lpc.h" +#include "delay.h" #include #include #include #include "string.h" +/* Maximum number of 512-byte blocks transferred per SD command / USB bulk + * transfer. The data region of usb_bulk_buffer is 16 KiB (0x0000..0x3FFF), + * so this must be <= 32. It is split into two equal halves for double + * buffering. */ +#define MAX_BLOCKS_PER_TRANSFER 32 +#define HALF_BLOCKS (MAX_BLOCKS_PER_TRANSFER / 2) + volatile bool usb_bulk_block_done = false; -void delay(uint32_t duration); +/* Per-buffer-half completion flags for the asynchronous (double-buffered) + * bulk transfers. Index 0 = usb_bulk_buffer[0], index 1 = second half. */ +volatile bool usb_bulk_block_done_async[2] = {false, false}; + +static uint32_t usb_bulk_buffer_index(const void* const data) { + return (data == &usb_bulk_buffer[HALF_BLOCKS * 512]) ? 1 : 0; +} + +void usb_bulk_block_cb_async(void* user_data, unsigned int bytes_transferred) { + const uint32_t idx = (uint32_t)(uintptr_t)user_data; + usb_bulk_block_done_async[idx] = true; + + (void)bytes_transferred; +} void usb_bulk_block_cb(void* user_data, unsigned int bytes_transferred) { usb_bulk_block_done = true; @@ -65,6 +86,49 @@ void usb_receive_bulk(void* const data, const uint32_t maximum_length) { while (!usb_bulk_block_done); } +/* Schedule a bulk IN transfer without waiting for it to complete. The caller + * must later call usb_send_bulk_wait_finish() with the same buffer before + * reusing it. */ +void usb_send_bulk_start(void* const data, const uint32_t maximum_length) { + const uint32_t idx = usb_bulk_buffer_index(data); + usb_bulk_block_done_async[idx] = false; + + usb_transfer_schedule_block( + &usb_endpoint_bulk_in, + data, + maximum_length, + usb_bulk_block_cb_async, + (void*)(uintptr_t)idx); +} + +/* Wait for a bulk IN transfer scheduled by usb_send_bulk_start() to finish. */ +void usb_send_bulk_wait_finish(void* const data) { + const uint32_t idx = usb_bulk_buffer_index(data); + while (!usb_bulk_block_done_async[idx]); +} + +/* Schedule a bulk OUT transfer without waiting for it to complete. The caller + * must later call usb_receive_bulk_finish() with the same buffer before + * reading from it. */ +void usb_receive_bulk_start(void* const data, const uint32_t maximum_length) { + const uint32_t idx = usb_bulk_buffer_index(data); + usb_bulk_block_done_async[idx] = false; + + usb_transfer_schedule_block( + &usb_endpoint_bulk_out, + data, + maximum_length, + usb_bulk_block_cb_async, + (void*)(uintptr_t)idx); +} + +/* Wait for a bulk OUT transfer scheduled by usb_receive_bulk_start() to + * finish. */ +void usb_receive_bulk_finish(void* const data) { + const uint32_t idx = usb_bulk_buffer_index(data); + while (!usb_bulk_block_done_async[idx]); +} + void usb_send_csw(msd_cbw_t* msd_cbw_data, uint8_t status) { msd_csw_t csw = { .signature = MSD_CSW_SIGNATURE, @@ -213,22 +277,95 @@ static data_request_t decode_data_request(const uint8_t* cmd) { uint8_t data_read10(msd_cbw_t* msd_cbw_data) { data_request_t req = decode_data_request(msd_cbw_data->cmd_data); - for (size_t block_index = 0; block_index < req.blk_cnt; block_index++) { - read_block(req.first_lba + block_index, &usb_bulk_buffer[0], 1 /* n blocks */); - usb_send_bulk(&usb_bulk_buffer[0], 512); + uint32_t lba = req.first_lba; + uint32_t remaining = req.blk_cnt; + uint8_t* buf[2] = {&usb_bulk_buffer[0], &usb_bulk_buffer[HALF_BLOCKS * 512]}; + uint32_t buf_idx = 0; + uint8_t* in_flight = NULL; + + if (remaining == 0) + return 0; + + /* Read the first chunk and start sending it. */ + uint32_t n = (remaining > HALF_BLOCKS) ? HALF_BLOCKS : remaining; + if (read_block(lba, buf[0], n)) + return 1; + usb_send_bulk_start(buf[0], n * 512); + in_flight = buf[0]; + lba += n; + remaining -= n; + + /* While USB sends the previous chunk, read the next one into the other + * half of the buffer. */ + while (remaining > 0) { + buf_idx ^= 1; + n = (remaining > HALF_BLOCKS) ? HALF_BLOCKS : remaining; + + if (read_block(lba, buf[buf_idx], n)) { + usb_send_bulk_wait_finish(in_flight); + return 1; + } + + usb_send_bulk_wait_finish(in_flight); + usb_send_bulk_start(buf[buf_idx], n * 512); + in_flight = buf[buf_idx]; + + lba += n; + remaining -= n; } + usb_send_bulk_wait_finish(in_flight); return 0; } uint8_t data_write10(msd_cbw_t* msd_cbw_data) { data_request_t req = decode_data_request(msd_cbw_data->cmd_data); - for (size_t block_index = 0; block_index < req.blk_cnt; block_index++) { - usb_receive_bulk(&usb_bulk_buffer[0], 512); - write_block(req.first_lba + block_index, &usb_bulk_buffer[0], 1 /* n blocks */); + uint32_t lba = req.first_lba; + uint32_t remaining = req.blk_cnt; + uint8_t* buf[2] = {&usb_bulk_buffer[0], &usb_bulk_buffer[HALF_BLOCKS * 512]}; + uint32_t buf_idx = 0; + uint8_t* pending_buf = NULL; + uint32_t pending_lba = 0; + uint32_t pending_n = 0; + + if (remaining == 0) + return 0; + + /* Start receiving the first chunk. */ + uint32_t n = (remaining > HALF_BLOCKS) ? HALF_BLOCKS : remaining; + usb_receive_bulk_start(buf[0], n * 512); + pending_buf = buf[0]; + pending_lba = lba; + pending_n = n; + lba += n; + remaining -= n; + + /* While USB receives the next chunk, write the previous one to the SD + * card. */ + while (remaining > 0) { + buf_idx ^= 1; + n = (remaining > HALF_BLOCKS) ? HALF_BLOCKS : remaining; + + usb_receive_bulk_start(buf[buf_idx], n * 512); + + usb_receive_bulk_finish(pending_buf); + if (write_block(pending_lba, pending_buf, pending_n)) { + usb_receive_bulk_finish(buf[buf_idx]); + return 1; + } + + pending_buf = buf[buf_idx]; + pending_lba = lba; + pending_n = n; + lba += n; + remaining -= n; } + usb_receive_bulk_finish(pending_buf); + if (write_block(pending_lba, pending_buf, pending_n)) + return 1; + return 0; } @@ -293,7 +430,7 @@ void scsi_command(msd_cbw_t* msd_cbw_data) { gpio_output(&dfu); gpio_clear(&dfu); - delay(50 * 40800); + delay_ms(50); RESET_CTRL0 = (1 << 0); break; diff --git a/firmware/baseband/sd_over_usb/sd_over_usb.c b/firmware/baseband/sd_over_usb/sd_over_usb.c index 244869fb1..8bb4fe761 100644 --- a/firmware/baseband/sd_over_usb/sd_over_usb.c +++ b/firmware/baseband/sd_over_usb/sd_over_usb.c @@ -23,6 +23,8 @@ #include "sd_over_usb.h" #include "scsi.h" #include "usb_descriptor.h" +#include +#include "delay.h" #include @@ -36,8 +38,14 @@ extern usb_configuration_t* usb_configurations[]; static const usb_device_t usb_device_sd_over_usb = { - .descriptor = usb_descriptor_device, - .descriptor_strings = usb_descriptor_strings, +#ifdef IS_NOT_PRALINE + .descriptor = usb_descriptor_device_hackrf, + .descriptor_strings = usb_descriptor_strings_hackrf_one, +#endif +#ifdef IS_PRALINE + .descriptor = usb_descriptor_device_hackrf, + .descriptor_strings = usb_descriptor_strings_praline, +#endif .qualifier_descriptor = usb_descriptor_device_qualifier, .configurations = &usb_configurations, .configuration = 0, @@ -91,16 +99,83 @@ void usb_configuration_changed(usb_device_t* const device) { usb_endpoint_init(&usb_endpoint_bulk_out, false); } +void usb_set_descriptor_by_serial_number(void) { + iap_cmd_res_t iap_cmd_res; + + /* Read IAP Serial Number Identification */ + iap_cmd_res.cmd_param.command_code = IAP_CMD_READ_SERIAL_NO; + iap_cmd_call(&iap_cmd_res); + + if (iap_cmd_res.status_res.status_ret == CMD_SUCCESS) { + usb_descriptor_string_serial_number[0] = + USB_DESCRIPTOR_STRING_SERIAL_BUF_LEN; + usb_descriptor_string_serial_number[1] = USB_DESCRIPTOR_TYPE_STRING; + + /* 32 characters of serial number, convert to UTF-16LE */ + for (size_t i = 0; i < USB_DESCRIPTOR_STRING_SERIAL_LEN; i++) { + const uint_fast8_t nibble = + (iap_cmd_res.status_res.iap_result[i >> 3] >> + (28 - (i & 7) * 4)) & + 0xf; + const char c = + (nibble > 9) ? ('a' + nibble - 10) : ('0' + nibble); + usb_descriptor_string_serial_number[2 + i * 2] = c; + usb_descriptor_string_serial_number[3 + i * 2] = 0x00; + } + } else { + usb_descriptor_string_serial_number[0] = 2; + usb_descriptor_string_serial_number[1] = USB_DESCRIPTOR_TYPE_STRING; + } +} + void start_usb(void) { + // Detect hardware platform before we do anything else. detect_hardware_platform(); - pin_setup(); + board_id_t board_id = detected_platform(); + + pins_shutdown(); + sgpio_pin_shutdown(&sgpio_config); + rf_path_pin_shutdown(); + if (board_id != BOARD_ID_RAD1O) { + clock_gen_shutdown(); + } + delay_ms(10); + pins_setup(); + cpld_jtag_pin_setup(); + cpu_clock_init(); - memcpy(&usb_device, &usb_device_sd_over_usb, sizeof(usb_device_sd_over_usb)); +#ifndef DFU_MODE + usb_set_descriptor_by_serial_number(); +#endif usb_set_configuration_changed_cb(usb_configuration_changed); usb_peripheral_reset(); +#ifdef IS_HACKRF_ONE + if (IS_HACKRF_ONE) { + memcpy(&usb_device, + &usb_device_sd_over_usb, + sizeof(usb_device_sd_over_usb)); + } +#endif +#ifdef IS_JAWBREAKER + if (IS_JAWBREAKER) { + memcpy(&usb_device, + &usb_device_jawbreaker, + sizeof(usb_device_jawbreaker)); + } +#endif +#ifdef IS_RAD1O + if (IS_RAD1O) { + memcpy(&usb_device, &usb_device_rad1o, sizeof(usb_device_rad1o)); + } +#endif +#ifdef IS_PRALINE + if (IS_PRALINE) { + memcpy(&usb_device, &usb_device_sd_over_usb, sizeof(usb_device_sd_over_usb)); + } +#endif usb_device_init(0, &usb_device); usb_queue_init(&usb_endpoint_control_out_queue); @@ -109,9 +184,6 @@ void start_usb(void) { usb_queue_init(&usb_endpoint_bulk_in_queue); usb_endpoint_init(&usb_endpoint_control_out, false); - /* Match the new usb_endpoint_init() contract introduced upstream by - * db73ecbf, control IN needs ZLP for transfers whose length is a - * multiple of the EP0 max packet size, otherwise the host hangs. */ usb_endpoint_init(&usb_endpoint_control_in, true); nvic_set_priority(NVIC_USB0_IRQ, 255); diff --git a/firmware/baseband/sd_over_usb/usb_api_transceiver.c b/firmware/baseband/sd_over_usb/usb_api_transceiver.c new file mode 100644 index 000000000..00c9ba842 --- /dev/null +++ b/firmware/baseband/sd_over_usb/usb_api_transceiver.c @@ -0,0 +1,766 @@ +/* + * Copyright 2012-2022 Great Scott Gadgets + * Copyright 2012 Jared Boone + * Copyright 2013 Benjamin Vernoux + * Copyright 2024 Bernd Herzog + * + * This file is part of HackRF. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that 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; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#include "usb_api_transceiver.h" + +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "common/usb.h" +#include +#include +#include + +#include "usb_buffer.h" +#include "usb_endpoint.h" + +#define USB_TRANSFER_SIZE 0x4000 +#define DMA_TRANSFER_SIZE 0x2000 + +#define BUF_HALF_MASK (USB_SAMP_BUFFER_SIZE >> 1) + +// Unless we know the host knows our buffer size, we'll avoid leaving TX +// until we've transmitted all bytes sent by the host. This flag is cleared +// when the host requests our buffer size. +bool auto_tx_flush = true; + +volatile uint32_t dma_started, dma_pending, usb_started, usb_completed; + +typedef struct { + uint32_t freq_mhz; + uint32_t freq_hz; +} set_freq_params_t; + +set_freq_params_t set_freq_params; + +struct set_freq_explicit_params { + uint64_t if_freq_hz; /* intermediate frequency */ + uint64_t lo_freq_hz; /* front-end local oscillator frequency */ + uint8_t path; /* image rejection filter path */ +}; + +struct set_freq_explicit_params explicit_params; + +typedef struct { + uint32_t freq_hz; + uint32_t divider; +} set_sample_r_params_t; + +set_sample_r_params_t set_sample_r_params; + +void transceiver_dma_setup(void); + +usb_request_status_t usb_vendor_request_set_baseband_filter_bandwidth( + usb_endpoint_t* const endpoint, + const usb_transfer_stage_t stage) { + if (stage == USB_TRANSFER_STAGE_SETUP) { + const uint32_t bandwidth = + (endpoint->setup.index << 16) | endpoint->setup.value; + radio_reg_write( + &radio, + RADIO_BANK_REQUESTED, + RADIO_BB_BANDWIDTH_TX, + bandwidth); + radio_reg_write( + &radio, + RADIO_BANK_REQUESTED, + RADIO_BB_BANDWIDTH_RX, + bandwidth); + usb_transfer_schedule_ack(endpoint->in); + } + return USB_REQUEST_STATUS_OK; +} + +usb_request_status_t usb_vendor_request_set_freq( + usb_endpoint_t* const endpoint, + const usb_transfer_stage_t stage) { + if (stage == USB_TRANSFER_STAGE_SETUP) { + usb_transfer_schedule_block( + endpoint->out, + &set_freq_params, + sizeof(set_freq_params_t), + NULL, + NULL); + } else if (stage == USB_TRANSFER_STAGE_DATA) { + const uint64_t freq = + set_freq_params.freq_mhz * 1000000ULL + set_freq_params.freq_hz; + radio_reg_write( + &radio, + RADIO_BANK_REQUESTED, + RADIO_FREQUENCY_RF, + freq * FP_ONE_HZ); + radio_reg_write( + &radio, + RADIO_BANK_REQUESTED, + RADIO_FREQUENCY_IF, + RADIO_UNSET); + radio_reg_write( + &radio, + RADIO_BANK_REQUESTED, + RADIO_FREQUENCY_LO, + RADIO_UNSET); + radio_reg_write( + &radio, + RADIO_BANK_REQUESTED, + RADIO_IMAGE_REJECT, + RADIO_UNSET); + usb_transfer_schedule_ack(endpoint->in); + } + return USB_REQUEST_STATUS_OK; +} + +usb_request_status_t usb_vendor_request_set_freq_explicit( + usb_endpoint_t* const endpoint, + const usb_transfer_stage_t stage) { + if (stage == USB_TRANSFER_STAGE_SETUP) { + usb_transfer_schedule_block( + endpoint->out, + &explicit_params, + sizeof(struct set_freq_explicit_params), + NULL, + NULL); + } else if (stage == USB_TRANSFER_STAGE_DATA) { + radio_reg_write( + &radio, + RADIO_BANK_REQUESTED, + RADIO_FREQUENCY_IF, + explicit_params.if_freq_hz * FP_ONE_HZ); + radio_reg_write( + &radio, + RADIO_BANK_REQUESTED, + RADIO_FREQUENCY_LO, + explicit_params.lo_freq_hz * FP_ONE_HZ); + radio_reg_write( + &radio, + RADIO_BANK_REQUESTED, + RADIO_IMAGE_REJECT, + explicit_params.path); + usb_transfer_schedule_ack(endpoint->in); + } + return USB_REQUEST_STATUS_OK; +} + +/* + * Convert fractional sample rate to units of 1/(2**36) Hz. + */ +static inline fp_28_36_t round_sample_rate(uint64_t num, uint32_t denom) { + uint64_t q1, r1, q2, r2, q3; + + if (denom == 0) { + denom = 1; + } + + q1 = num / denom; + r1 = num % denom; + + q2 = (r1 << 32) / denom; + r2 = (r1 << 32) % denom; + + q3 = ((r2 << 4) + (denom >> 1)) / denom; + + return (q1 << 36) + (q2 << 4) + q3; +} + +usb_request_status_t usb_vendor_request_set_sample_rate_frac( + usb_endpoint_t* const endpoint, + const usb_transfer_stage_t stage) { + if (stage == USB_TRANSFER_STAGE_SETUP) { + usb_transfer_schedule_block( + endpoint->out, + &set_sample_r_params, + sizeof(set_sample_r_params_t), + NULL, + NULL); + } else if (stage == USB_TRANSFER_STAGE_DATA) { + uint32_t numerator = set_sample_r_params.freq_hz; + uint32_t denominator = set_sample_r_params.divider; + uint64_t value = round_sample_rate(numerator, denominator); + radio_reg_write(&radio, RADIO_BANK_REQUESTED, RADIO_SAMPLE_RATE, value); + usb_transfer_schedule_ack(endpoint->in); + } + return USB_REQUEST_STATUS_OK; +} + +usb_request_status_t usb_vendor_request_set_amp_enable( + usb_endpoint_t* const endpoint, + const usb_transfer_stage_t stage) { + if (stage == USB_TRANSFER_STAGE_SETUP) { + radio_reg_write( + &radio, + RADIO_BANK_REQUESTED, + RADIO_GAIN_TX_RF, + endpoint->setup.value); + radio_reg_write( + &radio, + RADIO_BANK_REQUESTED, + RADIO_GAIN_RX_RF, + endpoint->setup.value); + usb_transfer_schedule_ack(endpoint->in); + } + return USB_REQUEST_STATUS_OK; +} + +usb_request_status_t usb_vendor_request_set_lna_gain( + usb_endpoint_t* const endpoint, + const usb_transfer_stage_t stage) { + if (stage == USB_TRANSFER_STAGE_SETUP) { + uint8_t gain = endpoint->setup.index; + radio_reg_write(&radio, RADIO_BANK_REQUESTED, RADIO_GAIN_RX_IF, gain); + endpoint->buffer[0] = RADIO_OK; + usb_transfer_schedule_block( + endpoint->in, + &endpoint->buffer, + 1, + NULL, + NULL); + usb_transfer_schedule_ack(endpoint->out); + } + return USB_REQUEST_STATUS_OK; +} + +usb_request_status_t usb_vendor_request_set_vga_gain( + usb_endpoint_t* const endpoint, + const usb_transfer_stage_t stage) { + if (stage == USB_TRANSFER_STAGE_SETUP) { + uint8_t gain = endpoint->setup.index; + radio_reg_write(&radio, RADIO_BANK_REQUESTED, RADIO_GAIN_RX_BB, gain); + endpoint->buffer[0] = RADIO_OK; + usb_transfer_schedule_block( + endpoint->in, + &endpoint->buffer, + 1, + NULL, + NULL); + usb_transfer_schedule_ack(endpoint->out); + } + return USB_REQUEST_STATUS_OK; +} + +usb_request_status_t usb_vendor_request_set_txvga_gain( + usb_endpoint_t* const endpoint, + const usb_transfer_stage_t stage) { + if (stage == USB_TRANSFER_STAGE_SETUP) { + uint8_t gain = endpoint->setup.index; + radio_reg_write(&radio, RADIO_BANK_REQUESTED, RADIO_GAIN_TX_IF, gain); + endpoint->buffer[0] = RADIO_OK; + usb_transfer_schedule_block( + endpoint->in, + &endpoint->buffer, + 1, + NULL, + NULL); + usb_transfer_schedule_ack(endpoint->out); + } + return USB_REQUEST_STATUS_OK; +} + +usb_request_status_t usb_vendor_request_set_antenna_enable( + usb_endpoint_t* const endpoint, + const usb_transfer_stage_t stage) { + switch (detected_platform()) { + case BOARD_ID_HACKRF1_OG: + case BOARD_ID_HACKRF1_R9: + case BOARD_ID_PRALINE: + // supported + break; + default: + return USB_REQUEST_STATUS_STALL; + } + + if (stage == USB_TRANSFER_STAGE_SETUP) { + radio_reg_write( + &radio, + RADIO_BANK_REQUESTED, + RADIO_BIAS_TEE, + endpoint->setup.value); + usb_transfer_schedule_ack(endpoint->in); + } + return USB_REQUEST_STATUS_OK; +} + +static volatile uint32_t _tx_underrun_limit; +static volatile uint32_t _rx_overrun_limit; + +volatile transceiver_request_t transceiver_request = { + .mode = TRANSCEIVER_MODE_OFF, + .seq = 0, +}; + +void transceiver_usb_setup_complete(usb_endpoint_t* const endpoint) { + if (transceiver_request.mode == TRANSCEIVER_MODE_TX && + endpoint->setup.request == 1 && auto_tx_flush) { + // This is a request to leave TX mode. Do so but NAK for now. + request_transceiver_mode(endpoint->setup.value); + } else { + usb_setup_complete(endpoint); + } +} + +// Must be called from an atomic context (normally USB ISR) +void request_transceiver_mode(transceiver_mode_t mode) { + usb_endpoint_flush(&usb_endpoint_bulk_in); + usb_endpoint_flush(&usb_endpoint_bulk_out); + + transceiver_request.mode = mode; + transceiver_request.seq++; +} + +void transceiver_shutdown(void) { + baseband_streaming_disable(&sgpio_config); + operacake_sctimer_reset_state(); + + usb_endpoint_flush(&usb_endpoint_bulk_in); + usb_endpoint_flush(&usb_endpoint_bulk_out); + + led_off(LED2); + led_off(LED3); + radio_switch_opmode(&radio, TRANSCEIVER_MODE_OFF); + m0_set_mode(M0_MODE_IDLE); +} + +void transceiver_startup(const transceiver_mode_t mode) { + dma_started = 0; + dma_pending = 0; + usb_started = 0; + usb_completed = 0; + + transceiver_dma_setup(); + + radio_switch_opmode(&radio, mode); + + switch (mode) { + case TRANSCEIVER_MODE_RX_SWEEP: + case TRANSCEIVER_MODE_RX: + led_off(LED3); + led_on(LED2); + m0_set_mode(M0_MODE_RX); + m0_state.shortfall_limit = _rx_overrun_limit; + break; + case TRANSCEIVER_MODE_TX: + led_off(LED2); + led_on(LED3); + m0_set_mode(M0_MODE_TX_START); + m0_state.shortfall_limit = _tx_underrun_limit; + break; + default: + break; + } + + activate_best_clock_source(); +} + +usb_request_status_t usb_vendor_request_set_transceiver_mode( + usb_endpoint_t* const endpoint, + const usb_transfer_stage_t stage) { + if (stage == USB_TRANSFER_STAGE_SETUP) { + switch (endpoint->setup.value) { + case TRANSCEIVER_MODE_OFF: + case TRANSCEIVER_MODE_RX: + case TRANSCEIVER_MODE_TX: + case TRANSCEIVER_MODE_RX_SWEEP: + case TRANSCEIVER_MODE_CPLD_UPDATE: + request_transceiver_mode(endpoint->setup.value); + usb_transfer_schedule_ack(endpoint->in); + return USB_REQUEST_STATUS_OK; + default: + return USB_REQUEST_STATUS_STALL; + } + } else { + return USB_REQUEST_STATUS_OK; + } +} + +usb_request_status_t usb_vendor_request_set_hw_sync_mode( + usb_endpoint_t* const endpoint, + const usb_transfer_stage_t stage) { + if (stage == USB_TRANSFER_STAGE_SETUP) { + radio_reg_write( + &radio, + RADIO_BANK_REQUESTED, + RADIO_TRIGGER, + endpoint->setup.value); + usb_transfer_schedule_ack(endpoint->in); + } + return USB_REQUEST_STATUS_OK; +} + +usb_request_status_t usb_vendor_request_set_tx_underrun_limit( + usb_endpoint_t* const endpoint, + const usb_transfer_stage_t stage) { + if (stage == USB_TRANSFER_STAGE_SETUP) { + uint32_t value = (endpoint->setup.index << 16) + endpoint->setup.value; + _tx_underrun_limit = value; + usb_transfer_schedule_ack(endpoint->in); + } + return USB_REQUEST_STATUS_OK; +} + +usb_request_status_t usb_vendor_request_set_rx_overrun_limit( + usb_endpoint_t* const endpoint, + const usb_transfer_stage_t stage) { + if (stage == USB_TRANSFER_STAGE_SETUP) { + uint32_t value = (endpoint->setup.index << 16) + endpoint->setup.value; + _rx_overrun_limit = value; + usb_transfer_schedule_ack(endpoint->in); + } + return USB_REQUEST_STATUS_OK; +} + +usb_request_status_t usb_vendor_request_get_buffer_size( + usb_endpoint_t* const endpoint, + const usb_transfer_stage_t stage) { + if (stage == USB_TRANSFER_STAGE_SETUP) { + uint32_t value = USB_SAMP_BUFFER_SIZE + USB_BULK_BUFFER_SIZE; + endpoint->buffer[0] = value & 0xff; + endpoint->buffer[1] = (value & 0xff00) >> 8; + endpoint->buffer[2] = (value & 0xff0000) >> 16; + endpoint->buffer[3] = (value & 0xff000000) >> 24; + usb_transfer_schedule_block( + endpoint->in, + &endpoint->buffer, + 4, + NULL, + NULL); + usb_transfer_schedule_ack(endpoint->out); + + // We now know the host is aware of our buffer size, so it + // can make its own decisions about flushing the buffer. + auto_tx_flush = false; + + return USB_REQUEST_STATUS_OK; + } + return USB_REQUEST_STATUS_OK; +} + +/* clang-format off */ + +// Which GPDMA channel to use. +const uint32_t DMA_CHANNEL = 1; + +// GPDMA CCONFIG register setting. +const uint32_t DMA_CONFIG = + GPDMA_CCONFIG_FLOWCNTRL(0) // memory-to-memory + | GPDMA_CCONFIG_IE(0) // no error interrupt + | GPDMA_CCONFIG_ITC(1) // terminal count interrupt + | GPDMA_CCONFIG_L(0) // do not lock + | GPDMA_CCONFIG_H(0); // do not halt + +// GPDMA CCONTROL register setting (excluding TRANSFERSIZE field). +const uint32_t DMA_CONTROL = + GPDMA_CCONTROL_SBSIZE(7) // 256-transfer src bursts + | GPDMA_CCONTROL_DBSIZE(7) // 256-transfer dst bursts + | GPDMA_CCONTROL_SWIDTH(2) // 32-bit src transfers + | GPDMA_CCONTROL_DWIDTH(2) // 32-bit dst transfers + | GPDMA_CCONTROL_S(0) // AHB Master 0 + | GPDMA_CCONTROL_D(1) // AHB Master 1 + | GPDMA_CCONTROL_SI(1) // increment source + | GPDMA_CCONTROL_DI(1) // increment destination + | GPDMA_CCONTROL_PROT1(0) // user mode + | GPDMA_CCONTROL_PROT2(0) // not bufferable + | GPDMA_CCONTROL_PROT3(0) // not cacheable + | GPDMA_CCONTROL_I(1); // interrupt enabled + +/* clang-format on */ + +// Called before any sequence of DMA transfers. +void transceiver_dma_setup(void) { + gpdma_controller_enable(); + GPDMA_CCONFIG(DMA_CHANNEL) = DMA_CONFIG; + GPDMA_CCONTROL(DMA_CHANNEL) = DMA_CONTROL; + GPDMA_CLLI(DMA_CHANNEL) = 0; + GPDMA_INTTCCLEAR = (1 << DMA_CHANNEL); + nvic_enable_irq(NVIC_DMA_IRQ); +} + +// Called to start each DMA transfer. +void transceiver_start_dma(void* src, void* dest, size_t size) { + uint32_t num_transfers = size >> 2; + GPDMA_CCONTROL(DMA_CHANNEL) = DMA_CONTROL | num_transfers; + GPDMA_CSRCADDR(DMA_CHANNEL) = (uint32_t)src; + GPDMA_CDESTADDR(DMA_CHANNEL) = (uint32_t)dest; + dma_pending = size; + gpdma_channel_enable(DMA_CHANNEL); +} + +// Called when a DMA transfer completes. +void dma_isr(void) { + gpdma_channel_disable(DMA_CHANNEL); + GPDMA_INTTCCLEAR = (1 << DMA_CHANNEL); + m0_state.m4_count += dma_pending; + dma_pending = 0; +} + +void transceiver_bulk_transfer_complete(void* user_data, unsigned int bytes_transferred) { + (void)user_data; + usb_completed += bytes_transferred; +} + +typedef enum { + DIRECTION_RX, + DIRECTION_TX, +} direction_t; + +void start_dma_if_possible(direction_t direction, size_t size) { + if (dma_pending) { + return; + } + + uint32_t sampling_completed = m0_state.m0_count; + uint32_t dma_completed = m0_state.m4_count; + uint32_t samp_offset = dma_started & USB_SAMP_BUFFER_MASK; + uint32_t bulk_offset = dma_started & USB_BULK_BUFFER_MASK; + uint32_t data_available, space_in_use, space_available, samp_buf_margin; + uint8_t *dest, *src; + + if (direction == DIRECTION_RX) { + data_available = sampling_completed - dma_started; + space_in_use = usb_completed - dma_completed; + space_available = USB_BULK_BUFFER_SIZE - space_in_use; + samp_buf_margin = USB_SAMP_BUFFER_SIZE - data_available; + src = &usb_samp_buffer[samp_offset]; + dest = &usb_bulk_buffer[bulk_offset]; + } else { + data_available = usb_completed - dma_started; + space_in_use = dma_completed - sampling_completed; + space_available = USB_SAMP_BUFFER_SIZE - space_in_use; + samp_buf_margin = space_in_use; + src = &usb_bulk_buffer[bulk_offset]; + dest = &usb_samp_buffer[samp_offset]; + } + + if (data_available < size || size > space_available) { + return; + } + + uint32_t m0_buf_half = sampling_completed & BUF_HALF_MASK; + uint32_t dma_buf_half = dma_started & BUF_HALF_MASK; + bool same_buf_half = m0_buf_half == dma_buf_half; + + if (same_buf_half && samp_buf_margin >= (USB_SAMP_BUFFER_SIZE / 2)) { + return; + } + + transceiver_start_dma(src, dest, size); + + dma_started += size; +} + +void start_usb_if_possible(direction_t direction) { + uint32_t bulk_offset = usb_started & USB_BULK_BUFFER_MASK; + uint32_t dma_completed = m0_state.m4_count; + uint32_t bytes_available; + usb_endpoint_t* usb_endpoint; + + if (direction == DIRECTION_RX) { + bytes_available = dma_completed - usb_started; + usb_endpoint = &usb_endpoint_bulk_in; + } else { + uint32_t space_used = usb_started - dma_completed; + bytes_available = USB_BULK_BUFFER_SIZE - space_used; + usb_endpoint = &usb_endpoint_bulk_out; + } + + if (bytes_available < USB_TRANSFER_SIZE) { + return; + } + + usb_transfer_schedule_block( + usb_endpoint, + &usb_bulk_buffer[bulk_offset], + USB_TRANSFER_SIZE, + transceiver_bulk_transfer_complete, + NULL); + + usb_started += USB_TRANSFER_SIZE; +} + +int8_t saturation_buffer = 0; +uint64_t saturation_buffer_time = 0; +volatile uint64_t systick_counter = 0; + +void sys_tick_handler(void) { + systick_counter++; +} + +void rx_mode(uint32_t seq) { + transceiver_startup(TRANSCEIVER_MODE_RX); + + baseband_streaming_enable(&sgpio_config); + + while (transceiver_request.seq == seq) { + start_dma_if_possible(DIRECTION_RX, DMA_TRANSFER_SIZE); + start_usb_if_possible(DIRECTION_RX); + + int8_t sample_value = *( + int8_t*)&usb_samp_buffer[m0_state.m0_count & USB_SAMP_BUFFER_MASK]; + + if (sample_value > saturation_buffer) + saturation_buffer = sample_value; + + if (-sample_value > saturation_buffer) + saturation_buffer = -sample_value; + + if (saturation_buffer_time + 4 < systick_counter) { + saturation_buffer_time = systick_counter; + hackrf_ui()->set_saturation(saturation_buffer); + saturation_buffer = 0; + } + + radio_update(&radio); + } + + transceiver_shutdown(); +} + +void tx_mode(uint32_t seq) { + transceiver_startup(TRANSCEIVER_MODE_TX); + + // First, make transfers directly into the sample buffer to fill it. + for (int i = 0; i < (USB_SAMP_BUFFER_SIZE / USB_TRANSFER_SIZE); i++) { + // Set up transfer. + usb_transfer_schedule_block( + &usb_endpoint_bulk_out, + &usb_samp_buffer[usb_started], + USB_TRANSFER_SIZE, + transceiver_bulk_transfer_complete, + NULL); + usb_started += USB_TRANSFER_SIZE; + + // Wait for the transfer to complete. + while (usb_completed < usb_started) { + // Handle the host switching modes before filling the buffer. + if (transceiver_request.seq != seq) { + transceiver_shutdown(); + return; + } + + radio_update(&radio); + } + } + + // Sample buffer is now full. Update DMA counters accordingly. + dma_started = USB_SAMP_BUFFER_SIZE; + m0_state.m4_count = USB_SAMP_BUFFER_SIZE; + + // Start transmitting samples. + baseband_streaming_enable(&sgpio_config); + + // Continue feeding samples to the sample buffer. + while (transceiver_request.seq == seq) { + start_dma_if_possible(DIRECTION_TX, DMA_TRANSFER_SIZE); + start_usb_if_possible(DIRECTION_TX); + radio_update(&radio); + } + + // Host has now requested to stop TX. If we're not auto-flushing, we + // should now stop TX immediately. + + if (!auto_tx_flush) { + transceiver_shutdown(); + return; + } + + // Otherwise, we should now ensure all bytes sent by the host are + // transmitted before we leave TX. First, we should make sure all data + // currently in the USB bulk buffer reaches the sample buffer. + + if ((usb_started - usb_completed) > 0) { + // We were part way through a 16KB firmware-side transfer when + // the transceiver mode change request to stop TX was received. + // + // We want to include the contents of that partial transfer in + // the data we move to the sample buffer. + // + // The transfer was already stopped by usb_endpoint_flush(), + // which was called from request_transceiver_mode(). + // + // We will not have had a callback, and the transfer descriptor + // (dTD) will not have been updated, since the transfer did not + // complete. + // + // However, as long as we haven't started a new transfer, we + // can retrieve the partial byte count from the transfer + // overlay in the endpoint queue head (dQH) (UM10503 25.9.1). + + usb_queue_head_t* const qh = + usb_queue_head(usb_endpoint_bulk_out.address); + unsigned int bytes_remaining = + (qh->total_bytes & USB_TD_DTD_TOKEN_TOTAL_BYTES_MASK) >> + USB_TD_DTD_TOKEN_TOTAL_BYTES_SHIFT; + unsigned int bytes_transferred = USB_TRANSFER_SIZE - bytes_remaining; + usb_completed += bytes_transferred; + } + + // Feed the remaining data from the bulk buffer to the sample buffer. + // At this point, we also need to handle the case where there is less data + // to be transferred to the sample buffer than a full-sized DMA transfer. + + // Any remainder of less than 4 bytes will be ignored; this is the chunk + // size of our DMA transfers. + while ((usb_completed - m0_state.m4_count) >= 4) { + uint32_t data_available = usb_completed - dma_started; + if (data_available > DMA_TRANSFER_SIZE) { + start_dma_if_possible(DIRECTION_TX, DMA_TRANSFER_SIZE); + } else { + start_dma_if_possible(DIRECTION_TX, data_available); + } + radio_update(&radio); + } + + // Wait for the data in the sample buffer to be transmitted. + + // Any remainder of less than 32 bytes will be ignored; this is + // the chunk size used by the M0 core to transfer samples to SGPIO. + while ((m0_state.m4_count - m0_state.m0_count) >= 32) { + radio_update(&radio); + } + + // All data received from the host has now been transmitted. + // Now we can ACK the control request that took us out of TX mode. + usb_transfer_schedule_ack(usb_endpoint_control_in.in); + + transceiver_shutdown(); +} + +void off_mode(uint32_t seq) { + while (transceiver_request.seq == seq) { + radio_update(&radio); + } +} diff --git a/firmware/baseband/sd_over_usb/usb_descriptor.c b/firmware/baseband/sd_over_usb/usb_descriptor.c index ceb60f9b5..5ea730d16 100644 --- a/firmware/baseband/sd_over_usb/usb_descriptor.c +++ b/firmware/baseband/sd_over_usb/usb_descriptor.c @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 Great Scott Gadgets + * Copyright 2012-2026 Great Scott Gadgets * Copyright 2012 Jared Boone * * This file is part of HackRF. @@ -20,22 +20,13 @@ * Boston, MA 02110-1301, USA. */ -#include - -#include "usb_type.h" #include "usb_descriptor.h" -#define USB_VENDOR_ID (0x0781) /* SanDisk Corp. */ +#include -#ifdef HACKRF_ONE -#define USB_PRODUCT_ID (0xa7a8) /* SD card reader */ -#elif JAWBREAKER -#define USB_PRODUCT_ID (0x604B) -#elif RAD1O -#define USB_PRODUCT_ID (0xCC15) -#else -#define USB_PRODUCT_ID (0xFFFF) -#endif +#include + +#define USB_VENDOR_ID (0x0781) /* SanDisk Corp. */ #define USB_API_VERSION (0x0127) /* hardware revision */ @@ -50,7 +41,8 @@ #define USB_STRING_LANGID (0x0409) -uint8_t usb_descriptor_device[] = { +#if defined(IS_HACKRF_ONE) || defined(IS_PRALINE) +uint8_t usb_descriptor_device_hackrf[] = { 18, // bLength USB_DESCRIPTOR_TYPE_DEVICE, // bDescriptorType USB_WORD(0x0200), // bcdUSB @@ -59,13 +51,50 @@ uint8_t usb_descriptor_device[] = { 0x00, // bDeviceProtocol USB_MAX_PACKET0, // bMaxPacketSize0 USB_WORD(USB_VENDOR_ID), // idVendor - USB_WORD(USB_PRODUCT_ID), // idProduct + USB_WORD(0xa7a8), // idProduct /* SD card reader */ USB_WORD(USB_API_VERSION), // bcdDevice 0x01, // iManufacturer 0x02, // iProduct 0x04, // iSerialNumber 0x01 // bNumConfigurations }; +#endif +#ifdef IS_JAWBREAKER +uint8_t usb_descriptor_device_jawbreaker[] = { + 18, // bLength + USB_DESCRIPTOR_TYPE_DEVICE, // bDescriptorType + USB_WORD(0x0200), // bcdUSB + 0x00, // bDeviceClass + 0x00, // bDeviceSubClass + 0x00, // bDeviceProtocol + USB_MAX_PACKET0, // bMaxPacketSize0 + USB_WORD(USB_VENDOR_ID), // idVendor + USB_WORD(0x604B), // idProduct + USB_WORD(USB_API_VERSION), // bcdDevice + 0x01, // iManufacturer + 0x02, // iProduct + 0x04, // iSerialNumber + 0x01 // bNumConfigurations +}; +#endif +#ifdef IS_RAD1O +uint8_t usb_descriptor_device_rad1o[] = { + 18, // bLength + USB_DESCRIPTOR_TYPE_DEVICE, // bDescriptorType + USB_WORD(0x0200), // bcdUSB + 0x00, // bDeviceClass + 0x00, // bDeviceSubClass + 0x00, // bDeviceProtocol + USB_MAX_PACKET0, // bMaxPacketSize0 + USB_WORD(USB_VENDOR_ID), // idVendor + USB_WORD(0xCC15), // idProduct + USB_WORD(USB_API_VERSION), // bcdDevice + 0x01, // iManufacturer + 0x02, // iProduct + 0x04, // iSerialNumber + 0x01 // bNumConfigurations +}; +#endif uint8_t usb_descriptor_device_qualifier[] = { 10, // bLength @@ -161,162 +190,213 @@ uint8_t usb_descriptor_string_languages[] = { // clang-format off uint8_t usb_descriptor_string_manufacturer[] = { - 40, // bLength - USB_DESCRIPTOR_TYPE_STRING, // bDescriptorType - 'G', 0x00, - 'r', 0x00, - 'e', 0x00, - 'a', 0x00, - 't', 0x00, - ' ', 0x00, - 'S', 0x00, - 'c', 0x00, - 'o', 0x00, - 't', 0x00, - 't', 0x00, - ' ', 0x00, - 'G', 0x00, - 'a', 0x00, - 'd', 0x00, - 'g', 0x00, - 'e', 0x00, - 't', 0x00, - 's', 0x00, + 40, // bLength + USB_DESCRIPTOR_TYPE_STRING, // bDescriptorType + 'G', 0x00, + 'r', 0x00, + 'e', 0x00, + 'a', 0x00, + 't', 0x00, + ' ', 0x00, + 'S', 0x00, + 'c', 0x00, + 'o', 0x00, + 't', 0x00, + 't', 0x00, + ' ', 0x00, + 'G', 0x00, + 'a', 0x00, + 'd', 0x00, + 'g', 0x00, + 'e', 0x00, + 't', 0x00, + 's', 0x00, }; -uint8_t usb_descriptor_string_product[] = { -#ifdef HACKRF_ONE - 34, // bLength - USB_DESCRIPTOR_TYPE_STRING, // bDescriptorType - 'P', 0x00, - 'o', 0x00, - 'r', 0x00, - 't', 0x00, - 'a', 0x00, - 'P', 0x00, - 'a', 0x00, - 'c', 0x00, - 'k', 0x00, - ' ', 0x00, +#ifdef IS_HACKRF_ONE +uint8_t usb_descriptor_string_product_hackrf_one[] = { + 34, // bLength + USB_DESCRIPTOR_TYPE_STRING, // bDescriptorType + 'P', 0x00, + 'o', 0x00, + 'r', 0x00, + 't', 0x00, + 'a', 0x00, + 'P', 0x00, + 'a', 0x00, + 'c', 0x00, + 'k', 0x00, + ' ', 0x00, - 'M', 0x00, - 'a', 0x00, - 'y', 0x00, - 'h', 0x00, - 'e', 0x00, - 'm', 0x00, - -#elif JAWBREAKER - 36, // bLength - USB_DESCRIPTOR_TYPE_STRING, // bDescriptorType - 'H', 0x00, - 'a', 0x00, - 'c', 0x00, - 'k', 0x00, - 'R', 0x00, - 'F', 0x00, - ' ', 0x00, - 'J', 0x00, - 'a', 0x00, - 'w', 0x00, - 'b', 0x00, - 'r', 0x00, - 'e', 0x00, - 'a', 0x00, - 'k', 0x00, - 'e', 0x00, - 'r', 0x00, -#elif RAD1O - 12, // bLength - USB_DESCRIPTOR_TYPE_STRING, // bDescriptorType - 'r', 0x00, - 'a', 0x00, - 'd', 0x00, - '1', 0x00, - 'o', 0x00, -#else - 14, // bLength - USB_DESCRIPTOR_TYPE_STRING, // bDescriptorType - 'H', 0x00, - 'a', 0x00, - 'c', 0x00, - 'k', 0x00, - 'R', 0x00, - 'F', 0x00, + 'M', 0x00, + 'a', 0x00, + 'y', 0x00, + 'h', 0x00, + 'e', 0x00, + 'm', 0x00, +}; #endif +#ifdef IS_PRALINE +uint8_t usb_descriptor_string_product_praline[] = { + 34, // bLength + USB_DESCRIPTOR_TYPE_STRING, // bDescriptorType + 'P', 0x00, + 'o', 0x00, + 'r', 0x00, + 't', 0x00, + 'a', 0x00, + 'P', 0x00, + 'a', 0x00, + 'c', 0x00, + 'k', 0x00, + ' ', 0x00, + + 'M', 0x00, + 'a', 0x00, + 'y', 0x00, + 'h', 0x00, + 'e', 0x00, + 'm', 0x00, }; +#endif +#ifdef IS_JAWBREAKER +uint8_t usb_descriptor_string_product_jawbreaker[] = { + 36, // bLength + USB_DESCRIPTOR_TYPE_STRING, // bDescriptorType + 'H', 0x00, + 'a', 0x00, + 'c', 0x00, + 'k', 0x00, + 'R', 0x00, + 'F', 0x00, + ' ', 0x00, + 'J', 0x00, + 'a', 0x00, + 'w', 0x00, + 'b', 0x00, + 'r', 0x00, + 'e', 0x00, + 'a', 0x00, + 'k', 0x00, + 'e', 0x00, + 'r', 0x00, +}; +#endif +#ifdef IS_RAD1O +uint8_t usb_descriptor_string_product_rad1o[] = { + 12, // bLength + USB_DESCRIPTOR_TYPE_STRING, // bDescriptorType + 'r', 0x00, + 'a', 0x00, + 'd', 0x00, + '1', 0x00, + 'o', 0x00, +}; +#endif uint8_t usb_descriptor_string_config_description[] = { - 24, // bLength - USB_DESCRIPTOR_TYPE_STRING, // bDescriptorType - 'T', 0x00, - 'r', 0x00, - 'a', 0x00, - 'n', 0x00, - 's', 0x00, - 'c', 0x00, - 'e', 0x00, - 'i', 0x00, - 'v', 0x00, - 'e', 0x00, - 'r', 0x00, + 24, // bLength + USB_DESCRIPTOR_TYPE_STRING, // bDescriptorType + 'T', 0x00, + 'r', 0x00, + 'a', 0x00, + 'n', 0x00, + 's', 0x00, + 'c', 0x00, + 'e', 0x00, + 'i', 0x00, + 'v', 0x00, + 'e', 0x00, + 'r', 0x00, }; #ifdef DFU_MODE uint8_t usb_descriptor_string_serial_number[] = { - 30, // bLength - USB_DESCRIPTOR_TYPE_STRING, // bDescriptorType - 'R', 0x00, - 'u', 0x00, - 'n', 0x00, - 'n', 0x00, - 'i', 0x00, - 'n', 0x00, - 'g', 0x00, - 'F', 0x00, - 'r', 0x00, - 'o', 0x00, - 'm', 0x00, - 'R', 0x00, - 'A', 0x00, - 'M', 0x00, + 30, // bLength + USB_DESCRIPTOR_TYPE_STRING, // bDescriptorType + 'R', 0x00, + 'u', 0x00, + 'n', 0x00, + 'n', 0x00, + 'i', 0x00, + 'n', 0x00, + 'g', 0x00, + 'F', 0x00, + 'r', 0x00, + 'o', 0x00, + 'm', 0x00, + 'R', 0x00, + 'A', 0x00, + 'M', 0x00, }; #else uint8_t usb_descriptor_string_serial_number[USB_DESCRIPTOR_STRING_SERIAL_BUF_LEN]; #endif -uint8_t* usb_descriptor_strings[] = { - usb_descriptor_string_languages, - usb_descriptor_string_manufacturer, - usb_descriptor_string_product, - usb_descriptor_string_config_description, - usb_descriptor_string_serial_number, - 0, // TERMINATOR +#ifdef IS_HACKRF_ONE +uint8_t* usb_descriptor_strings_hackrf_one[] = { + usb_descriptor_string_languages, + usb_descriptor_string_manufacturer, + usb_descriptor_string_product_hackrf_one, + usb_descriptor_string_config_description, + usb_descriptor_string_serial_number, + 0, // TERMINATOR }; +#endif +#ifdef IS_PRALINE +uint8_t* usb_descriptor_strings_praline[] = { + usb_descriptor_string_languages, + usb_descriptor_string_manufacturer, + usb_descriptor_string_product_praline, + usb_descriptor_string_config_description, + usb_descriptor_string_serial_number, + 0, // TERMINATOR +}; +#endif +#ifdef IS_JAWBREAKER +uint8_t* usb_descriptor_strings_jawbreaker[] = { + usb_descriptor_string_languages, + usb_descriptor_string_manufacturer, + usb_descriptor_string_product_jawbreaker, + usb_descriptor_string_config_description, + usb_descriptor_string_serial_number, + 0, // TERMINATOR +}; +#endif +#ifdef IS_RAD1O +uint8_t* usb_descriptor_strings_rad1o[] = { + usb_descriptor_string_languages, + usb_descriptor_string_manufacturer, + usb_descriptor_string_product_rad1o, + usb_descriptor_string_config_description, + usb_descriptor_string_serial_number, + 0, // TERMINATOR +}; +#endif uint8_t wcid_string_descriptor[] = { - 18, // bLength - USB_DESCRIPTOR_TYPE_STRING, // bDescriptorType - 'M', 0x00, - 'S', 0x00, - 'F', 0x00, - 'T', 0x00, - '1', 0x00, - '0', 0x00, - '0', 0x00, - USB_WCID_VENDOR_REQ, // vendor request code for further descriptor - 0x00 + 18, // bLength + USB_DESCRIPTOR_TYPE_STRING, // bDescriptorType + 'M', 0x00, + 'S', 0x00, + 'F', 0x00, + 'T', 0x00, + '1', 0x00, + '0', 0x00, + '0', 0x00, + USB_WCID_VENDOR_REQ, // vendor request code for further descriptor + 0x00 }; uint8_t wcid_feature_descriptor[] = { - 0x28, 0x00, 0x00, 0x00, // bLength - USB_WORD(0x0100), // WCID version - USB_WORD(0x0004), // WICD descriptor index - 0x01, // bNumSections - 0x00,0x00,0x00,0x00,0x00,0x00,0x00, // Reserved - 0x00, // bInterfaceNumber - 0x01, // Reserved - 'W', 'I', 'N', 'U', 'S', 'B', 0x00,0x00, // Compatible ID, padded with zeros - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // Sub-compatible ID - 0x00,0x00,0x00,0x00,0x00,0x00 // Reserved -}; + 0x28, 0x00, 0x00, 0x00, // bLength + USB_WORD(0x0100), // WCID version + USB_WORD(0x0004), // WICD descriptor index + 0x01, // bNumSections + 0x00,0x00,0x00,0x00,0x00,0x00,0x00, // Reserved + 0x00, // bInterfaceNumber + 0x01, // Reserved + 'W', 'I', 'N', 'U', 'S', 'B', 0x00,0x00, // Compatible ID, padded with zeros + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // Sub-compatible ID + 0x00,0x00,0x00,0x00,0x00,0x00 // Reserved +}; \ No newline at end of file diff --git a/firmware/baseband/sd_over_usb/usb_descriptor.h b/firmware/baseband/sd_over_usb/usb_descriptor.h index a4f19418b..79dfceaa7 100644 --- a/firmware/baseband/sd_over_usb/usb_descriptor.h +++ b/firmware/baseband/sd_over_usb/usb_descriptor.h @@ -20,22 +20,46 @@ * Boston, MA 02110-1301, USA. */ +#pragma once + #include -extern uint8_t usb_descriptor_device[]; +#if defined(IS_HACKRF_ONE) || defined(IS_PRALINE) +extern uint8_t usb_descriptor_device_hackrf[]; +#endif +#ifdef IS_JAWBREAKER +extern uint8_t usb_descriptor_device_jawbreaker[]; +#endif +#ifdef IS_RAD1O +extern uint8_t usb_descriptor_device_rad1o[]; +#endif extern uint8_t usb_descriptor_device_qualifier[]; extern uint8_t usb_descriptor_configuration_full_speed[]; extern uint8_t usb_descriptor_configuration_high_speed[]; extern uint8_t usb_descriptor_string_languages[]; extern uint8_t usb_descriptor_string_manufacturer[]; -extern uint8_t usb_descriptor_string_product[]; +#ifdef IS_HACKRF_ONE +extern uint8_t usb_descriptor_string_product_hackrf_one[]; +#endif +#ifdef IS_PRALINE +extern uint8_t usb_descriptor_string_product_praline[]; +#endif +#ifdef IS_JAWBREAKER +extern uint8_t usb_descriptor_string_product_jawbreaker[]; +#endif +#ifdef IS_RAD1O +extern uint8_t usb_descriptor_string_product_rad1o[]; +#endif #define USB_DESCRIPTOR_STRING_SERIAL_LEN 32 #define USB_DESCRIPTOR_STRING_SERIAL_BUF_LEN \ (USB_DESCRIPTOR_STRING_SERIAL_LEN * 2 + 2) /* UTF-16LE */ extern uint8_t usb_descriptor_string_serial_number[]; -extern uint8_t* usb_descriptor_strings[]; +extern uint8_t* usb_descriptor_strings_hackrf_one[]; +extern uint8_t* usb_descriptor_strings_jawbreaker[]; +extern uint8_t* usb_descriptor_strings_rad1o[]; +extern uint8_t* usb_descriptor_strings_praline[]; #define USB_WCID_VENDOR_REQ 0x19 extern uint8_t wcid_string_descriptor[];