diff --git a/firmware/common/radio.c b/firmware/common/radio.c index 36f65cad7..b947cd624 100644 --- a/firmware/common/radio.c +++ b/firmware/common/radio.c @@ -60,6 +60,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 +68,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 +83,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 +117,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]; diff --git a/firmware/common/radio.h b/firmware/common/radio.h index 8e12471a4..caaa222d3 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, @@ -231,6 +232,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 +256,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. diff --git a/firmware/hackrf_usb/hackrf_usb.c b/firmware/hackrf_usb/hackrf_usb.c index 813a94539..4cdfbd14e 100644 --- a/firmware/hackrf_usb/hackrf_usb.c +++ b/firmware/hackrf_usb/hackrf_usb.c @@ -182,6 +182,7 @@ 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, }; 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 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_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 5f8785b52..475130ebc 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"); @@ -709,6 +748,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'}, @@ -740,10 +780,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; @@ -782,7 +824,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:o", + "b:n:rw:l:d:cmsfgi1:2:C:N:P:ST:R:h?u:l:ta:o", long_options, &option_index)) != EOF) { switch (opt) { @@ -805,6 +847,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; @@ -931,8 +978,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; } @@ -949,6 +996,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(); @@ -959,11 +1010,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)) { - fprintf(stderr, "Specify read, write, or config option.\n"); + fprintf(stderr, "Specify read, write, lock, or config option.\n"); usage(); return EXIT_FAILURE; } @@ -1016,6 +1067,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/libhackrf/src/hackrf.c b/host/libhackrf/src/hackrf.c index 4018ac63d..eab5bfe73 100644 --- a/host/libhackrf/src/hackrf.c +++ b/host/libhackrf/src/hackrf.c @@ -121,6 +121,7 @@ typedef enum { HACKRF_VENDOR_REQUEST_RADIO_WRITE_REG = 59, HACKRF_VENDOR_REQUEST_RADIO_READ_REG = 60, HACKRF_VENDOR_REQUEST_GET_BUFFER_SIZE = 61, + HACKRF_VENDOR_REQUEST_RADIO_LOCK_REG = 62, } hackrf_vendor_request; #define USB_CONFIG_STANDARD 0x1 @@ -3575,6 +3576,33 @@ 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; +} + #ifdef __cplusplus } // __cplusplus defined. #endif diff --git a/host/libhackrf/src/hackrf.h b/host/libhackrf/src/hackrf.h index eeca65e03..6cf71dd91 100644 --- a/host/libhackrf/src/hackrf.h +++ b/host/libhackrf/src/hackrf.h @@ -2385,6 +2385,20 @@ 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); + #ifdef __cplusplus } // __cplusplus defined. #endif