diff --git a/firmware/common/LPC4320_M4_memory.ld b/firmware/common/LPC4320_M4_memory.ld index 48be94da2..182c5b807 100644 --- a/firmware/common/LPC4320_M4_memory.ld +++ b/firmware/common/LPC4320_M4_memory.ld @@ -20,16 +20,75 @@ * Boston, MA 02110-1301, USA. */ -/* Linker script for HackRF One (LPC4320, 1M SPI flash, 200K SRAM). */ +/* Top-level linker script for HackRF One (LPC4320, 1M SPI flash, 200K SRAM). */ + +/* Depending on the target, the following additional linker scripts are called: + * + * `hackrf_usb.bin`: + * - ${MCU_PARTNO}_M4_memory.ld -> This script + * - LPC43xx_M4_memory.ld -> Defines additional memory regions + * - LPC43xx_M4_M0_image_from_text.ld -> Adds the M0 core's firmware + * - LPC43xx_M4_memory_rom_only.ld -> Adds the fpga bitstreams + * - LPC43xx_M4_rtconfig.ld -> Adds code stored in ram_rtconfig + * - libopencm3_lpc43xx_m0.ld -> Controls linking for M0 code + * - libopencm3_lpc43xx_rom_to_ram.ld -> Controls linking for M4 and shadow region + * + * `hackrf_usb.dfu`: + * - ${MCU_PARTNO}_M4_memory.ld -> This script + * - LPC43xx_M4_memory.ld -> as above + * - LPC43xx_M0_memory.ld -> as above + * - LPC43xx_M4_M0_image_from_text.ld -> as above + * - LPC43xx_M4_rtconfig.ld -> as above + * - libopencm3_lpc43xx.ld -> Controls linking for M4 + * - libopencm3_lpc43xx_m0.ld -> as above + */ MEMORY { - /* rom is really the shadow region that points to SPI flash or elsewhere */ - rom (rx) : ORIGIN = 0x00000000, LENGTH = 1M - ram_local1 (rwx) : ORIGIN = 0x10000000, LENGTH = 64K - ram_usb (rw) : ORIGIN = 0x10010000, LENGTH = 32K - ram_local2 (rwx) : ORIGIN = 0x10080000, LENGTH = 32K - ram_sleep (rwx) : ORIGIN = 0x10088000, LENGTH = 8K + /* UM10503 Rev. 2.5, 10 September 2019 + * - Section 3.3.1 Table 10, LPC43xx/LPC43Sxx SRAM configuration + * - Section 3.3.3 On-chip flash + * - Section 3.3.5 Memory retention in the Power-down modes + * - Section 3.4 Figure 8, Flashless parts: Memory map with peripherals + * - Section 5 LPC43xx Boot ROM + * LPC4350/30/20/10 Product data sheet Rev. 4.6, 14 March 2016 + * - Section 7.17 Figure 7, LPC4350/30/20/10 Memory mapping (overview) + */ + + /* - 0x0000 0000 : 256 MB shadow area ---------------------------------- */ + /* The shadow area allows some LPC43xx memory regions to be remapped to + * address 0x0000 0000. + * + * - The default value at RESET is: + * -> Factory boot ROM @ 0x1040 0000 + * - Once the boot ROM has initialized SPIFI and established the boot map + * it is re-mapped to our 1M SPI Flash: + * -> SPIFI data @ 0x1400 0000 + * - Next, the `.text_ram` region is copied from SPIFI data to `ram_local1`. + * - Finally, execution jumps to `ram_local1` and the firmware begins. + */ + rom (rx) : ORIGIN = 0x00000000, LENGTH = 1M + + /* - 0x1000 0000 : 96 kB local SRAM ------------------------------------ */ + ram_local1 (rwx) : ORIGIN = 0x10000000, LENGTH = 64K + ram_usb (rw) : ORIGIN = 0x10010000, LENGTH = 32K + + /* - 0x1001 8000 : 32 kB local SRAM (LPC4350/30) ----------------------- */ + /* - 0x1002 0000 : reserved -------------------------------------------- */ + + /* - 0x1008 0000 : 32 kB local SRAM (LPC4350/30/20/10) ----------------- */ + ram_rtconfig (rx) : ORIGIN = 0x10080000, LENGTH = 16K + ram_local2 (rwx) : ORIGIN = 0x10084000, LENGTH = 16K + + /* - 0x1008 8000: 8 kB local SRAM (LPC4320/10) ------------------------- */ + ram_sleep (rwx) : ORIGIN = 0x10088000, LENGTH = 8K + + /* - 0x1008 A000 : 32 kB local SRAM (LPC4350/30) ----------------------- */ } +ASSERT( + SIZEOF(.text) + SIZEOF(.init_array) + SIZEOF(.ARM.exidx) <= 64K, + "Error: region `ram_local1' exceeds 64 kB." +) + INCLUDE LPC43xx_M4_memory.ld diff --git a/firmware/common/LPC4330_M4_memory.ld b/firmware/common/LPC4330_M4_memory.ld index 1d42ebee9..be4fc6ef5 100644 --- a/firmware/common/LPC4330_M4_memory.ld +++ b/firmware/common/LPC4330_M4_memory.ld @@ -20,16 +20,36 @@ * Boston, MA 02110-1301, USA. */ -/* Linker script for Rad1o badge - (LPC4330, 1M SPI flash, 264K SRAM). */ +/* Top-level linker script for Rad1o badge (LPC4330, 1M SPI flash, 264K SRAM). */ + +/* See `LPC4320_M4_memory.ld` for documentation. */ MEMORY { - /* rom is really the shadow region that points to SPI flash or elsewhere */ - rom (rx) : ORIGIN = 0x00000000, LENGTH = 128K - ram_local1 (rwx) : ORIGIN = 0x10000000, LENGTH = 96K - ram_usb(rw) : ORIGIN = 0x10018000, LENGTH = 32K - ram_local2 (rwx) : ORIGIN = 0x10080000, LENGTH = 64K - ram_sleep (rwx) : ORIGIN = 0x10090000, LENGTH = 8K + /* - 0x0000 0000 : 256 MB shadow area ---------------------------------- */ + rom (rx) : ORIGIN = 0x00000000, LENGTH = 128K + + /* - 0x1000 0000 : 96 kB local SRAM ------------------------------------ */ + ram_local1 (rwx) : ORIGIN = 0x10000000, LENGTH = 96K + + /* - 0x1001 8000 : 32 kB local SRAM (LPC4350/30) ----------------------- */ + ram_usb (rw) : ORIGIN = 0x10018000, LENGTH = 32K + + /* - 0x1002 0000 : reserved -------------------------------------------- */ + + /* - 0x1008 0000 : 32kB local SRAM (LPC4350/30/20/10) ------------------ */ + ram_rtconfig (rx) : ORIGIN = 0x10080000, LENGTH = 32K + + /* - 0x1008 A000 : 32kB local SRAM (LPC4350/30) ------------------------ */ + ram_local2 (rwx) : ORIGIN = 0x1008A000, LENGTH = 32K + + /* - 0x1009 0000 : 8kB local SRAM (LPC4350/30) ------------------------ */ + ram_sleep (rwx) : ORIGIN = 0x10090000, LENGTH = 8K } +ASSERT( + SIZEOF(.text) + SIZEOF(.init_array) + SIZEOF(.ARM.exidx) <= 96K, + "Error: region `ram_local1' exceeds 96 kB." +) + INCLUDE LPC43xx_M4_memory.ld diff --git a/firmware/common/LPC43xx_M4_M0_image_from_text.ld b/firmware/common/LPC43xx_M4_M0_image_from_text.ld index a2dfed68f..c04c158d8 100644 --- a/firmware/common/LPC43xx_M4_M0_image_from_text.ld +++ b/firmware/common/LPC43xx_M4_M0_image_from_text.ld @@ -23,9 +23,11 @@ SECTIONS { .m0_text : { - PROVIDE(__m0_start__ = .); + . = ALIGN(4); KEEP(*(.m0_bin*)); . = ALIGN(4); - PROVIDE(__m0_end__ = .); - } >rom + } >ram_m0 AT >rom + + PROVIDE(__m0_start__ = LOADADDR(.m0_text)); + PROVIDE(__m0_end__ = LOADADDR(.m0_text) + SIZEOF(.m0_text)); } diff --git a/firmware/common/LPC43xx_M4_memory.ld b/firmware/common/LPC43xx_M4_memory.ld index fc7dcee7f..0728890c6 100644 --- a/firmware/common/LPC43xx_M4_memory.ld +++ b/firmware/common/LPC43xx_M4_memory.ld @@ -22,20 +22,33 @@ MEMORY { - /* Physical address in Flash used to copy Code from Flash to RAM */ - rom_flash (rx) : ORIGIN = 0x80000000, LENGTH = 1M + /* UM10503 Rev. 2.5, 10 September 2019 + * - Section 3.3.1 Table 10, LPC43xx/LPC43Sxx SRAM configuration + * - Section 3.4 Figure 8, Flashless parts: Memory map with peripherals + * LPC4350/30/20/10 Product data sheet Rev. 4.6, 14 March 2016 + * - Section 7.17 Figure 7, LPC4350/30/20/10 Memory mapping (overview) + */ + + /* - 0x2000 0000 : 16 kB AHB SRAM (LPC4350/30/20/10) ------------------- */ + /* - 0x2000 4000 : 16 kB AHB SRAM (LPC4350/30/20) ---------------------- */ /* Buffers for LZ4 decompression */ - ram_lz4_in (rwx) : ORIGIN = 0x20000000, LENGTH = 4K - ram_lz4_out (rwx) : ORIGIN = 0x20001000, LENGTH = 4K + ram_lz4_in (rwx) : ORIGIN = 0x20000000, LENGTH = 4K + ram_lz4_out (rwx) : ORIGIN = 0x20001000, LENGTH = 4K /* RAM for M0 code */ - ram_m0 (rwx) : ORIGIN = 0x20002000, LENGTH = 20K + ram_m0 (rwx) : ORIGIN = 0x20002000, LENGTH = 20K /* RAM shared between M0 and M4 */ - ram_shared (rwx) : ORIGIN = 0x20007000, LENGTH = 4K - /* USB buffer. Straddles two blocks of RAM - * to get performance benefit of having two USB buffers addressable - * simultaneously (on two different buses of the AHB multilayer matrix) - */ - ram_samp (rwx) : ORIGIN = 0x20008000, LENGTH = 32K + ram_shared (rwx) : ORIGIN = 0x20007000, LENGTH = 4K + + /* - 0x2000 8000 : 16 kB AHB SRAM (LPC4350/30/20) ---------------------- */ + /* - 0x2000 C000 : 16 kB AHB SRAM (LPC4350/30/20/10) ------------------- */ + /* USB buffer. Straddles two blocks of RAM to get the performance benefit + * of having two USB buffers addressable simultaneously (on two different + * buses of the AHB multilayer matrix) */ + ram_samp (rwx) : ORIGIN = 0x20008000, LENGTH = 32K + + /* - 0x8000 0000 : SPIFI data ------------------------------------------ */ + /* Physical address in Flash used to copy Code from Flash to RAM */ + rom_flash (rx) : ORIGIN = 0x80000000, LENGTH = 1M } lz4_in_buf = ORIGIN(ram_lz4_in); @@ -43,4 +56,6 @@ lz4_out_buf = ORIGIN(ram_lz4_out); usb_samp_buffer = ORIGIN(ram_samp); usb_bulk_buffer = ORIGIN(ram_usb); m0_state = ORIGIN(ram_shared); + +PROVIDE(__ram_rtconfig_start__ = ORIGIN(ram_rtconfig)); PROVIDE(__ram_m0_start__ = ORIGIN(ram_m0)); diff --git a/firmware/common/LPC43xx_M4_rtconfig.ld b/firmware/common/LPC43xx_M4_rtconfig.ld new file mode 100644 index 000000000..255fb84d4 --- /dev/null +++ b/firmware/common/LPC43xx_M4_rtconfig.ld @@ -0,0 +1,32 @@ +/* + * Copyright 2012-2025 Great Scott Gadgets + * Copyright 2012 Jared Boone + * + * 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. + */ + +SECTIONS +{ + .rtconfig_text : { + KEEP(*(.rtconfig)); + . = ALIGN(4); + } >ram_rtconfig AT >rom + + PROVIDE(__rtconfig_start__ = LOADADDR(.rtconfig_text)); + PROVIDE(__rtconfig_end__ = LOADADDR(.rtconfig_text) + SIZEOF(.rtconfig_text)); +} diff --git a/firmware/common/fpga.h b/firmware/common/fpga.h index 1765fcdd1..58152e687 100644 --- a/firmware/common/fpga.h +++ b/firmware/common/fpga.h @@ -26,6 +26,14 @@ #include "ice40_spi.h" +/* Supported Bitstreams. */ +typedef enum { + FPGA_BITSTREAM_STANDARD = 0, + FPGA_BITSTREAM_HALFPREC = 1, + FPGA_BITSTREAM_EXTPREC_RX = 2, + FPGA_BITSTREAM_EXTPREC_TX = 3, +} fpga_bitstream_index_t; + /* Up to 7 registers, each containing up to 8 bits of data */ #define FPGA_NUM_REGS 7 #define FPGA_DATA_REGS_MAX_VALUE 255 diff --git a/firmware/common/platform_gpio.c b/firmware/common/platform_gpio.c index eeecd90e1..c4a24c730 100644 --- a/firmware/common/platform_gpio.c +++ b/firmware/common/platform_gpio.c @@ -27,6 +27,7 @@ // clang-format off +__attribute__((section(".rtconfig"))) const platform_gpio_t* platform_gpio(void) { static const platform_gpio_t* _platform_gpio = NULL; diff --git a/firmware/common/platform_scu.c b/firmware/common/platform_scu.c index c9e468e90..e0f5794d4 100644 --- a/firmware/common/platform_scu.c +++ b/firmware/common/platform_scu.c @@ -29,6 +29,7 @@ // clang-format off +__attribute__((section(".rtconfig"))) const platform_scu_t* platform_scu(void) { static const platform_scu_t* _platform_scu = NULL; diff --git a/firmware/common/portapack.c b/firmware/common/portapack.c index b84bf03c4..ca1923beb 100644 --- a/firmware/common/portapack.c +++ b/firmware/common/portapack.c @@ -617,7 +617,7 @@ bool portapack_present(void) return portapack_detected; } -bool portapack_init(void) +__attribute__((section(".rtconfig"))) bool portapack_init(void) { if (portapack_detect()) { portapack_if_init(); diff --git a/firmware/common/radio.c b/firmware/common/radio.c index 36f65cad7..33ef12db6 100644 --- a/firmware/common/radio.c +++ b/firmware/common/radio.c @@ -53,6 +53,7 @@ void radio_init(radio_t* const radio) radio->config[bank][reg] = RADIO_UNSET; } } + radio->config_mode = RADIO_CONFIG_STANDARD; radio->config[RADIO_BANK_APPLIED][RADIO_OPMODE] = TRANSCEIVER_MODE_OFF; radio->config[RADIO_BANK_REQUESTED][RADIO_OPMODE] = TRANSCEIVER_MODE_OFF; radio->config[RADIO_BANK_IDLE][RADIO_OPMODE] = TRANSCEIVER_MODE_OFF; @@ -60,6 +61,7 @@ void radio_init(radio_t* const radio) radio->config[RADIO_BANK_TX][RADIO_OPMODE] = TRANSCEIVER_MODE_TX; radio->config[RADIO_BANK_IDLE][RADIO_BIAS_TEE] = false; radio->regs_dirty = 0; + radio->regs_locked = 0; } static inline void mark_dirty(radio_t* const radio, radio_register_t reg) @@ -67,6 +69,11 @@ static inline void mark_dirty(radio_t* const radio, radio_register_t reg) radio->regs_dirty |= (1 << reg); } +static inline bool check_locked(radio_t* const radio, radio_register_t reg) +{ + return radio->regs_locked & (1 << reg); +} + radio_error_t radio_reg_write( radio_t* const radio, const radio_register_bank_t bank, @@ -77,6 +84,10 @@ radio_error_t radio_reg_write( return RADIO_ERR_INVALID_REGISTER; } + if (check_locked(radio, reg)) { + return RADIO_ERR_LOCKED_REGISTER; + } + switch (bank) { case RADIO_BANK_REQUESTED: mark_dirty(radio, reg); @@ -107,6 +118,20 @@ uint64_t radio_reg_read( return radio->config[bank][reg]; } +radio_error_t radio_reg_lock( + radio_t* const radio, + const radio_register_t reg, + const bool locked) +{ + if (reg > RADIO_NUM_REGS) { + return RADIO_ERR_INVALID_REGISTER; + } + + radio->regs_locked = (radio->regs_locked & ~(1 << reg)) | (locked << reg); + + return RADIO_OK; +} + static uint32_t radio_update_direction(radio_t* const radio, uint64_t* bank) { const uint64_t requested = bank[RADIO_OPMODE]; @@ -1010,3 +1035,69 @@ void radio_switch_opmode(radio_t* const radio, const transceiver_mode_t mode) nvic_enable_irq(NVIC_USB0_IRQ); radio_update(radio); } + +bool radio_set_config_mode(radio_t* const radio, const radio_config_mode_t mode) +{ + // Check if the requested mode is supported. + switch (mode) { + case RADIO_CONFIG_STANDARD: + // supported on all boards + break; +#ifdef IS_PRALINE + case RADIO_CONFIG_HALF_PRECISION: + case RADIO_CONFIG_EXT_PRECISION_RX: + case RADIO_CONFIG_EXT_PRECISION_TX: + // only supported on praline + if (!IS_PRALINE) { + return false; + } + break; +#endif + default: + return false; + } + + // Don't do anything if we're already in the requested mode. + if (mode == radio->config_mode) { + return true; + } + +#if defined(IS_PRALINE) && !(defined(DFU_MODE) || defined(RAM_MODE)) + if (IS_PRALINE) { + fpga_bitstream_index_t bitstream_index; + switch (mode) { + case RADIO_CONFIG_STANDARD: + bitstream_index = FPGA_BITSTREAM_STANDARD; + break; + case RADIO_CONFIG_HALF_PRECISION: + bitstream_index = FPGA_BITSTREAM_HALFPREC; + break; + case RADIO_CONFIG_EXT_PRECISION_RX: + bitstream_index = FPGA_BITSTREAM_EXTPREC_RX; + break; + case RADIO_CONFIG_EXT_PRECISION_TX: + bitstream_index = FPGA_BITSTREAM_EXTPREC_TX; + break; + default: + return false; + } + + // Reset radio state. + for (uint8_t reg = 0; reg < RADIO_NUM_REGS; reg++) { + radio_reg_write(radio, RADIO_BANK_APPLIED, reg, RADIO_UNSET); + radio_reg_write(radio, RADIO_BANK_REQUESTED, reg, RADIO_UNSET); + } + + // Load bitstream. + extern struct fpga_loader_t fpga_loader; + if (!fpga_image_load(&fpga_loader, bitstream_index)) { + return false; + } + } +#endif + + // Update radio config mode. + radio->config_mode = mode; + + return true; +} diff --git a/firmware/common/radio.h b/firmware/common/radio.h index 8e12471a4..2747cf974 100644 --- a/firmware/common/radio.h +++ b/firmware/common/radio.h @@ -34,6 +34,7 @@ typedef enum { RADIO_ERR_INVALID_PARAM = -2, RADIO_ERR_INVALID_BANK = -3, RADIO_ERR_INVALID_REGISTER = -4, + RADIO_ERR_LOCKED_REGISTER = -5, RADIO_ERR_UNSUPPORTED_OPERATION = -10, RADIO_ERR_UNIMPLEMENTED = -19, RADIO_ERR_OTHER = -9999, @@ -41,11 +42,12 @@ typedef enum { /* radio configuration modes */ typedef enum { - RADIO_CONFIG_LEGACY = 0, - RADIO_CONFIG_STANDARD = 1, - RADIO_CONFIG_EXT_PRECISION_RX = 2, - RADIO_CONFIG_EXT_PRECISION_TX = 3, - RADIO_CONFIG_HALF_PRECISION = 4, + RADIO_CONFIG_STANDARD = 0, +#ifdef IS_PRALINE + RADIO_CONFIG_EXT_PRECISION_RX = 1, + RADIO_CONFIG_EXT_PRECISION_TX = 2, + RADIO_CONFIG_HALF_PRECISION = 3, +#endif } radio_config_mode_t; typedef struct { @@ -231,6 +233,7 @@ typedef struct { radio_config_mode_t config_mode; uint64_t config[RADIO_NUM_BANKS][RADIO_NUM_REGS]; volatile uint32_t regs_dirty; + uint32_t regs_locked; update_fn update_cb; } radio_t; @@ -254,6 +257,15 @@ uint64_t radio_reg_read( const radio_register_bank_t bank, const radio_register_t reg); +/** + * Lock a register. Prevents any future calls to `radio_reg_write` + * from overwriting the current stored value of the register. + */ +radio_error_t radio_reg_lock( + radio_t* const radio, + const radio_register_t reg, + const bool locked); + /** * Apply changes requested in RADIO_BANK_REQUESTED. * Return true if any changes were applied. @@ -266,6 +278,12 @@ bool radio_update(radio_t* const radio); */ void radio_switch_opmode(radio_t* const radio, const transceiver_mode_t mode); +/** + * Switch to a new configuration mode. + * Return true if the mode was successfully switched. + */ +bool radio_set_config_mode(radio_t* const radio, const radio_config_mode_t mode); + /** * Driver instance. */ diff --git a/firmware/common/ui_portapack.c b/firmware/common/ui_portapack.c index 3387b5ebf..c4326c63a 100644 --- a/firmware/common/ui_portapack.c +++ b/firmware/common/ui_portapack.c @@ -33,7 +33,8 @@ /* Pixel data within a font or bitmap byte is "reversed": LSB is left-most pixel. */ -static const uint8_t font_fixed_8x16_glyph_data[] = { +__attribute__(( + section(".rtconfig"))) static const uint8_t font_fixed_8x16_glyph_data[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x08, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x24, 0x24, 0x24, @@ -152,10 +153,11 @@ static const uint8_t font_fixed_8x16_glyph_data[] = { 0x10, 0x20, 0x10, 0x10, 0x10, 0x10, 0x10, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8e, 0x71, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; -static const ui_font_t font_fixed_8x16 = +__attribute__((section(".rtconfig"))) static const ui_font_t font_fixed_8x16 = {{8, 16}, font_fixed_8x16_glyph_data, 0x20, 95, (8 * 16 + 7U) >> 3}; -static const uint8_t font_fixed_24x19_glyph_data[] = { +__attribute__(( + section(".rtconfig"))) static const uint8_t font_fixed_24x19_glyph_data[] = { 0xe0, 0xff, 0x07, 0xf8, 0xff, 0x1f, 0xf8, 0xff, 0x1f, 0x3c, 0x00, 0x3c, 0x1c, 0x00, 0x38, 0x1c, 0x00, 0x38, 0x1c, 0x00, 0x38, 0x1c, 0x00, 0x38, 0x1c, 0x00, 0x38, 0x1c, 0x00, 0x38, 0x1c, 0x00, 0x38, 0x1c, 0x00, 0x38, 0x1c, 0x00, 0x38, @@ -202,10 +204,11 @@ static const uint8_t font_fixed_24x19_glyph_data[] = { 0x00, 0x3c, 0xf8, 0xff, 0x1f, 0xf8, 0xff, 0x1f, 0xe0, 0xff, 0x07, }; -static const ui_font_t font_fixed_24x19 = +__attribute__((section(".rtconfig"))) static const ui_font_t font_fixed_24x19 = {{24, 19}, font_fixed_24x19_glyph_data, 0x30, 10, (24 * 19 + 7U) >> 3}; -static const uint8_t font_fixed_16x14_glyph_data[] = { +__attribute__(( + section(".rtconfig"))) static const uint8_t font_fixed_16x14_glyph_data[] = { 0xf8, 0x1f, 0xfc, 0x3f, 0x0e, 0x70, 0x06, 0x60, 0x06, 0x60, 0x06, 0x60, 0x06, 0x60, 0x06, 0x60, 0x06, 0x60, 0x06, 0x60, 0x06, 0x60, 0x0e, 0x70, 0xfc, 0x3f, 0xf8, 0x1f, 0x00, 0x03, 0x80, 0x03, 0xc0, 0x03, 0xe0, 0x03, 0x70, 0x03, 0x20, @@ -230,10 +233,10 @@ static const uint8_t font_fixed_16x14_glyph_data[] = { 0x60, 0x0e, 0x70, 0xfc, 0x3f, 0xf8, 0x1f, }; -static const ui_font_t font_fixed_16x14 = +__attribute__((section(".rtconfig"))) static const ui_font_t font_fixed_16x14 = {{16, 14}, font_fixed_16x14_glyph_data, 0x30, 10, (16 * 14 + 7U) >> 3}; -static const uint8_t bitmap_amp_rx_data[] = { +__attribute__((section(".rtconfig"))) static const uint8_t bitmap_amp_rx_data[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x06, 0x00, 0x60, 0x06, 0x00, 0x60, 0x0c, 0x00, 0x30, 0x0c, 0x00, 0x30, 0x18, 0x00, 0x18, 0x18, 0x00, 0x18, 0x30, 0x00, 0x0c, 0x30, 0x00, 0x0c, 0x60, 0x00, 0x06, 0x60, 0x00, 0x06, @@ -241,9 +244,11 @@ static const uint8_t bitmap_amp_rx_data[] = { 0x00, 0xc3, 0x00, 0x00, 0xc3, 0x00, 0x00, 0x66, 0x00, 0x00, 0x66, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00}; -static const ui_bitmap_t bitmap_amp_rx = {{24, 24}, bitmap_amp_rx_data}; +__attribute__((section(".rtconfig"))) static const ui_bitmap_t bitmap_amp_rx = { + {24, 24}, + bitmap_amp_rx_data}; -static const uint8_t bitmap_amp_tx_data[] = { +__attribute__((section(".rtconfig"))) static const uint8_t bitmap_amp_tx_data[] = { 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x66, 0x00, 0x00, 0x66, 0x00, 0x00, 0xc3, 0x00, 0x00, 0xc3, 0x00, 0x80, 0x81, 0x01, 0x80, 0x81, 0x01, 0xc0, 0x00, 0x03, 0xc0, 0x00, 0x03, @@ -251,9 +256,11 @@ static const uint8_t bitmap_amp_tx_data[] = { 0x18, 0x00, 0x18, 0x18, 0x00, 0x18, 0x0c, 0x00, 0x30, 0x0c, 0x00, 0x30, 0x06, 0x00, 0x60, 0x06, 0x00, 0x60, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; -static const ui_bitmap_t bitmap_amp_tx = {{24, 24}, bitmap_amp_tx_data}; +__attribute__((section(".rtconfig"))) static const ui_bitmap_t bitmap_amp_tx = { + {24, 24}, + bitmap_amp_tx_data}; -static const uint8_t bitmap_antenna_data[] = { +__attribute__((section(".rtconfig"))) static const uint8_t bitmap_antenna_data[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x06, 0x18, 0x60, 0x06, 0x18, 0x60, 0x0c, 0x18, 0x30, 0x0c, 0x18, 0x30, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x30, 0x18, 0x0c, 0x30, 0x18, 0x0c, 0x60, 0x18, 0x06, 0x60, 0x18, 0x06, @@ -261,9 +268,11 @@ static const uint8_t bitmap_antenna_data[] = { 0x00, 0xdb, 0x00, 0x00, 0xdb, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00}; -static const ui_bitmap_t bitmap_antenna = {{24, 24}, bitmap_antenna_data}; +__attribute__((section(".rtconfig"))) static const ui_bitmap_t bitmap_antenna = { + {24, 24}, + bitmap_antenna_data}; -static const uint8_t bitmap_filter_hp_data[] = { +__attribute__((section(".rtconfig"))) static const uint8_t bitmap_filter_hp_data[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x03, 0x00, 0xc0, 0x03, 0x00, 0xc0, 0x03, 0x00, 0xc0, 0x03, 0x00, 0xc0, 0x03, 0xf8, 0xc7, 0x03, 0xfc, 0xc7, 0x03, 0x0e, 0xc0, 0x03, 0x06, 0xc0, 0x03, 0x03, 0xc0, 0x03, 0x03, 0xc0, @@ -271,9 +280,11 @@ static const uint8_t bitmap_filter_hp_data[] = { 0x63, 0x00, 0xc0, 0x63, 0x00, 0xc0, 0x03, 0x00, 0xc0, 0x03, 0x00, 0xc0, 0x03, 0x00, 0xc0, 0x03, 0x00, 0xc0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; -static const ui_bitmap_t bitmap_filter_hp = {{24, 24}, bitmap_filter_hp_data}; +__attribute__((section(".rtconfig"))) static const ui_bitmap_t bitmap_filter_hp = { + {24, 24}, + bitmap_filter_hp_data}; -static const uint8_t bitmap_filter_lp_data[] = { +__attribute__((section(".rtconfig"))) static const uint8_t bitmap_filter_lp_data[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x03, 0x00, 0xc0, 0x03, 0x00, 0xc0, 0x03, 0x00, 0xc0, 0x03, 0x00, 0xc0, 0xe3, 0x1f, 0xc0, 0xe3, 0x3f, 0xc0, 0x03, 0x70, 0xc0, 0x03, 0x60, 0xc0, 0x03, 0xc0, 0xc0, 0x03, 0xc0, 0xc0, @@ -281,9 +292,11 @@ static const uint8_t bitmap_filter_lp_data[] = { 0x03, 0x00, 0xc6, 0x03, 0x00, 0xc6, 0x03, 0x00, 0xc0, 0x03, 0x00, 0xc0, 0x03, 0x00, 0xc0, 0x03, 0x00, 0xc0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; -static const ui_bitmap_t bitmap_filter_lp = {{24, 24}, bitmap_filter_lp_data}; +__attribute__((section(".rtconfig"))) static const ui_bitmap_t bitmap_filter_lp = { + {24, 24}, + bitmap_filter_lp_data}; -static const uint8_t bitmap_mixer_data[] = { +__attribute__((section(".rtconfig"))) static const uint8_t bitmap_mixer_data[] = { 0x00, 0x7e, 0x00, 0xc0, 0xff, 0x03, 0xe0, 0x81, 0x07, 0x70, 0x00, 0x0e, 0x38, 0x00, 0x1c, 0x7c, 0x00, 0x3e, 0xee, 0x00, 0x77, 0xc6, 0x81, 0x63, 0x86, 0xc3, 0x61, 0x03, 0xe7, 0xc0, 0x03, 0x7e, 0xc0, 0x03, 0x3c, 0xc0, @@ -291,9 +304,11 @@ static const uint8_t bitmap_mixer_data[] = { 0xc6, 0x81, 0x63, 0xee, 0x00, 0x77, 0x7c, 0x00, 0x3e, 0x38, 0x00, 0x1c, 0x70, 0x00, 0x0e, 0xe0, 0x81, 0x07, 0xc0, 0xff, 0x03, 0x00, 0x7e, 0x00}; -static const ui_bitmap_t bitmap_mixer = {{24, 24}, bitmap_mixer_data}; +__attribute__((section(".rtconfig"))) static const ui_bitmap_t bitmap_mixer = { + {24, 24}, + bitmap_mixer_data}; -static const uint8_t bitmap_oscillator_data[] = { +__attribute__((section(".rtconfig"))) static const uint8_t bitmap_oscillator_data[] = { 0x00, 0x7e, 0x00, 0xc0, 0xff, 0x03, 0xe0, 0x81, 0x07, 0x70, 0x00, 0x0e, 0x38, 0x00, 0x1c, 0x1c, 0x00, 0x38, 0x0e, 0x03, 0x70, 0x86, 0x07, 0x60, 0xc6, 0x0f, 0x60, 0xc3, 0x0c, 0xc0, 0xe3, 0x1c, 0xc0, 0x63, 0x18, 0xc6, @@ -301,13 +316,18 @@ static const uint8_t bitmap_oscillator_data[] = { 0x06, 0xe0, 0x61, 0x0e, 0xc0, 0x70, 0x1c, 0x00, 0x38, 0x38, 0x00, 0x1c, 0x70, 0x00, 0x0e, 0xe0, 0x81, 0x07, 0xc0, 0xff, 0x03, 0x00, 0x7e, 0x00}; -static const ui_bitmap_t bitmap_oscillator = {{24, 24}, bitmap_oscillator_data}; +__attribute__((section(".rtconfig"))) static const ui_bitmap_t bitmap_oscillator = { + {24, 24}, + bitmap_oscillator_data}; -static const uint8_t bitmap_wire_8_data[] = {0xff, 0xff}; +__attribute__(( + section(".rtconfig"))) static const uint8_t bitmap_wire_8_data[] = {0xff, 0xff}; -static const ui_bitmap_t bitmap_wire_8 = {{2, 8}, bitmap_wire_8_data}; +__attribute__((section(".rtconfig"))) static const ui_bitmap_t bitmap_wire_8 = { + {2, 8}, + bitmap_wire_8_data}; -static const uint8_t bitmap_wire_24_data[] = { +__attribute__((section(".rtconfig"))) static const uint8_t bitmap_wire_24_data[] = { 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, @@ -315,9 +335,11 @@ static const uint8_t bitmap_wire_24_data[] = { 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x00}; -static const ui_bitmap_t bitmap_wire_24 = {{24, 24}, bitmap_wire_24_data}; +__attribute__((section(".rtconfig"))) static const ui_bitmap_t bitmap_wire_24 = { + {24, 24}, + bitmap_wire_24_data}; -static const uint8_t bitmap_blank_24_data[] = { +__attribute__((section(".rtconfig"))) static const uint8_t bitmap_blank_24_data[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -325,23 +347,29 @@ static const uint8_t bitmap_blank_24_data[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; -static const ui_bitmap_t bitmap_blank_24 = {{24, 24}, bitmap_blank_24_data}; +__attribute__((section(".rtconfig"))) static const ui_bitmap_t bitmap_blank_24 = { + {24, 24}, + bitmap_blank_24_data}; -static const uint8_t bitmap_waves_rx_data[] = { +__attribute__((section(".rtconfig"))) static const uint8_t bitmap_waves_rx_data[] = { 0xc0, 0x00, 0x60, 0x00, 0x70, 0x06, 0x30, 0x07, 0x38, 0x03, 0x98, 0x33, 0x98, 0x39, 0x98, 0x19, 0xcc, 0x18, 0xcc, 0x0c, 0xcc, 0x0c, 0xcc, 0x0c, 0xcc, 0x0c, 0xcc, 0x0c, 0xcc, 0x0c, 0xcc, 0x18, 0x98, 0x19, 0x98, 0x39, 0x98, 0x33, 0x38, 0x03, 0x30, 0x07, 0x70, 0x06, 0x60, 0x00, 0xc0, 0x00}; -static const ui_bitmap_t bitmap_waves_rx = {{16, 24}, bitmap_waves_rx_data}; +__attribute__((section(".rtconfig"))) static const ui_bitmap_t bitmap_waves_rx = { + {16, 24}, + bitmap_waves_rx_data}; -static const uint8_t bitmap_waves_tx_data[] = { +__attribute__((section(".rtconfig"))) static const uint8_t bitmap_waves_tx_data[] = { 0x00, 0x03, 0x00, 0x06, 0x60, 0x0e, 0xe0, 0x0c, 0xc0, 0x1c, 0xcc, 0x19, 0x9c, 0x19, 0x98, 0x19, 0x18, 0x33, 0x30, 0x33, 0x30, 0x33, 0x30, 0x33, 0x30, 0x33, 0x30, 0x33, 0x30, 0x33, 0x18, 0x33, 0x98, 0x19, 0x9c, 0x19, 0xcc, 0x19, 0xc0, 0x1c, 0xe0, 0x0c, 0x60, 0x0e, 0x00, 0x06, 0x00, 0x03}; -static const ui_bitmap_t bitmap_waves_tx = {{16, 24}, bitmap_waves_tx_data}; +__attribute__((section(".rtconfig"))) static const ui_bitmap_t bitmap_waves_tx = { + {16, 24}, + bitmap_waves_tx_data}; __attribute__((unused)) static ui_color_t portapack_color_rgb( const uint_fast8_t r, diff --git a/firmware/hackrf-common.cmake b/firmware/hackrf-common.cmake index 8f6698da8..0bd85f7f8 100644 --- a/firmware/hackrf-common.cmake +++ b/firmware/hackrf-common.cmake @@ -88,14 +88,14 @@ endif() SET(HACKRF_OPTS "-D${BOARD} -DLPC43XX -D${MCU_PARTNO} -DTX_ENABLE -D'VERSION_STRING=\"${VERSION}\"'") -SET(LDSCRIPT_M4 "-T${PATH_HACKRF_FIRMWARE_COMMON}/${MCU_PARTNO}_M4_memory.ld -Tlibopencm3_lpc43xx_rom_to_ram.ld -T${PATH_HACKRF_FIRMWARE_COMMON}/LPC43xx_M4_M0_image_from_text.ld -T${PATH_HACKRF_FIRMWARE_COMMON}/LPC43xx_M4_memory_rom_only.ld") +SET(LDSCRIPT_M4 "-T${PATH_HACKRF_FIRMWARE_COMMON}/${MCU_PARTNO}_M4_memory.ld -Tlibopencm3_lpc43xx_rom_to_ram.ld -T${PATH_HACKRF_FIRMWARE_COMMON}/LPC43xx_M4_M0_image_from_text.ld -T${PATH_HACKRF_FIRMWARE_COMMON}/LPC43xx_M4_memory_rom_only.ld -T${PATH_HACKRF_FIRMWARE_COMMON}/LPC43xx_M4_rtconfig.ld") -SET(LDSCRIPT_M4_RAM "-T${PATH_HACKRF_FIRMWARE_COMMON}/${MCU_PARTNO}_M4_memory.ld -Tlibopencm3_lpc43xx.ld -T${PATH_HACKRF_FIRMWARE_COMMON}/LPC43xx_M4_M0_image_from_text.ld") +SET(LDSCRIPT_M4_RAM "-T${PATH_HACKRF_FIRMWARE_COMMON}/${MCU_PARTNO}_M4_memory.ld -Tlibopencm3_lpc43xx.ld -T${PATH_HACKRF_FIRMWARE_COMMON}/LPC43xx_M4_M0_image_from_text.ld -T${PATH_HACKRF_FIRMWARE_COMMON}/LPC43xx_M4_rtconfig.ld") SET(LDSCRIPT_M0 "-T${PATH_HACKRF_FIRMWARE_COMMON}/LPC43xx_M0_memory.ld -Tlibopencm3_lpc43xx_m0.ld") SET(CFLAGS_COMMON "-Os -g3 -Wall -Wextra -Wstrict-prototypes -Wold-style-definition ${HACKRF_OPTS} -fno-common -MD") -SET(LDFLAGS_COMMON "-nostartfiles -Wl,--gc-sections") +SET(LDFLAGS_COMMON "-nostartfiles -Wl,--gc-sections -Wl,--print-memory-usage") if(V STREQUAL "1") SET(LDFLAGS_COMMON "${LDFLAGS_COMMON} -Wl,--print-gc-sections") diff --git a/firmware/hackrf_usb/CMakeLists.txt b/firmware/hackrf_usb/CMakeLists.txt index 871e0909f..027fc97a8 100644 --- a/firmware/hackrf_usb/CMakeLists.txt +++ b/firmware/hackrf_usb/CMakeLists.txt @@ -60,6 +60,7 @@ set(SRC_M4 usb_api_spiflash.c usb_api_transceiver.c usb_api_operacake.c + usb_api_radio.c usb_api_sweep.c usb_api_selftest.c usb_api_ui.c diff --git a/firmware/hackrf_usb/hackrf_usb.c b/firmware/hackrf_usb/hackrf_usb.c index d0c09c612..b035dba74 100644 --- a/firmware/hackrf_usb/hackrf_usb.c +++ b/firmware/hackrf_usb/hackrf_usb.c @@ -75,6 +75,7 @@ #include "usb_api_board_info.h" #include "usb_api_m0_state.h" #include "usb_api_operacake.h" +#include "usb_api_radio.h" #include "usb_api_register.h" #include "usb_api_selftest.h" #include "usb_api_spiflash.h" @@ -94,6 +95,9 @@ extern uint32_t __m0_start__; extern uint32_t __m0_end__; extern uint32_t __ram_m0_start__; +extern uint32_t __rtconfig_start__; +extern uint32_t __rtconfig_end__; +extern uint32_t __ram_rtconfig_start__; extern uint32_t _etext_ram, _text_ram, _etext_rom; static usb_request_handler_fn vendor_request_handler[] = { @@ -183,6 +187,13 @@ static usb_request_handler_fn vendor_request_handler[] = { usb_vendor_request_read_radio_reg, usb_vendor_request_get_buffer_size, usb_vendor_request_read_temperature, + usb_vendor_request_lock_radio_reg, + usb_vendor_request_open, + usb_vendor_request_close, + usb_vendor_request_set_radio_mode, + usb_vendor_request_set_radio_frequency, + usb_vendor_request_set_radio_frequency_explicit, + usb_vendor_request_set_radio_sample_rate, }; static const uint32_t vendor_request_handler_count = @@ -271,6 +282,20 @@ static void m0_rom_to_ram(void) memcpy(dest, (uint32_t*) (base + src), len); } +static void rtconfig_rom_to_ram(void) +{ + uint32_t* dest = &__ram_rtconfig_start__; + + // Calculate the base address of ROM + uint32_t base = (uint32_t) (&_etext_rom - (&_etext_ram - &_text_ram)); + + // rtconfig image location, relative to the start of ROM + uint32_t src = (uint32_t) &__rtconfig_start__; + + uint32_t len = (uint32_t) &__rtconfig_end__ - (uint32_t) src; + memcpy(dest, (uint32_t*) (base + src), len); +} + #if defined(IS_PRALINE) && !(defined(DFU_MODE) || defined(RAM_MODE)) extern uint32_t _binary_fpga_bin_start; @@ -422,6 +447,9 @@ int main(void) // Copy M0 image from ROM before SPIFI is disabled m0_rom_to_ram(); + // Copy rtconfig image from ROM before SPIFI is disabled + rtconfig_rom_to_ram(); + // This will be cleared if any self-test check fails. selftest.report.pass = true; diff --git a/firmware/hackrf_usb/usb_api_radio.c b/firmware/hackrf_usb/usb_api_radio.c new file mode 100644 index 000000000..3d31aa2a8 --- /dev/null +++ b/firmware/hackrf_usb/usb_api_radio.c @@ -0,0 +1,151 @@ +/* + * 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 +#include + +#include +#include +#include +#include + +#include "usb_queue.h" + +usb_request_status_t usb_vendor_request_set_radio_mode( + usb_endpoint_t* const endpoint, + const usb_transfer_stage_t stage) +{ + if (stage == USB_TRANSFER_STAGE_SETUP) { + radio_config_mode_t mode = endpoint->setup.value; + if (!radio_set_config_mode(&radio, mode)) { + return USB_REQUEST_STATUS_STALL; + } + usb_transfer_schedule_ack(endpoint->in); + } + + return USB_REQUEST_STATUS_OK; +} + +usb_request_status_t usb_vendor_request_set_radio_frequency( + usb_endpoint_t* const endpoint, + const usb_transfer_stage_t stage) +{ + static fp_40_24_t set_radio_frequency_param; + + if (stage == USB_TRANSFER_STAGE_SETUP) { + usb_transfer_schedule_block( + endpoint->out, + &set_radio_frequency_param, + sizeof(fp_40_24_t), + NULL, + NULL); + } else if (stage == USB_TRANSFER_STAGE_DATA) { + radio_reg_write( + &radio, + RADIO_BANK_REQUESTED, + RADIO_FREQUENCY_RF, + set_radio_frequency_param); + 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_radio_frequency_explicit( + usb_endpoint_t* const endpoint, + const usb_transfer_stage_t stage) +{ + typedef struct { + fp_40_24_t if_freq_hz; + fp_40_24_t lo_freq_hz; + uint8_t path; + } set_radio_frequency_explicit_params_t; + + static set_radio_frequency_explicit_params_t set_radio_frequency_explicit_params; + + if (stage == USB_TRANSFER_STAGE_SETUP) { + usb_transfer_schedule_block( + endpoint->out, + &set_radio_frequency_explicit_params, + sizeof(set_radio_frequency_explicit_params_t), + NULL, + NULL); + } else if (stage == USB_TRANSFER_STAGE_DATA) { + radio_reg_write( + &radio, + RADIO_BANK_REQUESTED, + RADIO_FREQUENCY_IF, + set_radio_frequency_explicit_params.if_freq_hz); + radio_reg_write( + &radio, + RADIO_BANK_REQUESTED, + RADIO_FREQUENCY_LO, + set_radio_frequency_explicit_params.lo_freq_hz); + radio_reg_write( + &radio, + RADIO_BANK_REQUESTED, + RADIO_IMAGE_REJECT, + set_radio_frequency_explicit_params.path); + usb_transfer_schedule_ack(endpoint->in); + } + + return USB_REQUEST_STATUS_OK; +} + +usb_request_status_t usb_vendor_request_set_radio_sample_rate( + usb_endpoint_t* const endpoint, + const usb_transfer_stage_t stage) +{ + static fp_28_36_t set_radio_sample_rate_param; + + if (stage == USB_TRANSFER_STAGE_SETUP) { + usb_transfer_schedule_block( + endpoint->out, + &set_radio_sample_rate_param, + sizeof(fp_28_36_t), + NULL, + NULL); + } else if (stage == USB_TRANSFER_STAGE_DATA) { + radio_reg_write( + &radio, + RADIO_BANK_REQUESTED, + RADIO_SAMPLE_RATE, + set_radio_sample_rate_param); + usb_transfer_schedule_ack(endpoint->in); + } + + return USB_REQUEST_STATUS_OK; +} diff --git a/firmware/hackrf_usb/usb_api_radio.h b/firmware/hackrf_usb/usb_api_radio.h new file mode 100644 index 000000000..c8940c112 --- /dev/null +++ b/firmware/hackrf_usb/usb_api_radio.h @@ -0,0 +1,41 @@ +/* + * 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. + */ + +#pragma once + +#include +#include + +usb_request_status_t usb_vendor_request_set_radio_mode( + usb_endpoint_t* const endpoint, + const usb_transfer_stage_t stage); + +usb_request_status_t usb_vendor_request_set_radio_frequency( + usb_endpoint_t* const endpoint, + const usb_transfer_stage_t stage); + +usb_request_status_t usb_vendor_request_set_radio_frequency_explicit( + usb_endpoint_t* const endpoint, + const usb_transfer_stage_t stage); + +usb_request_status_t usb_vendor_request_set_radio_sample_rate( + usb_endpoint_t* const endpoint, + const usb_transfer_stage_t stage); diff --git a/firmware/hackrf_usb/usb_api_register.c b/firmware/hackrf_usb/usb_api_register.c index 626c75089..a46a22404 100644 --- a/firmware/hackrf_usb/usb_api_register.c +++ b/firmware/hackrf_usb/usb_api_register.c @@ -416,3 +416,20 @@ usb_request_status_t usb_vendor_request_read_radio_reg( } return USB_REQUEST_STATUS_OK; } + +usb_request_status_t usb_vendor_request_lock_radio_reg( + usb_endpoint_t* const endpoint, + const usb_transfer_stage_t stage) +{ + if (stage == USB_TRANSFER_STAGE_SETUP) { + uint8_t reg = endpoint->setup.index; + bool locked = endpoint->setup.value != 0 ? true : false; + if (reg >= RADIO_NUM_REGS) { + return USB_REQUEST_STATUS_STALL; + } + radio_reg_lock(&radio, reg, locked); + usb_transfer_schedule_ack(endpoint->in); + } + + return USB_REQUEST_STATUS_OK; +} diff --git a/firmware/hackrf_usb/usb_api_register.h b/firmware/hackrf_usb/usb_api_register.h index a5a36550d..7e434c1a7 100644 --- a/firmware/hackrf_usb/usb_api_register.h +++ b/firmware/hackrf_usb/usb_api_register.h @@ -70,3 +70,6 @@ usb_request_status_t usb_vendor_request_write_radio_reg( usb_request_status_t usb_vendor_request_read_radio_reg( usb_endpoint_t* const endpoint, const usb_transfer_stage_t stage); +usb_request_status_t usb_vendor_request_lock_radio_reg( + usb_endpoint_t* const endpoint, + const usb_transfer_stage_t stage); diff --git a/firmware/hackrf_usb/usb_api_transceiver.c b/firmware/hackrf_usb/usb_api_transceiver.c index e72a408b9..bb894bdcb 100644 --- a/firmware/hackrf_usb/usb_api_transceiver.c +++ b/firmware/hackrf_usb/usb_api_transceiver.c @@ -486,6 +486,42 @@ usb_request_status_t usb_vendor_request_get_buffer_size( return USB_REQUEST_STATUS_OK; } +usb_request_status_t usb_vendor_request_open( + usb_endpoint_t* const endpoint, + const usb_transfer_stage_t stage) +{ + uint16_t usb_api_version; + radio_config_mode_t radio_config_mode; + + if (stage == USB_TRANSFER_STAGE_SETUP) { + usb_api_version = endpoint->setup.value; + radio_config_mode = (radio_config_mode_t) endpoint->setup.index; + + // TODO let device know we have a new libhackrf connection and its supported usb_api_version + (void) usb_api_version; + + // switch bitstreams and update radio mode + if (!radio_set_config_mode(&radio, radio_config_mode)) { + return USB_REQUEST_STATUS_STALL; + } + usb_transfer_schedule_ack(endpoint->in); + } + + return USB_REQUEST_STATUS_OK; +} + +usb_request_status_t usb_vendor_request_close( + usb_endpoint_t* const endpoint, + const usb_transfer_stage_t stage) +{ + if (stage == USB_TRANSFER_STAGE_SETUP) { + // TODO do nothing for now + usb_transfer_schedule_ack(endpoint->in); + } + + return USB_REQUEST_STATUS_OK; +} + /* clang-format off */ // Which GPDMA channel to use. diff --git a/firmware/hackrf_usb/usb_api_transceiver.h b/firmware/hackrf_usb/usb_api_transceiver.h index a29dec118..a28b8514e 100644 --- a/firmware/hackrf_usb/usb_api_transceiver.h +++ b/firmware/hackrf_usb/usb_api_transceiver.h @@ -80,6 +80,12 @@ usb_request_status_t usb_vendor_request_set_rx_overrun_limit( usb_request_status_t usb_vendor_request_get_buffer_size( usb_endpoint_t* const endpoint, const usb_transfer_stage_t stage); +usb_request_status_t usb_vendor_request_open( + usb_endpoint_t* const endpoint, + const usb_transfer_stage_t stage); +usb_request_status_t usb_vendor_request_close( + usb_endpoint_t* const endpoint, + const usb_transfer_stage_t stage); void request_transceiver_mode(transceiver_mode_t mode); void transceiver_startup(transceiver_mode_t mode); diff --git a/firmware/hackrf_usb/usb_descriptor.c b/firmware/hackrf_usb/usb_descriptor.c index dde22e6e3..726e126a0 100644 --- a/firmware/hackrf_usb/usb_descriptor.c +++ b/firmware/hackrf_usb/usb_descriptor.c @@ -28,7 +28,7 @@ #define USB_VENDOR_ID (0x1D50) -#define USB_API_VERSION (0x0113) +#define USB_API_VERSION (0x0114) #define USB_WORD(x) (x & 0xFF), ((x >> 8) & 0xFF) diff --git a/host/hackrf-tools/src/hackrf_debug.c b/host/hackrf-tools/src/hackrf_debug.c index c009e84cf..f179090f7 100644 --- a/host/hackrf-tools/src/hackrf_debug.c +++ b/host/hackrf-tools/src/hackrf_debug.c @@ -545,6 +545,30 @@ int radio_write_register( return result; } +int radio_lock_register( + hackrf_device* device, + const uint16_t register_number, + const bool register_locked) +{ + int result = HACKRF_SUCCESS; + result = hackrf_radio_lock_register( + device, + (uint8_t) register_number, + register_locked); + + if (result == HACKRF_SUCCESS) { + printf("register [%2d] -> %s\n", + register_number, + register_locked ? "locked" : "unlocked"); + } else { + printf("hackrf_radio_lock_register() failed: %s (%d)\n", + hackrf_error_name(result), + result); + } + + return result; +} + int read_register( hackrf_device* device, uint8_t part, @@ -621,6 +645,20 @@ int write_register( return HACKRF_ERROR_INVALID_PARAM; } +int lock_register( + hackrf_device* device, + uint8_t part, + const uint16_t register_number, + const bool register_locked) +{ + switch (part) { + case PART_RADIO: + return radio_lock_register(device, register_number, register_locked); + default: + return HACKRF_ERROR_INVALID_PARAM; + } +} + static const char* mode_name(uint32_t mode) { const char* mode_names[] = {"IDLE", "WAIT", "RX", "TX_START", "TX_RUN"}; @@ -675,6 +713,7 @@ static void usage() printf("\t-n, --register : set register number for read/write operations\n"); printf("\t-r, --read: read register specified by last -n argument, or all registers\n"); printf("\t-w, --write : write register specified by last -n argument with value \n"); + printf("\t-L, --lock : lock register specified by last -n argument (0 for unlocked, 1 for locked)\n"); printf("\t-c, --config: print SI5351C multisynth configuration information\n"); printf("\t-d, --device : specify a particular device by serial number\n"); printf("\t-m, --max283x: target MAX283x\n"); @@ -710,6 +749,7 @@ static struct option long_options[] = { {"register", required_argument, 0, 'n'}, {"write", required_argument, 0, 'w'}, {"read", no_argument, 0, 'r'}, + {"lock", required_argument, 0, 'L'}, {"device", required_argument, 0, 'd'}, {"help", no_argument, 0, 'h'}, {"max2837", no_argument, 0, 'm'}, @@ -742,10 +782,12 @@ int main(int argc, char** argv) int bank = -1; uint64_t register_number = REGISTER_INVALID; uint64_t register_value; + uint64_t register_locked; hackrf_device* device = NULL; int option_index = 0; bool read = false; bool write = false; + bool lock = false; bool dump_config = false; bool dump_state = false; uint8_t part = PART_NONE; @@ -785,7 +827,7 @@ int main(int argc, char** argv) while ((opt = getopt_long( argc, argv, - "b:n:rw:d:cmsfgi1:2:C:N:P:ST:R:h?u:l:ta:op", + "b:n:rw:L:d:cmsfgi1:2:C:N:P:ST:R:h?u:l:ta:op", long_options, &option_index)) != EOF) { switch (opt) { @@ -808,6 +850,11 @@ int main(int argc, char** argv) read = true; break; + case 'L': + lock = true; + result = parse_int(optarg, ®ister_locked); + break; + case 'c': dump_config = true; break; @@ -938,8 +985,8 @@ int main(int argc, char** argv) } } - if (write && read) { - fprintf(stderr, "Read and write options are mutually exclusive.\n"); + if ((write && read) || (lock && write) || (lock && read)) { + fprintf(stderr, "Read, write and lock options are mutually exclusive.\n"); usage(); return EXIT_FAILURE; } @@ -956,6 +1003,10 @@ int main(int argc, char** argv) return EXIT_FAILURE; } + if (lock && part != PART_RADIO) { + fprintf(stderr, "Lock option is only valid for radio.\n"); + } + if ((bank > -1) && (part != PART_RADIO)) { fprintf(stderr, "Bank valid only for radio.\n"); usage(); @@ -966,11 +1017,11 @@ int main(int argc, char** argv) bank = 0; } - if (!(write || read || dump_config || dump_state || set_tx_limit || + if (!(write || read || lock || dump_config || dump_state || set_tx_limit || set_rx_limit || set_ui || set_leds || set_p1 || set_p2 || set_clkin || set_narrowband || set_fpga_bitstream || read_selftest || test_rtc_osc || read_adc || read_temp)) { - fprintf(stderr, "Specify read, write, or config option.\n"); + fprintf(stderr, "Specify read, write, lock, or config option.\n"); usage(); return EXIT_FAILURE; } @@ -1023,6 +1074,10 @@ int main(int argc, char** argv) } } + if (lock) { + result = lock_register(device, part, register_number, register_locked); + } + if (dump_config) { si5351c_read_configuration(device); } diff --git a/host/hackrf-tools/src/hackrf_sweep.c b/host/hackrf-tools/src/hackrf_sweep.c index 38efa2237..206a29d0f 100644 --- a/host/hackrf-tools/src/hackrf_sweep.c +++ b/host/hackrf-tools/src/hackrf_sweep.c @@ -179,6 +179,8 @@ uint32_t amp_enable; bool antenna = false; uint32_t antenna_enable; +enum radio_config_mode config_mode = RADIO_CONFIG_STANDARD; + bool timestamp_normalized = false; bool binary_output = false; bool ifft_output = false; @@ -390,6 +392,8 @@ static void usage() "Usage:\n" "\t[-h] # this help\n" "\t[-d serial_number] # Serial number of desired HackRF\n" + "\t[-M mode] # Select radio configuration mode.\n" + "\tPossible values: 0=standard, 1=ext_precision_rx, 2=ext_precision_tx, 3=half_precision\n" "\t[-a amp_enable] # RX RF amplifier 1=Enable, 0=Disable\n" "\t[-f freq_min:freq_max] # minimum and maximum frequencies in MHz\n" "\t[-p antenna_enable] # Antenna port power, 1=Enable, 0=Disable\n" @@ -483,6 +487,10 @@ int main(int argc, char** argv) serial_number = optarg; break; + case 'M': + result = parse_u32(optarg, &config_mode); + break; + case 'a': amp = true; result = parse_u32(optarg, &_enable); @@ -724,10 +732,10 @@ int main(int argc, char** argv) return EXIT_FAILURE; } - result = hackrf_open_by_serial(serial_number, &device); + result = hackrf_open_mode_by_serial(config_mode, serial_number, &device); if (result != HACKRF_SUCCESS) { fprintf(stderr, - "hackrf_open() failed: %s (%d)\n", + "hackrf_open_mode_by_serial() failed: %s (%d)\n", hackrf_error_name(result), result); usage(); @@ -765,7 +773,7 @@ int main(int argc, char** argv) fprintf(stderr, "call hackrf_sample_rate_set(%.03f MHz)\n", ((float) DEFAULT_SAMPLE_RATE_HZ / (float) FREQ_ONE_MHZ)); - result = hackrf_set_sample_rate_manual(device, DEFAULT_SAMPLE_RATE_HZ, 1); + result = hackrf_radio_set_sample_rate(device, SR_FP(DEFAULT_SAMPLE_RATE_HZ)); if (result != HACKRF_SUCCESS) { fprintf(stderr, "hackrf_sample_rate_set() failed: %s (%d)\n", diff --git a/host/hackrf-tools/src/hackrf_transfer.c b/host/hackrf-tools/src/hackrf_transfer.c index ef5d68e1d..6940fea47 100644 --- a/host/hackrf-tools/src/hackrf_transfer.c +++ b/host/hackrf-tools/src/hackrf_transfer.c @@ -43,6 +43,7 @@ #include #ifdef _MSC_VER + #include #ifdef _WIN64 typedef int64_t ssize_t; @@ -194,11 +195,38 @@ typedef struct { t_u64toa ascii_u64_data[4]; +typedef struct { + char data[U64TOA_MAX_DIGIT + 1]; +} t_fp64toa; + +t_fp64toa ascii_fp64_data[4]; + static float TimevalDiff(const struct timeval* a, const struct timeval* b) { return (a->tv_sec - b->tv_sec) + 1e-6f * (a->tv_usec - b->tv_usec); } +/** + * (x * y) / z + */ +static uint64_t muldiv_fp64(uint64_t x, uint64_t y, uint64_t z) +{ +#ifdef _MSC_VER + uint64_t hi; + uint64_t lo; + uint64_t remainder; + lo = _umul128(x, y, &hi); + return _udiv128(hi, lo, z, &remainder); +#else + return (uint64_t) (((__uint128_t) x * (__uint128_t) y) / z); +#endif +} + +static uint64_t double_to_fp(double input, uint8_t Qn) +{ + return (uint64_t) round(input * (1ULL << Qn)); +} + int parse_u64(char* s, uint64_t* const value) { uint_fast8_t base = 10; @@ -255,18 +283,19 @@ int parse_u32(char* s, uint32_t* const value) } } -/* Parse frequencies as doubles to take advantage of notation parsing */ -int parse_frequency_i64(char* optarg, char* endptr, int64_t* value) +int parse_double(char* optarg, double* value) { - *value = (int64_t) strtod(optarg, &endptr); + char* endptr; + *value = strtod(optarg, &endptr); if (optarg == endptr) { return HACKRF_ERROR_INVALID_PARAM; } return HACKRF_SUCCESS; } -int parse_frequency_u32(char* optarg, char* endptr, uint32_t* value) +int parse_frequency_u32(char* optarg, uint32_t* value) { + char* endptr; *value = (uint32_t) strtod(optarg, &endptr); if (optarg == endptr) { return HACKRF_ERROR_INVALID_PARAM; @@ -274,6 +303,78 @@ int parse_frequency_u32(char* optarg, char* endptr, uint32_t* value) return HACKRF_SUCCESS; } +/** + * Expand a string in scientific notation to standard form. + */ +static int expand_notation(const char* src, char* dst) +{ + // validate input + char* endptr; + strtod(src, &endptr); + if (endptr == src || *endptr != '\0') { + return HACKRF_ERROR_INVALID_PARAM; + } + + // tokenize input + const char* token_sign = (*src == '-') ? src : NULL; + const char* token_int = (*src == '+' || *src == '-') ? src + 1 : src; + const char* token_dec = strchr(token_int, '.'); + const char* token_exp = strpbrk(token_int, "eE"); + const char* end_src = src + strlen(src); + const char* end_int = token_dec ? token_dec : (token_exp ? token_exp : end_src); + const char* end_dec = token_exp ? token_exp : end_src; + + // calculate positions + int len_int = end_int - token_int; + int len_dec = token_dec ? (end_dec - token_dec - 1) : 0; + int mag = token_exp ? atoi(token_exp + 1) : 0; + int pos_dec = len_int + mag; + int start = (pos_dec <= 0) ? (start = pos_dec - 1) : 0; + int finish = (pos_dec > len_int + len_dec) ? pos_dec : (len_int + len_dec); + + // expand input + char* out = dst; + if (token_sign) { + *out++ = '-'; + } + int i; + for (i = start; i < finish; i++) { + if (i == pos_dec) { + *out++ = '.'; + } + if (i < 0) { + *out++ = '0'; + } else if (i < len_int) { + *out++ = token_int[i]; + } else if (i < len_int + len_dec) { + *out++ = token_dec[1 + i - len_int]; + } else { + *out++ = '0'; + } + } + *out = '\0'; + + return HACKRF_SUCCESS; +} + +int parse_frequency_fp(char* optarg, fp_40_24_t* value) +{ + char buf[64]; + if (expand_notation(optarg, buf) != HACKRF_SUCCESS) { + return HACKRF_ERROR_INVALID_PARAM; + } + return hackrf_str_to_fp64(24, buf, value); +} + +int parse_sample_rate_fp(char* optarg, fp_28_36_t* value) +{ + char buf[64]; + if (expand_notation(optarg, buf) != HACKRF_SUCCESS) { + return HACKRF_ERROR_INVALID_PARAM; + } + return hackrf_str_to_fp64(36, buf, value); +} + static char* stringrev(char* str) { char *p1, *p2; @@ -319,6 +420,51 @@ char* u64toa(uint64_t val, t_u64toa* str) return res; } +char* fp64toa(uint8_t Qn, uint64_t value, t_fp64toa* str) +{ + uint64_t mask = (1ULL << Qn) - 1; + uint64_t m = value >> Qn; + uint64_t n = (Qn == 0) ? 0 : value & mask; + uint8_t precision = ceil((double) Qn * log10(2.0)); + + // format integer component + char* p = str->data; + p += sprintf(p, "%" PRIu64, m); + + if (precision == 0 || n == 0) { + *p = '\0'; + return str->data; + } + *p++ = '.'; + + // format fractional component + uint8_t i; + for (i = 0; i < precision; i++) { + n *= 10; + char c = '0' + (n >> Qn); + *p++ = c; + n &= mask; + } + + // trim trailing zeros + while (p > str->data && p[-1] == '0') { + --p; + } + *p = '\0'; + + return str->data; +} + +char* frequency_fp64toa(uint64_t value, t_fp64toa* str) +{ + return fp64toa(24, value, str); +} + +char* sample_rate_fp64toa(uint64_t value, t_fp64toa* str) +{ + return fp64toa(36, value, str); +} + static volatile bool do_exit = false; static volatile bool interrupted = false; static volatile bool tx_complete = false; @@ -351,13 +497,13 @@ struct timeval time_start; struct timeval t_start; bool automatic_tuning = false; -int64_t freq_hz; +fp_40_24_t freq_fp_hz; bool if_freq = false; -int64_t if_freq_hz; +fp_40_24_t if_freq_fp_hz; bool lo_freq = false; -int64_t lo_freq_hz = DEFAULT_LO_HZ; +fp_40_24_t lo_freq_fp_hz = FREQ_FP(DEFAULT_LO_HZ); bool image_reject = false; uint32_t image_reject_selection; @@ -369,7 +515,7 @@ bool antenna = false; uint32_t antenna_enable; bool sample_rate = false; -uint32_t sample_rate_hz; +fp_28_36_t sample_rate_fp_hz; bool force_ranges = false; @@ -385,10 +531,12 @@ uint32_t baseband_filter_bw_hz = 0; bool repeat = false; bool crystal_correct = false; -uint32_t crystal_correct_ppm; +double crystal_correct_ppm; int requested_mode_count = 0; +enum radio_config_mode config_mode = RADIO_CONFIG_STANDARD; + void stop_main_loop(void) { do_exit = true; @@ -692,6 +840,8 @@ static void usage() printf("\tPossible values: 1.75/2.5/3.5/5/5.5/6/7/8/9/10/12/14/15/20/24/28MHz, default <= 0.75 * sample_rate_hz.\n"); printf("\t[-C ppm] # Set Internal crystal clock error in ppm.\n"); printf("\t[-H] # Synchronize RX/TX to external trigger input.\n"); + printf("\t[-M mode] # Select radio configuration mode.\n"); + printf("\tPossible values: 0=standard, 1=ext_precision_rx, 2=ext_precision_tx, 3=half_precision\n"); } static hackrf_device* device = NULL; @@ -744,8 +894,10 @@ int main(int argc, char** argv) hackrf_m0_state state; stats_t stats = {0, 0}; - while ((opt = getopt(argc, argv, "Hwr:t:f:i:o:m:a:p:s:Fn:b:l:g:x:c:d:C:RS:Bh?")) != - EOF) { + while ((opt = + getopt(argc, + argv, + "Hwr:t:f:i:o:m:a:p:s:Fn:b:l:g:x:c:d:C:M:RS:Bh?")) != EOF) { result = HACKRF_SUCCESS; switch (opt) { case 'H': @@ -778,17 +930,17 @@ int main(int argc, char** argv) break; case 'f': - result = parse_frequency_i64(optarg, endptr, &freq_hz); + result = parse_frequency_fp(optarg, &freq_fp_hz); automatic_tuning = true; break; case 'i': - result = parse_frequency_i64(optarg, endptr, &if_freq_hz); + result = parse_frequency_fp(optarg, &if_freq_fp_hz); if_freq = true; break; case 'o': - result = parse_frequency_i64(optarg, endptr, &lo_freq_hz); + result = parse_frequency_fp(optarg, &lo_freq_fp_hz); lo_freq = true; break; @@ -820,7 +972,7 @@ int main(int argc, char** argv) break; case 's': - result = parse_frequency_u32(optarg, endptr, &sample_rate_hz); + result = parse_sample_rate_fp(optarg, &sample_rate_fp_hz); sample_rate = true; break; @@ -839,10 +991,7 @@ int main(int argc, char** argv) break; case 'b': - result = parse_frequency_u32( - optarg, - endptr, - &baseband_filter_bw_hz); + result = parse_frequency_u32(optarg, &baseband_filter_bw_hz); baseband_filter_bw = true; break; @@ -858,7 +1007,11 @@ int main(int argc, char** argv) case 'C': crystal_correct = true; - result = parse_u32(optarg, &crystal_correct_ppm); + result = parse_double(optarg, &crystal_correct_ppm); + break; + + case 'M': + result = parse_u32(optarg, &config_mode); break; case 'h': @@ -919,7 +1072,8 @@ int main(int argc, char** argv) usage(); return EXIT_FAILURE; } - if (((if_freq_hz > IF_MAX_HZ) || (if_freq_hz < IF_MIN_HZ)) && + if (((FP_FREQ(if_freq_fp_hz) > IF_MAX_HZ) || + (FP_FREQ(if_freq_fp_hz) < IF_MIN_HZ)) && !force_ranges) { fprintf(stderr, "argument error: if_freq_hz should be between %s and %s.\n", @@ -928,7 +1082,8 @@ int main(int argc, char** argv) usage(); return EXIT_FAILURE; } - if ((if_freq_hz > IF_ABS_MAX_HZ) || (if_freq_hz < IF_ABS_MIN_HZ)) { + if ((FP_FREQ(if_freq_fp_hz) > IF_ABS_MAX_HZ) || + (FP_FREQ(if_freq_fp_hz) < IF_ABS_MIN_HZ)) { fprintf(stderr, "argument error: if_freq_hz must be between %s and %s.\n", u64toa(IF_ABS_MIN_HZ, &ascii_u64_data[0]), @@ -936,7 +1091,8 @@ int main(int argc, char** argv) usage(); return EXIT_FAILURE; } - if ((lo_freq_hz > LO_MAX_HZ) || (lo_freq_hz < LO_MIN_HZ)) { + if ((FP_FREQ(lo_freq_fp_hz) > LO_MAX_HZ) || + (FP_FREQ(lo_freq_fp_hz) < LO_MIN_HZ)) { fprintf(stderr, "argument error: lo_freq_hz shall be between %s and %s.\n", u64toa(LO_MIN_HZ, &ascii_u64_data[0]), @@ -957,24 +1113,27 @@ int main(int argc, char** argv) } switch (image_reject_selection) { case RF_PATH_FILTER_BYPASS: - freq_hz = if_freq_hz; + freq_fp_hz = if_freq_fp_hz; break; case RF_PATH_FILTER_LOW_PASS: - freq_hz = (int64_t) labs((long int) (if_freq_hz - lo_freq_hz)); + freq_fp_hz = (if_freq_fp_hz > lo_freq_fp_hz) ? + if_freq_fp_hz - lo_freq_fp_hz : + lo_freq_fp_hz - if_freq_fp_hz; break; case RF_PATH_FILTER_HIGH_PASS: - freq_hz = if_freq_hz + lo_freq_hz; + freq_fp_hz = if_freq_fp_hz + lo_freq_fp_hz; break; default: - freq_hz = DEFAULT_FREQ_HZ; + freq_fp_hz = FREQ_FP(DEFAULT_FREQ_HZ); break; } fprintf(stderr, "explicit tuning specified for %s Hz.\n", - u64toa(freq_hz, &ascii_u64_data[0])); + frequency_fp64toa(freq_fp_hz, &ascii_fp64_data[0])); } else if (automatic_tuning) { - if (((freq_hz > FREQ_MAX_HZ) || (freq_hz < FREQ_MIN_HZ)) && + if (((FP_FREQ(freq_fp_hz) > FREQ_MAX_HZ) || + (FP_FREQ(freq_fp_hz) < FREQ_MIN_HZ)) && !force_ranges) { fprintf(stderr, "argument error: freq_hz should be between %s and %s.\n", @@ -983,7 +1142,7 @@ int main(int argc, char** argv) usage(); return EXIT_FAILURE; } - if (freq_hz > FREQ_ABS_MAX_HZ) { + if (FP_FREQ(freq_fp_hz) > FREQ_ABS_MAX_HZ) { fprintf(stderr, "argument error: freq_hz must be between %s and %s.\n", u64toa(FREQ_ABS_MIN_HZ, &ascii_u64_data[0]), @@ -993,7 +1152,7 @@ int main(int argc, char** argv) } } else { /* Use default freq */ - freq_hz = DEFAULT_FREQ_HZ; + freq_fp_hz = FREQ_FP(DEFAULT_FREQ_HZ); automatic_tuning = true; } @@ -1015,7 +1174,10 @@ int main(int argc, char** argv) } if (sample_rate) { - if (sample_rate_hz > SAMPLE_RATE_MAX_HZ && !force_ranges) { + fprintf(stderr, + "Setting sample rate to: %s Hz\n", + sample_rate_fp64toa(sample_rate_fp_hz, &ascii_fp64_data[0])); + if (FP_SR(sample_rate_fp_hz) > SAMPLE_RATE_MAX_HZ && !force_ranges) { fprintf(stderr, "argument error: sample_rate_hz should be less than or equal to %u Hz/%.03f MHz\n", SAMPLE_RATE_MAX_HZ, @@ -1023,7 +1185,7 @@ int main(int argc, char** argv) usage(); return EXIT_FAILURE; } - if (sample_rate_hz < SAMPLE_RATE_MIN_HZ && !force_ranges) { + if (FP_SR(sample_rate_fp_hz) < SAMPLE_RATE_MIN_HZ && !force_ranges) { fprintf(stderr, "argument error: sample_rate_hz should be greater than or equal to %u Hz/%.03f MHz\n", SAMPLE_RATE_MIN_HZ, @@ -1032,7 +1194,7 @@ int main(int argc, char** argv) return EXIT_FAILURE; } } else { - sample_rate_hz = DEFAULT_SAMPLE_RATE_HZ; + sample_rate_fp_hz = SR_FP(DEFAULT_SAMPLE_RATE_HZ); } if (baseband_filter_bw) { @@ -1100,7 +1262,7 @@ int main(int argc, char** argv) PATH_FILE_MAX_LEN, "HackRF_%sZ_%ukHz_IQ.wav", date_time, - (uint32_t) (freq_hz / (1000ull))); + (uint32_t) (FP_FREQ(freq_fp_hz) / (1000ull))); path = path_file; fprintf(stderr, "Receive wav file: %s\n", path); } @@ -1116,9 +1278,14 @@ int main(int argc, char** argv) // Change the freq and sample rate to correct the crystal clock error. if (crystal_correct) { - sample_rate_hz = - (uint32_t) ((double) sample_rate_hz * (1000000 - crystal_correct_ppm) / 1000000 + 0.5); - freq_hz = freq_hz * (1000000 - crystal_correct_ppm) / 1000000; + sample_rate_fp_hz = (fp_28_36_t) muldiv_fp64( + sample_rate_fp_hz, + (fp_28_36_t) double_to_fp(1000000.0 - crystal_correct_ppm, 36), + (fp_28_36_t) SR_FP(1000000)); + freq_fp_hz = (fp_40_24_t) muldiv_fp64( + freq_fp_hz, + (fp_40_24_t) double_to_fp(1000000.0 - crystal_correct_ppm, 24), + (fp_40_24_t) FREQ_FP(1000000)); } result = hackrf_init(); @@ -1131,10 +1298,10 @@ int main(int argc, char** argv) return EXIT_FAILURE; } - result = hackrf_open_by_serial(serial_number, &device); + result = hackrf_open_mode_by_serial(config_mode, serial_number, &device); if (result != HACKRF_SUCCESS) { fprintf(stderr, - "hackrf_open() failed: %s (%d)\n", + "hackrf_open_mode_by_serial() failed: %s (%d)\n", hackrf_error_name(result), result); usage(); @@ -1192,13 +1359,13 @@ int main(int argc, char** argv) #endif fprintf(stderr, - "call hackrf_set_sample_rate(%u Hz/%.03f MHz)\n", - sample_rate_hz, - ((float) sample_rate_hz / (float) FREQ_ONE_MHZ)); - result = hackrf_set_sample_rate(device, sample_rate_hz); + "call hackrf_radio_set_sample_rate(%s Hz/%.03f MHz)\n", + sample_rate_fp64toa(sample_rate_fp_hz, &ascii_fp64_data[0]), + ((float) FP_SR(sample_rate_fp_hz) / (float) FREQ_ONE_MHZ)); + result = hackrf_radio_set_sample_rate(device, sample_rate_fp_hz); if (result != HACKRF_SUCCESS) { fprintf(stderr, - "hackrf_set_sample_rate() failed: %s (%d)\n", + "hackrf_radio_set_sample_rate() failed: %s (%d)\n", hackrf_error_name(result), result); usage(); @@ -1246,13 +1413,13 @@ int main(int argc, char** argv) if (automatic_tuning) { fprintf(stderr, - "call hackrf_set_freq(%s Hz/%.03f MHz)\n", - u64toa(freq_hz, &ascii_u64_data[0]), - ((double) freq_hz / (double) FREQ_ONE_MHZ)); - result = hackrf_set_freq(device, freq_hz); + "call hackrf_radio_set_frequency(%s Hz/%.03f MHz)\n", + frequency_fp64toa(freq_fp_hz, &ascii_fp64_data[0]), + ((float) FP_FREQ(freq_fp_hz) / (float) FREQ_ONE_MHZ)); + result = hackrf_radio_set_frequency(device, freq_fp_hz); if (result != HACKRF_SUCCESS) { fprintf(stderr, - "hackrf_set_freq() failed: %s (%d)\n", + "hackrf_radio_set_frequency() failed: %s (%d)\n", hackrf_error_name(result), result); usage(); @@ -1261,13 +1428,13 @@ int main(int argc, char** argv) } else { fprintf(stderr, "call hackrf_set_freq_explicit() with %s Hz IF, %s Hz LO, %s\n", - u64toa(if_freq_hz, &ascii_u64_data[0]), - u64toa(lo_freq_hz, &ascii_u64_data[1]), + frequency_fp64toa(if_freq_fp_hz, &ascii_fp64_data[0]), + frequency_fp64toa(lo_freq_fp_hz, &ascii_fp64_data[0]), hackrf_filter_path_name(image_reject_selection)); - result = hackrf_set_freq_explicit( + result = hackrf_radio_set_frequency_explicit( device, - if_freq_hz, - lo_freq_hz, + if_freq_fp_hz, + lo_freq_fp_hz, image_reject_selection); if (result != HACKRF_SUCCESS) { fprintf(stderr, @@ -1532,7 +1699,8 @@ int main(int argc, char** argv) file_pos = ftell(file); /* Update Wav Header */ wave_file_hdr.hdr.size = file_pos - 8; - wave_file_hdr.fmt_chunk.dwSamplesPerSec = sample_rate_hz; + wave_file_hdr.fmt_chunk.dwSamplesPerSec = + FP_SR(sample_rate_fp_hz); wave_file_hdr.fmt_chunk.dwAvgBytesPerSec = wave_file_hdr.fmt_chunk.dwSamplesPerSec * 2; wave_file_hdr.data_chunk.chunkSize = diff --git a/host/libhackrf/src/hackrf.c b/host/libhackrf/src/hackrf.c index 2efbfea42..55b767817 100644 --- a/host/libhackrf/src/hackrf.c +++ b/host/libhackrf/src/hackrf.c @@ -122,6 +122,13 @@ typedef enum { HACKRF_VENDOR_REQUEST_RADIO_READ_REG = 60, HACKRF_VENDOR_REQUEST_GET_BUFFER_SIZE = 61, HACKRF_VENDOR_REQUEST_READ_TEMPERATURE = 62, + HACKRF_VENDOR_REQUEST_RADIO_LOCK_REG = 63, + HACKRF_VENDOR_REQUEST_OPEN = 64, + HACKRF_VENDOR_REQUEST_CLOSE = 65, + HACKRF_VENDOR_REQUEST_RADIO_SET_MODE = 66, + HACKRF_VENDOR_REQUEST_RADIO_SET_FREQUENCY = 67, + HACKRF_VENDOR_REQUEST_RADIO_SET_FREQUENCY_EXPLICIT = 68, + HACKRF_VENDOR_REQUEST_RADIO_SET_SAMPLE_RATE = 69, } hackrf_vendor_request; #define USB_CONFIG_STANDARD 0x1 @@ -199,6 +206,8 @@ static const max2837_ft_t max2837_ft[] = { if (device->usb_api_version < version) \ return HACKRF_ERROR_USB_API_VERSION; +#define USB_API_REQUIRED_OR(device, version) if (device->usb_api_version < version) + static const uint16_t hackrf_usb_vid = 0x1d50; static const uint16_t hackrf_jawbreaker_usb_pid = 0x604b; static const uint16_t hackrf_one_usb_pid = 0x6089; @@ -715,7 +724,10 @@ libusb_device_handle* hackrf_open_usb(const char* const desired_serial_number) return usb_device; } -static int hackrf_open_setup(libusb_device_handle* usb_device, hackrf_device** device) +static int hackrf_open_setup( + enum radio_config_mode mode, + libusb_device_handle* usb_device, + hackrf_device** device) { int result; hackrf_device* lib_device; @@ -797,6 +809,24 @@ static int hackrf_open_setup(libusb_device_handle* usb_device, hackrf_device** d lib_device->buffer_size = 32768; } + if (lib_device->usb_api_version >= 0x0114) { + // Send supported usb api version and requested configuration mode to device. + result = libusb_control_transfer( + lib_device->usb_device, + LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_VENDOR | + LIBUSB_RECIPIENT_DEVICE, + HACKRF_VENDOR_REQUEST_OPEN, + lib_device->usb_api_version, + (uint16_t) mode, + NULL, + 0, + FPGA_BITSTREAM_TIMEOUT); + if (result != 0) { + last_libusb_error = result; + return HACKRF_ERROR_LIBUSB; + } + } + result = pthread_mutex_init(&lib_device->transfer_lock, NULL); if (result != 0) { free(lib_device); @@ -835,7 +865,7 @@ static int hackrf_open_setup(libusb_device_handle* usb_device, hackrf_device** d return HACKRF_SUCCESS; } -int ADDCALL hackrf_open(hackrf_device** device) +int ADDCALL hackrf_open_mode(const enum radio_config_mode mode, hackrf_device** device) { libusb_device_handle* usb_device; @@ -866,17 +896,18 @@ int ADDCALL hackrf_open(hackrf_device** device) return HACKRF_ERROR_NOT_FOUND; } - return hackrf_open_setup(usb_device, device); + return hackrf_open_setup(mode, usb_device, device); } -int ADDCALL hackrf_open_by_serial( +int ADDCALL hackrf_open_mode_by_serial( + const enum radio_config_mode mode, const char* const desired_serial_number, hackrf_device** device) { libusb_device_handle* usb_device; if (desired_serial_number == NULL) { - return hackrf_open(device); + return hackrf_open_mode(mode, device); } if (device == NULL) { @@ -889,10 +920,11 @@ int ADDCALL hackrf_open_by_serial( return HACKRF_ERROR_NOT_FOUND; } - return hackrf_open_setup(usb_device, device); + return hackrf_open_setup(mode, usb_device, device); } -int ADDCALL hackrf_device_list_open( +int ADDCALL hackrf_device_list_open_mode( + const enum radio_config_mode mode, hackrf_device_list_t* list, int idx, hackrf_device** device) @@ -913,7 +945,30 @@ int ADDCALL hackrf_device_list_open( return HACKRF_ERROR_LIBUSB; } - return hackrf_open_setup(usb_device, device); + return hackrf_open_setup(mode, usb_device, device); +} + +int ADDCALL hackrf_open(hackrf_device** device) +{ + return hackrf_open_mode(RADIO_CONFIG_STANDARD, device); +} + +int ADDCALL hackrf_open_by_serial( + const char* const desired_serial_number, + hackrf_device** device) +{ + return hackrf_open_mode_by_serial( + RADIO_CONFIG_STANDARD, + desired_serial_number, + device); +} + +int ADDCALL hackrf_device_list_open( + hackrf_device_list_t* list, + int idx, + hackrf_device** device) +{ + return hackrf_device_list_open_mode(RADIO_CONFIG_STANDARD, list, idx, device); } int ADDCALL hackrf_device_list_bus_sharing(hackrf_device_list_t* list, int idx) @@ -2466,10 +2521,11 @@ int ADDCALL hackrf_stop_tx(hackrf_device* device) int ADDCALL hackrf_close(hackrf_device* device) { - int result1, result2; + int result1, result2, result3; result1 = HACKRF_SUCCESS; result2 = HACKRF_SUCCESS; + result3 = HACKRF_SUCCESS; if (device != NULL) { result1 = hackrf_stop_cmd(device); @@ -2479,6 +2535,27 @@ int ADDCALL hackrf_close(hackrf_device* device) * also cancel any pending transmit/receive transfers. */ result2 = kill_transfer_thread(device); + + if (device->usb_api_version >= 0x0114) { + /* + * Let the device know it's been closed. + */ + result3 = libusb_control_transfer( + device->usb_device, + LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_VENDOR | + LIBUSB_RECIPIENT_DEVICE, + HACKRF_VENDOR_REQUEST_CLOSE, + 0, + 0, + NULL, + 0, + DEFAULT_REQUEST_TIMEOUT); + if (result3 != 0) { + last_libusb_error = result3; + result3 = HACKRF_ERROR_LIBUSB; + } + } + if (device->usb_device != NULL) { libusb_release_interface(device->usb_device, 0); libusb_close(device->usb_device); @@ -2497,7 +2574,12 @@ int ADDCALL hackrf_close(hackrf_device* device) if (result2 != HACKRF_SUCCESS) { return result2; } - return result1; + + if (result1 != HACKRF_SUCCESS) { + return result1; + } + + return result3; } const char* ADDCALL hackrf_error_name(enum hackrf_error errcode) @@ -3598,6 +3680,234 @@ int ADDCALL hackrf_radio_write_register( } } +int ADDCALL hackrf_radio_lock_register( + hackrf_device* device, + const uint8_t register_number, + const bool register_locked) +{ + USB_API_REQUIRED(device, 0x0114); + int result; + + result = libusb_control_transfer( + device->usb_device, + LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_VENDOR | + LIBUSB_RECIPIENT_DEVICE, + HACKRF_VENDOR_REQUEST_RADIO_LOCK_REG, + register_locked, + register_number, + NULL, + 0, + DEFAULT_REQUEST_TIMEOUT); + + if (result != 0) { + last_libusb_error = result; + return HACKRF_ERROR_LIBUSB; + } + + return HACKRF_SUCCESS; +} + +int ADDCALL hackrf_radio_set_mode(hackrf_device* device, const enum radio_config_mode mode) +{ + USB_API_REQUIRED(device, 0x0114); + int result; + + result = libusb_control_transfer( + device->usb_device, + LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_VENDOR | + LIBUSB_RECIPIENT_DEVICE, + HACKRF_VENDOR_REQUEST_RADIO_SET_MODE, + mode, + 0, + NULL, + 0, + DEFAULT_REQUEST_TIMEOUT); + + if (result != 0) { + last_libusb_error = result; + return HACKRF_ERROR_LIBUSB; + } + + return HACKRF_SUCCESS; +} + +int ADDCALL hackrf_str_to_fp64(const uint8_t Qn, const char* str, uint64_t* const value) +{ + uint64_t m = 0; + uint64_t n = 0; + uint64_t div = 1; + uint64_t div_max = ceil((double) Qn * log10(2.0)) * 10; + + // validate input + char* endptr; + strtod(str, &endptr); + if (endptr == str || *endptr != '\0') { + return HACKRF_ERROR_INVALID_PARAM; + } + + // parse sign + bool negative = false; + if (*str == '-' || *str == '+') { + negative = (*str == '-'); + str++; + } + + // parse integer component + for (; (*str >= '0') && (*str <= '9'); str++) { + m = (m * 10) + (*str - '0'); + } + *value = (m << Qn) * (negative ? -1 : 1); + + if (*str != '.') { + return HACKRF_SUCCESS; + } + str++; + + // parse fractional component + for (; (*str >= '0') && (*str <= '9') && (div < div_max); str++) { + n = (n * 10) + (*str - '0'); + div *= 10; + } + + if (div == 1) { + return HACKRF_SUCCESS; + } + + *value += ((n << Qn) + (div / 2)) / div; + + return HACKRF_SUCCESS; +} + +int ADDCALL hackrf_radio_set_frequency(hackrf_device* device, const fp_40_24_t freq_hz) +{ + USB_API_REQUIRED_OR(device, 0x0114) + { + return hackrf_set_freq(device, FP_FREQ(freq_hz)); + } + + uint64_t set_freq_fp_param; + uint8_t length; + int result; + + // serialize parameters + set_freq_fp_param = TO_LE64(freq_hz); + length = sizeof(uint64_t); + + result = libusb_control_transfer( + device->usb_device, + LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_VENDOR | + LIBUSB_RECIPIENT_DEVICE, + HACKRF_VENDOR_REQUEST_RADIO_SET_FREQUENCY, + 0, + 0, + (unsigned char*) &set_freq_fp_param, + length, + DEFAULT_REQUEST_TIMEOUT); + + if (result < length) { + last_libusb_error = result; + return HACKRF_ERROR_LIBUSB; + } + + return HACKRF_SUCCESS; +} + +int ADDCALL hackrf_radio_set_frequency_explicit( + hackrf_device* device, + const fp_40_24_t if_freq_hz, + const fp_40_24_t lo_freq_hz, + const enum rf_path_filter path) +{ + USB_API_REQUIRED_OR(device, 0x0114) + { + return hackrf_set_freq_explicit( + device, + FP_FREQ(if_freq_hz), + FP_FREQ(lo_freq_hz), + path); + } + + struct { + fp_40_24_t if_freq_hz; + fp_40_24_t lo_freq_hz; + uint8_t path; + } params; + + uint8_t length; + int result; + + // TODO are these values still correct for HackRF Pro ? + if (FP_FREQ(if_freq_hz) < 2000000000 || FP_FREQ(if_freq_hz) > 3000000000) { + return HACKRF_ERROR_INVALID_PARAM; + } + + // TODO are these values still correct for HackRF Pro ? + if ((path != RF_PATH_FILTER_BYPASS) && + (FP_FREQ(lo_freq_hz) < 84375000 || FP_FREQ(lo_freq_hz) > 5400000000)) { + return HACKRF_ERROR_INVALID_PARAM; + } + + if (path > 2) { + return HACKRF_ERROR_INVALID_PARAM; + } + + params.if_freq_hz = TO_LE(if_freq_hz); + params.lo_freq_hz = TO_LE(lo_freq_hz); + params.path = (uint8_t) path; + length = sizeof(params); + + result = libusb_control_transfer( + device->usb_device, + LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_VENDOR | + LIBUSB_RECIPIENT_DEVICE, + HACKRF_VENDOR_REQUEST_RADIO_SET_FREQUENCY_EXPLICIT, + 0, + 0, + (unsigned char*) ¶ms, + length, + DEFAULT_REQUEST_TIMEOUT); + + if (result < length) { + last_libusb_error = result; + return HACKRF_ERROR_LIBUSB; + } else { + return HACKRF_SUCCESS; + } +} + +int ADDCALL hackrf_radio_set_sample_rate(hackrf_device* device, const fp_28_36_t freq_hz) +{ + USB_API_REQUIRED_OR(device, 0x0114) + { + return hackrf_set_sample_rate(device, (double) freq_hz / (1ULL << 36)); + } + + uint64_t set_sr_fp_param; + uint8_t length; + int result; + + set_sr_fp_param = TO_LE64(freq_hz); + length = sizeof(uint64_t); + + result = libusb_control_transfer( + device->usb_device, + LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_VENDOR | + LIBUSB_RECIPIENT_DEVICE, + HACKRF_VENDOR_REQUEST_RADIO_SET_SAMPLE_RATE, + 0, + 0, + (unsigned char*) &set_sr_fp_param, + length, + DEFAULT_REQUEST_TIMEOUT); + + if (result < length) { + last_libusb_error = result; + return HACKRF_ERROR_LIBUSB; + } + + return HACKRF_SUCCESS; +} + #ifdef __cplusplus } // __cplusplus defined. #endif diff --git a/host/libhackrf/src/hackrf.h b/host/libhackrf/src/hackrf.h index d9322d3c2..75b1edb86 100644 --- a/host/libhackrf/src/hackrf.h +++ b/host/libhackrf/src/hackrf.h @@ -23,6 +23,7 @@ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSI #pragma once +#include #include #include #include // for bool @@ -946,6 +947,18 @@ enum clkin_ctrl_signal { CLKIN_SIGNAL_P22 = 1, }; +/** + * HackRF Pro Radio Configuration Mode. + * + * Used by @ref hackrf_open, @ref hackrf_open_mode_by_serial and @ref hackrf_device_list_open_mode to set the active configuration mode. + */ +enum radio_config_mode { + RADIO_CONFIG_STANDARD = 0, + RADIO_CONFIG_EXT_PRECISION_RX = 1, + RADIO_CONFIG_EXT_PRECISION_TX = 2, + RADIO_CONFIG_HALF_PRECISION = 3, +}; + /** * Opaque struct for hackrf device info. Object can be created via @ref hackrf_open, @ref hackrf_device_list_open or @ref hackrf_open_by_serial and be destroyed via @ref hackrf_close * @ingroup device @@ -1199,6 +1212,7 @@ extern ADDAPI hackrf_device_list_t* ADDCALL hackrf_device_list(); /** * Open a @ref hackrf_device from a device list + * @deprecated this function has been replaced by @ref hackrf_device_list_open_mode * @param[in] list device list to open device from * @param[in] idx index of the device to open * @param[out] device device handle to open @@ -1210,6 +1224,24 @@ extern ADDAPI int ADDCALL hackrf_device_list_open( int idx, hackrf_device** device); +/** + * Open a @ref hackrf_device from a device list and initialize it to the given radio configuration mode. + * + * Modes other than RADIO_CONFIG_STANDARD are only supported on HackRF Pro hardware. + * + * @param[in] mode configuration mode. Defaults to RADIO_CONFIG_STANDARD. Available modes are defined in @ref radio_config_mode. + * @param[in] list device list to open device from + * @param[in] idx index of the device to open + * @param[out] device device handle to open + * @return @ref HACKRF_SUCCESS on success, @ref HACKRF_ERROR_INVALID_PARAM on invalid parameters or other @ref hackrf_error variant + * @ingroup device + */ +extern ADDAPI int ADDCALL hackrf_device_list_open_mode( + const enum radio_config_mode mode, + hackrf_device_list_t* list, + int idx, + hackrf_device** device); + /** * Check if a listed HackRF device is sharing its USB bus with other devices. * @@ -1231,14 +1263,30 @@ extern ADDAPI void ADDCALL hackrf_device_list_free(hackrf_device_list_t* list); /** * Open first available HackRF device + * @deprecated this function has been replaced by @ref hackrf_open_mode * @param[out] device device handle * @return @ref HACKRF_SUCCESS on success, @ref HACKRF_ERROR_INVALID_PARAM if @p device is NULL, @ref HACKRF_ERROR_NOT_FOUND if no HackRF devices are found or other @ref hackrf_error variant * @ingroup device */ extern ADDAPI int ADDCALL hackrf_open(hackrf_device** device); +/** + * Open first available HackRF device and initialize it to the given radio configuration mode. + * + * Modes other than RADIO_CONFIG_STANDARD are only supported on HackRF Pro hardware. + * + * @param[in] mode configuration mode. Defaults to RADIO_CONFIG_STANDARD. Available modes are defined in @ref radio_config_mode. + * @param[out] device device handle + * @return @ref HACKRF_SUCCESS on success, @ref HACKRF_ERROR_INVALID_PARAM if @p device is NULL, @ref HACKRF_ERROR_NOT_FOUND if no HackRF devices are found or other @ref hackrf_error variant + * @ingroup device + */ +extern ADDAPI int ADDCALL hackrf_open_mode( + const enum radio_config_mode mode, + hackrf_device** device); + /** * Open HackRF device by serial number + * @deprecated this function has been replaced by @ref hackrf_open_mode_by_serial * @param[in] desired_serial_number serial number of device to open. If NULL then default to first device found. * @param[out] device device handle * @return @ref HACKRF_SUCCESS on success, @ref HACKRF_ERROR_INVALID_PARAM if @p device is NULL, @ref HACKRF_ERROR_NOT_FOUND if no HackRF devices are found or other @ref hackrf_error variant @@ -1248,6 +1296,22 @@ extern ADDAPI int ADDCALL hackrf_open_by_serial( const char* const desired_serial_number, hackrf_device** device); +/** + * Open HackRF device by serial number and initialize it to the given radio configuration mode. + * + * Modes other than RADIO_CONFIG_STANDARD are only supported on HackRF Pro hardware. + * + * @param[in] mode configuration mode. Defaults to RADIO_CONFIG_STANDARD. Available modes are defined in @ref radio_config_mode. + * @param[in] desired_serial_number serial number of device to open. If NULL then default to first device found. + * @param[out] device device handle + * @return @ref HACKRF_SUCCESS on success, @ref HACKRF_ERROR_INVALID_PARAM if @p device is NULL, @ref HACKRF_ERROR_NOT_FOUND if no HackRF devices are found or other @ref hackrf_error variant + * @ingroup device + */ +extern ADDAPI int ADDCALL hackrf_open_mode_by_serial( + const enum radio_config_mode mode, + const char* const desired_serial_number, + hackrf_device** device); + /** * Close a previously opened device * @param[in] device device to close @@ -1762,7 +1826,8 @@ extern ADDAPI int ADDCALL hackrf_usb_api_version_read( * Simple (auto) tuning via specifying a center frequency in Hz * * This setting is not exact and depends on the PLL settings. Exact resolution is not determined, but the actual tuned frequency will be queryable in the future. - * + * + * @deprecated this function has been replaced by @ref hackrf_radio_set_frequency * @param device device to tune * @param freq_hz center frequency in Hz. Defaults to 900MHz. Should be in range 1-6000MHz, but 0-7250MHz is possible. The resolution is ~50Hz, I could not find the exact number. * @return @ref HACKRF_SUCCESS on success or @ref hackrf_error variant @@ -1774,7 +1839,8 @@ extern ADDAPI int ADDCALL hackrf_set_freq(hackrf_device* device, const uint64_t * Set the center frequency via explicit tuning * * Center frequency is set to \f$f_{center} = f_{IF} + k\cdot f_{LO}\f$ where \f$k\in\left\{-1; 0; 1\right\}\f$, depending on the value of @p path. See the documentation of @ref rf_path_filter for details - * + * + * @deprecated this function has been replaced by @ref hackrf_radio_set_frequency_explicit * @param device device to tune * @param if_freq_hz tuning frequency of the MAX2837 transceiver IC in Hz. Must be in the range of 2150-2750MHz * @param lo_freq_hz tuning frequency of the RFFC5072 mixer/synthesizer IC in Hz. Must be in the range 84.375-5400MHz, defaults to 1000MHz. No effect if @p path is set to @ref RF_PATH_FILTER_BYPASS @@ -1795,7 +1861,8 @@ extern ADDAPI int ADDCALL hackrf_set_freq_explicit( * * This function sets the sample rate by specifying a clock frequency in Hz and a divider, so the resulting sample rate will be @p freq_hz / @p divider. * This function also sets the baseband filter bandwidth to a value \f$ \le 0.75 \cdot F_s \f$, so any calls to @ref hackrf_set_baseband_filter_bandwidth should only be made after this. - * + * + * @deprecated this function has been replaced by @ref hackrf_radio_set_sample_rate * @param device device to configure * @param freq_hz sample rate base frequency in Hz * @param divider frequency divider. Must be in the range 1-31 @@ -1813,6 +1880,7 @@ extern ADDAPI int ADDCALL hackrf_set_sample_rate_manual( * Sample rate should be in the range 2-20MHz, with the default being 10MHz. Lower & higher values are technically possible, but the performance is not guaranteed. * This function also sets the baseband filter bandwidth to a value \f$ \le 0.75 \cdot F_s \f$, so any calls to @ref hackrf_set_baseband_filter_bandwidth should only be made after this. * + * @deprecated this function has been replaced by @ref hackrf_radio_set_sample_rate * @param device device to configure * @param freq_hz sample rate frequency in Hz. Should be in the range 2-20MHz * @return @ref HACKRF_SUCCESS on success or @ref hackrf_error variant @@ -2395,6 +2463,128 @@ extern ADDAPI int ADDCALL hackrf_radio_write_register( const uint8_t register_number, const uint64_t value); +/** + * Lock or unlock a radio configuration register. + * + * @param[in] device device to write + * @param[in] register_number register number to mask + * @param[out] locked locked state for the register + * @return @ref HACKRF_SUCCESS on success or @ref hackrf_error variant + * @ingroup debug + */ +extern ADDAPI int ADDCALL hackrf_radio_lock_register( + hackrf_device* device, + const uint8_t register_number, + const bool register_locked); + +/** + * Switches the radio configuration mode. + * + * @param[in] device device to configure + * @param[in] mode configuration mode. Defaults to RADIO_CONFIG_STANDARD. Available modes are defined in @ref radio_config_mode. + * @return @ref HACKRF_SUCCESS on success or @ref hackrf_error variant + * @ingroup configuration + */ +extern ADDAPI int ADDCALL hackrf_radio_set_mode( + hackrf_device* device, + const enum radio_config_mode mode); + +/** + * 40.24 Fixed-point type. + * + * Used by @ref hackrf_radio_set_frequency and @ref hackrf_radio_set_frequency_explicit + * to represent a fractional tuning frequency. + */ +typedef uint64_t fp_40_24_t; + +/** + * 28.36 Fixed-point type. + * + * Used by @ref hackrf_radio_set_sample_rate to represent a fractional sample rate. + */ +typedef uint64_t fp_28_36_t; + +/** + * Convert an integer frequency value to its corresponding fixed-point value. + */ +#define FREQ_FP(u64) ((uint64_t) u64 << 24) + +/** + * Convert an integer sample rate value to its corresponding fixed-point value. + */ +#define SR_FP(u64) ((uint64_t) u64 << 36) + +/** + * Convert a fixed-point frequency value to its corresponding integer value, rounding up to the nearest integer. + * + */ +#define FP_FREQ(u64) (((uint64_t) u64 + ((1ULL << 24) - 1)) >> 24) + +/** + * Convert a fixed-point sample rate value to its corresponding integer value, rounding up to the nearest integer. + */ +#define FP_SR(u64) (((uint64_t) u64 + ((1ULL << 36) - 1)) >> 36) + +/** + * Convert ASCII string to a 64 bit fixed-point number with a given number of bits of accuracy. + * + * @param[in] str string to parse + * @param[in] Qn number of bits to use for fractional part + * @param[out] value converted value + * @return @ref HACKRF_SUCCESS on success or @ref hackrf_error variant + * @ingroup configuration + */ +extern ADDAPI int ADDCALL hackrf_str_to_fp64( + const uint8_t Qn, + const char* str, + uint64_t* const value); + +/** + * Set the radio center frequency to a fractional, fixed-point value. + * + * @param[in] device device to tune + * @param[in] hz center frequency in Hz + * @return @ref HACKRF_SUCCESS on success or @ref hackrf_error variant + * @ingroup configuration + */ +extern ADDAPI int ADDCALL hackrf_radio_set_frequency( + hackrf_device* device, + const fp_40_24_t freq_hz); + +/** + * Set the radio center frequency to via explicit tuning. + * + * Center frequency is set to \f$f_{center} = f_{IF} + k\cdot f_{LO}\f$ where \f$k\in\left\{-1; 0; 1\right\}\f$, depending on the value of @p path. See the documentation of @ref rf_path_filter for details + * + * @param device device to tune + * TODO are these values still applicable for HackRF Pro ? + * @param if_freq_hz tuning frequency of the MAX2837 transceiver IC in Hz. Must be in the range of 2150-2750MHz + * TODO are these values still applicable for HackRF Pro ? + * @param lo_freq_hz tuning frequency of the RFFC5072 mixer/synthesizer IC in Hz. Must be in the range 84.375-5400MHz, defaults to 1000MHz. No effect if @p path is set to @ref RF_PATH_FILTER_BYPASS + * @param path filter path for mixer. See the documentation for @ref rf_path_filter for details + * @return @ref HACKRF_SUCCESS on success or @ref hackrf_error variant + * @ingroup configuration + */ +extern ADDAPI int ADDCALL hackrf_radio_set_frequency_explicit( + hackrf_device* device, + const fp_40_24_t if_freq_hz, + const fp_40_24_t lo_freq_hz, + const enum rf_path_filter path); + +/** + * Set the radio sample rate to a fractional, fixed-point value. + * + * This function does not automatically configure the baseband filter bandwidth, so any calls to this function should be followed by @ref hackrf_set_baseband_filter_bandwidth should it require adjustment. + * + * @param[in] device device to configure + * @param[in] sps samples per second + * @return @ref HACKRF_SUCCESS on success or @ref hackrf_error variant + * @ingroup configuration + */ +extern ADDAPI int ADDCALL hackrf_radio_set_sample_rate( + hackrf_device* device, + const fp_28_36_t freq_hz); + #ifdef __cplusplus } // __cplusplus defined. #endif