diff --git a/firmware/hackrf_usb/hackrf_usb.c b/firmware/hackrf_usb/hackrf_usb.c index 06e0079d..9986ceac 100644 --- a/firmware/hackrf_usb/hackrf_usb.c +++ b/firmware/hackrf_usb/hackrf_usb.c @@ -152,6 +152,8 @@ static usb_request_handler_fn vendor_request_handler[] = { usb_vendor_request_read_selftest, usb_vendor_request_adc_read, usb_vendor_request_test_rtc_osc, + usb_vendor_request_write_radio_reg, + usb_vendor_request_read_radio_reg, }; static const uint32_t vendor_request_handler_count = diff --git a/firmware/hackrf_usb/usb_api_register.c b/firmware/hackrf_usb/usb_api_register.c index 2cd57cb9..19360415 100644 --- a/firmware/hackrf_usb/usb_api_register.c +++ b/firmware/hackrf_usb/usb_api_register.c @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 Great Scott Gadgets + * Copyright 2012-2026 Great Scott Gadgets * Copyright 2012 Jared Boone * Copyright 2013 Benjamin Vernoux * @@ -283,3 +283,84 @@ usb_request_status_t usb_vendor_request_read_fpga_reg( return USB_REQUEST_STATUS_OK; } #endif + +/* + * Each register is transferred as a uint8_t register number followed by a + * little-endian uint64_t value for a total of 9 bytes. + */ +static uint8_t radio_reg_buf[RADIO_NUM_REGS * 9]; + +usb_request_status_t usb_vendor_request_write_radio_reg( + usb_endpoint_t* const endpoint, + const usb_transfer_stage_t stage) +{ + uint8_t bank; + if (stage == USB_TRANSFER_STAGE_SETUP) { + bank = endpoint->setup.index; + if (bank >= RADIO_NUM_BANKS) { + return USB_REQUEST_STATUS_STALL; + } + uint8_t num_regs = endpoint->setup.length / 9; + if ((num_regs == 0) || (num_regs > RADIO_NUM_REGS)) { + return USB_REQUEST_STATUS_STALL; + } + usb_transfer_schedule_block( + endpoint->out, + &radio_reg_buf, + endpoint->setup.length, + NULL, + NULL); + } else if (stage == USB_TRANSFER_STAGE_DATA) { + uint8_t address, i; + uint64_t value; + bank = endpoint->setup.index; + for (i = 0; i < endpoint->setup.length; i += 9) { + address = radio_reg_buf[i]; + value = radio_reg_buf[i + 1] | + ((uint64_t) radio_reg_buf[i + 2] << 8) | + ((uint64_t) radio_reg_buf[i + 3] << 16) | + ((uint64_t) radio_reg_buf[i + 4] << 24) | + ((uint64_t) radio_reg_buf[i + 5] << 32) | + ((uint64_t) radio_reg_buf[i + 6] << 40) | + ((uint64_t) radio_reg_buf[i + 7] << 48) | + ((uint64_t) radio_reg_buf[i + 8] << 56); + radio_error_t result = + radio_reg_write(&radio, bank, address, value); + if (result != RADIO_OK) { + return USB_REQUEST_STATUS_STALL; + } + } + usb_transfer_schedule_ack(endpoint->in); + } + return USB_REQUEST_STATUS_OK; +} + +usb_request_status_t usb_vendor_request_read_radio_reg( + usb_endpoint_t* const endpoint, + const usb_transfer_stage_t stage) +{ + if (stage == USB_TRANSFER_STAGE_SETUP) { + uint8_t bank = endpoint->setup.index; + uint8_t address = endpoint->setup.value; + if ((bank >= RADIO_NUM_BANKS) || (address >= RADIO_NUM_REGS)) { + return USB_REQUEST_STATUS_STALL; + } + uint64_t value = radio_reg_read(&radio, bank, address); + endpoint->buffer[0] = value & 0xff; + endpoint->buffer[1] = (value >> 8) & 0xff; + endpoint->buffer[2] = (value >> 16) & 0xff; + endpoint->buffer[3] = (value >> 24) & 0xff; + endpoint->buffer[4] = (value >> 32) & 0xff; + endpoint->buffer[5] = (value >> 40) & 0xff; + endpoint->buffer[6] = (value >> 48) & 0xff; + endpoint->buffer[7] = (value >> 56) & 0xff; + usb_transfer_schedule_block( + endpoint->in, + &endpoint->buffer, + 8, + NULL, + NULL); + usb_transfer_schedule_ack(endpoint->out); + } + return USB_REQUEST_STATUS_OK; +} diff --git a/firmware/hackrf_usb/usb_api_register.h b/firmware/hackrf_usb/usb_api_register.h index 8fc62c2c..07714b39 100644 --- a/firmware/hackrf_usb/usb_api_register.h +++ b/firmware/hackrf_usb/usb_api_register.h @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 Great Scott Gadgets + * Copyright 2012-2026 Great Scott Gadgets * Copyright 2012 Jared Boone * Copyright 2013 Benjamin Vernoux * @@ -65,5 +65,11 @@ usb_request_status_t usb_vendor_request_read_fpga_reg( usb_endpoint_t* const endpoint, const usb_transfer_stage_t stage); #endif +usb_request_status_t usb_vendor_request_write_radio_reg( + usb_endpoint_t* const endpoint, + const usb_transfer_stage_t stage); +usb_request_status_t usb_vendor_request_read_radio_reg( + usb_endpoint_t* const endpoint, + const usb_transfer_stage_t stage); #endif /* end of include guard: __USB_API_REGISTER_H__ */ diff --git a/firmware/hackrf_usb/usb_descriptor.c b/firmware/hackrf_usb/usb_descriptor.c index 68b2ea81..679a9589 100644 --- a/firmware/hackrf_usb/usb_descriptor.c +++ b/firmware/hackrf_usb/usb_descriptor.c @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 Great Scott Gadgets + * Copyright 2012-2026 Great Scott Gadgets * Copyright 2012 Jared Boone * * This file is part of HackRF. @@ -37,7 +37,7 @@ #define USB_PRODUCT_ID (0xFFFF) #endif -#define USB_API_VERSION (0x0110) +#define USB_API_VERSION (0x0111) #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 b3045b18..5f8785b5 100644 --- a/host/hackrf-tools/src/hackrf_debug.c +++ b/host/hackrf-tools/src/hackrf_debug.c @@ -1,5 +1,5 @@ /* - * Copyright 2012-2022 Great Scott Gadgets + * Copyright 2012-2026 Great Scott Gadgets * Copyright 2012 Jared Boone * Copyright 2013 Benjamin Vernoux * Copyright 2017 Dominic Spill @@ -29,6 +29,7 @@ #include #include #include +#include #define REGISTER_INVALID 32767 @@ -39,13 +40,14 @@ enum parts { PART_RFFC5072 = 3, PART_MAX2831 = 4, PART_GATEWARE = 5, + PART_RADIO = 6, }; -int parse_int(char* s, uint32_t* const value) +int parse_int(char* s, uint64_t* const value) { uint_fast8_t base = 10; char* s_end; - long long_value; + long long ll_value; if (strlen(s) > 2) { if (s[0] == '0') { @@ -60,9 +62,9 @@ int parse_int(char* s, uint32_t* const value) } s_end = s; - long_value = strtol(s, &s_end, base); + ll_value = strtoull(s, &s_end, base); if ((s != s_end) && (*s_end == 0)) { - *value = (uint32_t) long_value; + *value = (uint64_t) ll_value; return HACKRF_SUCCESS; } else { return HACKRF_ERROR_INVALID_PARAM; @@ -473,7 +475,81 @@ int fpga_write_register( return result; } -int read_register(hackrf_device* device, uint8_t part, const uint16_t register_number) +int radio_read_register( + hackrf_device* device, + const uint8_t bank, + const uint16_t register_number) +{ + uint64_t register_value; + int result = hackrf_radio_read_register( + device, + bank, + (uint8_t) register_number, + ®ister_value); + + if (result == HACKRF_SUCCESS) { + printf("bank %d register [%2d] -> 0x%016" PRIx64 "\n", + bank, + register_number, + register_value); + } else { + printf("hackrf_radio_read_register() failed: %s (%d)\n", + hackrf_error_name(result), + result); + } + + return result; +} + +#define RADIO_NUM_REGS (24) + +int radio_read_registers(hackrf_device* device, const uint8_t bank) +{ + uint16_t register_number; + int result = HACKRF_SUCCESS; + + for (register_number = 0; register_number < RADIO_NUM_REGS; register_number++) { + result = radio_read_register(device, bank, register_number); + if (result != HACKRF_SUCCESS) { + break; + } + } + + return result; +} + +int radio_write_register( + hackrf_device* device, + const uint8_t bank, + const uint16_t register_number, + const uint64_t register_value) +{ + int result = HACKRF_SUCCESS; + result = hackrf_radio_write_register( + device, + bank, + (uint8_t) register_number, + register_value); + + if (result == HACKRF_SUCCESS) { + printf("bank %d 0x%016" PRIx64 " -> [%2d]\n", + bank, + register_value, + register_number); + } else { + printf("hackrf_radio_write_register() failed: %s (%d)\n", + hackrf_error_name(result), + result); + } + + return result; +} + +int read_register( + hackrf_device* device, + uint8_t part, + const uint8_t bank, + const uint16_t register_number) { switch (part) { case PART_MAX2837: @@ -485,11 +561,13 @@ int read_register(hackrf_device* device, uint8_t part, const uint16_t register_n return rffc5072_read_register(device, register_number); case PART_GATEWARE: return fpga_read_register(device, register_number); + case PART_RADIO: + return radio_read_register(device, bank, register_number); } return HACKRF_ERROR_INVALID_PARAM; } -int read_registers(hackrf_device* device, uint8_t part) +int read_registers(hackrf_device* device, uint8_t part, const uint8_t bank) { switch (part) { case PART_MAX2837: @@ -501,6 +579,8 @@ int read_registers(hackrf_device* device, uint8_t part) return rffc5072_read_registers(device); case PART_GATEWARE: return fpga_read_registers(device); + case PART_RADIO: + return radio_read_registers(device, bank); } return HACKRF_ERROR_INVALID_PARAM; } @@ -508,8 +588,9 @@ int read_registers(hackrf_device* device, uint8_t part) int write_register( hackrf_device* device, uint8_t part, + const uint8_t bank, const uint16_t register_number, - const uint16_t register_value) + const uint64_t register_value) { switch (part) { case PART_MAX2837: @@ -517,14 +598,25 @@ int write_register( return max283x_write_register( device, register_number, - register_value, + (uint16_t) register_value, part); case PART_SI5351C: - return si5351c_write_register(device, register_number, register_value); + return si5351c_write_register( + device, + register_number, + (uint16_t) register_value); case PART_RFFC5072: - return rffc5072_write_register(device, register_number, register_value); + return rffc5072_write_register( + device, + register_number, + (uint16_t) register_value); case PART_GATEWARE: - return fpga_write_register(device, register_number, register_value); + return fpga_write_register( + device, + register_number, + (uint16_t) register_value); + case PART_RADIO: + return radio_write_register(device, bank, register_number, register_value); } return HACKRF_ERROR_INVALID_PARAM; } @@ -579,6 +671,7 @@ static void usage() { printf("\nUsage:\n"); printf("\t-h, --help: this help\n"); + printf("\t-b, --bank : set register bank for read/write operations (default 0)\n"); 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"); @@ -588,6 +681,7 @@ static void usage() printf("\t-s, --si5351c: target SI5351C\n"); printf("\t-f, --rffc5072: target RFFC5072\n"); printf("\t-g, --gateware: target gateware registers\n"); + printf("\t-i, --radio: target radio registers\n"); printf("\t-P, --fpga : load the n-th bitstream to the FPGA\n"); printf("\t-1, --p1 : P1 control\n"); printf("\t-2, --p2 : P2 control\n"); @@ -611,6 +705,7 @@ static void usage() static struct option long_options[] = { {"config", no_argument, 0, 'c'}, + {"bank", required_argument, 0, 'b'}, {"register", required_argument, 0, 'n'}, {"write", required_argument, 0, 'w'}, {"read", no_argument, 0, 'r'}, @@ -621,6 +716,7 @@ static struct option long_options[] = { {"si5351c", no_argument, 0, 's'}, {"rffc5072", no_argument, 0, 'f'}, {"gateware", no_argument, 0, 'g'}, + {"radio", no_argument, 0, 'i'}, {"fpga", required_argument, 0, 'P'}, {"p1", required_argument, 0, '1'}, {"p2", required_argument, 0, '2'}, @@ -641,8 +737,9 @@ int main(int argc, char** argv) { int opt; uint8_t board_id = BOARD_ID_UNDETECTED; - uint32_t register_number = REGISTER_INVALID; - uint32_t register_value; + int bank = -1; + uint64_t register_number = REGISTER_INVALID; + uint64_t register_value; hackrf_device* device = NULL; int option_index = 0; bool read = false; @@ -652,17 +749,17 @@ int main(int argc, char** argv) uint8_t part = PART_NONE; const char* serial_number = NULL; bool set_ui = false; - uint32_t ui_enable; + uint64_t ui_enable; bool set_leds = false; - uint32_t led_state; - uint32_t tx_limit; - uint32_t rx_limit; - uint32_t p1_state; - uint32_t p2_state; - uint32_t clkin_state; - uint32_t narrowband_state; - uint32_t bitstream_index; - uint32_t adc_channel; + uint64_t led_state; + uint64_t tx_limit; + uint64_t rx_limit; + uint64_t p1_state; + uint64_t p2_state; + uint64_t clkin_state; + uint64_t narrowband_state; + uint64_t bitstream_index; + uint64_t adc_channel; bool set_tx_limit = false; bool set_rx_limit = false; bool set_p1 = false; @@ -685,10 +782,16 @@ int main(int argc, char** argv) while ((opt = getopt_long( argc, argv, - "n:rw:d:cmsfg1:2:C:N:P:ST:R:h?u:l:ta:o", + "b:n:rw:d:cmsfgi1:2:C:N:P:ST:R:h?u:l:ta:o", long_options, &option_index)) != EOF) { switch (opt) { + case 'b': + uint64_t bank_arg; + result = parse_int(optarg, &bank_arg); + bank = (int) bank_arg; + break; + case 'n': result = parse_int(optarg, ®ister_number); break; @@ -755,6 +858,14 @@ int main(int argc, char** argv) part = PART_GATEWARE; break; + case 'i': + if (part != PART_NONE) { + fprintf(stderr, "Only one part can be specified.'\n"); + return EXIT_FAILURE; + } + part = PART_RADIO; + break; + case '1': set_p1 = true; result = parse_int(optarg, &p1_state); @@ -838,6 +949,16 @@ int main(int argc, char** argv) return EXIT_FAILURE; } + if ((bank > -1) && (part != PART_RADIO)) { + fprintf(stderr, "Bank valid only for radio.\n"); + usage(); + return EXIT_FAILURE; + } + + if ((bank == -1) && (part == PART_RADIO)) { + bank = 0; + } + if (!(write || read || 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 || @@ -879,14 +1000,19 @@ int main(int argc, char** argv) } if (write) { - result = write_register(device, part, register_number, register_value); + result = write_register( + device, + part, + bank, + register_number, + register_value); } if (read) { if (register_number == REGISTER_INVALID) { - result = read_registers(device, part); + result = read_registers(device, part, bank); } else { - result = read_register(device, part, register_number); + result = read_register(device, part, bank, register_number); } } @@ -1025,8 +1151,8 @@ int main(int argc, char** argv) return EXIT_FAILURE; } printf("ADC0_%d (%s pin): %d\n", - adc_channel & 0x7, - adc_channel & 0x80 ? "alternate" : "dedicated", + ((uint8_t) adc_channel) & 0x7, + ((uint8_t) adc_channel) & 0x80 ? "alternate" : "dedicated", value); } diff --git a/host/libhackrf/CMakeLists.txt b/host/libhackrf/CMakeLists.txt index 37f124e6..61ad494c 100644 --- a/host/libhackrf/CMakeLists.txt +++ b/host/libhackrf/CMakeLists.txt @@ -1,3 +1,4 @@ +# Copyright 2012-2026 Great Scott Gadgets # Copyright 2012 Jared Boone # Copyright 2013 Benjamin Vernoux # @@ -20,7 +21,7 @@ cmake_minimum_required(VERSION 3.10.0) project( libhackrf LANGUAGES C - VERSION 0.9.2) + VERSION 0.10.0) include(GNUInstallDirs) include(${PROJECT_SOURCE_DIR}/../cmake/set_release.cmake) set(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/../cmake/modules) diff --git a/host/libhackrf/src/hackrf.c b/host/libhackrf/src/hackrf.c index 3ada04b2..48a25868 100644 --- a/host/libhackrf/src/hackrf.c +++ b/host/libhackrf/src/hackrf.c @@ -1,5 +1,5 @@ /* -Copyright (c) 2012-2022 Great Scott Gadgets +Copyright (c) 2012-2026 Great Scott Gadgets Copyright (c) 2012, Jared Boone Copyright (c) 2013, Benjamin Vernoux @@ -45,11 +45,13 @@ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSI #define TO_LE64(x) __builtin_bswap64(x) #define FROM_LE16(x) __builtin_bswap16(x) #define FROM_LE32(x) __builtin_bswap32(x) + #define FROM_LE64(x) __builtin_bswap64(x) #else #define TO_LE(x) x #define TO_LE64(x) x #define FROM_LE16(x) x #define FROM_LE32(x) x + #define FROM_LE64(x) x #endif // TODO: Factor this into a shared #include so that firmware can use @@ -111,6 +113,8 @@ typedef enum { HACKRF_VENDOR_REQUEST_READ_SELFTEST = 56, HACKRF_VENDOR_REQUEST_READ_ADC = 57, HACKRF_VENDOR_REQUEST_TEST_RTC_OSC = 58, + HACKRF_VENDOR_REQUEST_RADIO_WRITE_REG = 59, + HACKRF_VENDOR_REQUEST_RADIO_READ_REG = 60, } hackrf_vendor_request; #define USB_CONFIG_STANDARD 0x1 @@ -3476,6 +3480,74 @@ int ADDCALL hackrf_set_fpga_bitstream(hackrf_device* device, const uint8_t index } } +int ADDCALL hackrf_radio_read_register( + hackrf_device* device, + const uint8_t bank, + const uint8_t register_number, + uint64_t* const value) +{ + USB_API_REQUIRED(device, 0x0111); + int result; + + const uint8_t length = sizeof(value); + result = libusb_control_transfer( + device->usb_device, + LIBUSB_ENDPOINT_IN | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE, + HACKRF_VENDOR_REQUEST_RADIO_READ_REG, + register_number, + bank, + (unsigned char*) value, + length, + 0); + *value = FROM_LE64(*value); + + if (result < length) { + last_libusb_error = result; + return HACKRF_ERROR_LIBUSB; + } else { + return HACKRF_SUCCESS; + } +} + +int ADDCALL hackrf_radio_write_register( + hackrf_device* device, + const uint8_t bank, + const uint8_t register_number, + const uint64_t value) +{ + USB_API_REQUIRED(device, 0x0111); + int result; + + unsigned char data[9]; + data[0] = register_number; + data[1] = value & 0xff; + data[2] = (value >> 8) & 0xff; + data[3] = (value >> 16) & 0xff; + data[4] = (value >> 24) & 0xff; + data[5] = (value >> 32) & 0xff; + data[6] = (value >> 40) & 0xff; + data[7] = (value >> 48) & 0xff; + data[8] = (value >> 56) & 0xff; + + result = libusb_control_transfer( + device->usb_device, + LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_VENDOR | + LIBUSB_RECIPIENT_DEVICE, + HACKRF_VENDOR_REQUEST_RADIO_WRITE_REG, + 0, + bank, + &data[0], + sizeof(data), + 0); + + if (result < 9) { + last_libusb_error = result; + return HACKRF_ERROR_LIBUSB; + } else { + return HACKRF_SUCCESS; + } +} + #ifdef __cplusplus } // __cplusplus defined. #endif diff --git a/host/libhackrf/src/hackrf.h b/host/libhackrf/src/hackrf.h index 0247e6ca..e00ba3ff 100644 --- a/host/libhackrf/src/hackrf.h +++ b/host/libhackrf/src/hackrf.h @@ -1,5 +1,5 @@ /* -Copyright (c) 2012-2022 Great Scott Gadgets +Copyright (c) 2012-2026 Great Scott Gadgets Copyright (c) 2012, Jared Boone Copyright (c) 2013, Benjamin Vernoux @@ -2354,6 +2354,38 @@ extern ADDAPI int ADDCALL hackrf_set_fpga_bitstream( hackrf_device* device, const uint8_t index); +/** + * Read a radio configuration register + * + * @param[in] device device to query + * @param[in] bank bank number to read + * @param[in] register_number register number to read + * @param[out] value value of the specified register + * @return @ref HACKRF_SUCCESS on success or @ref hackrf_error variant + * @ingroup debug + */ +extern ADDAPI int ADDCALL hackrf_radio_read_register( + hackrf_device* device, + const uint8_t bank, + const uint8_t register_number, + uint64_t* const value); + +/** + * Write to a radio configuration register + * + * @param[in] device device to write + * @param[in] bank bank number to write + * @param[in] register_number register number to write + * @param[out] value value to write in the specified register + * @return @ref HACKRF_SUCCESS on success or @ref hackrf_error variant + * @ingroup debug + */ +extern ADDAPI int ADDCALL hackrf_radio_write_register( + hackrf_device* device, + const uint8_t bank, + const uint8_t register_number, + const uint64_t value); + #ifdef __cplusplus } // __cplusplus defined. #endif