diff options
Diffstat (limited to 'Radio/HW/BladeRF/common/src')
-rw-r--r-- | Radio/HW/BladeRF/common/src/conversions.c | 730 | ||||
-rw-r--r-- | Radio/HW/BladeRF/common/src/dc_calibration.c | 1745 | ||||
-rw-r--r-- | Radio/HW/BladeRF/common/src/devcfg.c | 638 | ||||
-rw-r--r-- | Radio/HW/BladeRF/common/src/log.c | 98 | ||||
-rw-r--r-- | Radio/HW/BladeRF/common/src/osx/clock_gettime.c | 59 | ||||
-rw-r--r-- | Radio/HW/BladeRF/common/src/parse.c | 455 | ||||
-rw-r--r-- | Radio/HW/BladeRF/common/src/range.c | 74 | ||||
-rw-r--r-- | Radio/HW/BladeRF/common/src/sha256.c | 343 | ||||
-rw-r--r-- | Radio/HW/BladeRF/common/src/str_queue.c | 193 | ||||
-rw-r--r-- | Radio/HW/BladeRF/common/src/windows/clock_gettime.c | 58 | ||||
-rw-r--r-- | Radio/HW/BladeRF/common/src/windows/getopt_long.c | 326 | ||||
-rw-r--r-- | Radio/HW/BladeRF/common/src/windows/gettimeofday.c | 49 | ||||
-rw-r--r-- | Radio/HW/BladeRF/common/src/windows/mkdtemp.c | 250 | ||||
-rw-r--r-- | Radio/HW/BladeRF/common/src/windows/nanosleep.c | 41 | ||||
-rw-r--r-- | Radio/HW/BladeRF/common/src/windows/setenv.c | 50 |
15 files changed, 5109 insertions, 0 deletions
diff --git a/Radio/HW/BladeRF/common/src/conversions.c b/Radio/HW/BladeRF/common/src/conversions.c new file mode 100644 index 0000000..ad08520 --- /dev/null +++ b/Radio/HW/BladeRF/common/src/conversions.c @@ -0,0 +1,730 @@ +/* + * This file is part of the bladeRF project: + * http://www.github.com/nuand/bladeRF + * + * Copyright (c) 2017-2018 Nuand LLC. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "conversions.h" + +int str2version(const char *str, struct bladerf_version *version) +{ + unsigned long tmp; + const char *start = str; + char *end; + + /* Major version */ + errno = 0; + tmp = strtoul(start, &end, 10); + if (errno != 0 || tmp > UINT16_MAX || end == start || *end != '.') { + return -1; + } + version->major = (uint16_t)tmp; + + /* Minor version */ + if (end[0] == '\0' || end[1] == '\0') { + return -1; + } + errno = 0; + start = &end[1]; + tmp = strtoul(start, &end, 10); + if (errno != 0 || tmp > UINT16_MAX || end == start || *end != '.') { + return -1; + } + version->minor = (uint16_t)tmp; + + /* Patch version */ + if (end[0] == '\0' || end[1] == '\0') { + return -1; + } + errno = 0; + start = &end[1]; + tmp = strtoul(start, &end, 10); + if (errno != 0 || tmp > UINT16_MAX || end == start || + (*end != '-' && *end != '\0')) { + return -1; + } + version->patch = (uint16_t)tmp; + + version->describe = str; + + return 0; +} + +const char *devspeed2str(bladerf_dev_speed speed) +{ + switch (speed) { + case BLADERF_DEVICE_SPEED_HIGH: + /* Yeah, the USB IF actually spelled it "Hi" instead of "High". + * I know. It hurts me too. */ + return "Hi-Speed"; + + case BLADERF_DEVICE_SPEED_SUPER: + /* ...and no hyphen :( */ + return "SuperSpeed"; + + default: + return "Unknown"; + } +} + +bladerf_log_level str2loglevel(const char *str, bool *ok) +{ + bladerf_log_level level = BLADERF_LOG_LEVEL_ERROR; + bool valid = true; + + if (!strcasecmp(str, "critical")) { + level = BLADERF_LOG_LEVEL_CRITICAL; + } else if (!strcasecmp(str, "error")) { + level = BLADERF_LOG_LEVEL_ERROR; + } else if (!strcasecmp(str, "warning")) { + level = BLADERF_LOG_LEVEL_WARNING; + } else if (!strcasecmp(str, "info")) { + level = BLADERF_LOG_LEVEL_INFO; + } else if (!strcasecmp(str, "debug")) { + level = BLADERF_LOG_LEVEL_DEBUG; + } else if (!strcasecmp(str, "verbose")) { + level = BLADERF_LOG_LEVEL_VERBOSE; + } else { + valid = false; + } + + *ok = valid; + return level; +} + +const char *module2str(bladerf_module m) +{ + switch (m) { + case BLADERF_MODULE_RX: + return "RX"; + case BLADERF_MODULE_TX: + return "TX"; + default: + return "Unknown"; + } +} + +bladerf_module str2module(const char *str) +{ + if (!strcasecmp(str, "RX")) { + return BLADERF_MODULE_RX; + } else if (!strcasecmp(str, "TX")) { + return BLADERF_MODULE_TX; + } else { + return BLADERF_MODULE_INVALID; + } +} + +const char *channel2str(bladerf_channel ch) +{ + switch (ch) { + case BLADERF_CHANNEL_RX(0): + return "RX1"; + case BLADERF_CHANNEL_TX(0): + return "TX1"; + case BLADERF_CHANNEL_RX(1): + return "RX2"; + case BLADERF_CHANNEL_TX(1): + return "TX2"; + default: + return "Unknown"; + } +} + +bladerf_channel str2channel(char const *str) +{ + bladerf_channel rv; + + if (strcasecmp(str, "rx") == 0 || strcasecmp(str, "rx1") == 0) { + rv = BLADERF_CHANNEL_RX(0); + } else if (strcasecmp(str, "rx2") == 0) { + rv = BLADERF_CHANNEL_RX(1); + } else if (strcasecmp(str, "tx") == 0 || strcasecmp(str, "tx1") == 0) { + rv = BLADERF_CHANNEL_TX(0); + } else if (strcasecmp(str, "tx2") == 0) { + rv = BLADERF_CHANNEL_TX(1); + } else { + rv = BLADERF_CHANNEL_INVALID; + } + + return rv; +} + +bladerf_direction channel2direction (bladerf_channel ch) { + if (ch == BLADERF_CHANNEL_RX(0) || ch == BLADERF_CHANNEL_RX(1)) { + return BLADERF_RX; + } else { + return BLADERF_TX; + } +} + +const char *direction2str(bladerf_direction dir) +{ + switch (dir) { + case BLADERF_RX: + return "RX"; + case BLADERF_TX: + return "TX"; + default: + return "Unknown"; + } +} + +const char *trigger2str(bladerf_trigger_signal trigger) +{ + switch (trigger) { + case BLADERF_TRIGGER_J71_4: + return "J71-4"; + + case BLADERF_TRIGGER_J51_1: + return "J51-1"; + + case BLADERF_TRIGGER_MINI_EXP_1: + return "MiniExp-1"; + + case BLADERF_TRIGGER_USER_0: + return "User-0"; + + case BLADERF_TRIGGER_USER_1: + return "User-1"; + + case BLADERF_TRIGGER_USER_2: + return "User-2"; + + case BLADERF_TRIGGER_USER_3: + return "User-3"; + + case BLADERF_TRIGGER_USER_4: + return "User-4"; + + case BLADERF_TRIGGER_USER_5: + return "User-5"; + + case BLADERF_TRIGGER_USER_6: + return "User-6"; + + case BLADERF_TRIGGER_USER_7: + return "User-7"; + + default: + return "Unknown"; + } +} + +bladerf_trigger_signal str2trigger(const char *str) +{ + if (!strcasecmp("J71-4", str)) { + return BLADERF_TRIGGER_J71_4; + } else if (!strcasecmp("J51-1", str)) { + return BLADERF_TRIGGER_J51_1; + } else if (!strcasecmp("Miniexp-1", str)) { + return BLADERF_TRIGGER_MINI_EXP_1; + } else if (!strcasecmp("User-0", str)) { + return BLADERF_TRIGGER_USER_0; + } else if (!strcasecmp("User-1", str)) { + return BLADERF_TRIGGER_USER_1; + } else if (!strcasecmp("User-2", str)) { + return BLADERF_TRIGGER_USER_2; + } else if (!strcasecmp("User-3", str)) { + return BLADERF_TRIGGER_USER_3; + } else if (!strcasecmp("User-4", str)) { + return BLADERF_TRIGGER_USER_4; + } else if (!strcasecmp("User-5", str)) { + return BLADERF_TRIGGER_USER_5; + } else if (!strcasecmp("User-6", str)) { + return BLADERF_TRIGGER_USER_6; + } else if (!strcasecmp("User-7", str)) { + return BLADERF_TRIGGER_USER_7; + } else { + return BLADERF_TRIGGER_INVALID; + } +} + +const char *triggerrole2str(bladerf_trigger_role role) +{ + switch (role) { + case BLADERF_TRIGGER_ROLE_MASTER: + return "Master"; + case BLADERF_TRIGGER_ROLE_SLAVE: + return "Slave"; + case BLADERF_TRIGGER_ROLE_DISABLED: + return "Disabled"; + default: + return "Unknown"; + } +} + +bladerf_trigger_role str2triggerrole(const char *str) +{ + if (!strcasecmp("Master", str)) { + return BLADERF_TRIGGER_ROLE_MASTER; + } else if (!strcasecmp("Slave", str)) { + return BLADERF_TRIGGER_ROLE_SLAVE; + } else if (!strcasecmp("Disabled", str) || !strcasecmp("Off", str)) { + return BLADERF_TRIGGER_ROLE_DISABLED; + } else { + return BLADERF_TRIGGER_ROLE_INVALID; + } +} + +int str2loopback(const char *str, bladerf_loopback *loopback) +{ + int status = 0; + + if (!strcasecmp("firmware", str)) { + *loopback = BLADERF_LB_FIRMWARE; + } else if (!strcasecmp("bb_txlpf_rxvga2", str)) { + *loopback = BLADERF_LB_BB_TXLPF_RXVGA2; + } else if (!strcasecmp("bb_txlpf_rxlpf", str)) { + *loopback = BLADERF_LB_BB_TXLPF_RXLPF; + } else if (!strcasecmp("bb_txvga1_rxvga2", str)) { + *loopback = BLADERF_LB_BB_TXVGA1_RXVGA2; + } else if (!strcasecmp("bb_txvga1_rxlpf", str)) { + *loopback = BLADERF_LB_BB_TXVGA1_RXLPF; + } else if (!strcasecmp("rf_lna1", str)) { + *loopback = BLADERF_LB_RF_LNA1; + } else if (!strcasecmp("rf_lna2", str)) { + *loopback = BLADERF_LB_RF_LNA2; + } else if (!strcasecmp("rf_lna3", str)) { + *loopback = BLADERF_LB_RF_LNA3; + } else if (!strcasecmp("rfic_bist", str)) { + *loopback = BLADERF_LB_RFIC_BIST; + } else if (!strcasecmp("none", str)) { + *loopback = BLADERF_LB_NONE; + } else { + status = -1; + } + + return status; +} + +char const *loopback2str(bladerf_loopback loopback) +{ + switch (loopback) { + case BLADERF_LB_BB_TXLPF_RXVGA2: + return "bb_txlpf_rxvga2"; + + case BLADERF_LB_BB_TXLPF_RXLPF: + return "bb_txlpf_rxlpf"; + + case BLADERF_LB_BB_TXVGA1_RXVGA2: + return "bb_txvga1_rxvga2"; + + case BLADERF_LB_BB_TXVGA1_RXLPF: + return "bb_txvga1_rxlpf"; + + case BLADERF_LB_RF_LNA1: + return "rf_lna1"; + + case BLADERF_LB_RF_LNA2: + return "rf_lna2"; + + case BLADERF_LB_RF_LNA3: + return "rf_lna3"; + + case BLADERF_LB_FIRMWARE: + return "firmware"; + + case BLADERF_LB_NONE: + return "none"; + + case BLADERF_LB_RFIC_BIST: + return "rfic_bist"; + + default: + return "unknown"; + } +} + +int str2lnagain(const char *str, bladerf_lna_gain *gain) +{ + *gain = BLADERF_LNA_GAIN_MAX; + + if (!strcasecmp("max", str) || !strcasecmp("BLADERF_LNA_GAIN_MAX", str)) { + *gain = BLADERF_LNA_GAIN_MAX; + return 0; + } else if (!strcasecmp("mid", str) || + !strcasecmp("BLADERF_LNA_GAIN_MID", str)) { + *gain = BLADERF_LNA_GAIN_MID; + return 0; + } else if (!strcasecmp("bypass", str) || + !strcasecmp("BLADERF_LNA_GAIN_BYPASS", str)) { + *gain = BLADERF_LNA_GAIN_BYPASS; + return 0; + } else { + *gain = BLADERF_LNA_GAIN_UNKNOWN; + return -1; + } +} + +char const *tuningmode2str(bladerf_tuning_mode mode) +{ + switch (mode) { + case BLADERF_TUNING_MODE_HOST: + return "Host"; + case BLADERF_TUNING_MODE_FPGA: + return "FPGA"; + default: + return "Unknown"; + } +} + +const char *backend_description(bladerf_backend b) +{ + switch (b) { + case BLADERF_BACKEND_ANY: + return "Any"; + + case BLADERF_BACKEND_LINUX: + return "Linux kernel driver"; + + case BLADERF_BACKEND_LIBUSB: + return "libusb"; + + case BLADERF_BACKEND_CYPRESS: + return "Cypress driver"; + + case BLADERF_BACKEND_DUMMY: + return "Dummy"; + + default: + return "Unknown"; + } +} + +void sc16q11_to_float(const int16_t *in, float *out, unsigned int n) +{ + unsigned int i; + + for (i = 0; i < (2 * n); i += 2) { + out[i] = (float)in[i] * (1.0f / 2048.0f); + out[i + 1] = (float)in[i + 1] * (1.0f / 2048.0f); + } +} + +void float_to_sc16q11(const float *in, int16_t *out, unsigned int n) +{ + unsigned int i; + + for (i = 0; i < (2 * n); i += 2) { + out[i] = (int16_t)(in[i] * 2048.0f); + out[i + 1] = (int16_t)(in[i + 1] * 2048.0f); + } +} + +bladerf_cal_module str_to_bladerf_cal_module(const char *str) +{ + bladerf_cal_module module = BLADERF_DC_CAL_INVALID; + + if (!strcasecmp(str, "lpf_tuning") || !strcasecmp(str, "lpftuning") || + !strcasecmp(str, "tuning")) { + module = BLADERF_DC_CAL_LPF_TUNING; + } else if (!strcasecmp(str, "tx_lpf") || !strcasecmp(str, "txlpf")) { + module = BLADERF_DC_CAL_TX_LPF; + } else if (!strcasecmp(str, "rx_lpf") || !strcasecmp(str, "rxlpf")) { + module = BLADERF_DC_CAL_RX_LPF; + } else if (!strcasecmp(str, "rx_vga2") || !strcasecmp(str, "rxvga2")) { + module = BLADERF_DC_CAL_RXVGA2; + } + + return module; +} + +const char *smb_mode_to_str(bladerf_smb_mode mode) +{ + switch (mode) { + case BLADERF_SMB_MODE_DISABLED: + return "Disabled"; + + case BLADERF_SMB_MODE_OUTPUT: + return "Output"; + + case BLADERF_SMB_MODE_INPUT: + return "Input"; + + case BLADERF_SMB_MODE_UNAVAILBLE: + return "Unavailable"; + + default: + return "Unknown"; + } +}; + +bladerf_smb_mode str_to_smb_mode(const char *str) +{ + if (!strcasecmp(str, "disabled") || !strcasecmp(str, "off")) { + return BLADERF_SMB_MODE_DISABLED; + } else if (!strcasecmp(str, "output")) { + return BLADERF_SMB_MODE_OUTPUT; + } else if (!strcasecmp(str, "input")) { + return BLADERF_SMB_MODE_INPUT; + } else if (!strcasecmp(str, "unavailable")) { + return BLADERF_SMB_MODE_UNAVAILBLE; + } else { + return BLADERF_SMB_MODE_INVALID; + } +} + +bladerf_tuning_mode str_to_tuning_mode(const char *str) +{ + if (!strcasecmp(str, "fpga")) { + return BLADERF_TUNING_MODE_FPGA; + } else if (!strcasecmp(str, "host")) { + return BLADERF_TUNING_MODE_HOST; + } else { + return BLADERF_TUNING_MODE_INVALID; + } +} + +unsigned int str2uint(const char *str, + unsigned int min, + unsigned int max, + bool *ok) +{ + unsigned int ret; + char *optr; + + errno = 0; + ret = strtoul(str, &optr, 0); + + if (errno == ERANGE || (errno != 0 && ret == 0)) { + *ok = false; + return 0; + } + + if (str == optr) { + *ok = false; + return 0; + } + + if (ret >= min && ret <= max) { + *ok = true; + return (unsigned int)ret; + } + + *ok = false; + return 0; +} + +int str2int(const char *str, int min, int max, bool *ok) +{ + long int ret; + char *optr; + + errno = 0; + ret = strtol(str, &optr, 0); + + if ((errno == ERANGE && (ret == LONG_MAX || ret == LONG_MIN)) || + (errno != 0 && ret == 0)) { + *ok = false; + return 0; + } + + if (str == optr) { + *ok = false; + return 0; + } + + if (ret >= min && ret <= max) { + *ok = true; + return (int)ret; + } + + *ok = false; + return 0; +} + +uint64_t str2uint64(const char *str, uint64_t min, uint64_t max, bool *ok) +{ + uint64_t ret; + char *optr; + + errno = 0; + ret = (uint64_t)strtod(str, &optr); + + if ((errno == ERANGE && ret == ULONG_MAX) || (errno != 0 && ret == 0)) { + *ok = false; + return 0; + } + + if (str == optr) { + *ok = false; + return 0; + } + + if (ret >= min && ret <= max) { + *ok = true; + return ret; + } + + *ok = false; + return 0; +} + +double str2double(const char *str, double min, double max, bool *ok) +{ + double ret; + char *optr; + + errno = 0; + ret = strtod(str, &optr); + + if (errno == ERANGE || (errno != 0 && ret == 0)) { + *ok = false; + return NAN; + } + + if (str == optr) { + *ok = false; + return NAN; + } + + if (ret >= min && ret <= max) { + *ok = true; + return ret; + } + + *ok = false; + return NAN; +} + +static uint64_t suffix_multiplier(const char *str, + const struct numeric_suffix *suffixes, + const size_t num_suff, + bool *ok) +{ + unsigned i; + + *ok = true; + + if (!strlen(str)) + return 1; + + for (i = 0; i < num_suff; i++) { + if (!strcasecmp(suffixes[i].suffix, str)) { + return suffixes[i].multiplier; + } + } + + *ok = false; + return 0; +} + +unsigned int str2uint_suffix(const char *str, + unsigned int min, + unsigned int max, + const struct numeric_suffix *suffixes, + const size_t num_suff, + bool *ok) +{ + uint64_t mult; + unsigned int rv; + double val; + char *optr; + + errno = 0; + val = strtod(str, &optr); + + if (errno == ERANGE || (errno != 0 && val == 0)) { + *ok = false; + return 0; + } + + if (str == optr) { + *ok = false; + return 0; + } + + mult = suffix_multiplier(optr, suffixes, num_suff, ok); + if (!*ok) + return false; + + rv = (unsigned int)(val * mult); + + if (rv >= min && rv <= max) { + return rv; + } + + *ok = false; + return 0; +} + +uint64_t str2uint64_suffix(const char *str, + uint64_t min, + uint64_t max, + const struct numeric_suffix *suffixes, + const size_t num_suff, + bool *ok) +{ + uint64_t mult, rv; + long double val; + char *optr; + + errno = 0; + val = strtold(str, &optr); + + if (errno == ERANGE && (errno != 0 && val == 0)) { + *ok = false; + return 0; + } + + if (str == optr) { + *ok = false; + return 0; + } + + mult = suffix_multiplier(optr, suffixes, num_suff, ok); + if (!*ok) + return false; + + rv = (uint64_t)(val * mult); + if (rv >= min && rv <= max) { + return rv; + } + + *ok = false; + return 0; +} + +int str2bool(const char *str, bool *val) +{ + unsigned int i; + + char *str_true[] = { "true", "t", "one", "1", "enable", "en", "on" }; + char *str_false[] = { "false", "f", "zero", "0", "disable", "dis", "off" }; + + for (i = 0; i < ARRAY_SIZE(str_true); i++) { + if (!strcasecmp(str_true[i], str)) { + *val = true; + return 0; + } + } + + for (i = 0; i < ARRAY_SIZE(str_false); i++) { + if (!strcasecmp(str_false[i], str)) { + *val = false; + return 0; + } + } + + return BLADERF_ERR_INVAL; +} diff --git a/Radio/HW/BladeRF/common/src/dc_calibration.c b/Radio/HW/BladeRF/common/src/dc_calibration.c new file mode 100644 index 0000000..c746c3c --- /dev/null +++ b/Radio/HW/BladeRF/common/src/dc_calibration.c @@ -0,0 +1,1745 @@ +/** + * This file is part of the bladeRF project: + * http://www.github.com/nuand/bladeRF + * + * Copyright (c) 2015 Nuand LLC + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, 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 Affero General Public License for more details. + + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <assert.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <inttypes.h> +#include <limits.h> + +#define _USE_MATH_DEFINES /* Required for MSVC */ +#include <math.h> + +#include <libbladeRF.h> + +#include "dc_calibration.h" +#include "conversions.h" + +struct complexf { + float i; + float q; +}; + +struct gain_mode { + bladerf_lna_gain lna_gain; + int rxvga1, rxvga2; +}; +/******************************************************************************* + * Debug items + ******************************************************************************/ + +/* Enable this to print diagnostic and debug information */ +//#define ENABLE_DC_CALIBRATION_DEBUG +//#define ENABLE_DC_CALIBRATION_VERBOSE + +#ifndef PR_DBG +# ifdef ENABLE_DC_CALIBRATION_DEBUG +# define PR_DBG(...) fprintf(stderr, " " __VA_ARGS__) +# else +# define PR_DBG(...) do {} while (0) +# endif +#endif + +#ifndef PR_VERBOSE +# ifdef ENABLE_DC_CALIBRATION_VERBOSE +# define PR_VERBOSE(...) fprintf(stderr, " " __VA_ARGS__) +# else +# define PR_VERBOSE(...) do {} while (0) +# endif +#endif + +/******************************************************************************* + * Debug routines for saving samples + ******************************************************************************/ + +//#define ENABLE_SAVE_SC16Q11 +#ifdef ENABLE_SAVE_SC16Q11 +static void save_sc16q11(const char *name, int16_t *samples, unsigned int count) +{ + FILE *out = fopen(name, "wb"); + if (!out) { + return; + } + + fwrite(samples, 2 * sizeof(samples[0]), count, out); + fclose(out); +} +#else +# define save_sc16q11(name, samples, count) do {} while (0) +#endif + +//#define ENABLE_SAVE_COMPLEXF +#ifdef ENABLE_SAVE_COMPLEXF +static void save_complexf(const char *name, struct complexf *samples, + unsigned int count) +{ + unsigned int n; + FILE *out = fopen(name, "wb"); + if (!out) { + return; + } + + for (n = 0; n < count; n++) { + fwrite(&samples[n].i, sizeof(samples[n].i), 1, out); + fwrite(&samples[n].q, sizeof(samples[n].q), 1, out); + } + + fclose(out); +} +#else +# define save_complexf(name, samples, count) do {} while (0) +#endif + + +/******************************************************************************* + * LMS6002D DC offset calibration + ******************************************************************************/ + +/* We've found that running samples through the LMS6 tends to be required + * for the TX LPF calibration to converge */ +static inline int tx_lpf_dummy_tx(struct bladerf *dev) +{ + int status; + int retval = 0; + struct bladerf_metadata meta; + int16_t zero_sample[] = { 0, 0 }; + + bladerf_loopback loopback_backup; + struct bladerf_rational_rate sample_rate_backup; + + memset(&meta, 0, sizeof(meta)); + + status = bladerf_get_loopback(dev, &loopback_backup); + if (status != 0) { + return status; + } + + status = bladerf_get_rational_sample_rate(dev, BLADERF_MODULE_TX, + &sample_rate_backup); + if (status != 0) { + return status; + } + + status = bladerf_set_loopback(dev, BLADERF_LB_BB_TXVGA1_RXVGA2); + if (status != 0) { + goto out; + } + + status = bladerf_set_sample_rate(dev, BLADERF_MODULE_TX, 3000000, NULL); + if (status != 0) { + goto out; + } + + status = bladerf_sync_config(dev, BLADERF_MODULE_TX, + BLADERF_FORMAT_SC16_Q11_META, + 64, 16384, 16, 1000); + if (status != 0) { + goto out; + } + + status = bladerf_enable_module(dev, BLADERF_MODULE_TX, true); + if (status != 0) { + goto out; + } + + meta.flags = BLADERF_META_FLAG_TX_BURST_START | + BLADERF_META_FLAG_TX_BURST_END | + BLADERF_META_FLAG_TX_NOW; + + status = bladerf_sync_tx(dev, zero_sample, 1, &meta, 2000); + if (status != 0) { + goto out; + } + +out: + status = bladerf_enable_module(dev, BLADERF_MODULE_TX, false); + if (status != 0 && retval == 0) { + retval = status; + } + + status = bladerf_set_rational_sample_rate(dev, BLADERF_MODULE_TX, + &sample_rate_backup, NULL); + if (status != 0 && retval == 0) { + retval = status; + } + + status = bladerf_set_loopback(dev, loopback_backup); + if (status != 0 && retval == 0) { + retval = status; + } + + return retval; +} + +static int cal_tx_lpf(struct bladerf *dev) +{ + int status; + + status = tx_lpf_dummy_tx(dev); + if (status == 0) { + status = bladerf_calibrate_dc(dev, BLADERF_DC_CAL_TX_LPF); + } + + return status; +} + +int dc_calibration_lms6(struct bladerf *dev, const char *module_str) +{ + int status; + bladerf_cal_module module; + + if (!strcasecmp(module_str, "all")) { + status = bladerf_calibrate_dc(dev, BLADERF_DC_CAL_LPF_TUNING); + if (status != 0) { + return status; + } + + status = cal_tx_lpf(dev); + if (status != 0) { + return status; + } + + status = bladerf_calibrate_dc(dev, BLADERF_DC_CAL_RX_LPF); + if (status != 0) { + return status; + } + + status = bladerf_calibrate_dc(dev, BLADERF_DC_CAL_RXVGA2); + } else { + module = str_to_bladerf_cal_module(module_str); + if (module == BLADERF_DC_CAL_INVALID) { + return BLADERF_ERR_INVAL; + } + + if (module == BLADERF_DC_CAL_TX_LPF) { + status = cal_tx_lpf(dev); + } else { + status = bladerf_calibrate_dc(dev, module); + } + } + + return status; +} + + + +/******************************************************************************* + * Shared utility routines + ******************************************************************************/ + +/* Round float to int16_t */ +static inline int16_t float_to_int16(float val) +{ + if ((val - 0.5) <= INT16_MIN) { + return INT16_MIN; + } + if ((val + 0.5) >= INT16_MAX) { + return INT16_MAX; + } + return val >= 0 ? (int16_t)(val + 0.5) : (int16_t)(val - 0.5); +} + +/* Convert ms to samples */ +#define MS_TO_SAMPLES(ms_, rate_) (\ + (unsigned int) (ms_ * ((uint64_t) rate_) / 1000) \ +) + +/* RX samples, retrying if the machine is struggling to keep up. */ +static int rx_samples(struct bladerf *dev, int16_t *samples, + unsigned int count, uint64_t *ts, uint64_t ts_inc) +{ + int status = 0; + struct bladerf_metadata meta; + int retry = 0; + const int max_retries = 10; + bool overrun = true; + + memset(&meta, 0, sizeof(meta)); + meta.timestamp = *ts; + + while (status == 0 && overrun && retry < max_retries) { + meta.timestamp = *ts; + status = bladerf_sync_rx(dev, samples, count, &meta, 2000); + + if (status == BLADERF_ERR_TIME_PAST) { + status = bladerf_get_timestamp(dev, BLADERF_MODULE_RX, ts); + if (status != 0) { + return status; + } else { + *ts += 20 * ts_inc; + retry++; + status = 0; + } + } else if (status == 0) { + overrun = (meta.flags & BLADERF_META_STATUS_OVERRUN) != 0; + if (overrun) { + *ts += count + ts_inc; + retry++; + } + } else { + return status; + } + } + + if (retry >= max_retries) { + status = BLADERF_ERR_IO; + } else if (status == 0) { + *ts += count + ts_inc; + } + + return status; +} + + + +/******************************************************************************* + * RX DC offset calibration + ******************************************************************************/ + +#define RX_CAL_RATE (3000000) +#define RX_CAL_BW (1500000) +#define RX_CAL_TS_INC (MS_TO_SAMPLES(15, RX_CAL_RATE)) +#define RX_CAL_COUNT (MS_TO_SAMPLES(5, RX_CAL_RATE)) + +#define RX_CAL_MAX_SWEEP_LEN (2 * 2048 / 32) /* -2048 : 32 : 2048 */ + +struct rx_cal { + struct bladerf *dev; + + int16_t *samples; + unsigned int num_samples; + + int16_t *corr_sweep; + + uint64_t ts; + + uint64_t tx_freq; +}; + +struct rx_cal_backup { + struct bladerf_rational_rate rational_sample_rate; + unsigned int bandwidth; + uint64_t tx_freq; +}; + +static int get_rx_cal_backup(struct bladerf *dev, struct rx_cal_backup *b) +{ + int status; + + status = bladerf_get_rational_sample_rate(dev, BLADERF_MODULE_RX, + &b->rational_sample_rate); + if (status != 0) { + return status; + } + + status = bladerf_get_bandwidth(dev, BLADERF_MODULE_RX, &b->bandwidth); + if (status != 0) { + return status; + } + + status = bladerf_get_frequency(dev, BLADERF_MODULE_TX, &b->tx_freq); + if (status != 0) { + return status; + } + + return status; +} + +static int set_rx_cal_backup(struct bladerf *dev, struct rx_cal_backup *b) +{ + int status; + int retval = 0; + + status = bladerf_set_rational_sample_rate(dev, BLADERF_MODULE_RX, + &b->rational_sample_rate, NULL); + if (status != 0 && retval == 0) { + retval = status; + } + + status = bladerf_set_bandwidth(dev, BLADERF_MODULE_RX, b->bandwidth, NULL); + if (status != 0 && retval == 0) { + retval = status; + } + + status = bladerf_set_frequency(dev, BLADERF_MODULE_TX, b->tx_freq); + if (status != 0 && retval == 0) { + retval = status; + } + + return retval; +} + +/* Ensure TX >= 1 MHz away from the RX frequency to avoid any potential + * artifacts from the PLLs interfering with one another */ +static int rx_cal_update_frequency(struct rx_cal *cal, uint64_t rx_freq) +{ + int status = 0; + uint64_t f_diff; + + if (rx_freq < cal->tx_freq) { + f_diff = cal->tx_freq - rx_freq; + } else { + f_diff = rx_freq - cal->tx_freq; + } + + PR_DBG("Set F_RX = %u\n", rx_freq); + PR_DBG("F_diff(RX, TX) = %u\n", f_diff); + + if (f_diff < 1000000) { + if (rx_freq >= (BLADERF_FREQUENCY_MIN + 1000000)) { + cal->tx_freq = rx_freq - 1000000; + } else { + cal->tx_freq = rx_freq + 1000000; + } + + status = bladerf_set_frequency(cal->dev, BLADERF_MODULE_TX, + cal->tx_freq); + if (status != 0) { + return status; + } + + PR_DBG("Adjusted TX frequency: %u\n", cal->tx_freq); + } + + status = bladerf_set_frequency(cal->dev, BLADERF_MODULE_RX, rx_freq); + if (status != 0) { + return status; + } + + cal->ts += RX_CAL_TS_INC; + + return status; +} + +static inline void sample_mean(int16_t *samples, size_t count, + float *mean_i, float *mean_q) +{ + int64_t accum_i = 0; + int64_t accum_q = 0; + + size_t n; + + + if (count == 0) { + assert(!"Invalid count (0) provided to sample_mean()"); + *mean_i = 0; + *mean_q = 0; + return; + } + + for (n = 0; n < (2 * count); n += 2) { + accum_i += samples[n]; + accum_q += samples[n + 1]; + } + + *mean_i = ((float) accum_i) / count; + *mean_q = ((float) accum_q) / count; +} + +static inline int set_rx_dc_corr(struct bladerf *dev, int16_t i, int16_t q) +{ + int status; + + status = bladerf_set_correction(dev, BLADERF_MODULE_RX, + BLADERF_CORR_LMS_DCOFF_I, i); + if (status != 0) { + return status; + } + + status = bladerf_set_correction(dev, BLADERF_MODULE_RX, + BLADERF_CORR_LMS_DCOFF_Q, q); + return status; +} + +/* Get the mean for one of the coarse estimate points. If it seems that this + * value might be (or close) causing us to clamp, adjust it and retry */ +static int rx_cal_coarse_means(struct rx_cal *cal, int16_t *corr_value, + float *mean_i, float *mean_q) +{ + int status; + const int16_t mean_limit_high = 2000; + const int16_t mean_limit_low = -mean_limit_high; + const int16_t corr_limit = 128; + bool retry = false; + + do { + status = set_rx_dc_corr(cal->dev, *corr_value, *corr_value); + if (status != 0) { + return status; + } + + status = rx_samples(cal->dev, cal->samples, cal->num_samples, + &cal->ts, RX_CAL_TS_INC); + if (status != 0) { + return status; + } + + sample_mean(cal->samples, cal->num_samples, mean_i, mean_q); + + if (*mean_i > mean_limit_high || *mean_q > mean_limit_high || + *mean_i < mean_limit_low || *mean_q < mean_limit_low ) { + + if (*corr_value < 0) { + retry = (*corr_value <= -corr_limit); + } else { + retry = (*corr_value >= corr_limit); + } + + if (retry) { + PR_DBG("Coarse estimate point Corr=%4d yields extreme means: " + "(%4f, %4f). Retrying...\n", + *corr_value, *mean_i, *mean_q); + + *corr_value = *corr_value / 2; + } + } else { + retry = false; + } + } while (retry); + + if (retry) { + PR_DBG("Non-ideal values are being used.\n"); + } + + return 0; +} + +/* Estimate the DC correction values that yield zero DC offset via a linear + * approximation */ +static int rx_cal_coarse_estimate(struct rx_cal *cal, + int16_t *i_est, int16_t *q_est) +{ + int status; + int16_t x1 = -2048; + int16_t x2 = 2048; + float y1i, y1q, y2i, y2q; + float mi, mq; + float bi, bq; + float i_guess, q_guess; + + status = rx_cal_coarse_means(cal, &x1, &y1i, &y1q); + if (status != 0) { + *i_est = 0; + *q_est = 0; + return status; + } + + PR_VERBOSE("Means for x1=%d: y1i=%f, y1q=%f\n", x1, y1i, y1q); + + status = rx_cal_coarse_means(cal, &x2, &y2i, &y2q); + if (status != 0) { + *i_est = 0; + *q_est = 0; + return status; + } + + PR_VERBOSE("Means for x2: y2i=%f, y2q=%f\n", y2i, y2q); + + mi = (y2i - y1i) / (x2 - x1); + mq = (y2q - y1q) / (x2 - x1); + + bi = y1i - mi * x1; + bq = y1q - mq * x1; + + PR_VERBOSE("mi=%f, bi=%f, mq=%f, bq=%f\n", mi, bi, mq, bq); + + i_guess = -bi/mi + 0.5f; + if (i_guess < -2048) { + i_guess = -2048; + } else if (i_guess > 2048) { + i_guess = 2048; + } + + q_guess = -bq/mq + 0.5f; + if (q_guess < -2048) { + q_guess = -2048; + } else if (q_guess > 2048) { + q_guess = 2048; + } + + *i_est = (int16_t) i_guess; + *q_est = (int16_t) q_guess; + + PR_DBG("Coarse estimate: I=%d, Q=%d\n", *i_est, *q_est); + + return 0; +} + +static void init_rx_cal_sweep(int16_t *corr, unsigned int *sweep_len, + int16_t i_est, int16_t q_est) +{ + unsigned int actual_len = 0; + unsigned int i; + + int16_t sweep_min, sweep_max, sweep_val; + + /* LMS6002D RX DC calibrations have a limited range. libbladeRF throws away + * the lower 5 bits. */ + const int16_t sweep_inc = 32; + + const int16_t min_est = (i_est < q_est) ? i_est : q_est; + const int16_t max_est = (i_est > q_est) ? i_est : q_est; + + sweep_min = min_est - 12 * 32; + if (sweep_min < -2048) { + sweep_min = -2048; + } + + sweep_max = max_est + 12 * 32; + if (sweep_max > 2048) { + sweep_max = 2048; + } + + /* Given that these lower bits are thrown away, it can be confusing to + * see that values change in their LSBs that don't matter. Therefore, + * we'll adjust to muliples of sweep_inc */ + sweep_min = (sweep_min / 32) * 32; + sweep_max = (sweep_max / 32) * 32; + + + PR_DBG("Sweeping [%d : %d : %d]\n", sweep_min, sweep_inc, sweep_max); + + sweep_val = sweep_min; + for (i = 0; sweep_val < sweep_max && i < RX_CAL_MAX_SWEEP_LEN; i++) { + corr[i] = sweep_val; + sweep_val += sweep_inc; + actual_len++; + } + + *sweep_len = actual_len; +} + +static int save_gains(struct rx_cal *cal, struct gain_mode *gain) { + int status; + + status = bladerf_get_lna_gain(cal->dev, &gain->lna_gain); + if (status != 0) { + return status; + } + + status = bladerf_get_rxvga1(cal->dev, &gain->rxvga1); + if (status != 0) { + return status; + } + + status = bladerf_get_rxvga2(cal->dev, &gain->rxvga2); + if (status != 0) { + return status; + } + + return status; +} + +static int load_gains(struct rx_cal *cal, struct gain_mode *gain) { + int status; + + status = bladerf_set_lna_gain(cal->dev, gain->lna_gain); + if (status != 0) { + return status; + } + + status = bladerf_set_rxvga1(cal->dev, gain->rxvga1); + if (status != 0) { + return status; + } + + status = bladerf_set_rxvga2(cal->dev, gain->rxvga2); + if (status != 0) { + return status; + } + + return status; +} + +static int rx_cal_dc_off(struct rx_cal *cal, struct gain_mode *gains, + int16_t *dc_i, int16_t *dc_q) +{ + int status = BLADERF_ERR_UNEXPECTED; + + float mean_i, mean_q; + + status = load_gains(cal, gains); + if (status != 0) { + return status; + } + + status = rx_samples(cal->dev, cal->samples, cal->num_samples, + &cal->ts, RX_CAL_TS_INC); + if (status != 0) { + return status; + } + + sample_mean(cal->samples, cal->num_samples, &mean_i, &mean_q); + *dc_i = float_to_int16(mean_i); + *dc_q = float_to_int16(mean_q); + + return 0; +} + +static int rx_cal_sweep(struct rx_cal *cal, + int16_t *corr, unsigned int sweep_len, + int16_t *result_i, int16_t *result_q, + float *error_i, float *error_q) +{ + int status = BLADERF_ERR_UNEXPECTED; + unsigned int n; + + int16_t min_corr_i = 0; + int16_t min_corr_q = 0; + + float mean_i, mean_q; + float min_val_i, min_val_q; + + min_val_i = min_val_q = 2048; + + for (n = 0; n < sweep_len; n++) { + status = set_rx_dc_corr(cal->dev, corr[n], corr[n]); + if (status != 0) { + return status; + } + + status = rx_samples(cal->dev, cal->samples, cal->num_samples, + &cal->ts, RX_CAL_TS_INC); + if (status != 0) { + return status; + } + + sample_mean(cal->samples, cal->num_samples, &mean_i, &mean_q); + + PR_VERBOSE(" Corr=%4d, Mean_I=%4.2f, Mean_Q=%4.2f\n", + corr[n], mean_i, mean_q); + + /* Not using fabs() to avoid adding a -lm dependency */ + if (mean_i < 0) { + mean_i = -mean_i; + } + + if (mean_q < 0) { + mean_q = -mean_q; + } + + if (mean_i < min_val_i) { + min_val_i = mean_i; + min_corr_i = corr[n]; + } + + if (mean_q < min_val_q) { + min_val_q = mean_q; + min_corr_q = corr[n]; + } + } + + *result_i = min_corr_i; + *result_q = min_corr_q; + *error_i = min_val_i; + *error_q = min_val_q; + + return 0; +} + +static int perform_rx_cal(struct rx_cal *cal, struct dc_calibration_params *p) +{ + int status; + int16_t i_est, q_est; + unsigned int sweep_len = RX_CAL_MAX_SWEEP_LEN; + struct gain_mode saved_gains; + + struct gain_mode agc_gains[] = { + { .lna_gain = BLADERF_LNA_GAIN_MAX, .rxvga1 = 30, .rxvga2 = 15 }, /* AGC Max Gain */ + { .lna_gain = BLADERF_LNA_GAIN_MID, .rxvga1 = 30, .rxvga2 = 0 }, /* AGC Mid Gain */ + { .lna_gain = BLADERF_LNA_GAIN_MID, .rxvga1 = 12, .rxvga2 = 0 } /* AGC Min Gain */ + }; + + status = rx_cal_update_frequency(cal, p->frequency); + if (status != 0) { + return status; + } + + /* Get an initial guess at our correction values */ + status = rx_cal_coarse_estimate(cal, &i_est, &q_est); + if (status != 0) { + return status; + } + + /* Perform a finer sweep of correction values */ + init_rx_cal_sweep(cal->corr_sweep, &sweep_len, i_est, q_est); + + /* Advance our timestmap just to account for any time we may have lost */ + cal->ts += RX_CAL_TS_INC; + + status = rx_cal_sweep(cal, cal->corr_sweep, sweep_len, + &p->corr_i, &p->corr_q, + &p->error_i, &p->error_q); + + if (status != 0) { + return status; + } + + /* Apply the nominal correction values */ + status = set_rx_dc_corr(cal->dev, p->corr_i, p->corr_q); + if (status != 0) { + return status; + } + + bladerf_fpga_size fpga_size; + status = bladerf_get_fpga_size(cal->dev, &fpga_size); + if (status != 0) { + return status; + } + + if (fpga_size != BLADERF_FPGA_40KLE && + fpga_size != BLADERF_FPGA_115KLE) { + return 0; + } + + /* Measure DC correction for AGC */ + status = save_gains(cal, &saved_gains); + if (status != 0) { + return status; + } + + status = rx_cal_dc_off(cal, &agc_gains[2], &p->min_dc_i, &p->min_dc_q); + if (status != 0) { + return status; + } + + status = rx_cal_dc_off(cal, &agc_gains[1], &p->mid_dc_i, &p->mid_dc_q); + if (status != 0) { + return status; + } + + status = rx_cal_dc_off(cal, &agc_gains[0], &p->max_dc_i, &p->max_dc_q); + if (status != 0) { + return status; + } + + status = load_gains(cal, &saved_gains); + + return status; +} + +static int rx_cal_init_state(struct bladerf *dev, + const struct rx_cal_backup *backup, + struct rx_cal *state) +{ + int status; + + state->dev = dev; + + state->num_samples = RX_CAL_COUNT; + + state->samples = malloc(2 * sizeof(state->samples[0]) * RX_CAL_COUNT); + if (state->samples == NULL) { + return BLADERF_ERR_MEM; + } + + state->corr_sweep = malloc(sizeof(state->corr_sweep[0]) * RX_CAL_MAX_SWEEP_LEN); + if (state->corr_sweep == NULL) { + return BLADERF_ERR_MEM; + } + + state->tx_freq = backup->tx_freq; + + status = bladerf_get_timestamp(dev, BLADERF_MODULE_RX, &state->ts); + if (status != 0) { + return status; + } + + /* Schedule first RX well into the future */ + state->ts += 20 * RX_CAL_TS_INC; + + return status; +} + +static int rx_cal_init(struct bladerf *dev) +{ + int status; + + status = bladerf_set_sample_rate(dev, BLADERF_MODULE_RX, RX_CAL_RATE, NULL); + if (status != 0) { + return status; + } + + status = bladerf_set_bandwidth(dev, BLADERF_MODULE_RX, RX_CAL_BW, NULL); + if (status != 0) { + return status; + } + + status = bladerf_sync_config(dev, BLADERF_MODULE_RX, + BLADERF_FORMAT_SC16_Q11_META, + 64, 16384, 16, 1000); + if (status != 0) { + return status; + } + + status = bladerf_enable_module(dev, BLADERF_MODULE_RX, true); + if (status != 0) { + return status; + } + + return status; +} + +int dc_calibration_rx(struct bladerf *dev, + struct dc_calibration_params *params, + size_t params_count, bool print_status) +{ + int status = 0; + int retval = 0; + struct rx_cal state; + struct rx_cal_backup backup; + size_t i; + + memset(&state, 0, sizeof(state)); + + status = get_rx_cal_backup(dev, &backup); + if (status != 0) { + return status; + } + + status = rx_cal_init(dev); + if (status != 0) { + goto out; + } + + status = rx_cal_init_state(dev, &backup, &state); + if (status != 0) { + goto out; + } + + for (i = 0; i < params_count && status == 0; i++) { + status = perform_rx_cal(&state, ¶ms[i]); + + if (status == 0 && print_status) { +# ifdef DEBUG_DC_CALIBRATION + const char sol = '\n'; + const char eol = '\n'; +# else + const char sol = '\r'; + const char eol = '\0'; +# endif + printf("%cCalibrated @ %10" PRIu64 " Hz: I=%4d (Error: %4.2f), " + "Q=%4d (Error: %4.2f) ", + sol, + params[i].frequency, + params[i].corr_i, params[i].error_i, + params[i].corr_q, params[i].error_q); + printf("DC-LUT: Max (I=%3d, Q=%3d) Mid (I=%3d, Q=%3d)" + " Min (I=%3d, Q=%3d)%c", + params[i].max_dc_i, params[i].max_dc_q, params[i].mid_dc_i, params[i].mid_dc_q, + params[i].min_dc_i, params[i].min_dc_q, eol); + fflush(stdout); + } + } + + if (print_status) { + putchar('\n'); + } + +out: + free(state.samples); + free(state.corr_sweep); + + retval = status; + + status = bladerf_enable_module(dev, BLADERF_MODULE_RX, false); + if (status != 0 && retval == 0) { + retval = status; + } + + status = set_rx_cal_backup(dev, &backup); + if (status != 0 && retval == 0) { + retval = status; + } + + return retval; +} + + + +/******************************************************************************* + * TX DC offset calibration + ******************************************************************************/ + +#define TX_CAL_RATE (4000000) + +#define TX_CAL_RX_BW (3000000) +#define TX_CAL_RX_LNA (BLADERF_LNA_GAIN_MAX) +#define TX_CAL_RX_VGA1 (25) +#define TX_CAL_RX_VGA2 (0) + +#define TX_CAL_TX_BW (1500000) + +#define TX_CAL_TS_INC (MS_TO_SAMPLES(15, TX_CAL_RATE)) +#define TX_CAL_COUNT (MS_TO_SAMPLES(5, TX_CAL_RATE)) + +#define TX_CAL_CORR_SWEEP_LEN (4096 / 16) /* -2048:16:2048 */ + +#define TX_CAL_DEFAULT_LB (BLADERF_LB_RF_LNA1) + +struct tx_cal_backup { + uint64_t rx_freq; + struct bladerf_rational_rate rx_sample_rate; + unsigned int rx_bandwidth; + + bladerf_lna_gain rx_lna; + int rx_vga1; + int rx_vga2; + + struct bladerf_rational_rate tx_sample_rate; + unsigned int tx_bandwidth; + + bladerf_loopback loopback; +}; + +struct tx_cal { + struct bladerf *dev; + int16_t *samples; /* Raw samples */ + unsigned int num_samples; /* Number of raw samples */ + struct complexf *filt; /* Filter state */ + struct complexf *filt_out; /* Filter output */ + struct complexf *post_mix; /* Post-filter, mixed to baseband */ + int16_t *sweep; /* Correction sweep */ + float *mag; /* Magnitude results from sweep */ + uint64_t ts; /* Timestamp */ + bladerf_loopback loopback; /* Current loopback mode */ + bool rx_low; /* RX tuned lower than TX */ +}; + +/* Filter used to isolate contribution of TX LO leakage in received + * signal. 15th order Equiripple FIR with Fs=4e6, Fpass=1, Fstop=1e6 + */ +static const float tx_cal_filt[] = { + 0.000327949366768f, 0.002460188536582f, 0.009842382390924f, + 0.027274728394777f, 0.057835200476419f, 0.098632713294830f, + 0.139062540460741f, 0.164562494987592f, 0.164562494987592f, + 0.139062540460741f, 0.098632713294830f, 0.057835200476419f, + 0.027274728394777f, 0.009842382390924f, 0.002460188536582f, + 0.000327949366768f, +}; + +static const unsigned int tx_cal_filt_num_taps = + (sizeof(tx_cal_filt) / sizeof(tx_cal_filt[0])); + +static inline int set_tx_dc_corr(struct bladerf *dev, int16_t i, int16_t q) +{ + int status; + + status = bladerf_set_correction(dev, BLADERF_MODULE_TX, + BLADERF_CORR_LMS_DCOFF_I, i); + if (status != 0) { + return status; + } + + status = bladerf_set_correction(dev, BLADERF_MODULE_TX, + BLADERF_CORR_LMS_DCOFF_Q, q); + return status; +} + +static int get_tx_cal_backup(struct bladerf *dev, struct tx_cal_backup *b) +{ + int status; + + status = bladerf_get_frequency(dev, BLADERF_MODULE_RX, &b->rx_freq); + if (status != 0) { + return status; + } + + status = bladerf_get_rational_sample_rate(dev, BLADERF_MODULE_RX, + &b->rx_sample_rate); + if (status != 0) { + return status; + } + + status = bladerf_get_bandwidth(dev, BLADERF_MODULE_RX, &b->rx_bandwidth); + if (status != 0) { + return status; + } + + status = bladerf_get_lna_gain(dev, &b->rx_lna); + if (status != 0) { + return status; + } + + status = bladerf_get_rxvga1(dev, &b->rx_vga1); + if (status != 0) { + return status; + } + + status = bladerf_get_rxvga2(dev, &b->rx_vga2); + if (status != 0) { + return status; + } + + status = bladerf_get_rational_sample_rate(dev, BLADERF_MODULE_TX, + &b->tx_sample_rate); + if (status != 0) { + return status; + } + + status = bladerf_get_loopback(dev, &b->loopback); + + return status; +} + +static int set_tx_cal_backup(struct bladerf *dev, struct tx_cal_backup *b) +{ + int status; + int retval = 0; + + status = bladerf_set_loopback(dev, b->loopback); + if (status != 0 && retval == 0) { + retval = status; + } + + status = bladerf_set_frequency(dev, BLADERF_MODULE_RX, b->rx_freq); + if (status != 0 && retval == 0) { + retval = status; + } + + status = bladerf_set_rational_sample_rate(dev, BLADERF_MODULE_RX, + &b->rx_sample_rate, NULL); + if (status != 0 && retval == 0) { + retval = status; + } + + status = bladerf_set_bandwidth(dev, BLADERF_MODULE_RX, + b->rx_bandwidth, NULL); + if (status != 0 && retval == 0) { + retval = status; + } + + status = bladerf_set_lna_gain(dev, b->rx_lna); + if (status != 0 && retval == 0) { + retval = status; + } + + status = bladerf_set_rxvga1(dev, b->rx_vga1); + if (status != 0 && retval == 0) { + retval = status; + } + + status = bladerf_set_rxvga2(dev, b->rx_vga2); + if (status != 0 && retval == 0) { + retval = status; + } + + status = bladerf_set_rational_sample_rate(dev, BLADERF_MODULE_TX, + &b->tx_sample_rate, NULL); + if (status != 0 && retval == 0) { + retval = status; + } + + return retval; +} + +static int tx_cal_update_frequency(struct tx_cal *state, uint64_t freq) +{ + int status; + bladerf_loopback lb; + uint64_t rx_freq; + + status = bladerf_set_frequency(state->dev, BLADERF_MODULE_TX, freq); + if (status != 0) { + return status; + } + + rx_freq = freq - 1000000; + if (rx_freq < BLADERF_FREQUENCY_MIN) { + rx_freq = freq + 1000000; + state->rx_low = false; + } else { + state->rx_low = true; + } + + status = bladerf_set_frequency(state->dev, BLADERF_MODULE_RX, rx_freq); + if (status != 0) { + return status; + } + + if (freq < 1500000000) { + lb = BLADERF_LB_RF_LNA1; + PR_DBG("Switching to RF LNA1 loopback.\n"); + } else { + lb = BLADERF_LB_RF_LNA2; + PR_DBG("Switching to RF LNA2 loopback.\n"); + } + + if (state->loopback != lb) { + status = bladerf_set_loopback(state->dev, lb); + if (status == 0) { + state->loopback = lb; + } + } + + return status; +} + +static int apply_tx_cal_settings(struct bladerf *dev) +{ + int status; + + status = bladerf_set_sample_rate(dev, BLADERF_MODULE_RX, TX_CAL_RATE, NULL); + if (status != 0) { + return status; + } + + status = bladerf_set_bandwidth(dev, BLADERF_MODULE_RX, TX_CAL_RX_BW, NULL); + if (status != 0) { + return status; + } + + status = bladerf_set_lna_gain(dev, TX_CAL_RX_LNA); + if (status != 0) { + return status; + } + + status = bladerf_set_rxvga1(dev, TX_CAL_RX_VGA1); + if (status != 0) { + return status; + } + + status = bladerf_set_rxvga2(dev, TX_CAL_RX_VGA2); + if (status != 0) { + return status; + } + + status = bladerf_set_sample_rate(dev, BLADERF_MODULE_TX, TX_CAL_RATE, NULL); + if (status != 0) { + return status; + } + + status = bladerf_set_loopback(dev, TX_CAL_DEFAULT_LB); + if (status != 0) { + return status; + } + + return status; +} + +/* We just need to flush some zeros through the system to hole the DAC at + * 0+0j and remain there while letting it underrun. This alleviates the + * need to worry about continuously TX'ing zeros. */ +static int tx_cal_tx_init(struct bladerf *dev) +{ + int status; + int16_t zero_sample[] = { 0, 0 }; + struct bladerf_metadata meta; + + memset(&meta, 0, sizeof(meta)); + + status = bladerf_sync_config(dev, BLADERF_MODULE_TX, + BLADERF_FORMAT_SC16_Q11_META, + 4, 16384, 2, 1000); + if (status != 0) { + return status; + } + + status = bladerf_enable_module(dev, BLADERF_MODULE_TX, true); + if (status != 0) { + return status; + } + + meta.flags = BLADERF_META_FLAG_TX_BURST_START | + BLADERF_META_FLAG_TX_BURST_END | + BLADERF_META_FLAG_TX_NOW; + + status = bladerf_sync_tx(dev, &zero_sample, 1, &meta, 2000); + return status; +} + +static int tx_cal_rx_init(struct bladerf *dev) +{ + int status; + + status = bladerf_sync_config(dev, BLADERF_MODULE_RX, + BLADERF_FORMAT_SC16_Q11_META, + 64, 16384, 32, 1000); + if (status != 0) { + return status; + } + + status = bladerf_enable_module(dev, BLADERF_MODULE_RX, true); + return status; +} + +static void tx_cal_state_deinit(struct tx_cal *cal) +{ + free(cal->sweep); + free(cal->mag); + free(cal->samples); + free(cal->filt); + free(cal->filt_out); + free(cal->post_mix); +} + +/* This should be called immediately preceding the cal routines */ +static int tx_cal_state_init(struct bladerf *dev, struct tx_cal *cal) +{ + int status; + + cal->dev = dev; + cal->num_samples = TX_CAL_COUNT; + cal->loopback = TX_CAL_DEFAULT_LB; + + /* Interleaved SC16 Q11 samples */ + cal->samples = malloc(2 * sizeof(cal->samples[0]) * cal->num_samples); + if (cal->samples == NULL) { + return BLADERF_ERR_MEM; + } + + /* Filter state */ + cal->filt = malloc(2 * sizeof(cal->filt[0]) * tx_cal_filt_num_taps); + if (cal->filt == NULL) { + return BLADERF_ERR_MEM; + } + + /* Filter output */ + cal->filt_out = malloc(sizeof(cal->filt_out[0]) * cal->num_samples); + if (cal->filt_out == NULL) { + return BLADERF_ERR_MEM; + } + + /* Post-mix */ + cal->post_mix = malloc(sizeof(cal->post_mix[0]) * cal->num_samples); + if (cal->post_mix == NULL) { + return BLADERF_ERR_MEM; + } + + /* Correction sweep and results */ + cal->sweep = malloc(sizeof(cal->sweep[0]) * TX_CAL_CORR_SWEEP_LEN); + if (cal->sweep == NULL) { + return BLADERF_ERR_MEM; + } + + cal->mag = malloc(sizeof(cal->mag[0]) * TX_CAL_CORR_SWEEP_LEN); + if (cal->mag == NULL) { + return BLADERF_ERR_MEM; + } + + /* Set initial RX in the future */ + status = bladerf_get_timestamp(cal->dev, BLADERF_MODULE_RX, &cal->ts); + if (status == 0) { + cal->ts += 20 * TX_CAL_TS_INC; + } + + return status; +} + +/* Filter samples + * Input: state->post_mix + * Output: state->filt_out + */ +static void tx_cal_filter(struct tx_cal *state) +{ + unsigned int n, m; + struct complexf *ins1, *ins2; + struct complexf *curr; /* Current filter state */ + const struct complexf *filt_end = &state->filt[2 * tx_cal_filt_num_taps]; + + /* Reset filter state */ + ins1 = &state->filt[0]; + ins2 = &state->filt[tx_cal_filt_num_taps]; + memset(state->filt, 0, 2 * sizeof(state->filt[0]) * tx_cal_filt_num_taps); + + for (n = 0; n < state->num_samples; n++) { + /* Insert sample */ + *ins1 = *ins2 = state->post_mix[n]; + + /* Convolve */ + state->filt_out[n].i = 0; + state->filt_out[n].q = 0; + curr = ins2; + + for (m = 0; m < tx_cal_filt_num_taps; m++, curr--) { + state->filt_out[n].i += tx_cal_filt[m] * curr->i; + state->filt_out[n].q += tx_cal_filt[m] * curr->q; + } + + /* Update insertion points */ + ins2++; + if (ins2 == filt_end) { + ins1 = &state->filt[0]; + ins2 = &state->filt[tx_cal_filt_num_taps]; + } else { + ins1++; + } + + } +} + +/* Deinterleave, scale, and mix with an -Fs/4 tone to shift TX DC offset out at + * Fs/4 to baseband. + * Input: state->samples + * Output: state->post_mix + */ +static void tx_cal_mix(struct tx_cal *state) +{ + unsigned int n, m; + int mix_state; + float scaled_i, scaled_q; + + /* Mix with -Fs/4 if RX is tuned "lower" than TX, and Fs/4 otherwise */ + const int mix_state_inc = state->rx_low ? 1 : -1; + mix_state = 0; + + for (n = 0, m = 0; n < (2 * state->num_samples); n += 2, m++) { + scaled_i = state->samples[n] / 2048.0f; + scaled_q = state->samples[n+1] / 2048.0f; + + switch (mix_state) { + case 0: + state->post_mix[m].i = scaled_i; + state->post_mix[m].q = scaled_q; + break; + + case 1: + state->post_mix[m].i = scaled_q; + state->post_mix[m].q = -scaled_i; + break; + + case 2: + state->post_mix[m].i = -scaled_i; + state->post_mix[m].q = -scaled_q; + break; + + case 3: + state->post_mix[m].i = -scaled_q; + state->post_mix[m].q = scaled_i; + break; + } + + mix_state = (mix_state + mix_state_inc) & 0x3; + } +} + +static int tx_cal_avg_magnitude(struct tx_cal *state, float *avg_mag) +{ + int status; + const unsigned int start = (tx_cal_filt_num_taps + 1) / 2; + unsigned int n; + float accum; + + /* Fetch samples at the current settings */ + status = rx_samples(state->dev, state->samples, state->num_samples, + &state->ts, TX_CAL_TS_INC); + if (status != 0) { + return status; + } + + /* Deinterleave & mix TX's DC offset contribution to baseband */ + tx_cal_mix(state); + + /* Filter out everything other than the TX DC offset's contribution */ + tx_cal_filter(state); + + /* Compute the power (magnitude^2 to alleviate need for square root). + * We skip samples here to account for the group delay of the filter; + * the initial samples will be ramping up. */ + accum = 0; + for (n = start; n < state->num_samples; n++) { + const struct complexf *s = &state->filt_out[n]; + const float m = (float) sqrt(s->i * s->i + s->q * s->q); + accum += m; + } + + *avg_mag = (accum / (state->num_samples - start)); + + /* Scale this back up to DAC/ADC counts, just for convenience */ + *avg_mag *= 2048.0; + + return status; +} + +/* Apply the correction value and read the TX DC offset magnitude */ +static int tx_cal_measure_correction(struct tx_cal *state, + bladerf_correction c, + int16_t value, float *mag) +{ + int status; + + status = bladerf_set_correction(state->dev, BLADERF_MODULE_TX, c, value); + if (status != 0) { + return status; + } + + state->ts += TX_CAL_TS_INC; + + status = tx_cal_avg_magnitude(state, mag); + if (status == 0) { + PR_VERBOSE(" Corr=%5d, Avg_magnitude=%f\n", value, *mag); + } + + return status; +} + +static int tx_cal_get_corr(struct tx_cal *state, bool i_ch, + int16_t *corr_value, float *error_value) +{ + int status; + unsigned int n; + int16_t corr; + float mag[4]; + float m1, m2, b1, b2; + int16_t range_min, range_max; + int16_t min_corr; + float min_mag; + + const int16_t x[4] = { -1800, -1000, 1000, 1800 }; + + const bladerf_correction corr_module = + i_ch ? BLADERF_CORR_LMS_DCOFF_I : BLADERF_CORR_LMS_DCOFF_Q; + + PR_DBG("Getting coarse estimate for %c\n", i_ch ? 'I' : 'Q'); + + for (n = 0; n < 4; n++) { + status = tx_cal_measure_correction(state, corr_module, x[n], &mag[n]); + if (status != 0) { + return status; + } + + } + + m1 = (mag[1] - mag[0]) / (x[1] - x[0]); + b1 = mag[0] - m1 * x[0]; + + m2 = (mag[3] - mag[2]) / (x[3] - x[2]); + b2 = mag[2] - m2 * x[2]; + + PR_VERBOSE(" m1=%3.8f, b1=%3.8f, m2=%3.8f, b=%3.8f\n", m1, b1, m2, b2); + + if (m1 < 0 && m2 > 0) { + const int16_t tmp = (int16_t)((b2 - b1) / (m1 - m2) + 0.5); + const int16_t corr_est = (tmp / 16) * 16; + + /* Number of points to sweep on either side of our estimate */ + const unsigned int n_sweep = 10; + + PR_VERBOSE(" corr_est=%d\n", corr_est); + + range_min = corr_est - 16 * n_sweep; + if (range_min < -2048) { + range_min = -2048; + } + + range_max = corr_est + 16 * n_sweep; + if (range_max > 2048) { + range_max = 2048; + } + + } else { + /* The frequency and gain combination have yielded a set of + * points that do not form intersecting lines. This may be indicative + * of a case where the LMS6 DC bias settings can't pull the DC offset + * to a zero-crossing. We'll just do a slow, full scan to find + * a minimum */ + PR_VERBOSE(" Could not compute estimate. Performing full sweep.\n"); + range_min = -2048; + range_max = 2048; + } + + + PR_DBG("Performing correction value sweep: [%-5d : 16 :%5d]\n", + range_min, range_max); + + min_corr = 0; + min_mag = 2048; + + for (n = 0, corr = range_min; + corr <= range_max && n < TX_CAL_CORR_SWEEP_LEN; + n++, corr += 16) { + + float tmp; + + status = tx_cal_measure_correction(state, corr_module, corr, &tmp); + if (status != 0) { + return status; + } + + if (tmp < 0) { + tmp = -tmp; + } + + if (tmp < min_mag) { + min_corr = corr; + min_mag = tmp; + } + } + + /* Leave the device set to the minimum */ + status = bladerf_set_correction(state->dev, BLADERF_MODULE_TX, + corr_module, min_corr); + if (status == 0) { + *corr_value = min_corr; + *error_value = min_mag; + } + + return status; +} + +static int perform_tx_cal(struct tx_cal *state, struct dc_calibration_params *p) +{ + int status = 0; + + status = tx_cal_update_frequency(state, p->frequency); + if (status != 0) { + return status; + } + + state->ts += TX_CAL_TS_INC; + + /* Perform I calibration */ + status = tx_cal_get_corr(state, true, &p->corr_i, &p->error_i); + if (status != 0) { + return status; + } + + /* Perform Q calibration */ + status = tx_cal_get_corr(state, false, &p->corr_q, &p->error_q); + if (status != 0) { + return status; + } + + /* Re-do I calibration to try to further fine-tune result */ + status = tx_cal_get_corr(state, true, &p->corr_i, &p->error_i); + if (status != 0) { + return status; + } + + /* Apply the resulting nominal values */ + status = set_tx_dc_corr(state->dev, p->corr_i, p->corr_q); + + return status; +} + +int dc_calibration_tx(struct bladerf *dev, + struct dc_calibration_params *params, + size_t num_params, bool print_status) +{ + int status = 0; + int retval = 0; + struct tx_cal_backup backup; + struct tx_cal state; + size_t i; + + memset(&state, 0, sizeof(state)); + + /* Backup the device state prior to making changes */ + status = get_tx_cal_backup(dev, &backup); + if (status != 0) { + return status; + } + + /* Configure the device for our TX cal operation */ + status = apply_tx_cal_settings(dev); + if (status != 0) { + goto out; + } + + /* Enable TX and run zero samples through the device */ + status = tx_cal_tx_init(dev); + if (status != 0) { + goto out; + } + + /* Enable RX */ + status = tx_cal_rx_init(dev); + if (status != 0) { + goto out; + } + + /* Initialize calibration state information and resources */ + status = tx_cal_state_init(dev, &state); + if (status != 0) { + goto out; + } + + for (i = 0; i < num_params && status == 0; i++) { + status = perform_tx_cal(&state, ¶ms[i]); + + if (status == 0 && print_status) { +# ifdef DEBUG_DC_CALIBRATION + const char sol = '\n'; + const char eol = '\n'; +# else + const char sol = '\r'; + const char eol = '\0'; +# endif + printf("%cCalibrated @ %10" PRIu64 " Hz: " + "I=%4d (Error: %4.2f), " + "Q=%4d (Error: %4.2f) %c", + sol, + params[i].frequency, + params[i].corr_i, params[i].error_i, + params[i].corr_q, params[i].error_q, + eol); + fflush(stdout); + } + } + + if (print_status) { + putchar('\n'); + } + +out: + retval = status; + + status = bladerf_enable_module(dev, BLADERF_MODULE_RX, false); + if (status != 0 && retval == 0) { + retval = status; + } + + status = bladerf_enable_module(dev, BLADERF_MODULE_TX, false); + if (status != 0 && retval == 0) { + retval = status; + } + + tx_cal_state_deinit(&state); + + status = set_tx_cal_backup(dev, &backup); + if (status != 0 && retval == 0) { + retval = status; + } + + return retval; +} + +int dc_calibration(struct bladerf *dev, bladerf_module module, + struct dc_calibration_params *params, + size_t num_params, bool show_status) +{ + int status; + + switch (module) { + case BLADERF_MODULE_RX: + status = dc_calibration_rx(dev, params, num_params, show_status); + break; + + case BLADERF_MODULE_TX: + status = dc_calibration_tx(dev, params, num_params, show_status); + break; + + default: + status = BLADERF_ERR_INVAL; + } + + return status; +} diff --git a/Radio/HW/BladeRF/common/src/devcfg.c b/Radio/HW/BladeRF/common/src/devcfg.c new file mode 100644 index 0000000..497c54b --- /dev/null +++ b/Radio/HW/BladeRF/common/src/devcfg.c @@ -0,0 +1,638 @@ +/* + * This file is part of the bladeRF project: + * http://www.github.com/nuand/bladeRF + * + * Copyright (C) 2014-2015 Nuand LLC + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include <stdlib.h> +#include <stdio.h> +#include <stdint.h> +#include <limits.h> +#include <string.h> +#include <errno.h> +#include <getopt.h> + +#include "devcfg.h" +#include "conversions.h" + +#define DEVCFG_DFLT_TX_FREQ 1000000000 +#define DEVCFG_DFLT_TXVGA1 (-14) +#define DEVCFG_DFLT_TXVGA2 0 + +#define DEVCFG_DFLT_RX_FREQ 1000000000 +#define DEVCFG_DFLT_LNAGAIN BLADERF_LNA_GAIN_MAX +#define DEVCFG_DFLT_RXVGA1 30 +#define DEVCFG_DFLT_RXVGA2 0 + +#define DEVCFG_DFLT_SAMPLERATE 2000000 +#define DEVCFG_DFLT_BANDWIDTH 1500000 + +#define DEVCFG_DFLT_SAMPLES_PER_BUF 8192 +#define DEVCFG_DFLT_NUM_BUFFERS 64 +#define DEVCFG_DFLT_NUM_TRANSFERS 16 +#define DEVCFG_DFLT_STREAM_TIMEMOUT_MS 5000 +#define DEVCFG_DFLT_SYNC_TIMEOUT_MS (2 * DEVCFG_DFLT_STREAM_TIMEMOUT_MS) + +#define OPTION_HELP 'h' +#define OPTION_DEVICE 'd' +#define OPTION_LOOPBACK 'l' +#define OPTION_VERBOSITY 'v' +#define OPTION_SAMPLERATE 's' +#define OPTION_RX_SAMPLERATE 0x80 +#define OPTION_TX_SAMPLERATE 0x81 +#define OPTION_BANDWIDTH 'b' +#define OPTION_RX_BANDWIDTH 0x82 +#define OPTION_TX_BANDWIDTH 0x83 +#define OPTION_FREQUENCY 'f' +#define OPTION_RX_FREQUENCY 0x84 +#define OPTION_TX_FREQUENCY 0x85 +#define OPTION_LNAGAIN 0x86 +#define OPTION_RXVGA1 0x87 +#define OPTION_RXVGA2 0x88 +#define OPTION_TXVGA1 0x89 +#define OPTION_TXVGA2 0x8a +#define OPTION_SAMPLES_PER_BUF 0x91 +#define OPTION_NUM_BUFFERS 0x92 +#define OPTION_NUM_TRANSFERS 0x93 +#define OPTION_STREAM_TIMEOUT 0x94 +#define OPTION_SYNC_TIMEOUT 0x95 +#define OPTION_ENABLE_XB200 0x96 + +static const numeric_suffix hz_suffixes[] = { + { "K", 1000 }, + { "KHz", 1000 }, + { "M", 1000000 }, + { "MHz", 1000000 }, + { "G", 1000000000 }, + { "GHz", 1000000000 }, +}; + +static const struct option devcfg_long_options[] = +{ + { "help", no_argument, 0, OPTION_HELP }, + { "device", required_argument, 0, OPTION_DEVICE }, + { "verbosity", required_argument, 0, OPTION_VERBOSITY }, + + { "samplerate", required_argument, 0, OPTION_SAMPLERATE }, + { "rx-samplerate", required_argument, 0, OPTION_RX_SAMPLERATE }, + { "tx-samplerate", required_argument, 0, OPTION_TX_SAMPLERATE }, + + { "bandwidth", required_argument, 0, OPTION_BANDWIDTH }, + { "rx-bandwidth", required_argument, 0, OPTION_RX_BANDWIDTH }, + { "tx-bandwidth", required_argument, 0, OPTION_TX_BANDWIDTH }, + + { "frequency", required_argument, 0, OPTION_FREQUENCY }, + { "rx-frequency", required_argument, 0, OPTION_RX_FREQUENCY }, + { "tx-frequency", required_argument, 0, OPTION_TX_FREQUENCY }, + + { "lna-gain", required_argument, 0, OPTION_LNAGAIN }, + { "rxvga1-gain", required_argument, 0, OPTION_RXVGA1 }, + { "rxvga2-gain", required_argument, 0, OPTION_RXVGA2 }, + + { "txvga1-gain", required_argument, 0, OPTION_TXVGA1 }, + { "txvga2-gain", required_argument, 0, OPTION_TXVGA2 }, + + { "loopback", required_argument, 0, OPTION_LOOPBACK }, + + { "xb200", no_argument, 0, OPTION_ENABLE_XB200 }, + + { "samples-per-buffer", required_argument, 0, OPTION_SAMPLES_PER_BUF }, + { "num-buffers", required_argument, 0, OPTION_NUM_BUFFERS }, + { "num-transfers", required_argument, 0, OPTION_NUM_TRANSFERS }, + { "stream-timeout", required_argument, 0, OPTION_STREAM_TIMEOUT }, + { "sync-timeout", required_argument, 0, OPTION_SYNC_TIMEOUT }, +}; + +void devcfg_init(struct devcfg *c) +{ + c->device_specifier = NULL; + + c->tx_frequency = DEVCFG_DFLT_TX_FREQ; + c->tx_bandwidth = DEVCFG_DFLT_BANDWIDTH; + c->tx_samplerate = DEVCFG_DFLT_SAMPLERATE; + c->txvga1 = DEVCFG_DFLT_TXVGA1; + c->txvga2 = DEVCFG_DFLT_TXVGA2; + + c->rx_frequency = DEVCFG_DFLT_TX_FREQ; + c->rx_bandwidth = DEVCFG_DFLT_BANDWIDTH; + c->rx_samplerate = DEVCFG_DFLT_SAMPLERATE; + c->lnagain = DEVCFG_DFLT_LNAGAIN; + c->rxvga1 = DEVCFG_DFLT_RXVGA1; + c->rxvga2 = DEVCFG_DFLT_RXVGA2; + + c->loopback = BLADERF_LB_NONE; + + c->verbosity = BLADERF_LOG_LEVEL_INFO; + + c->enable_xb200 = false; + + c->samples_per_buffer = DEVCFG_DFLT_SAMPLES_PER_BUF; + c->num_buffers = DEVCFG_DFLT_NUM_BUFFERS; + c->num_transfers = DEVCFG_DFLT_NUM_TRANSFERS; + c->stream_timeout_ms = DEVCFG_DFLT_STREAM_TIMEMOUT_MS; + c->sync_timeout_ms = DEVCFG_DFLT_SYNC_TIMEOUT_MS; +} + +void devcfg_deinit(struct devcfg *c) +{ + free(c->device_specifier); +} + +void devcfg_print_common_help(const char *title) +{ + if (title != NULL) { + printf("%s", title); + } + + printf(" -h, --help Show this help text.\n"); + printf(" -d, --device <str> Open the specified device.\n"); + printf(" -v, --verbosity <level> Set the libbladeRF verbosity level.\n"); + printf(" --xb200 Enable the XB-200. This will remain enabled\n"); + printf(" until the device is power-cycled.\n"); + printf("\n"); + printf(" -f, --frequency <freq> Set RX and TX to the specified frequency.\n"); + printf(" --rx-frequency <freq> Set RX to the specified frequency.\n"); + printf(" --tx-frequency <freq> Set TX to the specified frequency.\n"); + printf("\n"); + printf(" -s, --samplerate <rate> Set RX and TX to the specified sample rate.\n"); + printf(" --rx-samplerate <rate> Set RX to the specified sample rate.\n"); + printf(" --tx-samplerate <rate> Set RX to the specified sample rate.\n"); + printf("\n"); + printf(" -b, --bandwidth <bw> Set RX and TX to the specified bandwidth.\n"); + printf(" --rx-bandwidth <bw> Set RX to the specified bandwidth.\n"); + printf(" --tx-bandwidth <bw> Set TX to the specified bandwidth.\n"); + printf("\n"); + printf(" --lna-gain <gain> Set the RX LNA to the specified gain.\n"); + printf(" Options are: bypass, mid, max\n"); + printf(" --rxvga1-gain <gain> Set RX VGA1 to the specified gain.\n"); + printf(" --rxvga2-gain <gain> Set RX VGA2 to the specified gain.\n"); + printf(" --txvga1-gain <gain> Set TX VGA1 to the specified gain.\n"); + printf(" --txvga2-gain <gain> Set TX VGA2 to the specified gain.\n"); + printf("\n"); + printf(" --num-buffers <n> Allocate <n> sample buffers.\n"); + printf(" --samples-per-buffer <n> Allocate <n> samples in each sample buffer.\n"); + printf(" --num-transfers <n> Utilize up to <n> simultaneous USB transfers.\n"); + printf(" --stream-timeout <n> Set stream timeout to <n> milliseconds.\n"); + printf(" --sync-timeout <n> Set sync function timeout to <n> milliseconds.\n"); + +} + +struct option * devcfg_get_long_options(const struct option *app_options) +{ + struct option *ret; + size_t app_size = 0; + const size_t devcfg_size = sizeof(devcfg_long_options); + + while (app_options[app_size].name != NULL) { + app_size++; + } + + /* Account for 0-entry */ + app_size = (app_size + 1) * sizeof(app_options[0]); + + ret = malloc(devcfg_size + app_size); + + if (ret != NULL) { + memcpy(ret, &devcfg_long_options, devcfg_size); + memcpy(ret + ARRAY_SIZE(devcfg_long_options) , app_options, app_size); + } + + return ret; +} + +int devcfg_handle_args(int argc, char **argv, const char *option_str, + const struct option *long_options, struct devcfg *config) +{ + int c; + bool ok; + int status; + unsigned int freq_min; + + while ((c = getopt_long(argc, argv, option_str, long_options, NULL)) >= 0) { + + if (config->enable_xb200) { + freq_min = BLADERF_FREQUENCY_MIN_XB200; + } else { + freq_min = BLADERF_FREQUENCY_MIN; + } + + switch (c) { + + case OPTION_HELP: + return 1; + break; + + case OPTION_DEVICE: + if (config->device_specifier == NULL) { + config->device_specifier = strdup(optarg); + if (config->device_specifier == NULL) { + perror("strdup"); + return -1; + } + } + break; + + case OPTION_LOOPBACK: + status = str2loopback(optarg, &config->loopback); + if (status != 0) { + fprintf(stderr, "Invalid loopback mode: %s\n", optarg); + return -1; + } + break; + + case OPTION_VERBOSITY: + config->verbosity = str2loglevel(optarg, &ok); + if (!ok) { + fprintf(stderr, "Invalid verbosity level: %s\n", optarg); + return -1; + } + break; + + case OPTION_ENABLE_XB200: + config->enable_xb200 = true; + break; + + case OPTION_FREQUENCY: + config->rx_frequency = + str2uint_suffix(optarg, + freq_min, + BLADERF_FREQUENCY_MAX, + hz_suffixes, ARRAY_SIZE(hz_suffixes), + &ok); + + if (!ok) { + fprintf(stderr, "Invalid frequency: %s\n", optarg); + return -1; + } else { + config->tx_frequency = config->rx_frequency; + } + break; + + case OPTION_RX_FREQUENCY: + config->rx_frequency = + str2uint_suffix(optarg, + freq_min, + BLADERF_FREQUENCY_MAX, + hz_suffixes, ARRAY_SIZE(hz_suffixes), + &ok); + + if (!ok) { + fprintf(stderr, "Invalid RX frequency: %s\n", optarg); + return -1; + } + break; + + case OPTION_TX_FREQUENCY: + config->tx_frequency = + str2uint_suffix(optarg, + freq_min, + BLADERF_FREQUENCY_MAX, + hz_suffixes, ARRAY_SIZE(hz_suffixes), + &ok); + + if (!ok) { + fprintf(stderr, "Invalid TX frequency: %s\n", optarg); + return -1; + } + break; + + case OPTION_SAMPLERATE: + config->rx_samplerate = + str2uint_suffix(optarg, + BLADERF_SAMPLERATE_MIN, + BLADERF_SAMPLERATE_REC_MAX, + hz_suffixes, ARRAY_SIZE(hz_suffixes), + &ok); + if (!ok) { + fprintf(stderr, "Invalid sample rate: %s\n", optarg); + return -1; + } else { + config->tx_samplerate = config->rx_samplerate; + } + break; + + case OPTION_RX_SAMPLERATE: + config->rx_samplerate = + str2uint_suffix(optarg, + BLADERF_SAMPLERATE_MIN, + BLADERF_SAMPLERATE_REC_MAX, + hz_suffixes, ARRAY_SIZE(hz_suffixes), + &ok); + if (!ok) { + fprintf(stderr, "Invalid RX sample rate: %s\n", optarg); + return -1; + } + break; + + case OPTION_TX_SAMPLERATE: + config->tx_samplerate = + str2uint_suffix(optarg, + BLADERF_SAMPLERATE_MIN, + BLADERF_SAMPLERATE_REC_MAX, + hz_suffixes, ARRAY_SIZE(hz_suffixes), + &ok); + if (!ok) { + fprintf(stderr, "Invalid TX sample rate: %s\n", optarg); + return -1; + } + break; + + case OPTION_BANDWIDTH: + config->rx_bandwidth = + str2uint_suffix(optarg, + BLADERF_BANDWIDTH_MIN, + BLADERF_BANDWIDTH_MAX, + hz_suffixes, ARRAY_SIZE(hz_suffixes), + &ok); + if (!ok) { + fprintf(stderr, "Invalid bandwidth: %s\n", optarg); + return -1; + } else { + config->tx_bandwidth = config->rx_bandwidth; + } + break; + + case OPTION_RX_BANDWIDTH: + config->rx_bandwidth = + str2uint_suffix(optarg, + BLADERF_BANDWIDTH_MIN, + BLADERF_BANDWIDTH_MAX, + hz_suffixes, ARRAY_SIZE(hz_suffixes), + &ok); + if (!ok) { + fprintf(stderr, "Invalid RX bandwidth: %s\n", optarg); + return -1; + } + break; + + case OPTION_TX_BANDWIDTH: + config->tx_bandwidth = + str2uint_suffix(optarg, + BLADERF_BANDWIDTH_MIN, + BLADERF_BANDWIDTH_MAX, + hz_suffixes, ARRAY_SIZE(hz_suffixes), + &ok); + if (!ok) { + fprintf(stderr, "Invalid TX bandwidth: %s\n", optarg); + return -1; + } + break; + + case OPTION_LNAGAIN: + status = str2lnagain(optarg, &config->lnagain); + if (status != 0) { + fprintf(stderr, "Invalid RX LNA gain: %s\n", optarg); + return -1; + } + break; + + case OPTION_RXVGA1: + config->rxvga1 = str2int(optarg, + BLADERF_RXVGA1_GAIN_MIN, + BLADERF_RXVGA1_GAIN_MAX, + &ok); + + if (!ok) { + fprintf(stderr, "Invalid RXVGA1 gain: %s\n", optarg); + return -1; + } + break; + + case OPTION_RXVGA2: + config->rxvga2 = str2int(optarg, + BLADERF_RXVGA2_GAIN_MIN, + BLADERF_RXVGA2_GAIN_MAX, + &ok); + + if (!ok) { + fprintf(stderr, "Invalid RXVGA1 gain: %s\n", optarg); + return -1; + } + break; + + case OPTION_TXVGA1: + config->txvga1 = str2int(optarg, + BLADERF_TXVGA1_GAIN_MIN, + BLADERF_TXVGA1_GAIN_MAX, + &ok); + + if (!ok) { + fprintf(stderr, "Invalid TXVGA1 gain: %s\n", optarg); + return -1; + } + break; + + case OPTION_TXVGA2: + config->txvga2 = str2int(optarg, + BLADERF_TXVGA2_GAIN_MIN, + BLADERF_TXVGA2_GAIN_MAX, + &ok); + + if (!ok) { + fprintf(stderr, "Invalid TXVGA2 gain: %s\n", optarg); + return -1; + } + break; + + case OPTION_NUM_BUFFERS: + config->num_buffers = str2uint(optarg, 1, UINT_MAX, &ok); + if (!ok) { + fprintf(stderr, "Invalid buffer count: %s\n", optarg); + return -1; + } + break; + + case OPTION_SAMPLES_PER_BUF: + config->samples_per_buffer = str2uint(optarg, + 1024, UINT_MAX, &ok); + if (!ok) { + fprintf(stderr, "Invalid buffer size (in samples): %s\n", + optarg); + return -1; + } + break; + + case OPTION_NUM_TRANSFERS: + config->num_transfers = str2uint(optarg, 1, UINT_MAX, &ok); + + if (!ok) { + fprintf(stderr, "Invalid transfer count: %s\n", optarg); + return -1; + } + break; + + case OPTION_STREAM_TIMEOUT: + config->stream_timeout_ms = str2uint(optarg, 0, UINT_MAX, &ok); + if (!ok) { + fprintf(stderr, "Invalid stream timeout: %s\n", optarg); + return -1; + } + break; + + case OPTION_SYNC_TIMEOUT: + config->sync_timeout_ms = str2uint(optarg, 0, UINT_MAX, &ok); + if (!ok) { + fprintf(stderr, "Invalid sync function timeout: %s\n", optarg); + return -1; + } + break; + } + } + + return 0; +} + +int devcfg_apply(struct bladerf *dev, const struct devcfg *c) +{ + int status; + const char *board_name; + + board_name = bladerf_get_board_name(dev); + + bladerf_log_set_verbosity(c->verbosity); + + status = bladerf_set_loopback(dev, c->loopback); + if (status != 0) { + fprintf(stderr, "Failed to set loopback: %s\n", + bladerf_strerror(status)); + return -1; + } + + if (c->enable_xb200) { + status = bladerf_expansion_attach(dev, BLADERF_XB_200); + if (status != 0) { + fprintf(stderr, "Failed to attach XB-200.\n"); + return -1; + } + } + + status = bladerf_set_frequency(dev, BLADERF_MODULE_RX, c->rx_frequency); + if (status != 0) { + fprintf(stderr, "Failed to set RX frequency: %s\n", + bladerf_strerror(status)); + return -1; + } + + status = bladerf_set_frequency(dev, BLADERF_MODULE_TX, c->tx_frequency); + if (status != 0) { + fprintf(stderr, "Failed to set TX frequency: %s\n", + bladerf_strerror(status)); + return -1; + } + + status = bladerf_set_sample_rate(dev, BLADERF_MODULE_RX, c->rx_samplerate, NULL); + if (status != 0) { + fprintf(stderr, "Failed to set RX sample rate:%s\n", + bladerf_strerror(status)); + return -1; + } + + status = bladerf_set_sample_rate(dev, BLADERF_MODULE_TX, c->tx_samplerate, NULL); + if (status != 0) { + fprintf(stderr, "Failed to set TX sample rate: %s\n", + bladerf_strerror(status)); + return -1; + } + + status = bladerf_set_bandwidth(dev, BLADERF_MODULE_RX, c->rx_bandwidth, NULL); + if (status != 0) { + fprintf(stderr, "Failed to set RX bandwidth: %s\n", + bladerf_strerror(status)); + return -1; + } + + status = bladerf_set_bandwidth(dev, BLADERF_MODULE_TX, c->tx_bandwidth, NULL); + if (status != 0) { + fprintf(stderr, "Failed to set RX bandwidth: %s\n", + bladerf_strerror(status)); + return -1; + } + + if (strcasecmp(board_name, "bladerf1") == 0) { + status = bladerf_set_lna_gain(dev, c->lnagain); + if (status != 0) { + fprintf(stderr, "Failed to set RX LNA gain: %s\n", + bladerf_strerror(status)); + return -1; + } + + status = bladerf_set_rxvga1(dev, c->rxvga1); + if (status != 0) { + fprintf(stderr, "Failed to set RX VGA1 gain: %s\n", + bladerf_strerror(status)); + return -1; + } + + status = bladerf_set_rxvga2(dev, c->rxvga2); + if (status != 0) { + fprintf(stderr, "Failed to set RX VGA2 gain: %s\n", + bladerf_strerror(status)); + return -1; + } + + status = bladerf_set_txvga1(dev, c->txvga1); + if (status != 0) { + fprintf(stderr, "Failed to set TX VGA1 gain: %s\n", + bladerf_strerror(status)); + return -1; + } + + status = bladerf_set_txvga2(dev, c->txvga2); + if (status != 0) { + fprintf(stderr, "Failed to set TX VGA2 gain: %s\n", + bladerf_strerror(status)); + return -1; + } + } + + return 0; +} + +int devcfg_perform_sync_config(struct bladerf *dev, + bladerf_module module, + bladerf_format format, + const struct devcfg *config, + bool enable_module) +{ + int status = bladerf_sync_config(dev, module, format, + config->num_buffers, + config->samples_per_buffer, + config->num_transfers, + config->stream_timeout_ms); + + if (status != 0) { + fprintf(stderr, "Failed to configure %s: %s\n", + module == BLADERF_MODULE_RX ? "RX" : "TX", + bladerf_strerror(status)); + return -1; + } + + if (enable_module) { + status = bladerf_enable_module(dev, module, true); + if (status != 0) { + fprintf(stderr, "Failed to enable %s module: %s\n", + module == BLADERF_MODULE_RX ? "RX" : "TX", + bladerf_strerror(status)); + } + } + + return 0; +} diff --git a/Radio/HW/BladeRF/common/src/log.c b/Radio/HW/BladeRF/common/src/log.c new file mode 100644 index 0000000..2872620 --- /dev/null +++ b/Radio/HW/BladeRF/common/src/log.c @@ -0,0 +1,98 @@ +/* + * This file is part of the bladeRF project: + * http://www.github.com/nuand/bladeRF + * + * Copyright (c) 2013 Nuand LLC. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifdef LOGGING_ENABLED +#include <log.h> +#if !defined(WIN32) && !defined(__CYGWIN__) && defined(LOG_SYSLOG_ENABLED) +#include <syslog.h> +#endif +#include <stdio.h> +#include <stdarg.h> + +static bladerf_log_level filter_level = BLADERF_LOG_LEVEL_INFO; + +void log_write(bladerf_log_level level, const char *format, ...) +{ + /* Only process this message if its level exceeds the current threshold */ + if (level >= filter_level) + { + va_list args; + + /* Write the log message */ + va_start(args, format); +#if defined(WIN32) || defined(__CYGWIN__) + vfprintf(stderr, format, args); +#else +# if defined (LOG_SYSLOG_ENABLED) + { + int syslog_level; + + switch (level) { + case BLADERF_LOG_LEVEL_VERBOSE: + case BLADERF_LOG_LEVEL_DEBUG: + syslog_level = LOG_DEBUG; + break; + + case BLADERF_LOG_LEVEL_INFO: + syslog_level = LOG_INFO; + break; + + case BLADERF_LOG_LEVEL_WARNING: + syslog_level = LOG_WARNING; + break; + + case BLADERF_LOG_LEVEL_ERROR: + syslog_level = LOG_ERR; + break; + + case BLADERF_LOG_LEVEL_CRITICAL: + syslog_level = LOG_CRIT; + break; + + default: + /* Shouldn't be used, so just route it to a low level */ + syslog_level = LOG_DEBUG; + break; + } + + vsyslog(syslog_level | LOG_USER, format, args); + } +# else + vfprintf(stderr, format, args); +# endif +#endif + va_end(args); + } +} + +void log_set_verbosity(bladerf_log_level level) +{ + filter_level = level; +} + +bladerf_log_level log_get_verbosity() +{ + return filter_level; +} +#endif diff --git a/Radio/HW/BladeRF/common/src/osx/clock_gettime.c b/Radio/HW/BladeRF/common/src/osx/clock_gettime.c new file mode 100644 index 0000000..5e7200e --- /dev/null +++ b/Radio/HW/BladeRF/common/src/osx/clock_gettime.c @@ -0,0 +1,59 @@ +/* + * clock_gettime() wrapper for OSX based upon jbenet's github "gist": + * https://gist.github.com/jbenet/1087739 + * + * Copyright (c) 2011 Juan Batiz-Benet (https://github.com/jbenet) + * Copyright (c) 2013-2017 Nuand LLC + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "clock_gettime.h" +#include <errno.h> +#include <mach/clock.h> +#include <mach/mach.h> + +int clock_gettime(clockid_t clk_id, struct timespec *tp) +{ + kern_return_t ret; + clock_serv_t cclock; + mach_timespec_t mts; + + ret = host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &cclock); + if (ret != 0) { + errno = EINVAL; + return -1; + } + + ret = clock_get_time(cclock, &mts); + if (ret != 0) { + goto clock_gettime_out; + } + + tp->tv_sec = mts.tv_sec; + tp->tv_nsec = mts.tv_nsec; + +clock_gettime_out: + if (mach_port_deallocate(mach_task_self(), cclock) != 0 || ret != 0) { + errno = EINVAL; + return -1; + } else { + return 0; + } +} diff --git a/Radio/HW/BladeRF/common/src/parse.c b/Radio/HW/BladeRF/common/src/parse.c new file mode 100644 index 0000000..dd6abe1 --- /dev/null +++ b/Radio/HW/BladeRF/common/src/parse.c @@ -0,0 +1,455 @@ +/* + * Copyright (c) 2017 Nuand LLC. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "parse.h" +#include "conversions.h" +#include "log.h" + +#include <stdio.h> +#include <stdlib.h> + +static char **add_arg( + char **argv, int argc, const char *buf, int start, int end, int quote_count) +{ + char **rv; + char *d_ptr; + int i; + int len; + + char c; + char quote_char; + + quote_char = 0; + + rv = (char **)realloc(argv, sizeof(char *) * (argc + 1)); + if (rv == NULL) { + return NULL; + } + + rv[argc] = NULL; + + len = end - start + 1; + + d_ptr = (char *)malloc(len + 1 - quote_count * 2); + if (d_ptr == NULL) { + free(rv); + return NULL; + } + + rv[argc - 1] = d_ptr; + + for (i = 0; i < len; i++) { + c = buf[start + i]; + + if (!quote_char) { + /* We are not in a quote, copy everything but quote chars */ + if (c == '"' || c == '\'') { + quote_char = c; + } else { + *d_ptr++ = c; + } + } else { + /* We are in a quote, copy everything but the current quote char */ + if (c == quote_char) { + quote_char = 0; + } else { + *d_ptr++ = c; + } + } + } + *d_ptr = '\0'; + + return rv; +} + +int str2args(const char *line, char comment_char, char ***argv) +{ + char **rv; + int argc; + + unsigned i; + size_t len; + + bool in_arg; + char c; + char quote_char; + + int arg_start; + int quote_count; + + rv = NULL; + argc = 0; + + quote_char = 0; + + arg_start = 0; + quote_count = 0; + + len = strlen(line); + + in_arg = false; + + for (i = 0; i < len; i++) { + c = line[i]; + + if (in_arg) { + /* Found the end of a quote! */ + if (quote_char) { + if (quote_char == c) { + quote_char = 0; + } + continue; + } + + /* Found the beginning of a quote! */ + if (c == '\'' || c == '"') { + quote_count++; + quote_char = c; + continue; + } + + /* Found whitespace outside of quote */ + if (c == ' ' || c == '\t' || c == '\r' || c == '\n') { + in_arg = false; + argc++; + rv = add_arg(rv, argc, line, arg_start, i - 1, quote_count); + if (rv == NULL) + return -1; + } + } else { + if (c == comment_char) { + break; + } + /* Enter a new argument */ + if (c != ' ' && c != '\t' && c != '\r' && c != '\n') { + /* If first argument is a tick it means we're in a quote */ + if (c == '\'' || c == '"') { + quote_char = c; + } else { + quote_char = 0; + } + quote_count = 0; + arg_start = i; + in_arg = true; + } + /* else this is still whitespace */ + } + } + + /* reached the end of the line, check to see if current arg needs to + * be closed */ + if (in_arg) { + if (quote_char) { + free_args(argc, rv); + return -2; + } else { + argc++; + rv = add_arg(rv, argc, line, arg_start, i - 1, quote_count); + if (rv == NULL) { + return -1; + } + } + } + + *argv = rv; + + return argc; +} + +void free_args(int argc, char **argv) +{ + int i; + for (i = 0; i < argc; i++) { + free(argv[i]); + } + free(argv); +} + +static struct config_options *add_opt( + struct config_options *optv, int optc, char *key, char *val, int lineno) +{ + struct config_options *rv; + char *ptr1, *ptr2; + rv = (struct config_options *)realloc(optv, + sizeof(struct config_options) * optc); + if (rv == NULL) { + return NULL; + } + + ptr1 = (char *)malloc(strlen(key) + 1); + if (ptr1 == NULL) { + free(rv); + return NULL; + } + strcpy(ptr1, key); + rv[optc - 1].key = ptr1; + + ptr2 = (char *)malloc(strlen(val) + 1); + if (ptr2 == NULL) { + free(ptr1); + free(rv); + return NULL; + } + strcpy(ptr2, val); + rv[optc - 1].value = ptr2; + + rv[optc - 1].lineno = lineno; + + return rv; +} + +bool update_match(struct bladerf *dev, char *str) +{ + size_t len; + int status; + struct bladerf_devinfo info; + bladerf_fpga_size fpga_size; + + status = bladerf_get_devinfo(dev, &info); + if (status < 0) + return false; + + bladerf_get_fpga_size(dev, &fpga_size); + if (status < 0) + return false; + + str++; + len = strlen(str); + if (str[len - 1] == ']') + str[len - 1] = '\0'; + + if (!strcmp(str, "x40")) { + return fpga_size == BLADERF_FPGA_40KLE; + } else if (!strcmp(str, "x115")) { + return fpga_size == BLADERF_FPGA_115KLE; + } + + status = bladerf_devstr_matches(str, &info); + + return status == 1; +} + +int str2options(struct bladerf *dev, + const char *buf, + size_t buf_sz, + struct config_options **opts) +{ + char *line; + char *d_ptr; + int line_num; + char c; + unsigned i; + + struct config_options *optv; + int optc; + + char **line_argv; + int line_argc; + + bool match; + + match = true; + + optv = NULL; + optc = 0; + + line_num = 1; + + line = malloc(buf_sz + 1); + if (!line) + return BLADERF_ERR_MEM; + + d_ptr = line; + + for (i = 0; i < buf_sz; i++) { + c = buf[i]; + if (c == '\n') { + /* deal with the old line */ + *d_ptr = 0; + line_argc = str2args(line, '#', &line_argv); + if (line_argc < 0) + goto out; + + /* handle line */ + if (line_argc > 3) { + log_error("Too many arguments in bladeRF.conf on line %d\n", + line_num); + goto out; + } else if (match && line_argc == 2) { + optc++; + optv = + add_opt(optv, optc, line_argv[0], line_argv[1], line_num); + if (!optv) { + optc = -1; + goto out; + } + } else if (line_argc == 1) { + if (*line_argv[0] != '[') { + log_error("Expecting scoping line (requires [ and ]) on " + "line %d\n", + line_num); + } + match = update_match(dev, line_argv[0]); + } + + /* free line */ + free_args(line_argc, line_argv); + + /* setup to capture the next line */ + line_num++; + d_ptr = line; + } else { + *d_ptr++ = c; + } + } + + if (opts) + *opts = optv; + +out: + free(line); + return optc; +} + +void free_opts(struct config_options *optv, int optc) +{ + int i; + + for (i = 0; i < optc; i++) { + free(optv[i].key); + free(optv[i].value); + } + free(optv); +} + +int csv2int(const char *line, int ***args) +{ + const char delim[] = " \r\n\t,.:"; /* supported delimiters */ + const size_t MAXLEN = 128; /* max line length (with newline and null) */ + static size_t arglen = 2; /* tunable: initial expected column count */ + char *myline = NULL; /* local copy of 'line' */ + char *parsestr = NULL; /* ptr to 'myline' on first strtok_r */ + char *saveptr = NULL; /* strtok_r state pointer */ + int **argout = NULL; /* array of output values */ + size_t count = 0; /* count of tokens extracted */ + size_t i; + + // Validity check + if (NULL == line) { + log_debug("line is null\n"); + return 0; + } + + if (NULL == args) { + log_error("args is null\n"); + goto fail; + } + + // strtok_r doesn't respect const, so make a copy of 'line' + myline = calloc(MAXLEN, 1); + if (NULL == myline) { + log_error("could not calloc myline\n"); + goto fail; + } + + myline = strncpy(myline, line, MAXLEN - 1); + + // Initial allocation of argout + argout = malloc(arglen * sizeof(int *)); + if (NULL == argout) { + log_error("could not malloc argout\n"); + goto fail; + } + + // Loop over input until strtok_r returns a NULL + for (i = 0, parsestr = myline; true; ++i, parsestr = NULL) { + char *token = NULL; /* return token from strtok_r */ + bool ok; + + token = strtok_r(parsestr, delim, &saveptr); + if (NULL == token) { + break; + } + + // Expand argout if necessary + if (i >= arglen) { + arglen *= 2; + log_verbose("expanding allocation to %zu column(s)\n", arglen); + int **newargout = realloc(argout, arglen * sizeof(int *)); + if (NULL == newargout) { + log_error("could not realloc(argout,%zu)\n", arglen); + goto fail; + } + argout = newargout; + } + + // Allocate memory for this value + argout[i] = malloc(sizeof(int)); + if (NULL == argout[i]) { + log_error("could not malloc argout[%zu]\n", i); + goto fail; + } + + // Update the count now, in case str2int fails and we need to dealloc + ++count; + + // Parse token into an integer value + *argout[i] = str2int(token, INT32_MIN, INT32_MAX, &ok); + if (!ok) { + log_error("str2int failed on '%s'\n", token); + goto fail; + } + } + + // Success! + *args = argout; + free(myline); + + // If arglen is too big, cut it in half next time... + if (count <= (arglen / 2)) { + arglen /= 2; + log_verbose("decreasing future arglen to %zu\n", arglen); + } + + return (int)count; + +fail: + // Deallocate everything... + free(myline); + free_csv2int((int)count, argout); + return -1; +} + +void free_csv2int(int argc, int **args) +{ + int i; + + if (NULL == args) { + return; + } + + for (i = 0; i < argc; ++i) { + free(args[i]); + } + + free(args); +} diff --git a/Radio/HW/BladeRF/common/src/range.c b/Radio/HW/BladeRF/common/src/range.c new file mode 100644 index 0000000..c16fe22 --- /dev/null +++ b/Radio/HW/BladeRF/common/src/range.c @@ -0,0 +1,74 @@ +/* + * This file is part of the bladeRF project: + * http://www.github.com/nuand/bladeRF + * + * Copyright (C) 2018 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 + */ + +#ifdef BLADERF_NIOS_BUILD +#include "devices.h" +#endif // BLADERF_NIOS_BUILD + +/* Avoid building this in low-memory situations */ +#if !defined(BLADERF_NIOS_BUILD) || defined(BLADERF_NIOS_LIBAD936X) + +#include "range.h" + +#if !defined(BLADERF_NIOS_BUILD) && !defined(BLADERF_NIOS_PC_SIMULATION) +#include "log.h" +#endif + +bool is_within_range(struct bladerf_range const *range, int64_t value) +{ + if (NULL == range) { + log_error("%s: range is null\n", __FUNCTION__); + return false; + } + + return (__scale(range, value) >= range->min && + __scale(range, value) <= range->max); +} + +int64_t clamp_to_range(struct bladerf_range const *range, int64_t value) +{ + if (NULL == range) { + log_error("%s: range is null\n", __FUNCTION__); + return value; + } + + if (__scale(range, value) < range->min) { + log_debug("%s: Requested value %" PRIi64 + " is below range [%g,%g], clamping to %" PRIi64 "\n", + __FUNCTION__, value, __unscale(range, range->min), + __unscale(range, range->max), + __unscale_int64(range, range->min)); + value = __unscale_int64(range, range->min); + } + + if (__scale(range, value) > range->max) { + log_debug("%s: Requested value %" PRIi64 + " is above range [%g,%g], clamping to %" PRIi64 "\n", + __FUNCTION__, value, __unscale(range, range->min), + __unscale(range, range->max), + __unscale_int64(range, range->max)); + value = __unscale_int64(range, range->max); + } + + return value; +} + +#endif // !defined(BLADERF_NIOS_BUILD) || defined(BLADERF_NIOS_LIBAD936X) diff --git a/Radio/HW/BladeRF/common/src/sha256.c b/Radio/HW/BladeRF/common/src/sha256.c new file mode 100644 index 0000000..b65c7ff --- /dev/null +++ b/Radio/HW/BladeRF/common/src/sha256.c @@ -0,0 +1,343 @@ +/*- + * Copyright 2005 Colin Percival + * Copyright 2013 Daniel Gröber + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <stdint.h> +#include <string.h> + +#include "sha256.h" + +#if BLADERF_BIG_ENDIAN == 1 + +/* Copy a vector of big-endian uint32_t into a vector of bytes */ +#define be32enc_vect(dst, src, len) \ + memcpy((void *)dst, (const void *)src, (size_t)len) + +/* Copy a vector of bytes into a vector of big-endian uint32_t */ +#define be32dec_vect(dst, src, len) \ + memcpy((void *)dst, (const void *)src, (size_t)len) + +#else + +/* From libbsd/include/bsd/sys/endian.h */ +/* + * Copyright © 2011 Guillem Jover <guillem@hadrons.org> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +static __inline uint32_t +be32dec(const void *pp) +{ + uint8_t const *p = (uint8_t const *)pp; + + return (((unsigned)p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]); +} + +static __inline void +be32enc(void *pp, uint32_t u) +{ + uint8_t *p = (uint8_t *)pp; + + p[0] = (u >> 24) & 0xff; + p[1] = (u >> 16) & 0xff; + p[2] = (u >> 8) & 0xff; + p[3] = u & 0xff; +} +/* END from libbsd/include/bsd/sys/endian.h */ + +/* + * Encode a length len/4 vector of (uint32_t) into a length len vector of + * (unsigned char) in big-endian form. Assumes len is a multiple of 4. + */ +static void +be32enc_vect(unsigned char *dst, const uint32_t *src, size_t len) +{ + size_t i; + + for (i = 0; i < len / 4; i++) + be32enc(dst + i * 4, src[i]); +} + +/* + * Decode a big-endian length len vector of (unsigned char) into a length + * len/4 vector of (uint32_t). Assumes len is a multiple of 4. + */ +static void +be32dec_vect(uint32_t *dst, const unsigned char *src, size_t len) +{ + size_t i; + + for (i = 0; i < len / 4; i++) + dst[i] = be32dec(src + i * 4); +} + +#endif + +/* Elementary functions used by SHA256 */ +#define Ch(x, y, z) ((x & (y ^ z)) ^ z) +#define Maj(x, y, z) ((x & (y | z)) | (y & z)) +#define SHR(x, n) (x >> n) +#define ROTR(x, n) ((x >> n) | (x << (32 - n))) +#define S0(x) (ROTR(x, 2) ^ ROTR(x, 13) ^ ROTR(x, 22)) +#define S1(x) (ROTR(x, 6) ^ ROTR(x, 11) ^ ROTR(x, 25)) +#define s0(x) (ROTR(x, 7) ^ ROTR(x, 18) ^ SHR(x, 3)) +#define s1(x) (ROTR(x, 17) ^ ROTR(x, 19) ^ SHR(x, 10)) + +/* SHA256 round function */ +#define RND(a, b, c, d, e, f, g, h, k) \ + t0 = h + S1(e) + Ch(e, f, g) + k; \ + t1 = S0(a) + Maj(a, b, c); \ + d += t0; \ + h = t0 + t1; + +/* Adjusted round function for rotating state */ +#define RNDr(S, W, i, k) \ + RND(S[(64 - i) % 8], S[(65 - i) % 8], \ + S[(66 - i) % 8], S[(67 - i) % 8], \ + S[(68 - i) % 8], S[(69 - i) % 8], \ + S[(70 - i) % 8], S[(71 - i) % 8], \ + W[i] + k) + +/* + * SHA256 block compression function. The 256-bit state is transformed via + * the 512-bit input block to produce a new state. + */ +static void +SHA256_Transform(uint32_t * state, const unsigned char block[64]) +{ + uint32_t W[64]; + uint32_t S[8]; + uint32_t t0, t1; + int i; + + /* 1. Prepare message schedule W. */ + be32dec_vect(W, block, 64); + for (i = 16; i < 64; i++) + W[i] = s1(W[i - 2]) + W[i - 7] + s0(W[i - 15]) + W[i - 16]; + + /* 2. Initialize working variables. */ + memcpy(S, state, 32); + + /* 3. Mix. */ + RNDr(S, W, 0, 0x428a2f98); + RNDr(S, W, 1, 0x71374491); + RNDr(S, W, 2, 0xb5c0fbcf); + RNDr(S, W, 3, 0xe9b5dba5); + RNDr(S, W, 4, 0x3956c25b); + RNDr(S, W, 5, 0x59f111f1); + RNDr(S, W, 6, 0x923f82a4); + RNDr(S, W, 7, 0xab1c5ed5); + RNDr(S, W, 8, 0xd807aa98); + RNDr(S, W, 9, 0x12835b01); + RNDr(S, W, 10, 0x243185be); + RNDr(S, W, 11, 0x550c7dc3); + RNDr(S, W, 12, 0x72be5d74); + RNDr(S, W, 13, 0x80deb1fe); + RNDr(S, W, 14, 0x9bdc06a7); + RNDr(S, W, 15, 0xc19bf174); + RNDr(S, W, 16, 0xe49b69c1); + RNDr(S, W, 17, 0xefbe4786); + RNDr(S, W, 18, 0x0fc19dc6); + RNDr(S, W, 19, 0x240ca1cc); + RNDr(S, W, 20, 0x2de92c6f); + RNDr(S, W, 21, 0x4a7484aa); + RNDr(S, W, 22, 0x5cb0a9dc); + RNDr(S, W, 23, 0x76f988da); + RNDr(S, W, 24, 0x983e5152); + RNDr(S, W, 25, 0xa831c66d); + RNDr(S, W, 26, 0xb00327c8); + RNDr(S, W, 27, 0xbf597fc7); + RNDr(S, W, 28, 0xc6e00bf3); + RNDr(S, W, 29, 0xd5a79147); + RNDr(S, W, 30, 0x06ca6351); + RNDr(S, W, 31, 0x14292967); + RNDr(S, W, 32, 0x27b70a85); + RNDr(S, W, 33, 0x2e1b2138); + RNDr(S, W, 34, 0x4d2c6dfc); + RNDr(S, W, 35, 0x53380d13); + RNDr(S, W, 36, 0x650a7354); + RNDr(S, W, 37, 0x766a0abb); + RNDr(S, W, 38, 0x81c2c92e); + RNDr(S, W, 39, 0x92722c85); + RNDr(S, W, 40, 0xa2bfe8a1); + RNDr(S, W, 41, 0xa81a664b); + RNDr(S, W, 42, 0xc24b8b70); + RNDr(S, W, 43, 0xc76c51a3); + RNDr(S, W, 44, 0xd192e819); + RNDr(S, W, 45, 0xd6990624); + RNDr(S, W, 46, 0xf40e3585); + RNDr(S, W, 47, 0x106aa070); + RNDr(S, W, 48, 0x19a4c116); + RNDr(S, W, 49, 0x1e376c08); + RNDr(S, W, 50, 0x2748774c); + RNDr(S, W, 51, 0x34b0bcb5); + RNDr(S, W, 52, 0x391c0cb3); + RNDr(S, W, 53, 0x4ed8aa4a); + RNDr(S, W, 54, 0x5b9cca4f); + RNDr(S, W, 55, 0x682e6ff3); + RNDr(S, W, 56, 0x748f82ee); + RNDr(S, W, 57, 0x78a5636f); + RNDr(S, W, 58, 0x84c87814); + RNDr(S, W, 59, 0x8cc70208); + RNDr(S, W, 60, 0x90befffa); + RNDr(S, W, 61, 0xa4506ceb); + RNDr(S, W, 62, 0xbef9a3f7); + RNDr(S, W, 63, 0xc67178f2); + + /* 4. Mix local working variables into global state */ + for (i = 0; i < 8; i++) + state[i] += S[i]; +} + +static unsigned char PAD[64] = { + 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +/* Add padding and terminating bit-count. */ +static void +SHA256_Pad(SHA256_CTX * ctx) +{ + unsigned char len[8]; + uint32_t r, plen; + + /* + * Convert length to a vector of bytes -- we do this now rather + * than later because the length will change after we pad. + */ + be32enc_vect(len, ctx->count, 8); + + /* Add 1--64 bytes so that the resulting length is 56 mod 64 */ + r = (ctx->count[1] >> 3) & 0x3f; + plen = (r < 56) ? (56 - r) : (120 - r); + SHA256_Update(ctx, PAD, (size_t)plen); + + /* Add the terminating bit-count */ + SHA256_Update(ctx, len, 8); +} + +/* SHA-256 initialization. Begins a SHA-256 operation. */ +void +SHA256_Init(SHA256_CTX * ctx) +{ + + /* Zero bits processed so far */ + ctx->count[0] = ctx->count[1] = 0; + + /* Magic initialization constants */ + ctx->state[0] = 0x6A09E667; + ctx->state[1] = 0xBB67AE85; + ctx->state[2] = 0x3C6EF372; + ctx->state[3] = 0xA54FF53A; + ctx->state[4] = 0x510E527F; + ctx->state[5] = 0x9B05688C; + ctx->state[6] = 0x1F83D9AB; + ctx->state[7] = 0x5BE0CD19; +} + +/* Add bytes into the hash */ +void +SHA256_Update(SHA256_CTX * ctx, const void *in, size_t len) +{ + uint32_t bitlen[2]; + uint32_t r; + const unsigned char *src = in; + + /* Number of bytes left in the buffer from previous updates */ + r = (ctx->count[1] >> 3) & 0x3f; + + /* Convert the length into a number of bits */ + bitlen[1] = ((uint32_t)len) << 3; + bitlen[0] = (uint32_t)(len >> 29); + + /* Update number of bits */ + if ((ctx->count[1] += bitlen[1]) < bitlen[1]) + ctx->count[0]++; + ctx->count[0] += bitlen[0]; + + /* Handle the case where we don't need to perform any transforms */ + if (len < 64 - r) { + memcpy(&ctx->buf[r], src, len); + return; + } + + /* Finish the current block */ + memcpy(&ctx->buf[r], src, 64 - r); + SHA256_Transform(ctx->state, ctx->buf); + src += 64 - r; + len -= 64 - r; + + /* Perform complete blocks */ + while (len >= 64) { + SHA256_Transform(ctx->state, src); + src += 64; + len -= 64; + } + + /* Copy left over data into buffer */ + memcpy(ctx->buf, src, len); +} + +/* + * SHA-256 finalization. Pads the input data, exports the hash value, + * and clears the context state. + */ +void +SHA256_Final(unsigned char digest[32], SHA256_CTX * ctx) +{ + + /* Add padding */ + SHA256_Pad(ctx); + + /* Write the hash */ + be32enc_vect(digest, ctx->state, 32); + + /* Clear the context state */ + memset((void *)ctx, 0, sizeof(*ctx)); +} diff --git a/Radio/HW/BladeRF/common/src/str_queue.c b/Radio/HW/BladeRF/common/src/str_queue.c new file mode 100644 index 0000000..d5b14cd --- /dev/null +++ b/Radio/HW/BladeRF/common/src/str_queue.c @@ -0,0 +1,193 @@ +/* + * This file is part of the bladeRF project: + * http://www.github.com/nuand/bladeRF + * + * Copyright (c) 2014 Nuand LLC + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include <string.h> +#include <stdlib.h> +#include "str_queue.h" +#include "host_config.h" + +struct str_queue_entry { + char *str; + struct str_queue_entry *next; +}; + +void str_queue_init(struct str_queue *q) +{ + memset(q, 0, sizeof(q[0])); +} + +void str_queue_deinit(struct str_queue *q) +{ + char *str; + + do { + str = str_queue_deq(q); + free(str); + } while (str != NULL); + + q->head = NULL; + q->tail = NULL; +} + +int str_queue_enq(struct str_queue *q, const char *str) +{ + struct str_queue_entry *entry = malloc(sizeof(entry[0])); + if (entry == NULL) { + return -1; + } + + entry->str = strdup(str); + if (entry->str == NULL) { + free(entry); + return -1; + } + entry->next = NULL; + + if (q->head == NULL) { + q->head = entry; + } + + if (q->tail != NULL) { + q->tail->next = entry; + } + + q->tail = entry; + return 0; +} + +char * str_queue_deq(struct str_queue *q) +{ + char *ret; + struct str_queue_entry *entry; + + entry = q->head; + if (entry == NULL) { + ret = NULL; + } else { + q->head = entry->next; + if (q->head == NULL) { + q->tail = NULL; + } + + ret = entry->str; + free(entry); + } + + return ret; +} + +bool str_queue_empty(struct str_queue *q) +{ + return q->head == NULL || q->tail == NULL; +} + +#if TEST_STR_QUEUE +#include <stdio.h> + +#define STR_NULL "(null)" +#define STR_1 "This is test string one." +#define STR_2 "A second string queue test string" +#define STR_3 "String thr33, this be" + +#define CHECK_EMPTY(q) do { \ + if (str_queue_empty(q) != true) { \ + fprintf(stderr, "Queue not empty!\n"); \ + return EXIT_FAILURE; \ + } \ + } while (0) + +#define CHECK_NONEMPTY(q) do { \ + if (str_queue_empty(q) != false) { \ + fprintf(stderr, "Queue unexpectedly empty!\n"); \ + return EXIT_FAILURE; \ + } \ + } while (0) + +#define ENQ(q, str) do { \ + int status_ = str_queue_enq(q, str); \ + if (status_ != 0) { \ + fprintf(stderr, "Failed to enqueue %s\n", str); \ + return EXIT_FAILURE; \ + } else { \ + printf("Enqueued: %s\n", str); \ + } \ + } while (0) + +#define DEQ(q, exp) do { \ + char * str_; \ + bool failed_; \ + if (!strcmp(exp, STR_NULL)) { \ + CHECK_EMPTY(q); \ + } else { \ + CHECK_NONEMPTY(q); \ + } \ + str_ = str_queue_deq(q); \ + printf("Dequeued: %s\n", str_); \ + if (str_ == NULL) { \ + failed_ = strcmp(exp, STR_NULL) != 0; \ + } else { \ + failed_ = strcmp(str_, exp) != 0; \ + } \ + if (failed_) { \ + fprintf(stderr, "Dequeue failed.\n"); \ + return EXIT_FAILURE; \ + } \ + } while (0) + +int main(void) +{ + char *str; + struct str_queue q; + + str_queue_init(&q); + str = str_queue_deq(&q); + + + DEQ(&q, STR_NULL); + ENQ(&q, STR_1); + DEQ(&q, STR_1); + DEQ(&q, STR_NULL); + DEQ(&q, STR_NULL); + + ENQ(&q, STR_1); + ENQ(&q, STR_2); + DEQ(&q, STR_1); + DEQ(&q, STR_2); + DEQ(&q, STR_NULL); + + ENQ(&q, STR_1); + ENQ(&q, STR_2); + DEQ(&q, STR_1); + ENQ(&q, STR_3); + DEQ(&q, STR_2); + ENQ(&q, STR_1); + DEQ(&q, STR_3); + DEQ(&q, STR_1); + DEQ(&q, STR_NULL); + DEQ(&q, STR_NULL); + + str_queue_deinit(&q); + return 0; +} +#endif diff --git a/Radio/HW/BladeRF/common/src/windows/clock_gettime.c b/Radio/HW/BladeRF/common/src/windows/clock_gettime.c new file mode 100644 index 0000000..803799e --- /dev/null +++ b/Radio/HW/BladeRF/common/src/windows/clock_gettime.c @@ -0,0 +1,58 @@ +/* + * This file is part of the bladeRF project: + * http://www.github.com/nuand/bladeRF + * + * Copyright (c) 2013 Nuand LLC + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef WIN32 +# error "This file is intended for use with WIN32 systems only." +#endif + +#include <Windows.h> +#include <errno.h> +#include "clock_gettime.h" +#include "ptw32_timespec.h" + +int clock_gettime(clockid_t clk_id, struct timespec *tp) +{ + BOOL success; + FILETIME file_time; + SYSTEMTIME system_time; + + if (clk_id != CLOCK_REALTIME) { + errno = EINVAL; + return -1; + } + + GetSystemTime(&system_time); + success = SystemTimeToFileTime(&system_time, &file_time); + + if (!success) { + /* For lack of a better or more compliant return value... */ + errno = EINVAL; + return -1; + } + + ptw32_filetime_to_timespec(&file_time, tp); + + return 0; +} + diff --git a/Radio/HW/BladeRF/common/src/windows/getopt_long.c b/Radio/HW/BladeRF/common/src/windows/getopt_long.c new file mode 100644 index 0000000..7a683fb --- /dev/null +++ b/Radio/HW/BladeRF/common/src/windows/getopt_long.c @@ -0,0 +1,326 @@ +/** @file getopt_long.c + ** @brief getopt_long - Definition + ** @author Andrea Vedaldi + **/ + +/* +Copyright (C) 2007-12 Andrea Vedaldi and Brian Fulkerson. +All rights reserved. + +This file is part of the VLFeat library and is made available under +the terms of the BSD license (see legal/licenses/LICENSE.BSD.vlfeat). + +*/ + +/** +@file getopt_long.h +@brief getopt_long +@author Andrea Vedaldi + +This is a drop-in replacament of GNU getopt_long meant to be used +on platforms that do not support such functionality. +**/ + +#include <stdlib.h> +#include <string.h> +#include <stdio.h> + +#include "getopt.h" + +int opterr = 1 ; +int optind = 1 ; +int optopt ; +char * optarg ; +int optreset ; + +#define BADCH '?' +#define BADARG ':' +#define EEND -1 +#define EMSG "" + +/** @brief Parse long options (BSD style) + ** @param argc number of arguments. + ** @param argv pointer to the vector of arguments. + ** @param optstring list of abbreviated options + ** @param longopts list of long options. + ** @param longindex index of current option in @a longopts. + ** @return the code of the next option. + ** + ** This function extract long and short options from the argument + ** list @a argv of @a argc entries. + ** + ** A short options sequence is introduced by a single dash character + ** @c -. Each short option is described by a single character in the + ** string @a optstring, possibly followed by a @c : character to + ** denote a (mandatory) argument of the short option. A short option + ** with an argument cannot appear in the middle of a short option + ** sequence, but only at the end. + ** + ** A long option is introduced by a double dash @c --. Each long + ** option is described by an instance of the ::option structure in + ** the @a longopts table (the last entry must be filled with zeroes + ** to denote the end). + ** + ** Illegal options and missing arguments cause the function to skip + ** the option and return '?'. If ::opterr is @c true (default), the + ** function prints an error message to @a stderr. Finally, if @a + ** optstring has a leading @c :, then error messages are suppressed + ** and a missing argument causes @a : to be returned. + ** + ** @remark The function is currently <em>not</em> thread safe. + **/ + +VL_EXPORT int +getopt_long(int argc, char *const argv[], + const char *optstring, + const struct option * longopts, + int *longindex) +{ + static char *place = EMSG; /* option letter processing */ + static int optbegin = 0 ; + static int optend = 0 ; + const char *oli; /* option letter list index */ + int has_colon = 0 ; + int ret_val = 0 ; + + /* + A semicolon at the beginning of optstring has a special meaning. + If we find one, we annote and remove it. + */ + has_colon = optstring && optstring[0] == ':' ; + if (has_colon) ++ optstring ; + + /* + Here we are either processing a short option sequence or + we start processing a new option. This is indicated by optreset. + */ + + if (optreset || *place == '\0') { + + /* --------------------------------------------------------------- + * Look for next short/long option + * ------------------------------------------------------------ */ + optreset = 0 ; + + /* no more arguments ? */ + if (optind >= argc) { + place = EMSG ; + return -1 ; + } + + /* next argument that may hold an option */ + optbegin = optind ; + + /* --------------------------------------------------------------- + * Look for an option to parse + * ------------------------------------------------------------ */ + + parse_option_at_optbegin : + + /* place points to the candidate option */ + place = argv [optbegin] ; + + /* an option is introduced by '-' */ + if (place [0] != '-') { + /* this argument is not an option: try next argument */ + ++ optbegin ; + if (optbegin >= argc) { + /* no more arguments to look for options */ + place = EMSG ; + return -1 ; + } + goto parse_option_at_optbegin ; + } + + /* consume leading `-' */ + ++ place ; + + /* assume the option is composed of one argument only */ + optend = optbegin + 1 ; + + /* assume no argument */ + optarg = 0 ; + + /* --------------------------------------------------------------- + * option `--' + * ------------------------------------------------------------ */ + + /* this special option (void long option) ends the option processing */ + if (place[0] && + place[0] == '-' && + place[1] == '\0') { + + optind = optend ; + place = EMSG ; + ret_val = -1 ; + goto done_option ; + } + + /* --------------------------------------------------------------- + * long option + * ------------------------------------------------------------ */ + + if (place[0] && + place[0] == '-' && + place[1] ) { + + size_t namelen ; + int i ; + + /* consume second `-' */ + ++ place ; + + /* count characters before `=' */ + namelen = strcspn(place, "=") ; + + /* scan longopts for this option */ + for (i = 0 ; longopts[i].name != NULL ; ++ i) { + + if (strlen ( longopts[i].name) == namelen && + strncmp (place, longopts[i].name, namelen) == 0 ) { + + /* save back long option index */ + if (longindex) *longindex = i ; + + /* process long option argument */ + if (longopts[i].has_arg == required_argument || + longopts[i].has_arg == optional_argument) { + + /* --option=value style */ + if (place[namelen] == '=') { + optarg = place + namelen + 1 ; + } + + /* --option value style (only required_argument) */ + else if (longopts[i].has_arg == required_argument) { + /* missing argument ? */ + if (optbegin >= argc - 1) { + if (! has_colon && opterr) + fprintf(stderr, + "%s: option requires an argument -- %s\n", + argv[0], place); + place = EMSG ; + ret_val = has_colon ? BADARG : BADCH ; + goto done_option ; + } + optarg = argv [optend] ; + ++ optend ; + } + } + + /* determine return value */ + if (longopts[i].flag == NULL) { + ret_val = longopts[i].val ; + } + else { + *longopts[i].flag = longopts[i].val; + ret_val = 0 ; + } + + /* mark sequence closed */ + place = EMSG ; + goto done_option ; + } /* if match */ + + } /* scan longoptions */ + + /* no matching option found */ + if (! has_colon && opterr) + fprintf(stderr, + "%s: illegal option -- %s\n", argv[0], place) ; + place = EMSG ; + ret_val = BADCH ; + goto done_option ; + } + } /* end new option */ + + /* ----------------------------------------------------------------- + * Finish short option sequence + * -------------------------------------------------------------- */ + optopt = (int) *place++ ; + + /* search charcater in option list */ + oli = strchr(optstring, optopt); + + /* short option not found */ + if (!oli) { + + if (! has_colon && opterr) + fprintf(stderr, + "%s: illegal option -- %c\n", + argv[0], optopt); + + if (*place) { + /* more short options in the list */ + return BADCH ; + } + + else { + /* error occured as last option in the list */ + place = EMSG ; + ret_val = BADCH ; + goto done_option ; + } + } /* end short option not found */ + + if (oli[1] != ':') { + /* short option with no argument */ + + if (*place) { + /* more short options in the list */ + return optopt ; + } + else { + /* last option in the list */ + place = EMSG ; + ret_val = optopt ; + goto done_option ; + } + + } else { + /* short option with argument */ + + /* -ovalue style */ + if (*place) { + optarg = place ; + place = EMSG ; + ret_val = optopt ; + goto done_option ; + } + /* -o value style: missing argument */ + else if (optbegin >= argc - 1) { + if (! has_colon && opterr) + fprintf(stderr, + "%s: option requires an argument -- %c\n", + argv[0], optopt); + place = EMSG ; + ret_val = has_colon ? BADARG : BADCH ; + goto done_option ; + } + + /* -o value style: process argument */ + optarg = argv [optend] ; + ++ optend ; + place = EMSG ; + ret_val = optopt ; + goto done_option ; + } /* short with argument */ + + done_option : + { + int pos = optend - optbegin ; /* n of circular shifts */ + int c = pos ; + + while (c --) { + int i ; + char *tmp = argv [optend - 1] ; + for (i = optend - 1 ; i > optind ; -- i) { + ((char**)argv) [i] = argv [i-1] ; + } + ((char**)argv) [optind] = tmp ; + } + optind += pos ; + } + + return ret_val ; +} diff --git a/Radio/HW/BladeRF/common/src/windows/gettimeofday.c b/Radio/HW/BladeRF/common/src/windows/gettimeofday.c new file mode 100644 index 0000000..144ee2f --- /dev/null +++ b/Radio/HW/BladeRF/common/src/windows/gettimeofday.c @@ -0,0 +1,49 @@ +/* + * This file is part of the bladeRF project: + * http://www.github.com/nuand/bladeRF + * + * Copyright (c) 2023 Nuand LLC + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef WIN32 +#error "This file is intended for use with WIN32 systems only." +#endif + +#include <windows.h> +#include <stdint.h> +int gettimeofday(struct timeval *tp, struct timezone *tzp) +{ + // Note: some broken versions only have 8 trailing zero's, the correct epoch + // has 9 trailing zero's + static const uint64_t EPOCH = ((uint64_t)116444736000000000ULL); + + SYSTEMTIME system_time; + FILETIME file_time; + uint64_t time; + + GetSystemTime(&system_time); + SystemTimeToFileTime(&system_time, &file_time); + time = ((uint64_t)file_time.dwLowDateTime); + time += ((uint64_t)file_time.dwHighDateTime) << 32; + + tp->tv_sec = (long)((time - EPOCH) / 10000000L); + tp->tv_usec = (long)(system_time.wMilliseconds * 1000); + return 0; +} diff --git a/Radio/HW/BladeRF/common/src/windows/mkdtemp.c b/Radio/HW/BladeRF/common/src/windows/mkdtemp.c new file mode 100644 index 0000000..0e2f804 --- /dev/null +++ b/Radio/HW/BladeRF/common/src/windows/mkdtemp.c @@ -0,0 +1,250 @@ +/* + * This file is part of the bladeRF project: + * http://www.github.com/Nuand/bladeRF + * + * Copyright (c) 2018 Nuand LLC + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +// _CRT_RAND_S must be defined before including stdlib.h, to enable rand_s +#define _CRT_RAND_S +#include <stdlib.h> + +#ifdef WINDOWS_MKDTEMP_TEST_SUITE +// Running in standalone test mode + +#include <stdbool.h> +#include <stdio.h> +#define __debug(...) fprintf(stderr, __VA_ARGS__) + +#ifndef WIN32 +// Running standalone test on non-Windows OS +#include <time.h> +#include <unistd.h> +#define errno_t int +errno_t rand_s(unsigned int *randomValue) +{ + *randomValue = rand(); + return 0; +} +#endif // WIN32 + +#else +// Building as part of a library + +#ifndef WIN32 +#error "This file is intended for use with WIN32 systems only." +#endif // WIN32 + +#define __debug(...) + +#endif // WINDOWS_MKDTEMP_TEST_SUITE + +#include <errno.h> +#include <string.h> +#include <sys/stat.h> +#include "host_config.h" + +// The concepts of F_OK and S_IRWXU do not exist on Win32 +#ifndef F_OK +#define F_OK 0 +#endif + +#ifndef S_IRWXU +#define S_IRWXU 00700 +#endif + +/* h/t https://stackoverflow.com/a/14598879 */ +static int random_number(int min_num, int max_num) +{ + int result = 0, low_num = 0, hi_num = 0; + unsigned int randomValue; + + if (min_num < max_num) { + low_num = min_num; + hi_num = max_num + 1; // include max_num in output + } else { + low_num = max_num + 1; // include max_num in output + hi_num = min_num; + } + + // Use rand_s() on Windows, so that we don't have to deal with srand() + errno_t err = rand_s(&randomValue); + if (0 != err) { + return -1; + } + + result = (randomValue % (hi_num - low_num)) + low_num; + return result; +} + +char *mkdtemp(char *template) +{ + size_t const TEMPL_LEN = 6; + char const TEMPL_CHAR = 'X'; + + if (strlen(template) <= TEMPL_LEN) { + // template is too short + errno = EINVAL; + goto error; + } + + // Loop through the end of the template, replacing 'X' with random char + for (size_t i = strlen(template) - TEMPL_LEN; i < strlen(template); ++i) { + // The last TEMPL_LEN characters MUST be 'X' + if (template[i] != TEMPL_CHAR) { + errno = EINVAL; + goto error; + } + + // Pick a random letter + if (random_number(0, 1)) { + template[i] = (char)random_number('A', 'Z'); + } else { + template[i] = (char)random_number('a', 'z'); + } + } + + // Error out if the file already exists + if (access(template, F_OK) != -1) { + __debug("%s: failed: %s exists\n", __FUNCTION__, template); + errno = EEXIST; + goto error; + } + + // Try to create the directory... + if (0 != mkdir(template, S_IRWXU)) { + int errsv = errno; + __debug("%s: mkdir() failed: %s\n", __FUNCTION__, strerror(errsv)); + goto error; + } + + // Success! + errno = 0; + return template; + +error: + return NULL; +} + +#ifdef WINDOWS_MKDTEMP_TEST_SUITE +/** + * These functions are intended to verify proper operation of the test suite. + */ +static bool test(char *template, bool expect_success) +{ + char *rv = mkdtemp(template); + + if (NULL == rv) { + int errsv = errno; + printf("%s: mkdtemp failed: %s\n", __FUNCTION__, strerror(errsv)); + return (false == expect_success); + } else { + printf("%s: mkdtemp created: %s\n", __FUNCTION__, rv); + + if (0 != rmdir(rv)) { + int errsv = errno; + printf("%s: rmdir failed: %s\n", __FUNCTION__, strerror(errsv)); + } + + return (true == expect_success); + } +} + +int main(int argc, char *argv[]) +{ +#ifndef WIN32 + srand(time(NULL)); +#endif // WIN32 + + int success = 0, failure = 0; + + // Normal: should pass + char template1[] = "/tmp/asdf.XXXXXX"; + if (test(template1, true)) { + printf("*** Test case 1: PASS\n"); + ++success; + } else { + printf("*** Test case 1: FAIL\n"); + ++failure; + } + + // Too short: should fail + char template2[] = "XXXXXX"; + if (test(template2, false)) { + printf("*** Test case 2: PASS\n"); + ++success; + } else { + printf("*** Test case 2: FAIL\n"); + ++failure; + } + + // Not enough replacement Xs: should fail + char template3[] = "/tmp/asdf.XXXXX"; + if (test(template3, false)) { + printf("*** Test case 3: PASS\n"); + ++success; + } else { + printf("*** Test case 3: FAIL\n"); + ++failure; + } + + // Make sure it only replaces the end: should pass + char template4[] = "/tmp/asdfXXXXXX.XXXXXX"; + if (test(template4, true)) { + printf("*** Test case 4: PASS\n"); + ++success; + } else { + printf("*** Test case 4: FAIL\n"); + ++failure; + } + + // Really long: should fail + char template5[] = "/tmp/asdfaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaXXXXXX"; + if (test(template5, false)) { + printf("*** Test case 5: PASS\n"); + ++success; + } else { + printf("*** Test case 5: FAIL\n"); + ++failure; + } + + // Unwriteable path: should fail + char template6[] = "/asdfkjavblkjadv/asdf.XXXX"; + if (test(template6, false)) { + printf("*** Test case 6: PASS\n"); + ++success; + } else { + printf("*** Test case 6: FAIL\n"); + ++failure; + } + + printf("TEST SUMMARY: Success=%d, Failure=%d\n", success, failure); + + return failure; +} +#endif // WINDOWS_MKDTEMP_TEST_SUITE diff --git a/Radio/HW/BladeRF/common/src/windows/nanosleep.c b/Radio/HW/BladeRF/common/src/windows/nanosleep.c new file mode 100644 index 0000000..12d470a --- /dev/null +++ b/Radio/HW/BladeRF/common/src/windows/nanosleep.c @@ -0,0 +1,41 @@ +/* + * This file is part of the bladeRF project: + * http://www.github.com/nuand/bladeRF + * + * Copyright (c) 2018 Nuand LLC + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef WIN32 +#error "This file is intended for use with WIN32 systems only." +#endif + +#include "nanosleep.h" +#include <windows.h> + +int nanosleep(const struct timespec *req, struct timespec *rem) +{ + DWORD sleep_ms; + + sleep_ms = ((DWORD)req->tv_sec * 1000) + ((DWORD)req->tv_nsec / 1000000); + + Sleep(sleep_ms); + + return 0; +} diff --git a/Radio/HW/BladeRF/common/src/windows/setenv.c b/Radio/HW/BladeRF/common/src/windows/setenv.c new file mode 100644 index 0000000..1f7eb8c --- /dev/null +++ b/Radio/HW/BladeRF/common/src/windows/setenv.c @@ -0,0 +1,50 @@ +/* + * This file is part of the bladeRF project: + * http://www.github.com/Nuand/bladeRF + * + * Copyright (c) 2018 Nuand LLC + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef WIN32 +#error "This file is intended for use with WIN32 systems only." +#endif // WIN32 + +#include <stdlib.h> + +int setenv(const char *name, const char *value, int overwrite) +{ + errno_t rv = 0; + size_t envsize = 0; + + if (!overwrite) { + // Test for existence + rv = getenv_s(&envsize, NULL, 0, name); + if (rv != 0 || envsize != 0) { + return rv; + } + } + return _putenv_s(name, value); +} + +int unsetenv(const char *name) +{ + return _putenv_s(name, ""); +} |