SD Over USB for the Hackrf Pro (#3291)

* enabled sd over usb compilation
* updated hackrf pro usb stack
* fixed hackrf one code path
* fixed hackrf pro code path
* improved performance
* fixed sd card clock
* refactoring
* formatted code
This commit is contained in:
Bernd Herzog
2026-08-16 20:08:19 +02:00
committed by GitHub
parent b048b8f4f1
commit 47c94dbf26
15 changed files with 1803 additions and 1289 deletions
+4 -8
View File
@@ -32,10 +32,6 @@
#include "core_control.hpp" #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. /* Set true to enable check for baseband messages getting stuck.
* This implies the baseband thread is not dequeuing and has probably stalled. * 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 * 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; 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) { if (baseband_image_running) {
chDbgPanic("BBRunning"); chDbgPanic("BBRunning");
} }
@@ -487,7 +483,7 @@ void run_image(const spi_flash::image_tag_t image_tag) {
creg::m4txevent::enable(); creg::m4txevent::enable();
if constexpr (enforce_core_sync) { if (enforce_core_sync) {
// Wait up to 3 seconds for baseband to start handling events. // Wait up to 3 seconds for baseband to start handling events.
auto count = 3'000u; auto count = 3'000u;
while (!shared_memory.baseband_ready && --count) 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) { if (baseband_image_running) {
chDbgPanic("BBRunning"); chDbgPanic("BBRunning");
} }
@@ -511,7 +507,7 @@ void run_prepared_image(const uint32_t m4_code) {
creg::m4txevent::enable(); creg::m4txevent::enable();
if constexpr (enforce_core_sync) { if (enforce_core_sync) {
// Wait up to 3 seconds for baseband to start handling events. // Wait up to 3 seconds for baseband to start handling events.
auto count = 3'000u; auto count = 3'000u;
while (!shared_memory.baseband_ready && --count) while (!shared_memory.baseband_ready && --count)
+2 -2
View File
@@ -131,8 +131,8 @@ void request_beep_stop();
void request_audio_beep(uint32_t freq, uint32_t sample_rate, uint32_t duration_ms); void request_audio_beep(uint32_t freq, uint32_t sample_rate, uint32_t duration_ms);
bool is_image_running(); bool is_image_running();
void run_image(const portapack::spi_flash::image_tag_t image_tag); 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); void run_prepared_image(const uint32_t m4_code, bool enforce_core_sync = true);
void shutdown(); void shutdown();
void spectrum_streaming_start(); void spectrum_streaming_start();
+5 -9
View File
@@ -407,6 +407,10 @@ set(EXTCPPSRC
external/aprs_tx/main.cpp external/aprs_tx/main.cpp
external/aprs_tx/ui_aprs_tx.cpp external/aprs_tx/ui_aprs_tx.cpp
#sd over usb
external/sdusb/main.cpp
external/sdusb/ui_sd_over_usb.cpp
) )
set(EXTAPPLIST set(EXTAPPLIST
@@ -505,13 +509,5 @@ set(EXTAPPLIST
ais_rx ais_rx
aprs_rx aprs_rx
aprs_tx 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()
+1 -1
View File
@@ -46,7 +46,7 @@ SdOverUsbView::SdOverUsbView(NavigationView& nav)
sdcStop(&SDCD1); sdcStop(&SDCD1);
portapack::shutdown(true, false); 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(); m0_halt();
/* will not return*/ /* will not return*/
}; };
+21 -11
View File
@@ -748,13 +748,6 @@ set(MODE_CPPSRC
) )
DeclareTargets(PRTT rtty_tx) DeclareTargets(PRTT rtty_tx)
### SD over USB
set(MODE_INCDIR
${HACKRF_PATH}/firmware
${HACKRF_PATH}/firmware/common
${HACKRF_PATH}/firmware/libopencm3/include
)
### Morse TX ### Morse TX
@@ -794,6 +787,15 @@ set(MODE_CPPSRC
) )
DeclareTargets(PTET tetra_rx) 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 set(MODE_CPPSRC
sd_over_usb/proc_sd_over_usb.cpp sd_over_usb/proc_sd_over_usb.cpp
@@ -802,7 +804,9 @@ set(MODE_CPPSRC
sd_over_usb/diskio.c sd_over_usb/diskio.c
sd_over_usb/sd_over_usb.c sd_over_usb/sd_over_usb.c
sd_over_usb/usb_descriptor.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/adc.c
${HACKRF_PATH}/firmware/common/selftest.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_spi.c
${HACKRF_PATH}/firmware/common/rffc5071.c ${HACKRF_PATH}/firmware/common/rffc5071.c
${HACKRF_PATH}/firmware/common/gpdma.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/nvic.c
${HACKRF_PATH}/firmware/libopencm3/lib/cm3/sync.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/timer.c
${HACKRF_PATH}/firmware/libopencm3/lib/lpc43xx/i2c.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) DeclareTargets(PUSB sd_over_usb)
endif()
### HackRF "factory" firmware ### HackRF "factory" firmware
+263
View File
@@ -0,0 +1,263 @@
/*
* Copyright 2026 Great Scott Gadgets <info@greatscottgadgets.com>
*
* 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 <stdint.h>
#include <libopencm3/lpc43xx/cgu.h>
#if defined(IS_JAWBREAKER) || defined(IS_HACKRF_ONE) || defined(IS_PRALINE)
#include <libopencm3/lpc43xx/ccu.h>
#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
}
@@ -0,0 +1 @@
#pragma once
File diff suppressed because it is too large Load Diff
+254
View File
@@ -0,0 +1,254 @@
/*
* Copyright 2012-2026 Great Scott Gadgets <info@greatscottgadgets.com>
* Copyright 2012 Jared Boone <jared@sharebrained.com>
* Copyright 2013 Benjamin Vernoux <titanmkd@gmail.com>
*
* 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 <libopencm3/lpc43xx/scu.h>
#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);
}
@@ -44,11 +44,11 @@ CH_IRQ_HANDLER(Vector60) {
} }
int main() { int main() {
start_usb();
sdcStart(&SDCD1, nullptr); sdcStart(&SDCD1, nullptr);
if (sdcConnect(&SDCD1) == CH_FAILED) chDbgPanic("no sd card #1"); if (sdcConnect(&SDCD1) == CH_FAILED) chDbgPanic("no sd card #1");
start_usb();
while (true) { while (true) {
usb_transfer(); usb_transfer();
} }
+145 -8
View File
@@ -23,14 +23,35 @@
#include "scsi.h" #include "scsi.h"
#include "diskio.h" #include "diskio.h"
#include "gpio_lpc.h" #include "gpio_lpc.h"
#include "delay.h"
#include <libopencm3/lpc43xx/scu.h> #include <libopencm3/lpc43xx/scu.h>
#include <libopencm3/lpc43xx/rgu.h> #include <libopencm3/lpc43xx/rgu.h>
#include <libopencm3/lpc43xx/wwdt.h> #include <libopencm3/lpc43xx/wwdt.h>
#include "string.h" #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; 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) { void usb_bulk_block_cb(void* user_data, unsigned int bytes_transferred) {
usb_bulk_block_done = true; 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); 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) { void usb_send_csw(msd_cbw_t* msd_cbw_data, uint8_t status) {
msd_csw_t csw = { msd_csw_t csw = {
.signature = MSD_CSW_SIGNATURE, .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) { uint8_t data_read10(msd_cbw_t* msd_cbw_data) {
data_request_t req = decode_data_request(msd_cbw_data->cmd_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++) { uint32_t lba = req.first_lba;
read_block(req.first_lba + block_index, &usb_bulk_buffer[0], 1 /* n blocks */); uint32_t remaining = req.blk_cnt;
usb_send_bulk(&usb_bulk_buffer[0], 512); 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; return 0;
} }
uint8_t data_write10(msd_cbw_t* msd_cbw_data) { uint8_t data_write10(msd_cbw_t* msd_cbw_data) {
data_request_t req = decode_data_request(msd_cbw_data->cmd_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++) { uint32_t lba = req.first_lba;
usb_receive_bulk(&usb_bulk_buffer[0], 512); uint32_t remaining = req.blk_cnt;
write_block(req.first_lba + block_index, &usb_bulk_buffer[0], 1 /* n blocks */); 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; return 0;
} }
@@ -293,7 +430,7 @@ void scsi_command(msd_cbw_t* msd_cbw_data) {
gpio_output(&dfu); gpio_output(&dfu);
gpio_clear(&dfu); gpio_clear(&dfu);
delay(50 * 40800); delay_ms(50);
RESET_CTRL0 = (1 << 0); RESET_CTRL0 = (1 << 0);
break; break;
+79 -7
View File
@@ -23,6 +23,8 @@
#include "sd_over_usb.h" #include "sd_over_usb.h"
#include "scsi.h" #include "scsi.h"
#include "usb_descriptor.h" #include "usb_descriptor.h"
#include <rom_iap.h>
#include "delay.h"
#include <string.h> #include <string.h>
@@ -36,8 +38,14 @@
extern usb_configuration_t* usb_configurations[]; extern usb_configuration_t* usb_configurations[];
static const usb_device_t usb_device_sd_over_usb = { static const usb_device_t usb_device_sd_over_usb = {
.descriptor = usb_descriptor_device, #ifdef IS_NOT_PRALINE
.descriptor_strings = usb_descriptor_strings, .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, .qualifier_descriptor = usb_descriptor_device_qualifier,
.configurations = &usb_configurations, .configurations = &usb_configurations,
.configuration = 0, .configuration = 0,
@@ -91,16 +99,83 @@ void usb_configuration_changed(usb_device_t* const device) {
usb_endpoint_init(&usb_endpoint_bulk_out, false); 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) { void start_usb(void) {
// Detect hardware platform before we do anything else.
detect_hardware_platform(); 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(); 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_set_configuration_changed_cb(usb_configuration_changed);
usb_peripheral_reset(); 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_device_init(0, &usb_device);
usb_queue_init(&usb_endpoint_control_out_queue); 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_queue_init(&usb_endpoint_bulk_in_queue);
usb_endpoint_init(&usb_endpoint_control_out, false); 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); usb_endpoint_init(&usb_endpoint_control_in, true);
nvic_set_priority(NVIC_USB0_IRQ, 255); nvic_set_priority(NVIC_USB0_IRQ, 255);
@@ -0,0 +1,766 @@
/*
* Copyright 2012-2022 Great Scott Gadgets <info@greatscottgadgets.com>
* 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 <stdbool.h>
#include <stddef.h>
#include <libopencm3/cm3/nvic.h>
#include <libopencm3/lpc43xx/gpdma.h>
#include <libopencm3/lpc43xx/usb.h>
#include <clock_gen.h>
#include <fixed_point.h>
#include <gpdma.h>
#include <hackrf_ui.h>
#include <leds.h>
#include <m0_state.h>
#include <operacake_sctimer.h>
#include <platform_detect.h>
#include <radio.h>
#include <sgpio.h>
#include <streaming.h>
#include <transceiver_mode.h>
#include "common/usb.h"
#include <usb_queue.h>
#include <usb_request.h>
#include <usb_type.h>
#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);
}
}
+232 -152
View File
@@ -1,5 +1,5 @@
/* /*
* Copyright 2012-2022 Great Scott Gadgets <info@greatscottgadgets.com> * Copyright 2012-2026 Great Scott Gadgets <info@greatscottgadgets.com>
* Copyright 2012 Jared Boone * Copyright 2012 Jared Boone
* *
* This file is part of HackRF. * This file is part of HackRF.
@@ -20,22 +20,13 @@
* Boston, MA 02110-1301, USA. * Boston, MA 02110-1301, USA.
*/ */
#include <stdint.h>
#include "usb_type.h"
#include "usb_descriptor.h" #include "usb_descriptor.h"
#define USB_VENDOR_ID (0x0781) /* SanDisk Corp. */ #include <stdint.h>
#ifdef HACKRF_ONE #include <usb_type.h>
#define USB_PRODUCT_ID (0xa7a8) /* SD card reader */
#elif JAWBREAKER #define USB_VENDOR_ID (0x0781) /* SanDisk Corp. */
#define USB_PRODUCT_ID (0x604B)
#elif RAD1O
#define USB_PRODUCT_ID (0xCC15)
#else
#define USB_PRODUCT_ID (0xFFFF)
#endif
#define USB_API_VERSION (0x0127) /* hardware revision */ #define USB_API_VERSION (0x0127) /* hardware revision */
@@ -50,7 +41,8 @@
#define USB_STRING_LANGID (0x0409) #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 18, // bLength
USB_DESCRIPTOR_TYPE_DEVICE, // bDescriptorType USB_DESCRIPTOR_TYPE_DEVICE, // bDescriptorType
USB_WORD(0x0200), // bcdUSB USB_WORD(0x0200), // bcdUSB
@@ -59,13 +51,50 @@ uint8_t usb_descriptor_device[] = {
0x00, // bDeviceProtocol 0x00, // bDeviceProtocol
USB_MAX_PACKET0, // bMaxPacketSize0 USB_MAX_PACKET0, // bMaxPacketSize0
USB_WORD(USB_VENDOR_ID), // idVendor 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 USB_WORD(USB_API_VERSION), // bcdDevice
0x01, // iManufacturer 0x01, // iManufacturer
0x02, // iProduct 0x02, // iProduct
0x04, // iSerialNumber 0x04, // iSerialNumber
0x01 // bNumConfigurations 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[] = { uint8_t usb_descriptor_device_qualifier[] = {
10, // bLength 10, // bLength
@@ -161,162 +190,213 @@ uint8_t usb_descriptor_string_languages[] = {
// clang-format off // clang-format off
uint8_t usb_descriptor_string_manufacturer[] = { uint8_t usb_descriptor_string_manufacturer[] = {
40, // bLength 40, // bLength
USB_DESCRIPTOR_TYPE_STRING, // bDescriptorType USB_DESCRIPTOR_TYPE_STRING, // bDescriptorType
'G', 0x00, 'G', 0x00,
'r', 0x00, 'r', 0x00,
'e', 0x00, 'e', 0x00,
'a', 0x00, 'a', 0x00,
't', 0x00, 't', 0x00,
' ', 0x00, ' ', 0x00,
'S', 0x00, 'S', 0x00,
'c', 0x00, 'c', 0x00,
'o', 0x00, 'o', 0x00,
't', 0x00, 't', 0x00,
't', 0x00, 't', 0x00,
' ', 0x00, ' ', 0x00,
'G', 0x00, 'G', 0x00,
'a', 0x00, 'a', 0x00,
'd', 0x00, 'd', 0x00,
'g', 0x00, 'g', 0x00,
'e', 0x00, 'e', 0x00,
't', 0x00, 't', 0x00,
's', 0x00, 's', 0x00,
}; };
uint8_t usb_descriptor_string_product[] = { #ifdef IS_HACKRF_ONE
#ifdef HACKRF_ONE uint8_t usb_descriptor_string_product_hackrf_one[] = {
34, // bLength 34, // bLength
USB_DESCRIPTOR_TYPE_STRING, // bDescriptorType USB_DESCRIPTOR_TYPE_STRING, // bDescriptorType
'P', 0x00, 'P', 0x00,
'o', 0x00, 'o', 0x00,
'r', 0x00, 'r', 0x00,
't', 0x00, 't', 0x00,
'a', 0x00, 'a', 0x00,
'P', 0x00, 'P', 0x00,
'a', 0x00, 'a', 0x00,
'c', 0x00, 'c', 0x00,
'k', 0x00, 'k', 0x00,
' ', 0x00, ' ', 0x00,
'M', 0x00, 'M', 0x00,
'a', 0x00, 'a', 0x00,
'y', 0x00, 'y', 0x00,
'h', 0x00, 'h', 0x00,
'e', 0x00, 'e', 0x00,
'm', 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,
#endif #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[] = { uint8_t usb_descriptor_string_config_description[] = {
24, // bLength 24, // bLength
USB_DESCRIPTOR_TYPE_STRING, // bDescriptorType USB_DESCRIPTOR_TYPE_STRING, // bDescriptorType
'T', 0x00, 'T', 0x00,
'r', 0x00, 'r', 0x00,
'a', 0x00, 'a', 0x00,
'n', 0x00, 'n', 0x00,
's', 0x00, 's', 0x00,
'c', 0x00, 'c', 0x00,
'e', 0x00, 'e', 0x00,
'i', 0x00, 'i', 0x00,
'v', 0x00, 'v', 0x00,
'e', 0x00, 'e', 0x00,
'r', 0x00, 'r', 0x00,
}; };
#ifdef DFU_MODE #ifdef DFU_MODE
uint8_t usb_descriptor_string_serial_number[] = { uint8_t usb_descriptor_string_serial_number[] = {
30, // bLength 30, // bLength
USB_DESCRIPTOR_TYPE_STRING, // bDescriptorType USB_DESCRIPTOR_TYPE_STRING, // bDescriptorType
'R', 0x00, 'R', 0x00,
'u', 0x00, 'u', 0x00,
'n', 0x00, 'n', 0x00,
'n', 0x00, 'n', 0x00,
'i', 0x00, 'i', 0x00,
'n', 0x00, 'n', 0x00,
'g', 0x00, 'g', 0x00,
'F', 0x00, 'F', 0x00,
'r', 0x00, 'r', 0x00,
'o', 0x00, 'o', 0x00,
'm', 0x00, 'm', 0x00,
'R', 0x00, 'R', 0x00,
'A', 0x00, 'A', 0x00,
'M', 0x00, 'M', 0x00,
}; };
#else #else
uint8_t usb_descriptor_string_serial_number[USB_DESCRIPTOR_STRING_SERIAL_BUF_LEN]; uint8_t usb_descriptor_string_serial_number[USB_DESCRIPTOR_STRING_SERIAL_BUF_LEN];
#endif #endif
uint8_t* usb_descriptor_strings[] = { #ifdef IS_HACKRF_ONE
usb_descriptor_string_languages, uint8_t* usb_descriptor_strings_hackrf_one[] = {
usb_descriptor_string_manufacturer, usb_descriptor_string_languages,
usb_descriptor_string_product, usb_descriptor_string_manufacturer,
usb_descriptor_string_config_description, usb_descriptor_string_product_hackrf_one,
usb_descriptor_string_serial_number, usb_descriptor_string_config_description,
0, // TERMINATOR 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[] = { uint8_t wcid_string_descriptor[] = {
18, // bLength 18, // bLength
USB_DESCRIPTOR_TYPE_STRING, // bDescriptorType USB_DESCRIPTOR_TYPE_STRING, // bDescriptorType
'M', 0x00, 'M', 0x00,
'S', 0x00, 'S', 0x00,
'F', 0x00, 'F', 0x00,
'T', 0x00, 'T', 0x00,
'1', 0x00, '1', 0x00,
'0', 0x00, '0', 0x00,
'0', 0x00, '0', 0x00,
USB_WCID_VENDOR_REQ, // vendor request code for further descriptor USB_WCID_VENDOR_REQ, // vendor request code for further descriptor
0x00 0x00
}; };
uint8_t wcid_feature_descriptor[] = { uint8_t wcid_feature_descriptor[] = {
0x28, 0x00, 0x00, 0x00, // bLength 0x28, 0x00, 0x00, 0x00, // bLength
USB_WORD(0x0100), // WCID version USB_WORD(0x0100), // WCID version
USB_WORD(0x0004), // WICD descriptor index USB_WORD(0x0004), // WICD descriptor index
0x01, // bNumSections 0x01, // bNumSections
0x00,0x00,0x00,0x00,0x00,0x00,0x00, // Reserved 0x00,0x00,0x00,0x00,0x00,0x00,0x00, // Reserved
0x00, // bInterfaceNumber 0x00, // bInterfaceNumber
0x01, // Reserved 0x01, // Reserved
'W', 'I', 'N', 'U', 'S', 'B', 0x00,0x00, // Compatible ID, padded with zeros '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,0x00,0x00, // Sub-compatible ID
0x00,0x00,0x00,0x00,0x00,0x00 // Reserved 0x00,0x00,0x00,0x00,0x00,0x00 // Reserved
}; };
+27 -3
View File
@@ -20,22 +20,46 @@
* Boston, MA 02110-1301, USA. * Boston, MA 02110-1301, USA.
*/ */
#pragma once
#include <stdint.h> #include <stdint.h>
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_device_qualifier[];
extern uint8_t usb_descriptor_configuration_full_speed[]; extern uint8_t usb_descriptor_configuration_full_speed[];
extern uint8_t usb_descriptor_configuration_high_speed[]; extern uint8_t usb_descriptor_configuration_high_speed[];
extern uint8_t usb_descriptor_string_languages[]; extern uint8_t usb_descriptor_string_languages[];
extern uint8_t usb_descriptor_string_manufacturer[]; 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_LEN 32
#define USB_DESCRIPTOR_STRING_SERIAL_BUF_LEN \ #define USB_DESCRIPTOR_STRING_SERIAL_BUF_LEN \
(USB_DESCRIPTOR_STRING_SERIAL_LEN * 2 + 2) /* UTF-16LE */ (USB_DESCRIPTOR_STRING_SERIAL_LEN * 2 + 2) /* UTF-16LE */
extern uint8_t usb_descriptor_string_serial_number[]; 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 #define USB_WCID_VENDOR_REQ 0x19
extern uint8_t wcid_string_descriptor[]; extern uint8_t wcid_string_descriptor[];