diff options
Diffstat (limited to 'Radio/HW/BladeRF/src/bladerf.c')
-rw-r--r-- | Radio/HW/BladeRF/src/bladerf.c | 2387 |
1 files changed, 2387 insertions, 0 deletions
diff --git a/Radio/HW/BladeRF/src/bladerf.c b/Radio/HW/BladeRF/src/bladerf.c new file mode 100644 index 0000000..f19c3a6 --- /dev/null +++ b/Radio/HW/BladeRF/src/bladerf.c @@ -0,0 +1,2387 @@ +/* + * This file is part of the bladeRF project: + * http://www.github.com/nuand/bladeRF + * + * Copyright (C) 2013-2016 Nuand LLC + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <errno.h> +#include <limits.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <libbladeRF.h> + +#include "log.h" +#include "rel_assert.h" +#define LOGGER_ID_STRING +#include "logger_entry.h" +#include "logger_id.h" + +#include "backend/backend.h" +#include "backend/usb/usb.h" +#include "board/board.h" +#include "conversions.h" +#include "driver/fx3_fw.h" +#include "device_calibration.h" +#include "streaming/async.h" +#include "version.h" + +#include "expansion/xb100.h" +#include "expansion/xb200.h" +#include "expansion/xb300.h" + +#include "devinfo.h" +#include "helpers/configfile.h" +#include "helpers/file.h" +#include "helpers/have_cap.h" +#include "helpers/interleave.h" + +#define CHECK_NULL(...) do { \ + const void* _args[] = { __VA_ARGS__, NULL }; \ + for (size_t _i = 0; _args[_i] != NULL; ++_i) { \ + if (_args[_i] == NULL) { \ + log_error("%s:%d: Argument %zu is a NULL pointer\n", __FILE__, __LINE__, _i + 1); \ + return BLADERF_ERR_INVAL; \ + } \ + } \ +} while (0) + +#define CHECK_STATUS(fn) \ + do { \ + status = fn; \ + if (status != 0) { \ + log_error("%s: %s %s\n", __FUNCTION__, #fn, \ + bladerf_strerror(status)); \ + goto error; \ + } \ + } while (0) + + +/******************************************************************************/ +/* Private Function Declarations */ +/******************************************************************************/ + +/** + * Sets up the register configuration for oversample feature. + * + * @param dev Device handle + * + * @return 0 on success, value from \ref RETCODES list on failure + */ +int bladerf_set_oversample_register_config(struct bladerf *dev); + +/******************************************************************************/ +/* Open / Close */ +/******************************************************************************/ + +/* dev path becomes device specifier string (osmosdr-like) */ +int bladerf_open(struct bladerf **dev, const char *dev_id) +{ + struct bladerf_devinfo devinfo; + int status; + + *dev = NULL; + + /* Populate dev-info from string */ + status = str2devinfo(dev_id, &devinfo); + if (!status) { + status = bladerf_open_with_devinfo(dev, &devinfo); + } + + return status; +} + +int bladerf_open_with_devinfo(struct bladerf **opened_device, + struct bladerf_devinfo *devinfo) +{ + struct bladerf *dev; + struct bladerf_devinfo any_device; + unsigned int i; + int status; + + if (devinfo == NULL) { + bladerf_init_devinfo(&any_device); + devinfo = &any_device; + } + + *opened_device = NULL; + + dev = calloc(1, sizeof(struct bladerf)); + if (dev == NULL) { + return BLADERF_ERR_MEM; + } + + /* Open backend */ + status = backend_open(dev, devinfo); + if (status != 0) { + free(dev); + return status; + } + + /* Find matching board */ + for (i = 0; i < bladerf_boards_len; i++) { + if (bladerf_boards[i]->matches(dev)) { + dev->board = bladerf_boards[i]; + break; + } + } + /* If no matching board was found */ + if (i == bladerf_boards_len) { + dev->backend->close(dev); + free(dev); + return BLADERF_ERR_NODEV; + } + + MUTEX_INIT(&dev->lock); + + /* Open board */ + status = dev->board->open(dev, devinfo); + + if (status < 0) { + bladerf_close(dev); + return status; + } + + /* Load configuration file */ + status = config_load_options_file(dev); + + if (status < 0) { + bladerf_close(dev); + return status; + } + + *opened_device = dev; + + return 0; +} + +int bladerf_get_devinfo(struct bladerf *dev, struct bladerf_devinfo *info) +{ + if (dev) { + MUTEX_LOCK(&dev->lock); + memcpy(info, &dev->ident, sizeof(struct bladerf_devinfo)); + MUTEX_UNLOCK(&dev->lock); + return 0; + } else { + return BLADERF_ERR_INVAL; + } +} + +int bladerf_get_backendinfo(struct bladerf *dev, struct bladerf_backendinfo *info) +{ + if (dev) { + MUTEX_LOCK(&dev->lock); + info->lock_count = 1; + info->lock = &dev->lock; + + info->handle_count = 1; + dev->backend->get_handle(dev, &info->handle); + MUTEX_UNLOCK(&dev->lock); + return 0; + } else { + return BLADERF_ERR_INVAL; + } +} + +void bladerf_close(struct bladerf *dev) +{ + if (dev) { + MUTEX_LOCK(&dev->lock); + + dev->board->close(dev); + + if (dev->backend) { + dev->backend->close(dev); + } + + /** Free gain table entries */ + for (int i = 0; i < NUM_GAIN_CAL_TBLS; i++) { + gain_cal_tbl_free(&dev->gain_tbls[i]); + } + + MUTEX_UNLOCK(&dev->lock); + + free(dev); + } +} + +/******************************************************************************/ +/* FX3 Firmware (common to bladerf1 and bladerf2) */ +/******************************************************************************/ + +int bladerf_jump_to_bootloader(struct bladerf *dev) +{ + int status; + + if (!dev->backend->jump_to_bootloader) { + return BLADERF_ERR_UNSUPPORTED; + } + + MUTEX_LOCK(&dev->lock); + + status = dev->backend->jump_to_bootloader(dev); + + MUTEX_UNLOCK(&dev->lock); + + return status; +} + +int bladerf_get_bootloader_list(struct bladerf_devinfo **devices) +{ + return probe(BACKEND_PROBE_FX3_BOOTLOADER, devices); +} + +int bladerf_load_fw_from_bootloader(const char *device_identifier, + bladerf_backend backend, + uint8_t bus, + uint8_t addr, + const char *file) +{ + int status; + uint8_t *buf; + size_t buf_len; + struct fx3_firmware *fw = NULL; + struct bladerf_devinfo devinfo; + + if (device_identifier == NULL) { + bladerf_init_devinfo(&devinfo); + devinfo.backend = backend; + devinfo.usb_bus = bus; + devinfo.usb_addr = addr; + } else { + status = str2devinfo(device_identifier, &devinfo); + if (status != 0) { + return status; + } + } + + status = file_read_buffer(file, &buf, &buf_len); + if (status != 0) { + return status; + } + + status = fx3_fw_parse(&fw, buf, buf_len); + free(buf); + if (status != 0) { + return status; + } + + assert(fw != NULL); + + status = backend_load_fw_from_bootloader(devinfo.backend, devinfo.usb_bus, + devinfo.usb_addr, fw); + + fx3_fw_free(fw); + + return status; +} + +int bladerf_get_fw_log(struct bladerf *dev, const char *filename) +{ + int status; + FILE *f = NULL; + logger_entry e; + + MUTEX_LOCK(&dev->lock); + + if (!have_cap(dev->board->get_capabilities(dev), + BLADERF_CAP_READ_FW_LOG_ENTRY)) { + struct bladerf_version fw_version; + + if (dev->board->get_fw_version(dev, &fw_version) == 0) { + log_debug("FX3 FW v%s does not support log retrieval.\n", + fw_version.describe); + } + + status = BLADERF_ERR_UNSUPPORTED; + goto error; + } + + if (filename != NULL) { + f = fopen(filename, "w"); + if (f == NULL) { + switch (errno) { + case ENOENT: + status = BLADERF_ERR_NO_FILE; + break; + case EACCES: + status = BLADERF_ERR_PERMISSION; + break; + default: + status = BLADERF_ERR_IO; + break; + } + goto error; + } + } else { + f = stdout; + } + + do { + status = dev->backend->read_fw_log(dev, &e); + if (status != 0) { + log_debug("Failed to read FW log: %s\n", bladerf_strerror(status)); + goto error; + } + + if (e == LOG_ERR) { + fprintf(f, "<Unexpected error>,,\n"); + } else if (e != LOG_EOF) { + uint8_t file_id; + uint16_t line; + uint16_t data; + const char *src_file; + + logger_entry_unpack(e, &file_id, &line, &data); + src_file = logger_id_string(file_id); + + fprintf(f, "%s, %u, 0x%04x\n", src_file, line, data); + } + } while (e != LOG_EOF && e != LOG_ERR); + +error: + MUTEX_UNLOCK(&dev->lock); + + if (f != NULL && f != stdout) { + fclose(f); + } + + return status; +} + +/******************************************************************************/ +/* Properties */ +/******************************************************************************/ + +bladerf_dev_speed bladerf_device_speed(struct bladerf *dev) +{ + bladerf_dev_speed speed; + MUTEX_LOCK(&dev->lock); + + speed = dev->board->device_speed(dev); + + MUTEX_UNLOCK(&dev->lock); + return speed; +} + +int bladerf_get_serial(struct bladerf *dev, char *serial) +{ + int status; + MUTEX_LOCK(&dev->lock); + + /** TODO: add deprecation warning */ + + status = dev->board->get_serial(dev, serial); + + MUTEX_UNLOCK(&dev->lock); + return status; +} + +int bladerf_get_serial_struct(struct bladerf *dev, + struct bladerf_serial *serial) +{ + int status; + MUTEX_LOCK(&dev->lock); + + char serialstr[sizeof(serial->serial)]; + + status = dev->board->get_serial(dev, serialstr); + + if (status >= 0) { + strncpy(serial->serial, serialstr, sizeof(serial->serial)); + serial->serial[sizeof(serial->serial)-1] = '\0'; + } + + MUTEX_UNLOCK(&dev->lock); + return status; +} + +int bladerf_get_fpga_size(struct bladerf *dev, bladerf_fpga_size *size) +{ + int status; + MUTEX_LOCK(&dev->lock); + + status = dev->board->get_fpga_size(dev, size); + + MUTEX_UNLOCK(&dev->lock); + return status; +} + +int bladerf_get_fpga_bytes(struct bladerf *dev, size_t *size) +{ + int status; + MUTEX_LOCK(&dev->lock); + + status = dev->board->get_fpga_bytes(dev, size); + + MUTEX_UNLOCK(&dev->lock); + return status; +} + +int bladerf_get_flash_size(struct bladerf *dev, uint32_t *size, bool *is_guess) +{ + int status; + MUTEX_LOCK(&dev->lock); + + status = dev->board->get_flash_size(dev, size, is_guess); + + MUTEX_UNLOCK(&dev->lock); + return status; +} + +int bladerf_is_fpga_configured(struct bladerf *dev) +{ + int status; + MUTEX_LOCK(&dev->lock); + + status = dev->board->is_fpga_configured(dev); + + MUTEX_UNLOCK(&dev->lock); + return status; +} + +int bladerf_get_fpga_source(struct bladerf *dev, bladerf_fpga_source *source) +{ + int status; + MUTEX_LOCK(&dev->lock); + + status = dev->board->get_fpga_source(dev, source); + + MUTEX_UNLOCK(&dev->lock); + return status; +} + +const char *bladerf_get_board_name(struct bladerf *dev) +{ + return dev->board->name; +} + +size_t bladerf_get_channel_count(struct bladerf *dev, bladerf_direction dir) +{ + return dev->board->get_channel_count(dev, dir); +} + +/******************************************************************************/ +/* Versions */ +/******************************************************************************/ + +int bladerf_fpga_version(struct bladerf *dev, struct bladerf_version *version) +{ + int status; + MUTEX_LOCK(&dev->lock); + + status = dev->board->get_fpga_version(dev, version); + + MUTEX_UNLOCK(&dev->lock); + return status; +} + +int bladerf_fw_version(struct bladerf *dev, struct bladerf_version *version) +{ + int status; + MUTEX_LOCK(&dev->lock); + + status = dev->board->get_fw_version(dev, version); + + MUTEX_UNLOCK(&dev->lock); + return status; +} + +void bladerf_version(struct bladerf_version *version) +{ +/* Sanity checks for version reporting mismatches */ +#ifndef LIBBLADERF_API_VERSION +#error LIBBLADERF_API_VERSION is missing +#endif +#if LIBBLADERF_VERSION_MAJOR != ((LIBBLADERF_API_VERSION >> 24) & 0xff) +#error LIBBLADERF_API_VERSION: Major version mismatch +#endif +#if LIBBLADERF_VERSION_MINOR != ((LIBBLADERF_API_VERSION >> 16) & 0xff) +#error LIBBLADERF_API_VERSION: Minor version mismatch +#endif +#if LIBBLADERF_VERSION_PATCH != ((LIBBLADERF_API_VERSION >> 8) & 0xff) +#error LIBBLADERF_API_VERSION: Patch version mismatch +#endif + version->major = LIBBLADERF_VERSION_MAJOR; + version->minor = LIBBLADERF_VERSION_MINOR; + version->patch = LIBBLADERF_VERSION_PATCH; + version->describe = LIBBLADERF_VERSION; +} + +/******************************************************************************/ +/* Enable/disable */ +/******************************************************************************/ + +int bladerf_enable_module(struct bladerf *dev, bladerf_channel ch, bool enable) +{ + int status; + MUTEX_LOCK(&dev->lock); + + status = dev->board->enable_module(dev, ch, enable); + + MUTEX_UNLOCK(&dev->lock); + return status; +} + +/******************************************************************************/ +/* Gain */ +/******************************************************************************/ + +int bladerf_set_gain(struct bladerf *dev, bladerf_channel ch, int gain) +{ + int status; + bladerf_gain_mode gain_mode; + bladerf_frequency freq; + bladerf_gain assigned_gain = gain; + MUTEX_LOCK(&dev->lock); + + /* Change gain mode to manual if ch = RX */ + if (BLADERF_CHANNEL_IS_TX(ch) == false) { + status = dev->board->get_gain_mode(dev, ch, &gain_mode); + if (status != 0) { + log_error("Failed to get gain mode\n"); + goto error; + } + + if (gain_mode != BLADERF_GAIN_MGC) { + log_warning("Setting gain mode to manual\n"); + status = dev->board->set_gain_mode(dev, ch, BLADERF_GAIN_MGC); + if (status != 0) { + log_error("Failed to set gain mode\n"); + goto error; + } + } + } + + dev->gain_tbls[ch].gain_target = gain; + + if (dev->gain_tbls[ch].enabled == true) { + dev->board->get_frequency(dev, ch, &freq); + get_gain_correction(dev, freq, ch, &assigned_gain); + } + + status = dev->board->set_gain(dev, ch, assigned_gain); + if (status != 0) { + log_error("Failed to set gain\n"); + goto error; + } + +error: + MUTEX_UNLOCK(&dev->lock); + return status; +} + +int bladerf_get_gain(struct bladerf *dev, bladerf_channel ch, int *gain) +{ + int status; + MUTEX_LOCK(&dev->lock); + + status = dev->board->get_gain(dev, ch, gain); + + MUTEX_UNLOCK(&dev->lock); + return status; +} + +int bladerf_set_gain_mode(struct bladerf *dev, + bladerf_channel ch, + bladerf_gain_mode mode) +{ + int status; + MUTEX_LOCK(&dev->lock); + + status = dev->board->set_gain_mode(dev, ch, mode); + + MUTEX_UNLOCK(&dev->lock); + return status; +} + +int bladerf_get_gain_mode(struct bladerf *dev, + bladerf_channel ch, + bladerf_gain_mode *mode) +{ + int status; + MUTEX_LOCK(&dev->lock); + + status = dev->board->get_gain_mode(dev, ch, mode); + + MUTEX_UNLOCK(&dev->lock); + return status; +} + +int bladerf_get_gain_modes(struct bladerf *dev, + bladerf_channel ch, + struct bladerf_gain_modes const **modes) +{ + return dev->board->get_gain_modes(dev, ch, modes); +} + +int bladerf_get_gain_range(struct bladerf *dev, + bladerf_channel ch, + struct bladerf_range const **range) +{ + return dev->board->get_gain_range(dev, ch, range); +} + +int bladerf_set_gain_stage(struct bladerf *dev, + bladerf_channel ch, + const char *stage, + bladerf_gain gain) +{ + int status; + MUTEX_LOCK(&dev->lock); + + status = dev->board->set_gain_stage(dev, ch, stage, gain); + + MUTEX_UNLOCK(&dev->lock); + return status; +} + +int bladerf_get_gain_stage(struct bladerf *dev, + bladerf_channel ch, + const char *stage, + bladerf_gain *gain) +{ + int status; + MUTEX_LOCK(&dev->lock); + + status = dev->board->get_gain_stage(dev, ch, stage, gain); + + MUTEX_UNLOCK(&dev->lock); + return status; +} + +int bladerf_get_gain_stage_range(struct bladerf *dev, + bladerf_channel ch, + const char *stage, + struct bladerf_range const **range) +{ + return dev->board->get_gain_stage_range(dev, ch, stage, range); +} + +int bladerf_get_gain_stages(struct bladerf *dev, + bladerf_channel ch, + const char **stages, + size_t count) +{ + return dev->board->get_gain_stages(dev, ch, stages, count); +} + +/******************************************************************************/ +/* Sample Rate */ +/******************************************************************************/ + +int bladerf_set_sample_rate(struct bladerf *dev, + bladerf_channel ch, + bladerf_sample_rate rate, + bladerf_sample_rate *actual) +{ + int status; + bladerf_feature feature = dev->feature; + + MUTEX_LOCK(&dev->lock); + status = dev->board->set_sample_rate(dev, ch, rate, actual); + MUTEX_UNLOCK(&dev->lock); + + /***************************************************** + Sample rate assignments clear previous register + values. We must reassign oversample register config + for every set_samplerate(). + *******************************************************/ + if ((feature & BLADERF_FEATURE_OVERSAMPLE)) { + status = bladerf_set_oversample_register_config(dev); + if (status != 0) { + log_error("Oversample register config failure\n"); + } + } + + return status; +} + +int bladerf_get_sample_rate(struct bladerf *dev, + bladerf_channel ch, + bladerf_sample_rate *rate) +{ + int status; + MUTEX_LOCK(&dev->lock); + + status = dev->board->get_sample_rate(dev, ch, rate); + + MUTEX_UNLOCK(&dev->lock); + return status; +} + +int bladerf_get_sample_rate_range(struct bladerf *dev, + bladerf_channel ch, + const struct bladerf_range **range) +{ + return dev->board->get_sample_rate_range(dev, ch, range); +} + +int bladerf_set_rational_sample_rate(struct bladerf *dev, + bladerf_channel ch, + struct bladerf_rational_rate *rate, + struct bladerf_rational_rate *actual) +{ + int status; + bladerf_feature feature = dev->feature; + + MUTEX_LOCK(&dev->lock); + status = dev->board->set_rational_sample_rate(dev, ch, rate, actual); + MUTEX_UNLOCK(&dev->lock); + + /***************************************************** + Register config for OVERSAMPLE operation + + Sample rate assignments clear previous register + values. We must reassign for every set_samplerate(). + + Note: bladerf_set_rfic_register is mutex locked. Must + be placed outside of a mutex lock like above. + *******************************************************/ + if ((feature & BLADERF_FEATURE_OVERSAMPLE)) { + status = bladerf_set_oversample_register_config(dev); + if (status != 0) { + log_error("Oversample register config failure\n"); + } + } + + return status; +} + +int bladerf_get_rational_sample_rate(struct bladerf *dev, + bladerf_channel ch, + struct bladerf_rational_rate *rate) +{ + int status; + MUTEX_LOCK(&dev->lock); + + status = dev->board->get_rational_sample_rate(dev, ch, rate); + + MUTEX_UNLOCK(&dev->lock); + return status; +} + +/******************************************************************************/ +/* Bandwidth */ +/******************************************************************************/ + +int bladerf_set_bandwidth(struct bladerf *dev, + bladerf_channel ch, + bladerf_bandwidth bandwidth, + bladerf_bandwidth *actual) +{ + int status; + MUTEX_LOCK(&dev->lock); + + status = dev->board->set_bandwidth(dev, ch, bandwidth, actual); + + MUTEX_UNLOCK(&dev->lock); + return status; +} + +int bladerf_get_bandwidth(struct bladerf *dev, + bladerf_channel ch, + bladerf_bandwidth *bandwidth) +{ + int status; + MUTEX_LOCK(&dev->lock); + + status = dev->board->get_bandwidth(dev, ch, bandwidth); + + MUTEX_UNLOCK(&dev->lock); + return status; +} + +int bladerf_get_bandwidth_range(struct bladerf *dev, + bladerf_channel ch, + const struct bladerf_range **range) +{ + return dev->board->get_bandwidth_range(dev, ch, range); +} + +/******************************************************************************/ +/* Frequency */ +/******************************************************************************/ + +int bladerf_set_frequency(struct bladerf *dev, + bladerf_channel ch, + bladerf_frequency frequency) +{ + int status; + MUTEX_LOCK(&dev->lock); + + status = dev->board->set_frequency(dev, ch, frequency); + + if (dev->gain_tbls[ch].enabled && status == 0) { + status = apply_gain_correction(dev, ch, frequency); + if (status != 0) { + log_error("Failed to set gain correction\n"); + } + } + + MUTEX_UNLOCK(&dev->lock); + return status; +} + +int bladerf_get_frequency(struct bladerf *dev, + bladerf_channel ch, + bladerf_frequency *frequency) +{ + int status; + MUTEX_LOCK(&dev->lock); + + status = dev->board->get_frequency(dev, ch, frequency); + + MUTEX_UNLOCK(&dev->lock); + return status; +} + +int bladerf_get_frequency_range(struct bladerf *dev, + bladerf_channel ch, + const struct bladerf_range **range) +{ + return dev->board->get_frequency_range(dev, ch, range); +} + +int bladerf_select_band(struct bladerf *dev, + bladerf_channel ch, + bladerf_frequency frequency) +{ + int status; + MUTEX_LOCK(&dev->lock); + + status = dev->board->select_band(dev, ch, frequency); + + MUTEX_UNLOCK(&dev->lock); + return status; +} + +/******************************************************************************/ +/* RF Ports*/ +/******************************************************************************/ + +int bladerf_set_rf_port(struct bladerf *dev, + bladerf_channel ch, + const char *port) +{ + int status; + MUTEX_LOCK(&dev->lock); + + status = dev->board->set_rf_port(dev, ch, port); + + MUTEX_UNLOCK(&dev->lock); + return status; +} + +int bladerf_get_rf_port(struct bladerf *dev, + bladerf_channel ch, + const char **port) +{ + int status; + MUTEX_LOCK(&dev->lock); + + status = dev->board->get_rf_port(dev, ch, port); + + MUTEX_UNLOCK(&dev->lock); + return status; +} + +int bladerf_get_rf_ports(struct bladerf *dev, + bladerf_channel ch, + const char **ports, + unsigned int count) +{ + int status; + MUTEX_LOCK(&dev->lock); + + status = dev->board->get_rf_ports(dev, ch, ports, count); + + MUTEX_UNLOCK(&dev->lock); + return status; +} + +/******************************************************************************/ +/* Scheduled Tuning */ +/******************************************************************************/ + +int bladerf_get_quick_tune(struct bladerf *dev, + bladerf_channel ch, + struct bladerf_quick_tune *quick_tune) +{ + int status; + MUTEX_LOCK(&dev->lock); + + status = dev->board->get_quick_tune(dev, ch, quick_tune); + + MUTEX_UNLOCK(&dev->lock); + return status; +} + +int bladerf_print_quick_tune(struct bladerf *dev, const struct bladerf_quick_tune *qt) { + if (dev == NULL || qt == NULL) { + log_error("Device handle or quick tune structure is NULL.\n"); + return BLADERF_ERR_INVAL; + } + + const char *board_name = bladerf_get_board_name(dev); + if (board_name == NULL) { + log_error("Failed to get board name.\n"); + return BLADERF_ERR_UNEXPECTED; + } + + printf("board: %s\n", board_name); + if (strcmp(board_name, "bladerf1") == 0) { + printf("freqsel: %u\n", qt->freqsel); + printf("vcocap: %u\n", qt->vcocap); + printf("nint: %u\n", qt->nint); + printf("nfrac: %u\n", qt->nfrac); + printf("flags: %u\n", qt->flags); + printf("xb_gpio: %u\n", qt->xb_gpio); + } else if (strcmp(board_name, "bladerf2") == 0) { + printf("nios_profile: %u\n", qt->nios_profile); + printf("rffe_profile: %u\n", qt->rffe_profile); + printf("port: %u\n", qt->port); + printf("spdt: %u\n", qt->spdt); + } else { + log_error("Unknown bladeRF board name: %s\n", board_name); + return BLADERF_ERR_UNEXPECTED; + } + + return 0; +} + +int bladerf_schedule_retune(struct bladerf *dev, + bladerf_channel ch, + bladerf_timestamp timestamp, + bladerf_frequency frequency, + struct bladerf_quick_tune *quick_tune) + +{ + int status; + MUTEX_LOCK(&dev->lock); + + status = + dev->board->schedule_retune(dev, ch, timestamp, frequency, quick_tune); + + MUTEX_UNLOCK(&dev->lock); + return status; +} + +int bladerf_cancel_scheduled_retunes(struct bladerf *dev, bladerf_channel ch) +{ + int status; + MUTEX_LOCK(&dev->lock); + + status = dev->board->cancel_scheduled_retunes(dev, ch); + + MUTEX_UNLOCK(&dev->lock); + return status; +} + +/******************************************************************************/ +/* DC/Phase/Gain Correction */ +/******************************************************************************/ + +int bladerf_get_correction(struct bladerf *dev, + bladerf_channel ch, + bladerf_correction corr, + bladerf_correction_value *value) +{ + int status; + MUTEX_LOCK(&dev->lock); + + status = dev->board->get_correction(dev, ch, corr, value); + + MUTEX_UNLOCK(&dev->lock); + return status; +} + +int bladerf_set_correction(struct bladerf *dev, + bladerf_channel ch, + bladerf_correction corr, + bladerf_correction_value value) +{ + int status; + MUTEX_LOCK(&dev->lock); + + status = dev->board->set_correction(dev, ch, corr, value); + + MUTEX_UNLOCK(&dev->lock); + return status; +} + +/******************************************************************************/ +/* Trigger */ +/******************************************************************************/ + +int bladerf_trigger_init(struct bladerf *dev, + bladerf_channel ch, + bladerf_trigger_signal signal, + struct bladerf_trigger *trigger) +{ + int status; + MUTEX_LOCK(&dev->lock); + + status = dev->board->trigger_init(dev, ch, signal, trigger); + + MUTEX_UNLOCK(&dev->lock); + return status; +} + +int bladerf_trigger_arm(struct bladerf *dev, + const struct bladerf_trigger *trigger, + bool arm, + uint64_t resv1, + uint64_t resv2) +{ + int status; + MUTEX_LOCK(&dev->lock); + + status = dev->board->trigger_arm(dev, trigger, arm, resv1, resv2); + + MUTEX_UNLOCK(&dev->lock); + return status; +} + +int bladerf_trigger_fire(struct bladerf *dev, + const struct bladerf_trigger *trigger) +{ + int status; + MUTEX_LOCK(&dev->lock); + + status = dev->board->trigger_fire(dev, trigger); + + MUTEX_UNLOCK(&dev->lock); + return status; +} + +int bladerf_trigger_state(struct bladerf *dev, + const struct bladerf_trigger *trigger, + bool *is_armed, + bool *has_fired, + bool *fire_requested, + uint64_t *reserved1, + uint64_t *reserved2) +{ + int status; + MUTEX_LOCK(&dev->lock); + + status = dev->board->trigger_state(dev, trigger, is_armed, has_fired, + fire_requested, reserved1, reserved2); + + MUTEX_UNLOCK(&dev->lock); + return status; +} + +/******************************************************************************/ +/* Streaming */ +/******************************************************************************/ + +int bladerf_init_stream(struct bladerf_stream **stream, + struct bladerf *dev, + bladerf_stream_cb callback, + void ***buffers, + size_t num_buffers, + bladerf_format format, + size_t samples_per_buffer, + size_t num_transfers, + void *data) +{ + int status; + bladerf_sample_rate tx_samp_rate; + bladerf_sample_rate rx_samp_rate; + MUTEX_LOCK(&dev->lock); + + if (format == BLADERF_FORMAT_SC8_Q7 || format == BLADERF_FORMAT_SC8_Q7_META) { + if (strcmp(bladerf_get_board_name(dev), "bladerf2") != 0) { + log_error("bladeRF 2.0 required for 8bit format\n"); + return BLADERF_ERR_UNSUPPORTED; + } + } + + status = dev->board->init_stream(stream, dev, callback, buffers, + num_buffers, format, samples_per_buffer, + num_transfers, data); + + dev->board->get_sample_rate(dev, BLADERF_MODULE_TX, &tx_samp_rate); + if (tx_samp_rate) { + if (tx_samp_rate < num_transfers * samples_per_buffer / (*stream)->transfer_timeout) { + log_warning("TX samples may be dropped.\n"); + log_warning("Condition to meet: samp_rate > num_transfers * samples_per_buffer / transfer_timeout\n"); + } + } + + dev->board->get_sample_rate(dev, BLADERF_MODULE_RX, &rx_samp_rate); + if (rx_samp_rate) { + if (rx_samp_rate < num_transfers * samples_per_buffer / (*stream)->transfer_timeout) { + log_warning("RX samples may be dropped.\n"); + log_warning("Condition to meet: samp_rate > num_transfers * samples_per_buffer / transfer_timeout\n"); + } + } + + MUTEX_UNLOCK(&dev->lock); + return status; +} + +int bladerf_stream(struct bladerf_stream *stream, bladerf_channel_layout layout) +{ + return stream->dev->board->stream(stream, layout); +} + +int bladerf_submit_stream_buffer(struct bladerf_stream *stream, + void *buffer, + unsigned int timeout_ms) +{ + return stream->dev->board->submit_stream_buffer(stream, buffer, timeout_ms, + false); +} + +int bladerf_submit_stream_buffer_nb(struct bladerf_stream *stream, void *buffer) +{ + return stream->dev->board->submit_stream_buffer(stream, buffer, 0, true); +} + +void bladerf_deinit_stream(struct bladerf_stream *stream) +{ + if (stream) { + stream->dev->board->deinit_stream(stream); + } +} + +int bladerf_set_stream_timeout(struct bladerf *dev, + bladerf_direction dir, + unsigned int timeout) +{ + int status; + MUTEX_LOCK(&dev->lock); + + status = dev->board->set_stream_timeout(dev, dir, timeout); + + MUTEX_UNLOCK(&dev->lock); + return status; +} + +int bladerf_get_stream_timeout(struct bladerf *dev, + bladerf_direction dir, + unsigned int *timeout) +{ + int status; + MUTEX_LOCK(&dev->lock); + + status = dev->board->get_stream_timeout(dev, dir, timeout); + + MUTEX_UNLOCK(&dev->lock); + return status; +} + +int bladerf_sync_config(struct bladerf *dev, + bladerf_channel_layout layout, + bladerf_format format, + unsigned int num_buffers, + unsigned int buffer_size, + unsigned int num_transfers, + unsigned int stream_timeout) +{ + int status; + MUTEX_LOCK(&dev->lock); + + if (format == BLADERF_FORMAT_SC8_Q7 || format == BLADERF_FORMAT_SC8_Q7_META) { + if (strcmp(bladerf_get_board_name(dev), "bladerf2") != 0) { + log_error("bladeRF 2.0 required for 8bit format\n"); + return BLADERF_ERR_UNSUPPORTED; + } + } + + status = + dev->board->sync_config(dev, layout, format, num_buffers, buffer_size, + num_transfers, stream_timeout); + + MUTEX_UNLOCK(&dev->lock); + return status; +} + +int bladerf_sync_tx(struct bladerf *dev, + void const *samples, + unsigned int num_samples, + struct bladerf_metadata *metadata, + unsigned int timeout_ms) +{ + CHECK_NULL(samples); + return dev->board->sync_tx(dev, samples, num_samples, metadata, timeout_ms); +} + +int bladerf_sync_rx(struct bladerf *dev, + void *samples, + unsigned int num_samples, + struct bladerf_metadata *metadata, + unsigned int timeout_ms) +{ + return dev->board->sync_rx(dev, samples, num_samples, metadata, timeout_ms); +} + +int bladerf_get_timestamp(struct bladerf *dev, + bladerf_direction dir, + bladerf_timestamp *timestamp) +{ + int status; + MUTEX_LOCK(&dev->lock); + + status = dev->board->get_timestamp(dev, dir, timestamp); + + MUTEX_UNLOCK(&dev->lock); + return status; +} + +int bladerf_interleave_stream_buffer(bladerf_channel_layout layout, + bladerf_format format, + unsigned int buffer_size, + void *samples) +{ + return _interleave_interleave_buf(layout, format, buffer_size, samples); +} + +int bladerf_deinterleave_stream_buffer(bladerf_channel_layout layout, + bladerf_format format, + unsigned int buffer_size, + void *samples) +{ + return _interleave_deinterleave_buf(layout, format, buffer_size, samples); +} + +/******************************************************************************/ +/* FPGA/Firmware Loading/Flashing */ +/******************************************************************************/ + +int bladerf_load_fpga(struct bladerf *dev, const char *fpga_file) +{ + uint8_t *buf = NULL; + size_t buf_size; + int status; + + status = file_read_buffer(fpga_file, &buf, &buf_size); + if (status != 0) { + log_error("Failed to read FPGA image: %s\n", bladerf_strerror(status)); + goto exit; + } + + status = dev->board->load_fpga(dev, buf, buf_size); + +exit: + free(buf); + return status; +} + +int bladerf_flash_fpga(struct bladerf *dev, const char *fpga_file) +{ + uint8_t *buf = NULL; + size_t buf_size; + int status; + + status = file_read_buffer(fpga_file, &buf, &buf_size); + if (status != 0) { + goto exit; + } + + MUTEX_LOCK(&dev->lock); + status = dev->board->flash_fpga(dev, buf, buf_size); + MUTEX_UNLOCK(&dev->lock); + +exit: + free(buf); + return status; +} + +int bladerf_erase_stored_fpga(struct bladerf *dev) +{ + int status; + MUTEX_LOCK(&dev->lock); + + status = dev->board->erase_stored_fpga(dev); + + MUTEX_UNLOCK(&dev->lock); + return status; +} + +int bladerf_flash_firmware(struct bladerf *dev, const char *firmware_file) +{ + uint8_t *buf = NULL; + size_t buf_size; + int status; + + status = file_read_buffer(firmware_file, &buf, &buf_size); + if (status != 0) { + goto exit; + } + + MUTEX_LOCK(&dev->lock); + status = dev->board->flash_firmware(dev, buf, buf_size); + MUTEX_UNLOCK(&dev->lock); + +exit: + free(buf); + return status; +} + +int bladerf_device_reset(struct bladerf *dev) +{ + int status; + MUTEX_LOCK(&dev->lock); + + status = dev->board->device_reset(dev); + + MUTEX_UNLOCK(&dev->lock); + return status; +} + +/******************************************************************************/ +/* Tuning mode */ +/******************************************************************************/ + +int bladerf_set_tuning_mode(struct bladerf *dev, bladerf_tuning_mode mode) +{ + int status; + MUTEX_LOCK(&dev->lock); + + status = dev->board->set_tuning_mode(dev, mode); + + MUTEX_UNLOCK(&dev->lock); + return status; +} + +int bladerf_get_tuning_mode(struct bladerf *dev, bladerf_tuning_mode *mode) +{ + int status; + MUTEX_LOCK(&dev->lock); + + status = dev->board->get_tuning_mode(dev, mode); + + MUTEX_UNLOCK(&dev->lock); + return status; +} + +/******************************************************************************/ +/* Loopback */ +/******************************************************************************/ + +int bladerf_get_loopback_modes(struct bladerf *dev, + struct bladerf_loopback_modes const **modes) +{ + int status; + + status = dev->board->get_loopback_modes(dev, modes); + + return status; +} + +bool bladerf_is_loopback_mode_supported(struct bladerf *dev, + bladerf_loopback mode) +{ + struct bladerf_loopback_modes modes; + struct bladerf_loopback_modes const *modesptr = &modes; + int i, count; + + count = bladerf_get_loopback_modes(dev, &modesptr); + + for (i = 0; i < count; ++i) { + if (modesptr[i].mode == mode) { + return true; + } + } + + return false; +} + +int bladerf_set_loopback(struct bladerf *dev, bladerf_loopback l) +{ + int status; + MUTEX_LOCK(&dev->lock); + + status = dev->board->set_loopback(dev, l); + + MUTEX_UNLOCK(&dev->lock); + return status; +} + +int bladerf_get_loopback(struct bladerf *dev, bladerf_loopback *l) +{ + int status; + MUTEX_LOCK(&dev->lock); + + status = dev->board->get_loopback(dev, l); + + MUTEX_UNLOCK(&dev->lock); + return status; +} + +/******************************************************************************/ +/* Sample RX FPGA Mux */ +/******************************************************************************/ + +int bladerf_set_rx_mux(struct bladerf *dev, bladerf_rx_mux mux) +{ + int status; + MUTEX_LOCK(&dev->lock); + + status = dev->board->set_rx_mux(dev, mux); + + MUTEX_UNLOCK(&dev->lock); + return status; +} + +int bladerf_get_rx_mux(struct bladerf *dev, bladerf_rx_mux *mux) +{ + int status; + MUTEX_LOCK(&dev->lock); + + status = dev->board->get_rx_mux(dev, mux); + + MUTEX_UNLOCK(&dev->lock); + return status; +} + +/******************************************************************************/ +/* Low-level VCTCXO Tamer Mode */ +/******************************************************************************/ + +int bladerf_set_vctcxo_tamer_mode(struct bladerf *dev, + bladerf_vctcxo_tamer_mode mode) +{ + int status; + MUTEX_LOCK(&dev->lock); + + status = dev->board->set_vctcxo_tamer_mode(dev, mode); + + MUTEX_UNLOCK(&dev->lock); + return status; +} + +int bladerf_get_vctcxo_tamer_mode(struct bladerf *dev, + bladerf_vctcxo_tamer_mode *mode) +{ + int status; + MUTEX_LOCK(&dev->lock); + + status = dev->board->get_vctcxo_tamer_mode(dev, mode); + + MUTEX_UNLOCK(&dev->lock); + return status; +} + +/******************************************************************************/ +/* Low-level VCTCXO Trim DAC access */ +/******************************************************************************/ + +int bladerf_get_vctcxo_trim(struct bladerf *dev, uint16_t *trim) +{ + int status; + MUTEX_LOCK(&dev->lock); + + status = dev->board->get_vctcxo_trim(dev, trim); + + MUTEX_UNLOCK(&dev->lock); + return status; +} + +int bladerf_trim_dac_read(struct bladerf *dev, uint16_t *trim) +{ + int status; + MUTEX_LOCK(&dev->lock); + + status = dev->board->trim_dac_read(dev, trim); + + MUTEX_UNLOCK(&dev->lock); + return status; +} + +int bladerf_trim_dac_write(struct bladerf *dev, uint16_t trim) +{ + int status; + MUTEX_LOCK(&dev->lock); + + status = dev->board->trim_dac_write(dev, trim); + + MUTEX_UNLOCK(&dev->lock); + return status; +} + +int bladerf_dac_read(struct bladerf *dev, uint16_t *trim) +{ + return bladerf_trim_dac_read(dev, trim); +} +int bladerf_dac_write(struct bladerf *dev, uint16_t trim) +{ + return bladerf_trim_dac_write(dev, trim); +} + +/******************************************************************************/ +/* Low-level Trigger control access */ +/******************************************************************************/ + +int bladerf_read_trigger(struct bladerf *dev, + bladerf_channel ch, + bladerf_trigger_signal trigger, + uint8_t *val) +{ + int status; + MUTEX_LOCK(&dev->lock); + + status = dev->board->read_trigger(dev, ch, trigger, val); + + MUTEX_UNLOCK(&dev->lock); + return status; +} + +int bladerf_write_trigger(struct bladerf *dev, + bladerf_channel ch, + bladerf_trigger_signal trigger, + uint8_t val) +{ + int status; + MUTEX_LOCK(&dev->lock); + + status = dev->board->write_trigger(dev, ch, trigger, val); + + MUTEX_UNLOCK(&dev->lock); + return status; +} + +/******************************************************************************/ +/* Low-level Wishbone Master access */ +/******************************************************************************/ +int bladerf_wishbone_master_read(struct bladerf *dev, uint32_t addr, uint32_t *data) +{ + int status; + MUTEX_LOCK(&dev->lock); + + status = dev->board->wishbone_master_read(dev, addr, data); + + MUTEX_UNLOCK(&dev->lock); + return status; +} + +int bladerf_wishbone_master_write(struct bladerf *dev, uint32_t addr, uint32_t data) +{ + int status; + MUTEX_LOCK(&dev->lock); + + status = dev->board->wishbone_master_write(dev, addr, data); + + MUTEX_UNLOCK(&dev->lock); + return status; +} + +/******************************************************************************/ +/* Low-level Configuration GPIO access */ +/******************************************************************************/ + +int bladerf_config_gpio_read(struct bladerf *dev, uint32_t *val) +{ + int status; + MUTEX_LOCK(&dev->lock); + + status = dev->board->config_gpio_read(dev, val); + + MUTEX_UNLOCK(&dev->lock); + return status; +} + +int bladerf_config_gpio_write(struct bladerf *dev, uint32_t val) +{ + int status; + MUTEX_LOCK(&dev->lock); + + status = dev->board->config_gpio_write(dev, val); + + MUTEX_UNLOCK(&dev->lock); + return status; +} + +/******************************************************************************/ +/* Low-level SPI Flash access */ +/******************************************************************************/ + +int bladerf_erase_flash(struct bladerf *dev, + uint32_t erase_block, + uint32_t count) +{ + int status; + MUTEX_LOCK(&dev->lock); + + status = dev->board->erase_flash(dev, erase_block, count); + + MUTEX_UNLOCK(&dev->lock); + return status; +} + +int bladerf_erase_flash_bytes(struct bladerf *dev, + uint32_t address, + uint32_t length) +{ + int status; + uint32_t eb; + uint32_t count; + + /* Make sure address is aligned to an erase block boundary */ + if( (address % dev->flash_arch->ebsize_bytes) == 0 ) { + /* Convert into units of flash pages */ + eb = address / dev->flash_arch->ebsize_bytes; + } else { + log_error("Address or length not aligned on a flash page boundary.\n"); + return BLADERF_ERR_INVAL; + } + + /* Check for the case of erasing less than 1 erase block. + * For example, the calibration data. If so, round up to 1 EB. + * If erasing more than 1 EB worth of data, make sure the length + * is aligned to an EB boundary. */ + if( (length > 0) && (length < dev->flash_arch->ebsize_bytes) ) { + count = 1; + } else if ((length % dev->flash_arch->ebsize_bytes) == 0) { + /* Convert into units of flash pages */ + count = length / dev->flash_arch->ebsize_bytes; + } else { + log_error("Address or length not aligned on a flash page boundary.\n"); + return BLADERF_ERR_INVAL; + } + + status = bladerf_erase_flash(dev, eb, count); + + return status; +} + +int bladerf_read_flash(struct bladerf *dev, + uint8_t *buf, + uint32_t page, + uint32_t count) +{ + int status; + MUTEX_LOCK(&dev->lock); + + status = dev->board->read_flash(dev, buf, page, count); + + MUTEX_UNLOCK(&dev->lock); + return status; +} + +int bladerf_read_flash_bytes(struct bladerf *dev, + uint8_t *buf, + uint32_t address, + uint32_t length) +{ + int status; + uint32_t page; + uint32_t count; + + /* Check alignment */ + if( ((address % dev->flash_arch->psize_bytes) != 0) || + ((length % dev->flash_arch->psize_bytes) != 0) ) { + log_error("Address or length not aligned on a flash page boundary.\n"); + return BLADERF_ERR_INVAL; + } + + /* Convert into units of flash pages */ + page = address / dev->flash_arch->psize_bytes; + count = length / dev->flash_arch->psize_bytes; + + status = bladerf_read_flash(dev, buf, page, count); + + return status; +} + +int bladerf_write_flash(struct bladerf *dev, + const uint8_t *buf, + uint32_t page, + uint32_t count) +{ + int status; + MUTEX_LOCK(&dev->lock); + + status = dev->board->write_flash(dev, buf, page, count); + + MUTEX_UNLOCK(&dev->lock); + return status; +} + +int bladerf_write_flash_bytes(struct bladerf *dev, + const uint8_t *buf, + uint32_t address, + uint32_t length) +{ + int status; + uint32_t page; + uint32_t count; + + /* Check alignment */ + if( ((address % dev->flash_arch->psize_bytes) != 0) || + ((length % dev->flash_arch->psize_bytes) != 0) ) { + log_error("Address or length not aligned on a flash page boundary.\n"); + return BLADERF_ERR_INVAL; + } + + /* Convert address and length into units of flash pages */ + page = address / dev->flash_arch->psize_bytes; + count = length / dev->flash_arch->psize_bytes; + + status = bladerf_write_flash(dev, buf, page, count); + return status; +} + +int bladerf_read_otp(struct bladerf *dev, + uint8_t *buf) +{ + int status; + MUTEX_LOCK(&dev->lock); + + status = dev->backend->get_otp(dev, (char *)buf); + + MUTEX_UNLOCK(&dev->lock); + return status; +} + +int bladerf_write_otp(struct bladerf *dev, + uint8_t *buf) +{ + int status; + MUTEX_LOCK(&dev->lock); + + status = dev->backend->write_otp(dev, (char *)buf); + + MUTEX_UNLOCK(&dev->lock); + return status; +} + +int bladerf_lock_otp(struct bladerf *dev) +{ + int status; + MUTEX_LOCK(&dev->lock); + + status = dev->backend->lock_otp(dev); + + MUTEX_UNLOCK(&dev->lock); + return status; +} + +/******************************************************************************/ +/* Helpers & Miscellaneous */ +/******************************************************************************/ + +const char *bladerf_strerror(int error) +{ + switch (error) { + case BLADERF_ERR_UNEXPECTED: + return "An unexpected error occurred"; + case BLADERF_ERR_RANGE: + return "Provided parameter was out of the allowable range"; + case BLADERF_ERR_INVAL: + return "Invalid operation or parameter"; + case BLADERF_ERR_MEM: + return "A memory allocation error occurred"; + case BLADERF_ERR_IO: + return "File or device I/O failure"; + case BLADERF_ERR_TIMEOUT: + return "Operation timed out"; + case BLADERF_ERR_NODEV: + return "No devices available"; + case BLADERF_ERR_UNSUPPORTED: + return "Operation not supported"; + case BLADERF_ERR_MISALIGNED: + return "Misaligned flash access"; + case BLADERF_ERR_CHECKSUM: + return "Invalid checksum"; + case BLADERF_ERR_NO_FILE: + return "File not found"; + case BLADERF_ERR_UPDATE_FPGA: + return "An FPGA update is required"; + case BLADERF_ERR_UPDATE_FW: + return "A firmware update is required"; + case BLADERF_ERR_TIME_PAST: + return "Requested timestamp is in the past"; + case BLADERF_ERR_QUEUE_FULL: + return "Could not enqueue data into full queue"; + case BLADERF_ERR_FPGA_OP: + return "An FPGA operation reported a failure"; + case BLADERF_ERR_PERMISSION: + return "Insufficient permissions for the requested operation"; + case BLADERF_ERR_WOULD_BLOCK: + return "The operation would block, but has been requested to be " + "non-blocking"; + case BLADERF_ERR_NOT_INIT: + return "Insufficient initialization for the requested operation"; + case 0: + return "Success"; + default: + return "Unknown error code"; + } +} + +const char *bladerf_backend_str(bladerf_backend backend) +{ + return backend2str(backend); +} + +void bladerf_log_set_verbosity(bladerf_log_level level) +{ + log_set_verbosity(level); +#if defined(LOG_SYSLOG_ENABLED) + log_debug("Log verbosity has been set to: %d", level); +#endif +} + +void bladerf_set_usb_reset_on_open(bool enabled) +{ +#if ENABLE_USB_DEV_RESET_ON_OPEN + bladerf_usb_reset_device_on_open = enabled; + + log_verbose("USB reset on open %s\n", enabled ? "enabled" : "disabled"); +#else + log_verbose("%s has no effect. " + "ENABLE_USB_DEV_RESET_ON_OPEN not set at compile-time.\n", + __FUNCTION__); +#endif +} + +/******************************************************************************/ +/* Expansion board APIs */ +/******************************************************************************/ + +int bladerf_expansion_attach(struct bladerf *dev, bladerf_xb xb) +{ + int status; + MUTEX_LOCK(&dev->lock); + + status = dev->board->expansion_attach(dev, xb); + + MUTEX_UNLOCK(&dev->lock); + return status; +} + +int bladerf_expansion_get_attached(struct bladerf *dev, bladerf_xb *xb) +{ + int status; + MUTEX_LOCK(&dev->lock); + + status = dev->board->expansion_get_attached(dev, xb); + + MUTEX_UNLOCK(&dev->lock); + return status; +} + +/* XB100 */ + +int bladerf_expansion_gpio_read(struct bladerf *dev, uint32_t *val) +{ + int status; + MUTEX_LOCK(&dev->lock); + + status = xb100_gpio_read(dev, val); + + MUTEX_UNLOCK(&dev->lock); + return status; +} + +int bladerf_expansion_gpio_write(struct bladerf *dev, uint32_t val) +{ + int status; + MUTEX_LOCK(&dev->lock); + + status = xb100_gpio_write(dev, val); + + MUTEX_UNLOCK(&dev->lock); + return status; +} + +int bladerf_expansion_gpio_masked_write(struct bladerf *dev, + uint32_t mask, + uint32_t val) +{ + int status; + MUTEX_LOCK(&dev->lock); + + status = xb100_gpio_masked_write(dev, mask, val); + + MUTEX_UNLOCK(&dev->lock); + return status; +} + +int bladerf_expansion_gpio_dir_read(struct bladerf *dev, uint32_t *val) +{ + int status; + MUTEX_LOCK(&dev->lock); + + status = xb100_gpio_dir_read(dev, val); + + MUTEX_UNLOCK(&dev->lock); + return status; +} + +int bladerf_expansion_gpio_dir_write(struct bladerf *dev, uint32_t val) +{ + int status; + MUTEX_LOCK(&dev->lock); + + status = xb100_gpio_dir_write(dev, val); + + MUTEX_UNLOCK(&dev->lock); + return status; +} + +int bladerf_expansion_gpio_dir_masked_write(struct bladerf *dev, + uint32_t mask, + uint32_t val) +{ + int status; + MUTEX_LOCK(&dev->lock); + + status = xb100_gpio_dir_masked_write(dev, mask, val); + + MUTEX_UNLOCK(&dev->lock); + return status; +} + +/* XB200 */ + +int bladerf_xb200_set_filterbank(struct bladerf *dev, + bladerf_channel ch, + bladerf_xb200_filter filter) +{ + int status; + MUTEX_LOCK(&dev->lock); + + status = xb200_set_filterbank(dev, ch, filter); + + MUTEX_UNLOCK(&dev->lock); + return status; +} + +int bladerf_xb200_get_filterbank(struct bladerf *dev, + bladerf_channel ch, + bladerf_xb200_filter *filter) +{ + int status; + MUTEX_LOCK(&dev->lock); + + status = xb200_get_filterbank(dev, ch, filter); + + MUTEX_UNLOCK(&dev->lock); + return status; +} + +int bladerf_xb200_set_path(struct bladerf *dev, + bladerf_channel ch, + bladerf_xb200_path path) +{ + int status; + MUTEX_LOCK(&dev->lock); + + status = xb200_set_path(dev, ch, path); + + MUTEX_UNLOCK(&dev->lock); + return status; +} + +int bladerf_xb200_get_path(struct bladerf *dev, + bladerf_channel ch, + bladerf_xb200_path *path) +{ + int status; + MUTEX_LOCK(&dev->lock); + + status = xb200_get_path(dev, ch, path); + + MUTEX_UNLOCK(&dev->lock); + return status; +} + +/* XB300 */ + +int bladerf_xb300_set_trx(struct bladerf *dev, bladerf_xb300_trx trx) +{ + int status; + MUTEX_LOCK(&dev->lock); + + status = xb300_set_trx(dev, trx); + + MUTEX_UNLOCK(&dev->lock); + return status; +} + +int bladerf_xb300_get_trx(struct bladerf *dev, bladerf_xb300_trx *trx) +{ + int status; + MUTEX_LOCK(&dev->lock); + + status = xb300_get_trx(dev, trx); + + MUTEX_UNLOCK(&dev->lock); + return status; +} + +int bladerf_xb300_set_amplifier_enable(struct bladerf *dev, + bladerf_xb300_amplifier amp, + bool enable) +{ + int status; + MUTEX_LOCK(&dev->lock); + + status = xb300_set_amplifier_enable(dev, amp, enable); + + MUTEX_UNLOCK(&dev->lock); + return status; +} + +int bladerf_xb300_get_amplifier_enable(struct bladerf *dev, + bladerf_xb300_amplifier amp, + bool *enable) +{ + int status; + MUTEX_LOCK(&dev->lock); + + status = xb300_get_amplifier_enable(dev, amp, enable); + + MUTEX_UNLOCK(&dev->lock); + return status; +} + +int bladerf_xb300_get_output_power(struct bladerf *dev, float *val) +{ + int status; + MUTEX_LOCK(&dev->lock); + + status = xb300_get_output_power(dev, val); + + MUTEX_UNLOCK(&dev->lock); + return status; +} + +/******************************************************************************/ +/* Features */ +/******************************************************************************/ + +int bladerf_enable_feature(struct bladerf *dev, bladerf_feature feature, bool enable) +{ + int status; + MUTEX_LOCK(&dev->lock); + + status = 0; + + if(feature == BLADERF_FEATURE_DEFAULT) { + dev->feature = 0; + } else { + if(feature == BLADERF_FEATURE_OVERSAMPLE) { + if (strcmp(bladerf_get_board_name(dev), "bladerf2") != 0) { + log_error("BladeRF2 required for OVERSAMPLE feature\n"); + status = BLADERF_ERR_UNSUPPORTED; + } + } else { + /* Unknown / Unsupported feature */ + status = BLADERF_ERR_UNSUPPORTED; + } + + if (status == 0) { + if (enable) { + dev->feature |= feature; + } else { + dev->feature &= ~feature; + } + } + } + + MUTEX_UNLOCK(&dev->lock); + return status; +} + +int bladerf_get_feature(struct bladerf *dev, bladerf_feature* feature) +{ + MUTEX_LOCK(&dev->lock); + *feature = dev->feature; + MUTEX_UNLOCK(&dev->lock); + + return 0; +} + +int bladerf_set_oversample_register_config(struct bladerf *dev) { + const char *board_name; + board_name = bladerf_get_board_name(dev); + + if (strcmp(board_name, "bladerf2") != 0) { + log_error("Oversample register config only applicable\n" + "for the bladeRF 2.0"); + + return BLADERF_ERR_UNSUPPORTED; + } + + bladerf_set_rfic_register(dev,0x003,0x54); // OC Register + + /* TX Register Assignments */ + bladerf_set_rfic_register(dev,0x02,0xc0); // TX Enable and Filter Control + bladerf_set_rfic_register(dev,0xc2,0x9f); // TX BBF R1 + bladerf_set_rfic_register(dev,0xc3,0x9f); // TX baseband filter R2 + bladerf_set_rfic_register(dev,0xc4,0x9f); // TX baseband filter R3 + bladerf_set_rfic_register(dev,0xc5,0x9f); // TX baseband filter R4 + bladerf_set_rfic_register(dev,0xc6,0x9f); // TX baseband filter real pole word + bladerf_set_rfic_register(dev,0xc7,0x00); // TX baseband filter C1 + bladerf_set_rfic_register(dev,0xc8,0x00); // TX baseband filter C2 + bladerf_set_rfic_register(dev,0xc9,0x00); // TX baseband filter real pole word + + /* RX Register Assignments */ + // Gain and calibration + bladerf_set_rfic_register(dev,0x1e0,0x00); // RX1 BBF R1A + bladerf_set_rfic_register(dev,0x1e1,0x00); // RX2 BBF R1A + bladerf_set_rfic_register(dev,0x1e2,0x00); // RX1 tune control + bladerf_set_rfic_register(dev,0x1e3,0x00); // RX2 tune control + bladerf_set_rfic_register(dev,0x1e4,0x00); // RX1 BBF R5 + bladerf_set_rfic_register(dev,0x1e5,0x00); // RX2 BBF R5 + bladerf_set_rfic_register(dev,0x1e6,0x00); // RX BBF R2346 + + // Miller and BBF caps + bladerf_set_rfic_register(dev,0x1e7,0x00); // RX BBF C1 MSB + bladerf_set_rfic_register(dev,0x1e8,0x00); // RX BBF C1 LSB + bladerf_set_rfic_register(dev,0x1e9,0x00); // RX baseband filter real pole word + bladerf_set_rfic_register(dev,0x1ea,0x00); + bladerf_set_rfic_register(dev,0x1eb,0x00); + bladerf_set_rfic_register(dev,0x1ec,0x00); + bladerf_set_rfic_register(dev,0x1ed,0x00); + bladerf_set_rfic_register(dev,0x1ee,0x00); + bladerf_set_rfic_register(dev,0x1ef,0x00); + + // BIST and Data Port Test Config [D1:D0] "Must be 2’b00" + bladerf_set_rfic_register(dev,0x3f6,0x03); + + return 0; +} + +/******************************************************************************/ +/* Calibration */ +/******************************************************************************/ + +int bladerf_load_gain_calibration(struct bladerf *dev, bladerf_channel ch, const char* cal_file_loc) +{ + int status = 0; + const char *board_name; + char *full_path = NULL; + char *full_path_bin = NULL; + char *ext; + + size_t filename_len = PATH_MAX; + char *filename = (char *)calloc(1, filename_len + 1); + CHECK_NULL(filename); + + bladerf_gain_mode gain_mode_before_gain_reset; + + log_debug("Loading gain calibration\n"); + MUTEX_LOCK(&dev->lock); + + board_name = bladerf_get_board_name(dev); + if (strcmp(board_name, "bladerf2") != 0) { + log_error("Gain calibration unsupported on this device: %s\n", board_name); + status = BLADERF_ERR_UNSUPPORTED; + goto error; + } + + if (cal_file_loc != NULL) { + strcpy(filename, cal_file_loc); + } else { + log_debug("No calibration file specified, using serial number\n"); + strcpy(filename, dev->ident.serial); + filename_len -= strlen(filename); + + if (BLADERF_CHANNEL_IS_TX(ch)) + strncat(filename, "_tx_gain_cal.tbl", filename_len); + else + strncat(filename, "_rx_gain_cal.tbl", filename_len); + } + + full_path = file_find(filename); + if (full_path == NULL) { + log_error("Failed to find gain calibration file: %s\n", filename); + status = BLADERF_ERR_NO_FILE; + goto error; + } + + /** Convert to binary format if CSV */ + full_path_bin = (char*)malloc(strlen(full_path) + 1); + strcpy(full_path_bin, full_path); + ext = strstr(full_path_bin, ".csv"); + if (ext) { + log_debug("Converting gain calibration to binary format\n"); + strcpy(ext, ".tbl"); + status = gain_cal_csv_to_bin(dev, full_path, full_path_bin, ch); + if (status != 0) { + log_error("Failed to convert csv to binary: %s -> %s\n", + full_path, full_path_bin); + status = EXIT_FAILURE; + goto error; + } + } + + status = load_gain_calibration(dev, ch, full_path_bin); + if (status != 0) { + log_error("Failed to load calibration\n"); + status = BLADERF_ERR_UNEXPECTED; + goto error; + } + + MUTEX_UNLOCK(&dev->lock); + + /* Save current gain mode before gain reset */ + if (BLADERF_CHANNEL_IS_TX(ch) == false) + dev->board->get_gain_mode(dev, ch, &gain_mode_before_gain_reset); + + /* Reset gain to ensure calibration adjustment is applied after loading */ + status = bladerf_set_gain(dev, ch, dev->gain_tbls[ch].gain_target); + if (status != 0) { + log_error("%s: Failed to reset gain.\n", __FUNCTION__); + goto error; + } + + /** Restore previous gain mode */ + if (BLADERF_CHANNEL_IS_TX(ch) == false) { + status = bladerf_set_gain_mode(dev, ch, gain_mode_before_gain_reset); + if (status != 0) { + log_error("%s: Failed to reset gain mode.\n", __FUNCTION__); + goto error; + } + } + +error: + if (full_path) + free(full_path); + if (full_path_bin) + free(full_path_bin); + if (filename) + free(filename); + + MUTEX_UNLOCK(&dev->lock); + return status; +} + +int bladerf_enable_gain_calibration(struct bladerf *dev, bladerf_channel ch, bool en) +{ + CHECK_NULL(dev); + int status = 0; + + if (dev->gain_tbls[ch].state == BLADERF_GAIN_CAL_UNINITIALIZED) { + log_warning("%s: Gain calibration not loaded\n", __FUNCTION__); + return 0; + } + + dev->gain_tbls[ch].enabled = en; + status = bladerf_set_gain(dev, ch, dev->gain_tbls[ch].gain_target); + if (status != 0) { + log_error("%s: Failed to reset gain.\n", __FUNCTION__); + return status; + } + + return status; +} + +int bladerf_print_gain_calibration(struct bladerf *dev, bladerf_channel ch, bool with_entries) +{ + CHECK_NULL(dev); + int status = 0; + const char *board_name; + struct bladerf_gain_cal_tbl *gain_tbls = dev->gain_tbls; + + board_name = bladerf_get_board_name(dev); + if (strcmp(board_name, "bladerf2") != 0) { + log_error("Gain calibration unsupported on this device: %s\n", board_name); + status = BLADERF_ERR_UNSUPPORTED; + goto error; + } + + if (gain_tbls[ch].state == BLADERF_GAIN_CAL_UNINITIALIZED) { + printf("Gain Calibration [%s]: uninitialized\n", channel2str(ch)); + return 0; + } + + printf("Gain Calibration [%s]: loaded\n", channel2str(ch)); + printf(" Status: %s\n", (gain_tbls[ch].enabled) ? "enabled" : "disabled"); + printf(" Version: %i.%i.%i\n", + gain_tbls[ch].version.major, gain_tbls[ch].version.minor, gain_tbls[ch].version.patch); + printf(" Number of Entries: %u\n", gain_tbls[ch].n_entries); + printf(" Start Frequency: %" PRIu64 " Hz\n", gain_tbls[ch].start_freq); + printf(" Stop Frequency: %" PRIu64 " Hz\n", gain_tbls[ch].stop_freq); + printf(" File Path: %s\n", gain_tbls[ch].file_path); + + if (with_entries) { + for (size_t i = 0; i < gain_tbls[ch].n_entries; i++) { + printf("%" PRIu64 ",%f\n", gain_tbls[ch].entries[i].freq, gain_tbls[ch].entries[i].gain_corr); + } + } + +error: + return status; +} + +int bladerf_get_gain_calibration(struct bladerf *dev, bladerf_channel ch, const struct bladerf_gain_cal_tbl **tbl) +{ + CHECK_NULL(dev); + MUTEX_LOCK(&dev->lock); + + if (dev->gain_tbls[ch].state != BLADERF_GAIN_CAL_LOADED) { + log_error("%s: Gain calibration not loaded\n", __FUNCTION__); + MUTEX_UNLOCK(&dev->lock); + return BLADERF_ERR_UNEXPECTED; + } + + *tbl = &(dev->gain_tbls[ch]); + + MUTEX_UNLOCK(&dev->lock); + return 0; +} + +int bladerf_get_gain_target(struct bladerf *dev, bladerf_channel ch, int *gain_target) +{ + int status = 0; + CHECK_NULL(dev); + MUTEX_LOCK(&dev->lock); + bladerf_frequency current_frequency; + struct bladerf_gain_cal_tbl *cal_table = &dev->gain_tbls[ch]; + struct bladerf_gain_cal_entry current_entry; + bladerf_gain current_gain; + bladerf_gain_mode gain_mode; + + + if (dev->gain_tbls[ch].state == BLADERF_GAIN_CAL_UNINITIALIZED) { + log_error("Gain calibration not loaded\n"); + status = BLADERF_ERR_UNEXPECTED; + goto error; + } + + if (BLADERF_CHANNEL_IS_TX(ch) == true) { + *gain_target = cal_table->gain_target; + goto error; + } + + dev->board->get_gain_mode(dev, ch, &gain_mode); + + if (gain_mode == BLADERF_GAIN_MGC) { + *gain_target = cal_table->gain_target; + goto error; + } + + CHECK_STATUS(dev->board->get_gain(dev, ch, ¤t_gain)); + CHECK_STATUS(dev->board->get_frequency(dev, ch, ¤t_frequency)); + CHECK_STATUS(get_gain_cal_entry(cal_table, current_frequency, ¤t_entry)); + *gain_target = current_gain + current_entry.gain_corr; + +error: + MUTEX_UNLOCK(&dev->lock); + return status; +} |