Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions firmware/common/fpga.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
89 changes: 89 additions & 0 deletions firmware/common/radio.c
Original file line number Diff line number Diff line change
Expand Up @@ -59,13 +59,19 @@ 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)
{
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,
Expand All @@ -76,6 +82,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_ACTIVE:
mark_dirty(radio, reg);
Expand Down Expand Up @@ -106,6 +116,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];
Expand Down Expand Up @@ -928,3 +952,68 @@ 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_ALL, 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;
}
28 changes: 23 additions & 5 deletions firmware/common/radio.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,18 +34,20 @@ 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,
} radio_error_t;

/* 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 {
Expand Down Expand Up @@ -225,6 +227,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;

Expand All @@ -248,6 +251,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_ACTIVE.
* Return true if any changes were applied.
Expand All @@ -260,6 +272,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.
*/
Expand Down
1 change: 1 addition & 0 deletions firmware/hackrf_usb/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
5 changes: 5 additions & 0 deletions firmware/hackrf_usb/hackrf_usb.c
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -182,6 +183,10 @@ static usb_request_handler_fn vendor_request_handler[] = {
usb_vendor_request_write_radio_reg,
usb_vendor_request_read_radio_reg,
usb_vendor_request_get_buffer_size,
usb_vendor_request_lock_radio_reg,
usb_vendor_request_open,
usb_vendor_request_close,
usb_vendor_request_set_radio_mode,
};

static const uint32_t vendor_request_handler_count =
Expand Down
41 changes: 41 additions & 0 deletions firmware/hackrf_usb/usb_api_radio.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
* 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 <radio.h>
#include <usb_request.h>
#include <usb_type.h>

#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;
}
29 changes: 29 additions & 0 deletions firmware/hackrf_usb/usb_api_radio.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
* 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.
*/

#pragma once

#include <usb_request.h>
#include <usb_type.h>

usb_request_status_t usb_vendor_request_set_radio_mode(
usb_endpoint_t* const endpoint,
const usb_transfer_stage_t stage);
17 changes: 17 additions & 0 deletions firmware/hackrf_usb/usb_api_register.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
3 changes: 3 additions & 0 deletions firmware/hackrf_usb/usb_api_register.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
36 changes: 36 additions & 0 deletions firmware/hackrf_usb/usb_api_transceiver.c
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
6 changes: 6 additions & 0 deletions firmware/hackrf_usb/usb_api_transceiver.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
2 changes: 1 addition & 1 deletion firmware/hackrf_usb/usb_descriptor.c
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@

#define USB_VENDOR_ID (0x1D50)

#define USB_API_VERSION (0x0112)
#define USB_API_VERSION (0x0113)

#define USB_WORD(x) (x & 0xFF), ((x >> 8) & 0xFF)

Expand Down
Loading
Loading