diff --git a/CMakeLists.txt b/CMakeLists.txt index 07b3f2a6..06507927 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -106,7 +106,7 @@ endif() set(OSC_SRC osc.c oscplot.c datatypes.c iio_widget.c iio_utils.c fru.c dialogs.c trigger_dialog.c xml_utils.c libini/libini.c libini2.c phone_home.c plugins/dac_data_manager.c - plugins/fir_filter.c eeprom.c osc_preferences.c) + plugins/fir_filter.c eeprom.c osc_preferences.c cJSON/cJSON.c) find_package(PkgConfig) pkg_check_modules(GLIB REQUIRED glib-2.0) diff --git a/cJSON/cJSON.c b/cJSON/cJSON.c new file mode 100644 index 00000000..dc866f0b --- /dev/null +++ b/cJSON/cJSON.c @@ -0,0 +1,3120 @@ +/* + Copyright (c) 2009-2017 Dave Gamble and cJSON contributors + + 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. +*/ + +/* cJSON */ +/* JSON parser in C. */ + +/* disable warnings about old C89 functions in MSVC */ +#if !defined(_CRT_SECURE_NO_DEPRECATE) && defined(_MSC_VER) +#define _CRT_SECURE_NO_DEPRECATE +#endif + +#ifdef __GNUC__ +#pragma GCC visibility push(default) +#endif +#if defined(_MSC_VER) +#pragma warning (push) +/* disable warning about single line comments in system headers */ +#pragma warning (disable : 4001) +#endif + +#include +#include +#include +#include +#include +#include +#include + +#ifdef ENABLE_LOCALES +#include +#endif + +#if defined(_MSC_VER) +#pragma warning (pop) +#endif +#ifdef __GNUC__ +#pragma GCC visibility pop +#endif + +#include "cJSON.h" + +/* define our own boolean type */ +#ifdef true +#undef true +#endif +#define true ((cJSON_bool)1) + +#ifdef false +#undef false +#endif +#define false ((cJSON_bool)0) + +/* define isnan and isinf for ANSI C, if in C99 or above, isnan and isinf has been defined in math.h */ +#ifndef isinf +#define isinf(d) (isnan((d - d)) && !isnan(d)) +#endif +#ifndef isnan +#define isnan(d) (d != d) +#endif + +#ifndef NAN +#ifdef _WIN32 +#define NAN sqrt(-1.0) +#else +#define NAN 0.0/0.0 +#endif +#endif + +typedef struct { + const unsigned char *json; + size_t position; +} error; +static error global_error = { NULL, 0 }; + +CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void) +{ + return (const char*) (global_error.json + global_error.position); +} + +CJSON_PUBLIC(char *) cJSON_GetStringValue(const cJSON * const item) +{ + if (!cJSON_IsString(item)) + { + return NULL; + } + + return item->valuestring; +} + +CJSON_PUBLIC(double) cJSON_GetNumberValue(const cJSON * const item) +{ + if (!cJSON_IsNumber(item)) + { + return (double) NAN; + } + + return item->valuedouble; +} + +/* This is a safeguard to prevent copy-pasters from using incompatible C and header files */ +#if (CJSON_VERSION_MAJOR != 1) || (CJSON_VERSION_MINOR != 7) || (CJSON_VERSION_PATCH != 16) + #error cJSON.h and cJSON.c have different versions. Make sure that both have the same. +#endif + +CJSON_PUBLIC(const char*) cJSON_Version(void) +{ + static char version[15]; + sprintf(version, "%i.%i.%i", CJSON_VERSION_MAJOR, CJSON_VERSION_MINOR, CJSON_VERSION_PATCH); + + return version; +} + +/* Case insensitive string comparison, doesn't consider two NULL pointers equal though */ +static int case_insensitive_strcmp(const unsigned char *string1, const unsigned char *string2) +{ + if ((string1 == NULL) || (string2 == NULL)) + { + return 1; + } + + if (string1 == string2) + { + return 0; + } + + for(; tolower(*string1) == tolower(*string2); (void)string1++, string2++) + { + if (*string1 == '\0') + { + return 0; + } + } + + return tolower(*string1) - tolower(*string2); +} + +typedef struct internal_hooks +{ + void *(CJSON_CDECL *allocate)(size_t size); + void (CJSON_CDECL *deallocate)(void *pointer); + void *(CJSON_CDECL *reallocate)(void *pointer, size_t size); +} internal_hooks; + +#if defined(_MSC_VER) +/* work around MSVC error C2322: '...' address of dllimport '...' is not static */ +static void * CJSON_CDECL internal_malloc(size_t size) +{ + return malloc(size); +} +static void CJSON_CDECL internal_free(void *pointer) +{ + free(pointer); +} +static void * CJSON_CDECL internal_realloc(void *pointer, size_t size) +{ + return realloc(pointer, size); +} +#else +#define internal_malloc malloc +#define internal_free free +#define internal_realloc realloc +#endif + +/* strlen of character literals resolved at compile time */ +#define static_strlen(string_literal) (sizeof(string_literal) - sizeof("")) + +static internal_hooks global_hooks = { internal_malloc, internal_free, internal_realloc }; + +static unsigned char* cJSON_strdup(const unsigned char* string, const internal_hooks * const hooks) +{ + size_t length = 0; + unsigned char *copy = NULL; + + if (string == NULL) + { + return NULL; + } + + length = strlen((const char*)string) + sizeof(""); + copy = (unsigned char*)hooks->allocate(length); + if (copy == NULL) + { + return NULL; + } + memcpy(copy, string, length); + + return copy; +} + +CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks) +{ + if (hooks == NULL) + { + /* Reset hooks */ + global_hooks.allocate = malloc; + global_hooks.deallocate = free; + global_hooks.reallocate = realloc; + return; + } + + global_hooks.allocate = malloc; + if (hooks->malloc_fn != NULL) + { + global_hooks.allocate = hooks->malloc_fn; + } + + global_hooks.deallocate = free; + if (hooks->free_fn != NULL) + { + global_hooks.deallocate = hooks->free_fn; + } + + /* use realloc only if both free and malloc are used */ + global_hooks.reallocate = NULL; + if ((global_hooks.allocate == malloc) && (global_hooks.deallocate == free)) + { + global_hooks.reallocate = realloc; + } +} + +/* Internal constructor. */ +static cJSON *cJSON_New_Item(const internal_hooks * const hooks) +{ + cJSON* node = (cJSON*)hooks->allocate(sizeof(cJSON)); + if (node) + { + memset(node, '\0', sizeof(cJSON)); + } + + return node; +} + +/* Delete a cJSON structure. */ +CJSON_PUBLIC(void) cJSON_Delete(cJSON *item) +{ + cJSON *next = NULL; + while (item != NULL) + { + next = item->next; + if (!(item->type & cJSON_IsReference) && (item->child != NULL)) + { + cJSON_Delete(item->child); + } + if (!(item->type & cJSON_IsReference) && (item->valuestring != NULL)) + { + global_hooks.deallocate(item->valuestring); + } + if (!(item->type & cJSON_StringIsConst) && (item->string != NULL)) + { + global_hooks.deallocate(item->string); + } + global_hooks.deallocate(item); + item = next; + } +} + +/* get the decimal point character of the current locale */ +static unsigned char get_decimal_point(void) +{ +#ifdef ENABLE_LOCALES + struct lconv *lconv = localeconv(); + return (unsigned char) lconv->decimal_point[0]; +#else + return '.'; +#endif +} + +typedef struct +{ + const unsigned char *content; + size_t length; + size_t offset; + size_t depth; /* How deeply nested (in arrays/objects) is the input at the current offset. */ + internal_hooks hooks; +} parse_buffer; + +/* check if the given size is left to read in a given parse buffer (starting with 1) */ +#define can_read(buffer, size) ((buffer != NULL) && (((buffer)->offset + size) <= (buffer)->length)) +/* check if the buffer can be accessed at the given index (starting with 0) */ +#define can_access_at_index(buffer, index) ((buffer != NULL) && (((buffer)->offset + index) < (buffer)->length)) +#define cannot_access_at_index(buffer, index) (!can_access_at_index(buffer, index)) +/* get a pointer to the buffer at the position */ +#define buffer_at_offset(buffer) ((buffer)->content + (buffer)->offset) + +/* Parse the input text to generate a number, and populate the result into item. */ +static cJSON_bool parse_number(cJSON * const item, parse_buffer * const input_buffer) +{ + double number = 0; + unsigned char *after_end = NULL; + unsigned char number_c_string[64]; + unsigned char decimal_point = get_decimal_point(); + size_t i = 0; + + if ((input_buffer == NULL) || (input_buffer->content == NULL)) + { + return false; + } + + /* copy the number into a temporary buffer and replace '.' with the decimal point + * of the current locale (for strtod) + * This also takes care of '\0' not necessarily being available for marking the end of the input */ + for (i = 0; (i < (sizeof(number_c_string) - 1)) && can_access_at_index(input_buffer, i); i++) + { + switch (buffer_at_offset(input_buffer)[i]) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case '+': + case '-': + case 'e': + case 'E': + number_c_string[i] = buffer_at_offset(input_buffer)[i]; + break; + + case '.': + number_c_string[i] = decimal_point; + break; + + default: + goto loop_end; + } + } +loop_end: + number_c_string[i] = '\0'; + + number = strtod((const char*)number_c_string, (char**)&after_end); + if (number_c_string == after_end) + { + return false; /* parse_error */ + } + + item->valuedouble = number; + + /* use saturation in case of overflow */ + if (number >= INT_MAX) + { + item->valueint = INT_MAX; + } + else if (number <= (double)INT_MIN) + { + item->valueint = INT_MIN; + } + else + { + item->valueint = (int)number; + } + + item->type = cJSON_Number; + + input_buffer->offset += (size_t)(after_end - number_c_string); + return true; +} + +/* don't ask me, but the original cJSON_SetNumberValue returns an integer or double */ +CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number) +{ + if (number >= INT_MAX) + { + object->valueint = INT_MAX; + } + else if (number <= (double)INT_MIN) + { + object->valueint = INT_MIN; + } + else + { + object->valueint = (int)number; + } + + return object->valuedouble = number; +} + +CJSON_PUBLIC(char*) cJSON_SetValuestring(cJSON *object, const char *valuestring) +{ + char *copy = NULL; + /* if object's type is not cJSON_String or is cJSON_IsReference, it should not set valuestring */ + if (!(object->type & cJSON_String) || (object->type & cJSON_IsReference)) + { + return NULL; + } + if (strlen(valuestring) <= strlen(object->valuestring)) + { + strcpy(object->valuestring, valuestring); + return object->valuestring; + } + copy = (char*) cJSON_strdup((const unsigned char*)valuestring, &global_hooks); + if (copy == NULL) + { + return NULL; + } + if (object->valuestring != NULL) + { + cJSON_free(object->valuestring); + } + object->valuestring = copy; + + return copy; +} + +typedef struct +{ + unsigned char *buffer; + size_t length; + size_t offset; + size_t depth; /* current nesting depth (for formatted printing) */ + cJSON_bool noalloc; + cJSON_bool format; /* is this print a formatted print */ + internal_hooks hooks; +} printbuffer; + +/* realloc printbuffer if necessary to have at least "needed" bytes more */ +static unsigned char* ensure(printbuffer * const p, size_t needed) +{ + unsigned char *newbuffer = NULL; + size_t newsize = 0; + + if ((p == NULL) || (p->buffer == NULL)) + { + return NULL; + } + + if ((p->length > 0) && (p->offset >= p->length)) + { + /* make sure that offset is valid */ + return NULL; + } + + if (needed > INT_MAX) + { + /* sizes bigger than INT_MAX are currently not supported */ + return NULL; + } + + needed += p->offset + 1; + if (needed <= p->length) + { + return p->buffer + p->offset; + } + + if (p->noalloc) { + return NULL; + } + + /* calculate new buffer size */ + if (needed > (INT_MAX / 2)) + { + /* overflow of int, use INT_MAX if possible */ + if (needed <= INT_MAX) + { + newsize = INT_MAX; + } + else + { + return NULL; + } + } + else + { + newsize = needed * 2; + } + + if (p->hooks.reallocate != NULL) + { + /* reallocate with realloc if available */ + newbuffer = (unsigned char*)p->hooks.reallocate(p->buffer, newsize); + if (newbuffer == NULL) + { + p->hooks.deallocate(p->buffer); + p->length = 0; + p->buffer = NULL; + + return NULL; + } + } + else + { + /* otherwise reallocate manually */ + newbuffer = (unsigned char*)p->hooks.allocate(newsize); + if (!newbuffer) + { + p->hooks.deallocate(p->buffer); + p->length = 0; + p->buffer = NULL; + + return NULL; + } + + memcpy(newbuffer, p->buffer, p->offset + 1); + p->hooks.deallocate(p->buffer); + } + p->length = newsize; + p->buffer = newbuffer; + + return newbuffer + p->offset; +} + +/* calculate the new length of the string in a printbuffer and update the offset */ +static void update_offset(printbuffer * const buffer) +{ + const unsigned char *buffer_pointer = NULL; + if ((buffer == NULL) || (buffer->buffer == NULL)) + { + return; + } + buffer_pointer = buffer->buffer + buffer->offset; + + buffer->offset += strlen((const char*)buffer_pointer); +} + +/* securely comparison of floating-point variables */ +static cJSON_bool compare_double(double a, double b) +{ + double maxVal = fabs(a) > fabs(b) ? fabs(a) : fabs(b); + return (fabs(a - b) <= maxVal * DBL_EPSILON); +} + +/* Render the number nicely from the given item into a string. */ +static cJSON_bool print_number(const cJSON * const item, printbuffer * const output_buffer) +{ + unsigned char *output_pointer = NULL; + double d = item->valuedouble; + int length = 0; + size_t i = 0; + unsigned char number_buffer[26] = {0}; /* temporary buffer to print the number into */ + unsigned char decimal_point = get_decimal_point(); + double test = 0.0; + + if (output_buffer == NULL) + { + return false; + } + + /* This checks for NaN and Infinity */ + if (isnan(d) || isinf(d)) + { + length = sprintf((char*)number_buffer, "null"); + } + else if(d == (double)item->valueint) + { + length = sprintf((char*)number_buffer, "%d", item->valueint); + } + else + { + /* Try 15 decimal places of precision to avoid nonsignificant nonzero digits */ + length = sprintf((char*)number_buffer, "%1.15g", d); + + /* Check whether the original double can be recovered */ + if ((sscanf((char*)number_buffer, "%lg", &test) != 1) || !compare_double((double)test, d)) + { + /* If not, print with 17 decimal places of precision */ + length = sprintf((char*)number_buffer, "%1.17g", d); + } + } + + /* sprintf failed or buffer overrun occurred */ + if ((length < 0) || (length > (int)(sizeof(number_buffer) - 1))) + { + return false; + } + + /* reserve appropriate space in the output */ + output_pointer = ensure(output_buffer, (size_t)length + sizeof("")); + if (output_pointer == NULL) + { + return false; + } + + /* copy the printed number to the output and replace locale + * dependent decimal point with '.' */ + for (i = 0; i < ((size_t)length); i++) + { + if (number_buffer[i] == decimal_point) + { + output_pointer[i] = '.'; + continue; + } + + output_pointer[i] = number_buffer[i]; + } + output_pointer[i] = '\0'; + + output_buffer->offset += (size_t)length; + + return true; +} + +/* parse 4 digit hexadecimal number */ +static unsigned parse_hex4(const unsigned char * const input) +{ + unsigned int h = 0; + size_t i = 0; + + for (i = 0; i < 4; i++) + { + /* parse digit */ + if ((input[i] >= '0') && (input[i] <= '9')) + { + h += (unsigned int) input[i] - '0'; + } + else if ((input[i] >= 'A') && (input[i] <= 'F')) + { + h += (unsigned int) 10 + input[i] - 'A'; + } + else if ((input[i] >= 'a') && (input[i] <= 'f')) + { + h += (unsigned int) 10 + input[i] - 'a'; + } + else /* invalid */ + { + return 0; + } + + if (i < 3) + { + /* shift left to make place for the next nibble */ + h = h << 4; + } + } + + return h; +} + +/* converts a UTF-16 literal to UTF-8 + * A literal can be one or two sequences of the form \uXXXX */ +static unsigned char utf16_literal_to_utf8(const unsigned char * const input_pointer, const unsigned char * const input_end, unsigned char **output_pointer) +{ + long unsigned int codepoint = 0; + unsigned int first_code = 0; + const unsigned char *first_sequence = input_pointer; + unsigned char utf8_length = 0; + unsigned char utf8_position = 0; + unsigned char sequence_length = 0; + unsigned char first_byte_mark = 0; + + if ((input_end - first_sequence) < 6) + { + /* input ends unexpectedly */ + goto fail; + } + + /* get the first utf16 sequence */ + first_code = parse_hex4(first_sequence + 2); + + /* check that the code is valid */ + if (((first_code >= 0xDC00) && (first_code <= 0xDFFF))) + { + goto fail; + } + + /* UTF16 surrogate pair */ + if ((first_code >= 0xD800) && (first_code <= 0xDBFF)) + { + const unsigned char *second_sequence = first_sequence + 6; + unsigned int second_code = 0; + sequence_length = 12; /* \uXXXX\uXXXX */ + + if ((input_end - second_sequence) < 6) + { + /* input ends unexpectedly */ + goto fail; + } + + if ((second_sequence[0] != '\\') || (second_sequence[1] != 'u')) + { + /* missing second half of the surrogate pair */ + goto fail; + } + + /* get the second utf16 sequence */ + second_code = parse_hex4(second_sequence + 2); + /* check that the code is valid */ + if ((second_code < 0xDC00) || (second_code > 0xDFFF)) + { + /* invalid second half of the surrogate pair */ + goto fail; + } + + + /* calculate the unicode codepoint from the surrogate pair */ + codepoint = 0x10000 + (((first_code & 0x3FF) << 10) | (second_code & 0x3FF)); + } + else + { + sequence_length = 6; /* \uXXXX */ + codepoint = first_code; + } + + /* encode as UTF-8 + * takes at maximum 4 bytes to encode: + * 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */ + if (codepoint < 0x80) + { + /* normal ascii, encoding 0xxxxxxx */ + utf8_length = 1; + } + else if (codepoint < 0x800) + { + /* two bytes, encoding 110xxxxx 10xxxxxx */ + utf8_length = 2; + first_byte_mark = 0xC0; /* 11000000 */ + } + else if (codepoint < 0x10000) + { + /* three bytes, encoding 1110xxxx 10xxxxxx 10xxxxxx */ + utf8_length = 3; + first_byte_mark = 0xE0; /* 11100000 */ + } + else if (codepoint <= 0x10FFFF) + { + /* four bytes, encoding 1110xxxx 10xxxxxx 10xxxxxx 10xxxxxx */ + utf8_length = 4; + first_byte_mark = 0xF0; /* 11110000 */ + } + else + { + /* invalid unicode codepoint */ + goto fail; + } + + /* encode as utf8 */ + for (utf8_position = (unsigned char)(utf8_length - 1); utf8_position > 0; utf8_position--) + { + /* 10xxxxxx */ + (*output_pointer)[utf8_position] = (unsigned char)((codepoint | 0x80) & 0xBF); + codepoint >>= 6; + } + /* encode first byte */ + if (utf8_length > 1) + { + (*output_pointer)[0] = (unsigned char)((codepoint | first_byte_mark) & 0xFF); + } + else + { + (*output_pointer)[0] = (unsigned char)(codepoint & 0x7F); + } + + *output_pointer += utf8_length; + + return sequence_length; + +fail: + return 0; +} + +/* Parse the input text into an unescaped cinput, and populate item. */ +static cJSON_bool parse_string(cJSON * const item, parse_buffer * const input_buffer) +{ + const unsigned char *input_pointer = buffer_at_offset(input_buffer) + 1; + const unsigned char *input_end = buffer_at_offset(input_buffer) + 1; + unsigned char *output_pointer = NULL; + unsigned char *output = NULL; + + /* not a string */ + if (buffer_at_offset(input_buffer)[0] != '\"') + { + goto fail; + } + + { + /* calculate approximate size of the output (overestimate) */ + size_t allocation_length = 0; + size_t skipped_bytes = 0; + while (((size_t)(input_end - input_buffer->content) < input_buffer->length) && (*input_end != '\"')) + { + /* is escape sequence */ + if (input_end[0] == '\\') + { + if ((size_t)(input_end + 1 - input_buffer->content) >= input_buffer->length) + { + /* prevent buffer overflow when last input character is a backslash */ + goto fail; + } + skipped_bytes++; + input_end++; + } + input_end++; + } + if (((size_t)(input_end - input_buffer->content) >= input_buffer->length) || (*input_end != '\"')) + { + goto fail; /* string ended unexpectedly */ + } + + /* This is at most how much we need for the output */ + allocation_length = (size_t) (input_end - buffer_at_offset(input_buffer)) - skipped_bytes; + output = (unsigned char*)input_buffer->hooks.allocate(allocation_length + sizeof("")); + if (output == NULL) + { + goto fail; /* allocation failure */ + } + } + + output_pointer = output; + /* loop through the string literal */ + while (input_pointer < input_end) + { + if (*input_pointer != '\\') + { + *output_pointer++ = *input_pointer++; + } + /* escape sequence */ + else + { + unsigned char sequence_length = 2; + if ((input_end - input_pointer) < 1) + { + goto fail; + } + + switch (input_pointer[1]) + { + case 'b': + *output_pointer++ = '\b'; + break; + case 'f': + *output_pointer++ = '\f'; + break; + case 'n': + *output_pointer++ = '\n'; + break; + case 'r': + *output_pointer++ = '\r'; + break; + case 't': + *output_pointer++ = '\t'; + break; + case '\"': + case '\\': + case '/': + *output_pointer++ = input_pointer[1]; + break; + + /* UTF-16 literal */ + case 'u': + sequence_length = utf16_literal_to_utf8(input_pointer, input_end, &output_pointer); + if (sequence_length == 0) + { + /* failed to convert UTF16-literal to UTF-8 */ + goto fail; + } + break; + + default: + goto fail; + } + input_pointer += sequence_length; + } + } + + /* zero terminate the output */ + *output_pointer = '\0'; + + item->type = cJSON_String; + item->valuestring = (char*)output; + + input_buffer->offset = (size_t) (input_end - input_buffer->content); + input_buffer->offset++; + + return true; + +fail: + if (output != NULL) + { + input_buffer->hooks.deallocate(output); + } + + if (input_pointer != NULL) + { + input_buffer->offset = (size_t)(input_pointer - input_buffer->content); + } + + return false; +} + +/* Render the cstring provided to an escaped version that can be printed. */ +static cJSON_bool print_string_ptr(const unsigned char * const input, printbuffer * const output_buffer) +{ + const unsigned char *input_pointer = NULL; + unsigned char *output = NULL; + unsigned char *output_pointer = NULL; + size_t output_length = 0; + /* numbers of additional characters needed for escaping */ + size_t escape_characters = 0; + + if (output_buffer == NULL) + { + return false; + } + + /* empty string */ + if (input == NULL) + { + output = ensure(output_buffer, sizeof("\"\"")); + if (output == NULL) + { + return false; + } + strcpy((char*)output, "\"\""); + + return true; + } + + /* set "flag" to 1 if something needs to be escaped */ + for (input_pointer = input; *input_pointer; input_pointer++) + { + switch (*input_pointer) + { + case '\"': + case '\\': + case '\b': + case '\f': + case '\n': + case '\r': + case '\t': + /* one character escape sequence */ + escape_characters++; + break; + default: + if (*input_pointer < 32) + { + /* UTF-16 escape sequence uXXXX */ + escape_characters += 5; + } + break; + } + } + output_length = (size_t)(input_pointer - input) + escape_characters; + + output = ensure(output_buffer, output_length + sizeof("\"\"")); + if (output == NULL) + { + return false; + } + + /* no characters have to be escaped */ + if (escape_characters == 0) + { + output[0] = '\"'; + memcpy(output + 1, input, output_length); + output[output_length + 1] = '\"'; + output[output_length + 2] = '\0'; + + return true; + } + + output[0] = '\"'; + output_pointer = output + 1; + /* copy the string */ + for (input_pointer = input; *input_pointer != '\0'; (void)input_pointer++, output_pointer++) + { + if ((*input_pointer > 31) && (*input_pointer != '\"') && (*input_pointer != '\\')) + { + /* normal character, copy */ + *output_pointer = *input_pointer; + } + else + { + /* character needs to be escaped */ + *output_pointer++ = '\\'; + switch (*input_pointer) + { + case '\\': + *output_pointer = '\\'; + break; + case '\"': + *output_pointer = '\"'; + break; + case '\b': + *output_pointer = 'b'; + break; + case '\f': + *output_pointer = 'f'; + break; + case '\n': + *output_pointer = 'n'; + break; + case '\r': + *output_pointer = 'r'; + break; + case '\t': + *output_pointer = 't'; + break; + default: + /* escape and print as unicode codepoint */ + sprintf((char*)output_pointer, "u%04x", *input_pointer); + output_pointer += 4; + break; + } + } + } + output[output_length + 1] = '\"'; + output[output_length + 2] = '\0'; + + return true; +} + +/* Invoke print_string_ptr (which is useful) on an item. */ +static cJSON_bool print_string(const cJSON * const item, printbuffer * const p) +{ + return print_string_ptr((unsigned char*)item->valuestring, p); +} + +/* Predeclare these prototypes. */ +static cJSON_bool parse_value(cJSON * const item, parse_buffer * const input_buffer); +static cJSON_bool print_value(const cJSON * const item, printbuffer * const output_buffer); +static cJSON_bool parse_array(cJSON * const item, parse_buffer * const input_buffer); +static cJSON_bool print_array(const cJSON * const item, printbuffer * const output_buffer); +static cJSON_bool parse_object(cJSON * const item, parse_buffer * const input_buffer); +static cJSON_bool print_object(const cJSON * const item, printbuffer * const output_buffer); + +/* Utility to jump whitespace and cr/lf */ +static parse_buffer *buffer_skip_whitespace(parse_buffer * const buffer) +{ + if ((buffer == NULL) || (buffer->content == NULL)) + { + return NULL; + } + + if (cannot_access_at_index(buffer, 0)) + { + return buffer; + } + + while (can_access_at_index(buffer, 0) && (buffer_at_offset(buffer)[0] <= 32)) + { + buffer->offset++; + } + + if (buffer->offset == buffer->length) + { + buffer->offset--; + } + + return buffer; +} + +/* skip the UTF-8 BOM (byte order mark) if it is at the beginning of a buffer */ +static parse_buffer *skip_utf8_bom(parse_buffer * const buffer) +{ + if ((buffer == NULL) || (buffer->content == NULL) || (buffer->offset != 0)) + { + return NULL; + } + + if (can_access_at_index(buffer, 4) && (strncmp((const char*)buffer_at_offset(buffer), "\xEF\xBB\xBF", 3) == 0)) + { + buffer->offset += 3; + } + + return buffer; +} + +CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated) +{ + size_t buffer_length; + + if (NULL == value) + { + return NULL; + } + + /* Adding null character size due to require_null_terminated. */ + buffer_length = strlen(value) + sizeof(""); + + return cJSON_ParseWithLengthOpts(value, buffer_length, return_parse_end, require_null_terminated); +} + +/* Parse an object - create a new root, and populate. */ +CJSON_PUBLIC(cJSON *) cJSON_ParseWithLengthOpts(const char *value, size_t buffer_length, const char **return_parse_end, cJSON_bool require_null_terminated) +{ + parse_buffer buffer = { 0, 0, 0, 0, { 0, 0, 0 } }; + cJSON *item = NULL; + + /* reset error position */ + global_error.json = NULL; + global_error.position = 0; + + if (value == NULL || 0 == buffer_length) + { + goto fail; + } + + buffer.content = (const unsigned char*)value; + buffer.length = buffer_length; + buffer.offset = 0; + buffer.hooks = global_hooks; + + item = cJSON_New_Item(&global_hooks); + if (item == NULL) /* memory fail */ + { + goto fail; + } + + if (!parse_value(item, buffer_skip_whitespace(skip_utf8_bom(&buffer)))) + { + /* parse failure. ep is set. */ + goto fail; + } + + /* if we require null-terminated JSON without appended garbage, skip and then check for a null terminator */ + if (require_null_terminated) + { + buffer_skip_whitespace(&buffer); + if ((buffer.offset >= buffer.length) || buffer_at_offset(&buffer)[0] != '\0') + { + goto fail; + } + } + if (return_parse_end) + { + *return_parse_end = (const char*)buffer_at_offset(&buffer); + } + + return item; + +fail: + if (item != NULL) + { + cJSON_Delete(item); + } + + if (value != NULL) + { + error local_error; + local_error.json = (const unsigned char*)value; + local_error.position = 0; + + if (buffer.offset < buffer.length) + { + local_error.position = buffer.offset; + } + else if (buffer.length > 0) + { + local_error.position = buffer.length - 1; + } + + if (return_parse_end != NULL) + { + *return_parse_end = (const char*)local_error.json + local_error.position; + } + + global_error = local_error; + } + + return NULL; +} + +/* Default options for cJSON_Parse */ +CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value) +{ + return cJSON_ParseWithOpts(value, 0, 0); +} + +CJSON_PUBLIC(cJSON *) cJSON_ParseWithLength(const char *value, size_t buffer_length) +{ + return cJSON_ParseWithLengthOpts(value, buffer_length, 0, 0); +} + +#define cjson_min(a, b) (((a) < (b)) ? (a) : (b)) + +static unsigned char *print(const cJSON * const item, cJSON_bool format, const internal_hooks * const hooks) +{ + static const size_t default_buffer_size = 256; + printbuffer buffer[1]; + unsigned char *printed = NULL; + + memset(buffer, 0, sizeof(buffer)); + + /* create buffer */ + buffer->buffer = (unsigned char*) hooks->allocate(default_buffer_size); + buffer->length = default_buffer_size; + buffer->format = format; + buffer->hooks = *hooks; + if (buffer->buffer == NULL) + { + goto fail; + } + + /* print the value */ + if (!print_value(item, buffer)) + { + goto fail; + } + update_offset(buffer); + + /* check if reallocate is available */ + if (hooks->reallocate != NULL) + { + printed = (unsigned char*) hooks->reallocate(buffer->buffer, buffer->offset + 1); + if (printed == NULL) { + goto fail; + } + buffer->buffer = NULL; + } + else /* otherwise copy the JSON over to a new buffer */ + { + printed = (unsigned char*) hooks->allocate(buffer->offset + 1); + if (printed == NULL) + { + goto fail; + } + memcpy(printed, buffer->buffer, cjson_min(buffer->length, buffer->offset + 1)); + printed[buffer->offset] = '\0'; /* just to be sure */ + + /* free the buffer */ + hooks->deallocate(buffer->buffer); + } + + return printed; + +fail: + if (buffer->buffer != NULL) + { + hooks->deallocate(buffer->buffer); + } + + if (printed != NULL) + { + hooks->deallocate(printed); + } + + return NULL; +} + +/* Render a cJSON item/entity/structure to text. */ +CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item) +{ + return (char*)print(item, true, &global_hooks); +} + +CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item) +{ + return (char*)print(item, false, &global_hooks); +} + +CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt) +{ + printbuffer p = { 0, 0, 0, 0, 0, 0, { 0, 0, 0 } }; + + if (prebuffer < 0) + { + return NULL; + } + + p.buffer = (unsigned char*)global_hooks.allocate((size_t)prebuffer); + if (!p.buffer) + { + return NULL; + } + + p.length = (size_t)prebuffer; + p.offset = 0; + p.noalloc = false; + p.format = fmt; + p.hooks = global_hooks; + + if (!print_value(item, &p)) + { + global_hooks.deallocate(p.buffer); + return NULL; + } + + return (char*)p.buffer; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format) +{ + printbuffer p = { 0, 0, 0, 0, 0, 0, { 0, 0, 0 } }; + + if ((length < 0) || (buffer == NULL)) + { + return false; + } + + p.buffer = (unsigned char*)buffer; + p.length = (size_t)length; + p.offset = 0; + p.noalloc = true; + p.format = format; + p.hooks = global_hooks; + + return print_value(item, &p); +} + +/* Parser core - when encountering text, process appropriately. */ +static cJSON_bool parse_value(cJSON * const item, parse_buffer * const input_buffer) +{ + if ((input_buffer == NULL) || (input_buffer->content == NULL)) + { + return false; /* no input */ + } + + /* parse the different types of values */ + /* null */ + if (can_read(input_buffer, 4) && (strncmp((const char*)buffer_at_offset(input_buffer), "null", 4) == 0)) + { + item->type = cJSON_NULL; + input_buffer->offset += 4; + return true; + } + /* false */ + if (can_read(input_buffer, 5) && (strncmp((const char*)buffer_at_offset(input_buffer), "false", 5) == 0)) + { + item->type = cJSON_False; + input_buffer->offset += 5; + return true; + } + /* true */ + if (can_read(input_buffer, 4) && (strncmp((const char*)buffer_at_offset(input_buffer), "true", 4) == 0)) + { + item->type = cJSON_True; + item->valueint = 1; + input_buffer->offset += 4; + return true; + } + /* string */ + if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '\"')) + { + return parse_string(item, input_buffer); + } + /* number */ + if (can_access_at_index(input_buffer, 0) && ((buffer_at_offset(input_buffer)[0] == '-') || ((buffer_at_offset(input_buffer)[0] >= '0') && (buffer_at_offset(input_buffer)[0] <= '9')))) + { + return parse_number(item, input_buffer); + } + /* array */ + if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '[')) + { + return parse_array(item, input_buffer); + } + /* object */ + if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '{')) + { + return parse_object(item, input_buffer); + } + + return false; +} + +/* Render a value to text. */ +static cJSON_bool print_value(const cJSON * const item, printbuffer * const output_buffer) +{ + unsigned char *output = NULL; + + if ((item == NULL) || (output_buffer == NULL)) + { + return false; + } + + switch ((item->type) & 0xFF) + { + case cJSON_NULL: + output = ensure(output_buffer, 5); + if (output == NULL) + { + return false; + } + strcpy((char*)output, "null"); + return true; + + case cJSON_False: + output = ensure(output_buffer, 6); + if (output == NULL) + { + return false; + } + strcpy((char*)output, "false"); + return true; + + case cJSON_True: + output = ensure(output_buffer, 5); + if (output == NULL) + { + return false; + } + strcpy((char*)output, "true"); + return true; + + case cJSON_Number: + return print_number(item, output_buffer); + + case cJSON_Raw: + { + size_t raw_length = 0; + if (item->valuestring == NULL) + { + return false; + } + + raw_length = strlen(item->valuestring) + sizeof(""); + output = ensure(output_buffer, raw_length); + if (output == NULL) + { + return false; + } + memcpy(output, item->valuestring, raw_length); + return true; + } + + case cJSON_String: + return print_string(item, output_buffer); + + case cJSON_Array: + return print_array(item, output_buffer); + + case cJSON_Object: + return print_object(item, output_buffer); + + default: + return false; + } +} + +/* Build an array from input text. */ +static cJSON_bool parse_array(cJSON * const item, parse_buffer * const input_buffer) +{ + cJSON *head = NULL; /* head of the linked list */ + cJSON *current_item = NULL; + + if (input_buffer->depth >= CJSON_NESTING_LIMIT) + { + return false; /* to deeply nested */ + } + input_buffer->depth++; + + if (buffer_at_offset(input_buffer)[0] != '[') + { + /* not an array */ + goto fail; + } + + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ']')) + { + /* empty array */ + goto success; + } + + /* check if we skipped to the end of the buffer */ + if (cannot_access_at_index(input_buffer, 0)) + { + input_buffer->offset--; + goto fail; + } + + /* step back to character in front of the first element */ + input_buffer->offset--; + /* loop through the comma separated array elements */ + do + { + /* allocate next item */ + cJSON *new_item = cJSON_New_Item(&(input_buffer->hooks)); + if (new_item == NULL) + { + goto fail; /* allocation failure */ + } + + /* attach next item to list */ + if (head == NULL) + { + /* start the linked list */ + current_item = head = new_item; + } + else + { + /* add to the end and advance */ + current_item->next = new_item; + new_item->prev = current_item; + current_item = new_item; + } + + /* parse next value */ + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if (!parse_value(current_item, input_buffer)) + { + goto fail; /* failed to parse value */ + } + buffer_skip_whitespace(input_buffer); + } + while (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ',')); + + if (cannot_access_at_index(input_buffer, 0) || buffer_at_offset(input_buffer)[0] != ']') + { + goto fail; /* expected end of array */ + } + +success: + input_buffer->depth--; + + if (head != NULL) { + head->prev = current_item; + } + + item->type = cJSON_Array; + item->child = head; + + input_buffer->offset++; + + return true; + +fail: + if (head != NULL) + { + cJSON_Delete(head); + } + + return false; +} + +/* Render an array to text */ +static cJSON_bool print_array(const cJSON * const item, printbuffer * const output_buffer) +{ + unsigned char *output_pointer = NULL; + size_t length = 0; + cJSON *current_element = item->child; + + if (output_buffer == NULL) + { + return false; + } + + /* Compose the output array. */ + /* opening square bracket */ + output_pointer = ensure(output_buffer, 1); + if (output_pointer == NULL) + { + return false; + } + + *output_pointer = '['; + output_buffer->offset++; + output_buffer->depth++; + + while (current_element != NULL) + { + if (!print_value(current_element, output_buffer)) + { + return false; + } + update_offset(output_buffer); + if (current_element->next) + { + length = (size_t) (output_buffer->format ? 2 : 1); + output_pointer = ensure(output_buffer, length + 1); + if (output_pointer == NULL) + { + return false; + } + *output_pointer++ = ','; + if(output_buffer->format) + { + *output_pointer++ = ' '; + } + *output_pointer = '\0'; + output_buffer->offset += length; + } + current_element = current_element->next; + } + + output_pointer = ensure(output_buffer, 2); + if (output_pointer == NULL) + { + return false; + } + *output_pointer++ = ']'; + *output_pointer = '\0'; + output_buffer->depth--; + + return true; +} + +/* Build an object from the text. */ +static cJSON_bool parse_object(cJSON * const item, parse_buffer * const input_buffer) +{ + cJSON *head = NULL; /* linked list head */ + cJSON *current_item = NULL; + + if (input_buffer->depth >= CJSON_NESTING_LIMIT) + { + return false; /* to deeply nested */ + } + input_buffer->depth++; + + if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != '{')) + { + goto fail; /* not an object */ + } + + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '}')) + { + goto success; /* empty object */ + } + + /* check if we skipped to the end of the buffer */ + if (cannot_access_at_index(input_buffer, 0)) + { + input_buffer->offset--; + goto fail; + } + + /* step back to character in front of the first element */ + input_buffer->offset--; + /* loop through the comma separated array elements */ + do + { + /* allocate next item */ + cJSON *new_item = cJSON_New_Item(&(input_buffer->hooks)); + if (new_item == NULL) + { + goto fail; /* allocation failure */ + } + + /* attach next item to list */ + if (head == NULL) + { + /* start the linked list */ + current_item = head = new_item; + } + else + { + /* add to the end and advance */ + current_item->next = new_item; + new_item->prev = current_item; + current_item = new_item; + } + + /* parse the name of the child */ + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if (!parse_string(current_item, input_buffer)) + { + goto fail; /* failed to parse name */ + } + buffer_skip_whitespace(input_buffer); + + /* swap valuestring and string, because we parsed the name */ + current_item->string = current_item->valuestring; + current_item->valuestring = NULL; + + if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != ':')) + { + goto fail; /* invalid object */ + } + + /* parse the value */ + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if (!parse_value(current_item, input_buffer)) + { + goto fail; /* failed to parse value */ + } + buffer_skip_whitespace(input_buffer); + } + while (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ',')); + + if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != '}')) + { + goto fail; /* expected end of object */ + } + +success: + input_buffer->depth--; + + if (head != NULL) { + head->prev = current_item; + } + + item->type = cJSON_Object; + item->child = head; + + input_buffer->offset++; + return true; + +fail: + if (head != NULL) + { + cJSON_Delete(head); + } + + return false; +} + +/* Render an object to text. */ +static cJSON_bool print_object(const cJSON * const item, printbuffer * const output_buffer) +{ + unsigned char *output_pointer = NULL; + size_t length = 0; + cJSON *current_item = item->child; + + if (output_buffer == NULL) + { + return false; + } + + /* Compose the output: */ + length = (size_t) (output_buffer->format ? 2 : 1); /* fmt: {\n */ + output_pointer = ensure(output_buffer, length + 1); + if (output_pointer == NULL) + { + return false; + } + + *output_pointer++ = '{'; + output_buffer->depth++; + if (output_buffer->format) + { + *output_pointer++ = '\n'; + } + output_buffer->offset += length; + + while (current_item) + { + if (output_buffer->format) + { + size_t i; + output_pointer = ensure(output_buffer, output_buffer->depth); + if (output_pointer == NULL) + { + return false; + } + for (i = 0; i < output_buffer->depth; i++) + { + *output_pointer++ = '\t'; + } + output_buffer->offset += output_buffer->depth; + } + + /* print key */ + if (!print_string_ptr((unsigned char*)current_item->string, output_buffer)) + { + return false; + } + update_offset(output_buffer); + + length = (size_t) (output_buffer->format ? 2 : 1); + output_pointer = ensure(output_buffer, length); + if (output_pointer == NULL) + { + return false; + } + *output_pointer++ = ':'; + if (output_buffer->format) + { + *output_pointer++ = '\t'; + } + output_buffer->offset += length; + + /* print value */ + if (!print_value(current_item, output_buffer)) + { + return false; + } + update_offset(output_buffer); + + /* print comma if not last */ + length = ((size_t)(output_buffer->format ? 1 : 0) + (size_t)(current_item->next ? 1 : 0)); + output_pointer = ensure(output_buffer, length + 1); + if (output_pointer == NULL) + { + return false; + } + if (current_item->next) + { + *output_pointer++ = ','; + } + + if (output_buffer->format) + { + *output_pointer++ = '\n'; + } + *output_pointer = '\0'; + output_buffer->offset += length; + + current_item = current_item->next; + } + + output_pointer = ensure(output_buffer, output_buffer->format ? (output_buffer->depth + 1) : 2); + if (output_pointer == NULL) + { + return false; + } + if (output_buffer->format) + { + size_t i; + for (i = 0; i < (output_buffer->depth - 1); i++) + { + *output_pointer++ = '\t'; + } + } + *output_pointer++ = '}'; + *output_pointer = '\0'; + output_buffer->depth--; + + return true; +} + +/* Get Array size/item / object item. */ +CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array) +{ + cJSON *child = NULL; + size_t size = 0; + + if (array == NULL) + { + return 0; + } + + child = array->child; + + while(child != NULL) + { + size++; + child = child->next; + } + + /* FIXME: Can overflow here. Cannot be fixed without breaking the API */ + + return (int)size; +} + +static cJSON* get_array_item(const cJSON *array, size_t index) +{ + cJSON *current_child = NULL; + + if (array == NULL) + { + return NULL; + } + + current_child = array->child; + while ((current_child != NULL) && (index > 0)) + { + index--; + current_child = current_child->next; + } + + return current_child; +} + +CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index) +{ + if (index < 0) + { + return NULL; + } + + return get_array_item(array, (size_t)index); +} + +static cJSON *get_object_item(const cJSON * const object, const char * const name, const cJSON_bool case_sensitive) +{ + cJSON *current_element = NULL; + + if ((object == NULL) || (name == NULL)) + { + return NULL; + } + + current_element = object->child; + if (case_sensitive) + { + while ((current_element != NULL) && (current_element->string != NULL) && (strcmp(name, current_element->string) != 0)) + { + current_element = current_element->next; + } + } + else + { + while ((current_element != NULL) && (case_insensitive_strcmp((const unsigned char*)name, (const unsigned char*)(current_element->string)) != 0)) + { + current_element = current_element->next; + } + } + + if ((current_element == NULL) || (current_element->string == NULL)) { + return NULL; + } + + return current_element; +} + +CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON * const object, const char * const string) +{ + return get_object_item(object, string, false); +} + +CJSON_PUBLIC(cJSON *) cJSON_GetObjectItemCaseSensitive(const cJSON * const object, const char * const string) +{ + return get_object_item(object, string, true); +} + +CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON *object, const char *string) +{ + return cJSON_GetObjectItem(object, string) ? 1 : 0; +} + +/* Utility for array list handling. */ +static void suffix_object(cJSON *prev, cJSON *item) +{ + prev->next = item; + item->prev = prev; +} + +/* Utility for handling references. */ +static cJSON *create_reference(const cJSON *item, const internal_hooks * const hooks) +{ + cJSON *reference = NULL; + if (item == NULL) + { + return NULL; + } + + reference = cJSON_New_Item(hooks); + if (reference == NULL) + { + return NULL; + } + + memcpy(reference, item, sizeof(cJSON)); + reference->string = NULL; + reference->type |= cJSON_IsReference; + reference->next = reference->prev = NULL; + return reference; +} + +static cJSON_bool add_item_to_array(cJSON *array, cJSON *item) +{ + cJSON *child = NULL; + + if ((item == NULL) || (array == NULL) || (array == item)) + { + return false; + } + + child = array->child; + /* + * To find the last item in array quickly, we use prev in array + */ + if (child == NULL) + { + /* list is empty, start new one */ + array->child = item; + item->prev = item; + item->next = NULL; + } + else + { + /* append to the end */ + if (child->prev) + { + suffix_object(child->prev, item); + array->child->prev = item; + } + } + + return true; +} + +/* Add item to array/object. */ +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToArray(cJSON *array, cJSON *item) +{ + return add_item_to_array(array, item); +} + +#if defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5)))) + #pragma GCC diagnostic push +#endif +#ifdef __GNUC__ +#pragma GCC diagnostic ignored "-Wcast-qual" +#endif +/* helper function to cast away const */ +static void* cast_away_const(const void* string) +{ + return (void*)string; +} +#if defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5)))) + #pragma GCC diagnostic pop +#endif + + +static cJSON_bool add_item_to_object(cJSON * const object, const char * const string, cJSON * const item, const internal_hooks * const hooks, const cJSON_bool constant_key) +{ + char *new_key = NULL; + int new_type = cJSON_Invalid; + + if ((object == NULL) || (string == NULL) || (item == NULL) || (object == item)) + { + return false; + } + + if (constant_key) + { + new_key = (char*)cast_away_const(string); + new_type = item->type | cJSON_StringIsConst; + } + else + { + new_key = (char*)cJSON_strdup((const unsigned char*)string, hooks); + if (new_key == NULL) + { + return false; + } + + new_type = item->type & ~cJSON_StringIsConst; + } + + if (!(item->type & cJSON_StringIsConst) && (item->string != NULL)) + { + hooks->deallocate(item->string); + } + + item->string = new_key; + item->type = new_type; + + return add_item_to_array(object, item); +} + +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item) +{ + return add_item_to_object(object, string, item, &global_hooks, false); +} + +/* Add an item to an object with constant string as key */ +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item) +{ + return add_item_to_object(object, string, item, &global_hooks, true); +} + +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item) +{ + if (array == NULL) + { + return false; + } + + return add_item_to_array(array, create_reference(item, &global_hooks)); +} + +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item) +{ + if ((object == NULL) || (string == NULL)) + { + return false; + } + + return add_item_to_object(object, string, create_reference(item, &global_hooks), &global_hooks, false); +} + +CJSON_PUBLIC(cJSON*) cJSON_AddNullToObject(cJSON * const object, const char * const name) +{ + cJSON *null = cJSON_CreateNull(); + if (add_item_to_object(object, name, null, &global_hooks, false)) + { + return null; + } + + cJSON_Delete(null); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddTrueToObject(cJSON * const object, const char * const name) +{ + cJSON *true_item = cJSON_CreateTrue(); + if (add_item_to_object(object, name, true_item, &global_hooks, false)) + { + return true_item; + } + + cJSON_Delete(true_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddFalseToObject(cJSON * const object, const char * const name) +{ + cJSON *false_item = cJSON_CreateFalse(); + if (add_item_to_object(object, name, false_item, &global_hooks, false)) + { + return false_item; + } + + cJSON_Delete(false_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddBoolToObject(cJSON * const object, const char * const name, const cJSON_bool boolean) +{ + cJSON *bool_item = cJSON_CreateBool(boolean); + if (add_item_to_object(object, name, bool_item, &global_hooks, false)) + { + return bool_item; + } + + cJSON_Delete(bool_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddNumberToObject(cJSON * const object, const char * const name, const double number) +{ + cJSON *number_item = cJSON_CreateNumber(number); + if (add_item_to_object(object, name, number_item, &global_hooks, false)) + { + return number_item; + } + + cJSON_Delete(number_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddStringToObject(cJSON * const object, const char * const name, const char * const string) +{ + cJSON *string_item = cJSON_CreateString(string); + if (add_item_to_object(object, name, string_item, &global_hooks, false)) + { + return string_item; + } + + cJSON_Delete(string_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddRawToObject(cJSON * const object, const char * const name, const char * const raw) +{ + cJSON *raw_item = cJSON_CreateRaw(raw); + if (add_item_to_object(object, name, raw_item, &global_hooks, false)) + { + return raw_item; + } + + cJSON_Delete(raw_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddObjectToObject(cJSON * const object, const char * const name) +{ + cJSON *object_item = cJSON_CreateObject(); + if (add_item_to_object(object, name, object_item, &global_hooks, false)) + { + return object_item; + } + + cJSON_Delete(object_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddArrayToObject(cJSON * const object, const char * const name) +{ + cJSON *array = cJSON_CreateArray(); + if (add_item_to_object(object, name, array, &global_hooks, false)) + { + return array; + } + + cJSON_Delete(array); + return NULL; +} + +CJSON_PUBLIC(cJSON *) cJSON_DetachItemViaPointer(cJSON *parent, cJSON * const item) +{ + if ((parent == NULL) || (item == NULL)) + { + return NULL; + } + + if (item != parent->child) + { + /* not the first element */ + item->prev->next = item->next; + } + if (item->next != NULL) + { + /* not the last element */ + item->next->prev = item->prev; + } + + if (item == parent->child) + { + /* first element */ + parent->child = item->next; + } + else if (item->next == NULL) + { + /* last element */ + parent->child->prev = item->prev; + } + + /* make sure the detached item doesn't point anywhere anymore */ + item->prev = NULL; + item->next = NULL; + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromArray(cJSON *array, int which) +{ + if (which < 0) + { + return NULL; + } + + return cJSON_DetachItemViaPointer(array, get_array_item(array, (size_t)which)); +} + +CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON *array, int which) +{ + cJSON_Delete(cJSON_DetachItemFromArray(array, which)); +} + +CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObject(cJSON *object, const char *string) +{ + cJSON *to_detach = cJSON_GetObjectItem(object, string); + + return cJSON_DetachItemViaPointer(object, to_detach); +} + +CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObjectCaseSensitive(cJSON *object, const char *string) +{ + cJSON *to_detach = cJSON_GetObjectItemCaseSensitive(object, string); + + return cJSON_DetachItemViaPointer(object, to_detach); +} + +CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON *object, const char *string) +{ + cJSON_Delete(cJSON_DetachItemFromObject(object, string)); +} + +CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(cJSON *object, const char *string) +{ + cJSON_Delete(cJSON_DetachItemFromObjectCaseSensitive(object, string)); +} + +/* Replace array/object items with new ones. */ +CJSON_PUBLIC(cJSON_bool) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem) +{ + cJSON *after_inserted = NULL; + + if (which < 0) + { + return false; + } + + after_inserted = get_array_item(array, (size_t)which); + if (after_inserted == NULL) + { + return add_item_to_array(array, newitem); + } + + newitem->next = after_inserted; + newitem->prev = after_inserted->prev; + after_inserted->prev = newitem; + if (after_inserted == array->child) + { + array->child = newitem; + } + else + { + newitem->prev->next = newitem; + } + return true; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemViaPointer(cJSON * const parent, cJSON * const item, cJSON * replacement) +{ + if ((parent == NULL) || (parent->child == NULL) || (replacement == NULL) || (item == NULL)) + { + return false; + } + + if (replacement == item) + { + return true; + } + + replacement->next = item->next; + replacement->prev = item->prev; + + if (replacement->next != NULL) + { + replacement->next->prev = replacement; + } + if (parent->child == item) + { + if (parent->child->prev == parent->child) + { + replacement->prev = replacement; + } + parent->child = replacement; + } + else + { /* + * To find the last item in array quickly, we use prev in array. + * We can't modify the last item's next pointer where this item was the parent's child + */ + if (replacement->prev != NULL) + { + replacement->prev->next = replacement; + } + if (replacement->next == NULL) + { + parent->child->prev = replacement; + } + } + + item->next = NULL; + item->prev = NULL; + cJSON_Delete(item); + + return true; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem) +{ + if (which < 0) + { + return false; + } + + return cJSON_ReplaceItemViaPointer(array, get_array_item(array, (size_t)which), newitem); +} + +static cJSON_bool replace_item_in_object(cJSON *object, const char *string, cJSON *replacement, cJSON_bool case_sensitive) +{ + if ((replacement == NULL) || (string == NULL)) + { + return false; + } + + /* replace the name in the replacement */ + if (!(replacement->type & cJSON_StringIsConst) && (replacement->string != NULL)) + { + cJSON_free(replacement->string); + } + replacement->string = (char*)cJSON_strdup((const unsigned char*)string, &global_hooks); + if (replacement->string == NULL) + { + return false; + } + + replacement->type &= ~cJSON_StringIsConst; + + return cJSON_ReplaceItemViaPointer(object, get_object_item(object, string, case_sensitive), replacement); +} + +CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObject(cJSON *object, const char *string, cJSON *newitem) +{ + return replace_item_in_object(object, string, newitem, false); +} + +CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object, const char *string, cJSON *newitem) +{ + return replace_item_in_object(object, string, newitem, true); +} + +/* Create basic types: */ +CJSON_PUBLIC(cJSON *) cJSON_CreateNull(void) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = cJSON_NULL; + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateTrue(void) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = cJSON_True; + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = cJSON_False; + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool boolean) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = boolean ? cJSON_True : cJSON_False; + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = cJSON_Number; + item->valuedouble = num; + + /* use saturation in case of overflow */ + if (num >= INT_MAX) + { + item->valueint = INT_MAX; + } + else if (num <= (double)INT_MIN) + { + item->valueint = INT_MIN; + } + else + { + item->valueint = (int)num; + } + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = cJSON_String; + item->valuestring = (char*)cJSON_strdup((const unsigned char*)string, &global_hooks); + if(!item->valuestring) + { + cJSON_Delete(item); + return NULL; + } + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateStringReference(const char *string) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if (item != NULL) + { + item->type = cJSON_String | cJSON_IsReference; + item->valuestring = (char*)cast_away_const(string); + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateObjectReference(const cJSON *child) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if (item != NULL) { + item->type = cJSON_Object | cJSON_IsReference; + item->child = (cJSON*)cast_away_const(child); + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateArrayReference(const cJSON *child) { + cJSON *item = cJSON_New_Item(&global_hooks); + if (item != NULL) { + item->type = cJSON_Array | cJSON_IsReference; + item->child = (cJSON*)cast_away_const(child); + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = cJSON_Raw; + item->valuestring = (char*)cJSON_strdup((const unsigned char*)raw, &global_hooks); + if(!item->valuestring) + { + cJSON_Delete(item); + return NULL; + } + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateArray(void) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type=cJSON_Array; + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if (item) + { + item->type = cJSON_Object; + } + + return item; +} + +/* Create Arrays: */ +CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count) +{ + size_t i = 0; + cJSON *n = NULL; + cJSON *p = NULL; + cJSON *a = NULL; + + if ((count < 0) || (numbers == NULL)) + { + return NULL; + } + + a = cJSON_CreateArray(); + + for(i = 0; a && (i < (size_t)count); i++) + { + n = cJSON_CreateNumber(numbers[i]); + if (!n) + { + cJSON_Delete(a); + return NULL; + } + if(!i) + { + a->child = n; + } + else + { + suffix_object(p, n); + } + p = n; + } + + if (a && a->child) { + a->child->prev = n; + } + + return a; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count) +{ + size_t i = 0; + cJSON *n = NULL; + cJSON *p = NULL; + cJSON *a = NULL; + + if ((count < 0) || (numbers == NULL)) + { + return NULL; + } + + a = cJSON_CreateArray(); + + for(i = 0; a && (i < (size_t)count); i++) + { + n = cJSON_CreateNumber((double)numbers[i]); + if(!n) + { + cJSON_Delete(a); + return NULL; + } + if(!i) + { + a->child = n; + } + else + { + suffix_object(p, n); + } + p = n; + } + + if (a && a->child) { + a->child->prev = n; + } + + return a; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count) +{ + size_t i = 0; + cJSON *n = NULL; + cJSON *p = NULL; + cJSON *a = NULL; + + if ((count < 0) || (numbers == NULL)) + { + return NULL; + } + + a = cJSON_CreateArray(); + + for(i = 0; a && (i < (size_t)count); i++) + { + n = cJSON_CreateNumber(numbers[i]); + if(!n) + { + cJSON_Delete(a); + return NULL; + } + if(!i) + { + a->child = n; + } + else + { + suffix_object(p, n); + } + p = n; + } + + if (a && a->child) { + a->child->prev = n; + } + + return a; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char *const *strings, int count) +{ + size_t i = 0; + cJSON *n = NULL; + cJSON *p = NULL; + cJSON *a = NULL; + + if ((count < 0) || (strings == NULL)) + { + return NULL; + } + + a = cJSON_CreateArray(); + + for (i = 0; a && (i < (size_t)count); i++) + { + n = cJSON_CreateString(strings[i]); + if(!n) + { + cJSON_Delete(a); + return NULL; + } + if(!i) + { + a->child = n; + } + else + { + suffix_object(p,n); + } + p = n; + } + + if (a && a->child) { + a->child->prev = n; + } + + return a; +} + +/* Duplication */ +CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse) +{ + cJSON *newitem = NULL; + cJSON *child = NULL; + cJSON *next = NULL; + cJSON *newchild = NULL; + + /* Bail on bad ptr */ + if (!item) + { + goto fail; + } + /* Create new item */ + newitem = cJSON_New_Item(&global_hooks); + if (!newitem) + { + goto fail; + } + /* Copy over all vars */ + newitem->type = item->type & (~cJSON_IsReference); + newitem->valueint = item->valueint; + newitem->valuedouble = item->valuedouble; + if (item->valuestring) + { + newitem->valuestring = (char*)cJSON_strdup((unsigned char*)item->valuestring, &global_hooks); + if (!newitem->valuestring) + { + goto fail; + } + } + if (item->string) + { + newitem->string = (item->type&cJSON_StringIsConst) ? item->string : (char*)cJSON_strdup((unsigned char*)item->string, &global_hooks); + if (!newitem->string) + { + goto fail; + } + } + /* If non-recursive, then we're done! */ + if (!recurse) + { + return newitem; + } + /* Walk the ->next chain for the child. */ + child = item->child; + while (child != NULL) + { + newchild = cJSON_Duplicate(child, true); /* Duplicate (with recurse) each item in the ->next chain */ + if (!newchild) + { + goto fail; + } + if (next != NULL) + { + /* If newitem->child already set, then crosswire ->prev and ->next and move on */ + next->next = newchild; + newchild->prev = next; + next = newchild; + } + else + { + /* Set newitem->child and move to it */ + newitem->child = newchild; + next = newchild; + } + child = child->next; + } + if (newitem && newitem->child) + { + newitem->child->prev = newchild; + } + + return newitem; + +fail: + if (newitem != NULL) + { + cJSON_Delete(newitem); + } + + return NULL; +} + +static void skip_oneline_comment(char **input) +{ + *input += static_strlen("//"); + + for (; (*input)[0] != '\0'; ++(*input)) + { + if ((*input)[0] == '\n') { + *input += static_strlen("\n"); + return; + } + } +} + +static void skip_multiline_comment(char **input) +{ + *input += static_strlen("/*"); + + for (; (*input)[0] != '\0'; ++(*input)) + { + if (((*input)[0] == '*') && ((*input)[1] == '/')) + { + *input += static_strlen("*/"); + return; + } + } +} + +static void minify_string(char **input, char **output) { + (*output)[0] = (*input)[0]; + *input += static_strlen("\""); + *output += static_strlen("\""); + + + for (; (*input)[0] != '\0'; (void)++(*input), ++(*output)) { + (*output)[0] = (*input)[0]; + + if ((*input)[0] == '\"') { + (*output)[0] = '\"'; + *input += static_strlen("\""); + *output += static_strlen("\""); + return; + } else if (((*input)[0] == '\\') && ((*input)[1] == '\"')) { + (*output)[1] = (*input)[1]; + *input += static_strlen("\""); + *output += static_strlen("\""); + } + } +} + +CJSON_PUBLIC(void) cJSON_Minify(char *json) +{ + char *into = json; + + if (json == NULL) + { + return; + } + + while (json[0] != '\0') + { + switch (json[0]) + { + case ' ': + case '\t': + case '\r': + case '\n': + json++; + break; + + case '/': + if (json[1] == '/') + { + skip_oneline_comment(&json); + } + else if (json[1] == '*') + { + skip_multiline_comment(&json); + } else { + json++; + } + break; + + case '\"': + minify_string(&json, (char**)&into); + break; + + default: + into[0] = json[0]; + json++; + into++; + } + } + + /* and null-terminate. */ + *into = '\0'; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_Invalid; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_False; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xff) == cJSON_True; +} + + +CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & (cJSON_True | cJSON_False)) != 0; +} +CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_NULL; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_Number; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_String; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_Array; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_Object; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_Raw; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * const b, const cJSON_bool case_sensitive) +{ + if ((a == NULL) || (b == NULL) || ((a->type & 0xFF) != (b->type & 0xFF))) + { + return false; + } + + /* check if type is valid */ + switch (a->type & 0xFF) + { + case cJSON_False: + case cJSON_True: + case cJSON_NULL: + case cJSON_Number: + case cJSON_String: + case cJSON_Raw: + case cJSON_Array: + case cJSON_Object: + break; + + default: + return false; + } + + /* identical objects are equal */ + if (a == b) + { + return true; + } + + switch (a->type & 0xFF) + { + /* in these cases and equal type is enough */ + case cJSON_False: + case cJSON_True: + case cJSON_NULL: + return true; + + case cJSON_Number: + if (compare_double(a->valuedouble, b->valuedouble)) + { + return true; + } + return false; + + case cJSON_String: + case cJSON_Raw: + if ((a->valuestring == NULL) || (b->valuestring == NULL)) + { + return false; + } + if (strcmp(a->valuestring, b->valuestring) == 0) + { + return true; + } + + return false; + + case cJSON_Array: + { + cJSON *a_element = a->child; + cJSON *b_element = b->child; + + for (; (a_element != NULL) && (b_element != NULL);) + { + if (!cJSON_Compare(a_element, b_element, case_sensitive)) + { + return false; + } + + a_element = a_element->next; + b_element = b_element->next; + } + + /* one of the arrays is longer than the other */ + if (a_element != b_element) { + return false; + } + + return true; + } + + case cJSON_Object: + { + cJSON *a_element = NULL; + cJSON *b_element = NULL; + cJSON_ArrayForEach(a_element, a) + { + /* TODO This has O(n^2) runtime, which is horrible! */ + b_element = get_object_item(b, a_element->string, case_sensitive); + if (b_element == NULL) + { + return false; + } + + if (!cJSON_Compare(a_element, b_element, case_sensitive)) + { + return false; + } + } + + /* doing this twice, once on a and b to prevent true comparison if a subset of b + * TODO: Do this the proper way, this is just a fix for now */ + cJSON_ArrayForEach(b_element, b) + { + a_element = get_object_item(a, b_element->string, case_sensitive); + if (a_element == NULL) + { + return false; + } + + if (!cJSON_Compare(b_element, a_element, case_sensitive)) + { + return false; + } + } + + return true; + } + + default: + return false; + } +} + +CJSON_PUBLIC(void *) cJSON_malloc(size_t size) +{ + return global_hooks.allocate(size); +} + +CJSON_PUBLIC(void) cJSON_free(void *object) +{ + global_hooks.deallocate(object); +} + diff --git a/cJSON/cJSON.h b/cJSON/cJSON.h new file mode 100644 index 00000000..9a2e8c8d --- /dev/null +++ b/cJSON/cJSON.h @@ -0,0 +1,301 @@ +/* + Copyright (c) 2009-2017 Dave Gamble and cJSON contributors + + 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 cJSON__h +#define cJSON__h + +#ifdef __cplusplus +extern "C" +{ +#endif + +#if !defined(__WINDOWS__) && (defined(WIN32) || defined(WIN64) || defined(_MSC_VER) || defined(_WIN32)) +#define __WINDOWS__ +#endif + +#ifdef __WINDOWS__ + +/* When compiling for windows, we specify a specific calling convention to avoid issues where we are being called from a project with a different default calling convention. For windows you have 3 define options: + +CJSON_HIDE_SYMBOLS - Define this in the case where you don't want to ever dllexport symbols +CJSON_EXPORT_SYMBOLS - Define this on library build when you want to dllexport symbols (default) +CJSON_IMPORT_SYMBOLS - Define this if you want to dllimport symbol + +For *nix builds that support visibility attribute, you can define similar behavior by + +setting default visibility to hidden by adding +-fvisibility=hidden (for gcc) +or +-xldscope=hidden (for sun cc) +to CFLAGS + +then using the CJSON_API_VISIBILITY flag to "export" the same symbols the way CJSON_EXPORT_SYMBOLS does + +*/ + +#define CJSON_CDECL __cdecl +#define CJSON_STDCALL __stdcall + +/* export symbols by default, this is necessary for copy pasting the C and header file */ +#if !defined(CJSON_HIDE_SYMBOLS) && !defined(CJSON_IMPORT_SYMBOLS) && !defined(CJSON_EXPORT_SYMBOLS) +#define CJSON_EXPORT_SYMBOLS +#endif + +#if defined(CJSON_HIDE_SYMBOLS) +#define CJSON_PUBLIC(type) type CJSON_STDCALL +#elif defined(CJSON_EXPORT_SYMBOLS) +#define CJSON_PUBLIC(type) __declspec(dllexport) type CJSON_STDCALL +#elif defined(CJSON_IMPORT_SYMBOLS) +#define CJSON_PUBLIC(type) __declspec(dllimport) type CJSON_STDCALL +#endif +#else /* !__WINDOWS__ */ +#define CJSON_CDECL +#define CJSON_STDCALL + +#if (defined(__GNUC__) || defined(__SUNPRO_CC) || defined (__SUNPRO_C)) && defined(CJSON_API_VISIBILITY) +#define CJSON_PUBLIC(type) __attribute__((visibility("default"))) type +#else +#define CJSON_PUBLIC(type) type +#endif +#endif + +/* project version */ +#define CJSON_VERSION_MAJOR 1 +#define CJSON_VERSION_MINOR 7 +#define CJSON_VERSION_PATCH 16 + +#include + +/* cJSON Types: */ +#define cJSON_Invalid (0) +#define cJSON_False (1 << 0) +#define cJSON_True (1 << 1) +#define cJSON_NULL (1 << 2) +#define cJSON_Number (1 << 3) +#define cJSON_String (1 << 4) +#define cJSON_Array (1 << 5) +#define cJSON_Object (1 << 6) +#define cJSON_Raw (1 << 7) /* raw json */ + +#define cJSON_IsReference 256 +#define cJSON_StringIsConst 512 + +/* The cJSON structure: */ +typedef struct cJSON +{ + /* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */ + struct cJSON *next; + struct cJSON *prev; + /* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */ + struct cJSON *child; + + /* The type of the item, as above. */ + int type; + + /* The item's string, if type==cJSON_String and type == cJSON_Raw */ + char *valuestring; + /* writing to valueint is DEPRECATED, use cJSON_SetNumberValue instead */ + int valueint; + /* The item's number, if type==cJSON_Number */ + double valuedouble; + + /* The item's name string, if this item is the child of, or is in the list of subitems of an object. */ + char *string; +} cJSON; + +typedef struct cJSON_Hooks +{ + /* malloc/free are CDECL on Windows regardless of the default calling convention of the compiler, so ensure the hooks allow passing those functions directly. */ + void *(CJSON_CDECL *malloc_fn)(size_t sz); + void (CJSON_CDECL *free_fn)(void *ptr); +} cJSON_Hooks; + +typedef int cJSON_bool; + +/* Limits how deeply nested arrays/objects can be before cJSON rejects to parse them. + * This is to prevent stack overflows. */ +#ifndef CJSON_NESTING_LIMIT +#define CJSON_NESTING_LIMIT 1000 +#endif + +/* returns the version of cJSON as a string */ +CJSON_PUBLIC(const char*) cJSON_Version(void); + +/* Supply malloc, realloc and free functions to cJSON */ +CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks); + +/* Memory Management: the caller is always responsible to free the results from all variants of cJSON_Parse (with cJSON_Delete) and cJSON_Print (with stdlib free, cJSON_Hooks.free_fn, or cJSON_free as appropriate). The exception is cJSON_PrintPreallocated, where the caller has full responsibility of the buffer. */ +/* Supply a block of JSON, and this returns a cJSON object you can interrogate. */ +CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value); +CJSON_PUBLIC(cJSON *) cJSON_ParseWithLength(const char *value, size_t buffer_length); +/* ParseWithOpts allows you to require (and check) that the JSON is null terminated, and to retrieve the pointer to the final byte parsed. */ +/* If you supply a ptr in return_parse_end and parsing fails, then return_parse_end will contain a pointer to the error so will match cJSON_GetErrorPtr(). */ +CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated); +CJSON_PUBLIC(cJSON *) cJSON_ParseWithLengthOpts(const char *value, size_t buffer_length, const char **return_parse_end, cJSON_bool require_null_terminated); + +/* Render a cJSON entity to text for transfer/storage. */ +CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item); +/* Render a cJSON entity to text for transfer/storage without any formatting. */ +CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item); +/* Render a cJSON entity to text using a buffered strategy. prebuffer is a guess at the final size. guessing well reduces reallocation. fmt=0 gives unformatted, =1 gives formatted */ +CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt); +/* Render a cJSON entity to text using a buffer already allocated in memory with given length. Returns 1 on success and 0 on failure. */ +/* NOTE: cJSON is not always 100% accurate in estimating how much memory it will use, so to be safe allocate 5 bytes more than you actually need */ +CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format); +/* Delete a cJSON entity and all subentities. */ +CJSON_PUBLIC(void) cJSON_Delete(cJSON *item); + +/* Returns the number of items in an array (or object). */ +CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array); +/* Retrieve item number "index" from array "array". Returns NULL if unsuccessful. */ +CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index); +/* Get item "string" from object. Case insensitive. */ +CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON * const object, const char * const string); +CJSON_PUBLIC(cJSON *) cJSON_GetObjectItemCaseSensitive(const cJSON * const object, const char * const string); +CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON *object, const char *string); +/* For analysing failed parses. This returns a pointer to the parse error. You'll probably need to look a few chars back to make sense of it. Defined when cJSON_Parse() returns 0. 0 when cJSON_Parse() succeeds. */ +CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void); + +/* Check item type and return its value */ +CJSON_PUBLIC(char *) cJSON_GetStringValue(const cJSON * const item); +CJSON_PUBLIC(double) cJSON_GetNumberValue(const cJSON * const item); + +/* These functions check the type of an item */ +CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON * const item); + +/* These calls create a cJSON item of the appropriate type. */ +CJSON_PUBLIC(cJSON *) cJSON_CreateNull(void); +CJSON_PUBLIC(cJSON *) cJSON_CreateTrue(void); +CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void); +CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool boolean); +CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num); +CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string); +/* raw json */ +CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw); +CJSON_PUBLIC(cJSON *) cJSON_CreateArray(void); +CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void); + +/* Create a string where valuestring references a string so + * it will not be freed by cJSON_Delete */ +CJSON_PUBLIC(cJSON *) cJSON_CreateStringReference(const char *string); +/* Create an object/array that only references it's elements so + * they will not be freed by cJSON_Delete */ +CJSON_PUBLIC(cJSON *) cJSON_CreateObjectReference(const cJSON *child); +CJSON_PUBLIC(cJSON *) cJSON_CreateArrayReference(const cJSON *child); + +/* These utilities create an Array of count items. + * The parameter count cannot be greater than the number of elements in the number array, otherwise array access will be out of bounds.*/ +CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count); +CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count); +CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count); +CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char *const *strings, int count); + +/* Append item to the specified array/object. */ +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToArray(cJSON *array, cJSON *item); +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item); +/* Use this when string is definitely const (i.e. a literal, or as good as), and will definitely survive the cJSON object. + * WARNING: When this function was used, make sure to always check that (item->type & cJSON_StringIsConst) is zero before + * writing to `item->string` */ +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item); +/* Append reference to item to the specified array/object. Use this when you want to add an existing cJSON to a new cJSON, but don't want to corrupt your existing cJSON. */ +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item); +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item); + +/* Remove/Detach items from Arrays/Objects. */ +CJSON_PUBLIC(cJSON *) cJSON_DetachItemViaPointer(cJSON *parent, cJSON * const item); +CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromArray(cJSON *array, int which); +CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON *array, int which); +CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObject(cJSON *object, const char *string); +CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObjectCaseSensitive(cJSON *object, const char *string); +CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON *object, const char *string); +CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(cJSON *object, const char *string); + +/* Update array items. */ +CJSON_PUBLIC(cJSON_bool) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem); /* Shifts pre-existing items to the right. */ +CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemViaPointer(cJSON * const parent, cJSON * const item, cJSON * replacement); +CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem); +CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem); +CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object,const char *string,cJSON *newitem); + +/* Duplicate a cJSON item */ +CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse); +/* Duplicate will create a new, identical cJSON item to the one you pass, in new memory that will + * need to be released. With recurse!=0, it will duplicate any children connected to the item. + * The item->next and ->prev pointers are always zero on return from Duplicate. */ +/* Recursively compare two cJSON items for equality. If either a or b is NULL or invalid, they will be considered unequal. + * case_sensitive determines if object keys are treated case sensitive (1) or case insensitive (0) */ +CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * const b, const cJSON_bool case_sensitive); + +/* Minify a strings, remove blank characters(such as ' ', '\t', '\r', '\n') from strings. + * The input pointer json cannot point to a read-only address area, such as a string constant, + * but should point to a readable and writable address area. */ +CJSON_PUBLIC(void) cJSON_Minify(char *json); + +/* Helper functions for creating and adding items to an object at the same time. + * They return the added item or NULL on failure. */ +CJSON_PUBLIC(cJSON*) cJSON_AddNullToObject(cJSON * const object, const char * const name); +CJSON_PUBLIC(cJSON*) cJSON_AddTrueToObject(cJSON * const object, const char * const name); +CJSON_PUBLIC(cJSON*) cJSON_AddFalseToObject(cJSON * const object, const char * const name); +CJSON_PUBLIC(cJSON*) cJSON_AddBoolToObject(cJSON * const object, const char * const name, const cJSON_bool boolean); +CJSON_PUBLIC(cJSON*) cJSON_AddNumberToObject(cJSON * const object, const char * const name, const double number); +CJSON_PUBLIC(cJSON*) cJSON_AddStringToObject(cJSON * const object, const char * const name, const char * const string); +CJSON_PUBLIC(cJSON*) cJSON_AddRawToObject(cJSON * const object, const char * const name, const char * const raw); +CJSON_PUBLIC(cJSON*) cJSON_AddObjectToObject(cJSON * const object, const char * const name); +CJSON_PUBLIC(cJSON*) cJSON_AddArrayToObject(cJSON * const object, const char * const name); + +/* When assigning an integer value, it needs to be propagated to valuedouble too. */ +#define cJSON_SetIntValue(object, number) ((object) ? (object)->valueint = (object)->valuedouble = (number) : (number)) +/* helper for the cJSON_SetNumberValue macro */ +CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number); +#define cJSON_SetNumberValue(object, number) ((object != NULL) ? cJSON_SetNumberHelper(object, (double)number) : (number)) +/* Change the valuestring of a cJSON_String object, only takes effect when type of object is cJSON_String */ +CJSON_PUBLIC(char*) cJSON_SetValuestring(cJSON *object, const char *valuestring); + +/* If the object is not a boolean type this does nothing and returns cJSON_Invalid else it returns the new type*/ +#define cJSON_SetBoolValue(object, boolValue) ( \ + (object != NULL && ((object)->type & (cJSON_False|cJSON_True))) ? \ + (object)->type=((object)->type &(~(cJSON_False|cJSON_True)))|((boolValue)?cJSON_True:cJSON_False) : \ + cJSON_Invalid\ +) + +/* Macro for iterating over an array or object */ +#define cJSON_ArrayForEach(element, array) for(element = (array != NULL) ? (array)->child : NULL; element != NULL; element = element->next) + +/* malloc/free objects using the malloc/free functions that have been set with cJSON_InitHooks */ +CJSON_PUBLIC(void *) cJSON_malloc(size_t size); +CJSON_PUBLIC(void) cJSON_free(void *object); + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/glade/adrv9002.glade b/glade/adrv9002.glade index ab7c4ee6..b794a537 100644 --- a/glade/adrv9002.glade +++ b/glade/adrv9002.glade @@ -92,6 +92,50 @@ 1 10 + + 12000 + 40000000 + 1 + 10 + + + 600000000 + 1 + 10 + + + 12000 + 40000000 + 1 + 10 + + + 600000000 + 1 + 10 + + + 12000 + 40000000 + 1 + 10 + + + 600000000 + 1 + 10 + + + 12000 + 40000000 + 1 + 10 + + + 600000000 + 1 + 10 + True False @@ -289,6 +333,9 @@ 3 + + + True @@ -401,6 +448,9 @@ False 2 3 + + + True @@ -466,9 +516,6 @@ 5 - - - @@ -3336,6 +3383,24 @@ True False + + + True + False + + + True + False + gtk-missing-image + + + + + True + True + 1 + + True @@ -3432,24 +3497,6 @@ False True - 0 - - - - - True - False - - - True - False - gtk-missing-image - - - - - True - True 1 @@ -3469,22 +3516,1575 @@ False - - - True - True - 0 - - - - - - - - - - - - + + + True + False + 6 + 6 + 6 + 6 + + + True + False + 15 + 15 + 50 + + + True + False + start + 15 + + + True + False + 5 + + + True + False + Preset: + + + False + True + 0 + + + + + True + False + 1 + + LTE + Live Device + + + + True + True + 1 + + + + + False + True + 0 + + + + + Refresh + True + True + True + center + + + False + True + 1 + + + + + True + True + 0 + + + + + True + False + end + 15 + + + Save to file + True + True + True + center + 0 + + + False + False + 1 + + + + + Load to device + True + True + True + center + 0 + + + False + False + 2 + + + + + False + True + end + 1 + + + + + False + True + 0 + + + + + False + + + False + False + 50 + 0 + + + + + True + True + never + + + True + False + none + + + True + False + vertical + 50 + + + True + False + 5 + 5 + 0 + in + + + True + False + 5 + 5 + 5 + 5 + + + True + False + vertical + 25 + + + + True + False + start + start + 5 + 5 + + + True + False + end + SSI Interface: + + + 0 + 0 + + + + + True + False + end + Duplex mode: + + + 0 + 1 + + + + + True + False + 0 + + TDD + FDD + + + + 1 + 1 + + + + + True + False + interface + + + 1 + 0 + + + + + False + True + 0 + + + + + + True + False + 10 + True + + + True + False + 0 + in + + + True + False + 5 + 5 + 5 + 5 + + + True + False + vertical + 5 + + + True + False + 12 + True + + + True + False + end + Enabled: + + + False + True + 0 + + + + + True + True + False + 6 + 6 + True + True + + + False + True + 1 + + + + + False + True + 0 + + + + + True + False + 12 + True + + + True + False + end + Frequency Offset Correction: + + + False + True + 0 + + + + + True + True + False + 6 + 6 + True + True + + + False + True + 1 + + + + + False + True + 1 + + + + + True + False + 12 + True + + + True + False + end + Bandwidth (Hz): + + + False + True + 0 + + + + + True + False + True + + + True + True + 8 + 8 + + + + + False + True + 1 + + + + + False + True + 2 + + + + + True + False + 12 + True + + + True + False + end + Interface Sample Rate (Hz): + + + False + True + 0 + + + + + True + False + True + + + True + True + 8 + 8 + + + + + False + True + 1 + + + + + False + True + 3 + + + + + True + False + 12 + True + + + True + False + end + RX RF Input: + + + False + True + 0 + + + + + True + False + + Rx1A + Rx1B + + + + False + True + 1 + + + + + False + True + 4 + + + + + + + + + True + False + <b>RX 1</b> + True + + + + + 0 + 0 + + + + + True + False + 0 + in + + + True + False + 5 + 5 + 5 + 5 + + + True + False + vertical + 5 + + + True + False + 12 + True + + + True + False + end + Enabled: + + + False + True + 0 + + + + + True + True + False + 6 + 6 + True + True + + + False + True + 1 + + + + + False + True + 0 + + + + + True + False + 12 + True + + + True + False + end + Frequency Offset Correction: + + + False + True + 0 + + + + + True + True + False + 6 + 6 + True + True + + + False + True + 1 + + + + + False + True + 1 + + + + + True + False + 12 + True + + + True + False + end + Bandwidth (Hz): + + + False + True + 0 + + + + + True + False + True + + + True + True + 8 + 8 + + + + + False + True + 1 + + + + + False + True + 2 + + + + + True + False + 12 + True + + + True + False + end + Interface Sample Rate (Hz): + + + False + True + 0 + + + + + True + False + True + + + True + True + 8 + 8 + + + + + False + True + 1 + + + + + False + True + 3 + + + + + True + False + 12 + True + + + True + False + end + RX RF Input: + + + False + True + 0 + + + + + True + False + + Rx2A + Rx2B + + + + False + True + 1 + + + + + False + True + 4 + + + + + + + + + True + False + <b>RX 2</b> + True + + + + + 1 + 0 + + + + + False + True + 1 + + + + + + True + False + 10 + True + + + True + False + 0 + in + + + True + False + 5 + 5 + 5 + 5 + + + True + False + vertical + 5 + + + True + False + 12 + True + + + True + False + end + Enabled: + + + False + True + 0 + + + + + True + True + False + 6 + 6 + True + True + + + False + True + 1 + + + + + False + True + 0 + + + + + True + False + 12 + True + + + True + False + end + Frequency Offset Correction: + + + False + True + 0 + + + + + True + True + False + 6 + 6 + True + True + + + False + True + 1 + + + + + False + True + 1 + + + + + True + False + 12 + True + + + True + False + end + Bandwidth (Hz): + + + False + True + 0 + + + + + True + False + True + + + True + True + 8 + 8 + + + + + False + True + 1 + + + + + False + True + 2 + + + + + True + False + 12 + True + + + True + False + end + Interface Sample Rate (Hz): + + + False + True + 0 + + + + + True + False + True + + + True + True + 8 + 8 + + + + + False + True + 1 + + + + + False + True + 3 + + + + + + + + + True + False + <b>TX 1</b> + True + + + + + 0 + 0 + + + + + True + False + 0 + in + + + True + False + 5 + 5 + 5 + 5 + + + True + False + vertical + 5 + + + True + False + 12 + True + + + True + False + end + Enabled: + + + False + True + 0 + + + + + True + True + False + 6 + 6 + True + True + + + False + True + 1 + + + + + False + True + 0 + + + + + True + False + 12 + True + + + True + False + end + Frequency Offset Correction: + + + False + True + 0 + + + + + True + True + False + 6 + 6 + True + True + + + False + True + 1 + + + + + False + True + 1 + + + + + True + False + 12 + True + + + True + False + end + Bandwidth (Hz): + + + False + True + 0 + + + + + True + False + True + + + True + True + 8 + 8 + + + + + False + True + 1 + + + + + False + True + 2 + + + + + True + False + 12 + True + + + True + False + end + Interface Sample Rate (Hz): + + + False + True + 0 + + + + + True + False + True + + + True + True + 8 + 8 + + + + + False + True + 1 + + + + + False + True + 3 + + + + + + + + + True + False + <b>TX 2</b> + True + + + + + 1 + 0 + + + + + False + True + 2 + + + + + + True + False + 10 + True + + + True + False + 0 + in + + + True + False + 12 + + + True + False + vertical + 5 + + + True + False + 12 + True + + + True + False + end + Enabled: + + + False + True + 0 + + + + + True + True + False + 6 + 6 + True + True + + + False + True + 1 + + + + + False + True + 0 + + + + + + + + + True + False + <b>ORX 1</b> + True + + + + + 0 + 0 + + + + + True + False + 0 + in + + + True + False + 12 + + + True + False + vertical + 5 + + + True + False + 12 + True + + + True + False + end + Enabled: + + + False + True + 0 + + + + + True + True + False + 6 + 6 + True + True + + + False + True + 1 + + + + + False + True + 0 + + + + + + + + + True + False + <b>ORX 2</b> + True + + + + + 1 + 0 + + + + + False + True + 3 + + + + + + + + + True + False + <b>Radio Config</b> + True + + + + + False + True + 0 + + + + + True + False + 5 + 5 + 0 + in + + + True + False + + + True + True + never + 200 + + + True + False + none + + + True + False + start + start + True + char + True + + + + + + + + + + + True + False + <b>Debug Info</b> + True + + + + + True + True + 1 + + + + + + + + + True + True + 2 + + + + + 2 + + + + + True + False + Profile Generator + + + 2 + False + + + + + True + True + 0 + + + + + + False + 5 + ADI IIO oscilloscope - Save As + GtkFileChooserDialog + True + adi-osc + dialog + save + True + + + False + vertical + 2 + + + False + end + + + True + False + 1 + + Stream Image + Profile + + + + True + True + 0 + + + + + gtk-cancel + True + True + True + True + True + + + False + True + 1 + + + + + gtk-save + True + True + True + True + True + + + False + True + 5 + + + + + False + True + 0 + + + + + + save_cancel + save_profile + diff --git a/plugins/adrv9002.c b/plugins/adrv9002.c index d95e9091..ba6d82bc 100644 --- a/plugins/adrv9002.c +++ b/plugins/adrv9002.c @@ -21,6 +21,273 @@ #include "../iio_utils.h" #include "../config.h" #include "dac_data_manager.h" +#include "../cJSON/cJSON.h" + +/*--------------------------------------------------------------------------- + * libadrv9002-iio structures + */ + +/** + * @struct rx_radio_channel_config + * @brief RX channel configuration + * + * A structure containing the configuration for a single RX channel + */ +typedef struct rx_radio_channel_config +{ + /** Enable channel */ + bool enabled; + /** Enable high performance ADC, otherwise use low-power ADC */ + bool adc_high_performance_mode; + /** Enable ADC frequency offset correction */ + bool frequency_offset_correction_enable; + /** Power mode of front-end analog filter Options are: + 0 - Low power + 1 - Medium power + 2 - High power + */ + uint8_t analog_filter_power_mode; + /** Use second order (Biquad) analog filter, otherwise first order TIA is used */ + bool analog_filter_biquad; + /** Front-end analog filter 1dB (Biquad) or 3 dB (TIA) bandwidth in Hz*/ + uint32_t analog_filter_bandwidth_hz; + /** Channel bandwidth of interest at ADC in Hz*/ + uint32_t channel_bandwidth_hz; + /** RX channel sample rate at digital interface */ + uint32_t sample_rate_hz; + /** Enable NCO to perform frequency translation */ + bool nco_enable; + /** NCO frequency in Hz */ + int32_t nco_frequency_hz; + /** RF port source used for channel Options are: + 0 - RX_A + 1 - RX_B + */ + uint8_t rf_port; + +} rx_radio_channel_config; + +#define CHANNEL_COUNT 2 + +/** + * @struct tx_radio_channel_config + * @brief TX channel configuration + * + * A structure containing the configuration for a single TX channel + */ +typedef struct tx_radio_channel_config +{ + /** Enable channel */ + bool enabled; + /** Data rate at digital interface in Hz */ + uint32_t sample_rate_hz; + /** Enable DAC frequency offset correction */ + bool frequency_offset_correction_enable; + /** Power mode of front-end analog filter Options are: + 0 - Low power + 1 - Medium power + 2 - High power + */ + uint8_t analog_filter_power_mode; + /** Channel bandwidth of interest at DAC in Hz*/ + uint32_t channel_bandwidth_hz; + /** Enable observation path */ + bool orx_enabled; + /** Set external loopback mode. Options are: + 0 - Disabled + 1 - Before PA + 2 - After PA + */ + uint8_t elb_type; +} tx_radio_channel_config; + +/** + * @struct radio_config + * @brief Device configuration + * + * A structure containing the configuration for the top-level device + */ +typedef struct radio_config +{ + /** SSI lanes to use Valid cases: + 1 (CMOS/LVDS) + 2 (LVDS) + 4 (CMOS) + */ + uint8_t ssi_lanes; + /** Use DDR mode at digital interface, false will use SDR */ + bool ddr; + /** Use short strobe mode at digital interface, false will use long strobe */ + bool short_strobe; + /** Use LVDS mode at digital interface, false will use CMOS*/ + bool lvds; + /** ADC clock rate mode select. Options are: + 1 = LOW + 2 = MEDIUM + 3 = HIGH + */ + uint8_t adc_rate_mode; + /** Use FDD duplex mode, false will use TDD */ + bool fdd; + /** Channel configurations for RX1 and RX2 */ + rx_radio_channel_config rx_config[2]; + /** Channel configurations for TX1 and TX2 */ + tx_radio_channel_config tx_config[2]; + +} radio_config; + +/** + * @struct clock_config + * @brief Clock configuration + * + * A structure containing the configuration for the device clock + */ +typedef struct clock_config +{ + /** Device clock frequency in kHz */ + uint32_t device_clock_frequency_khz; + bool device_clock_output_enable; + uint8_t device_clock_output_divider; + /** Enable high performance PLL mode, otherwise low-power mode is used*/ + bool clock_pll_high_performance_enable; + /** PLL power mode. Options: + 0 = low power + 1 = medium performance + 2 = high performance + */ + uint8_t clock_pll_power_mode; + /** Processor clock divider. Valid values are 1, 2, 4, 8, 16, 32, 64, 128, 256 + */ + uint8_t processor_clock_divider; +} clock_config; + +/** + * @struct adrv9002_config + * @brief Top-level configuration + * + * A structure containing the configuration for the top-level device + */ +typedef struct adrv9002_config +{ + radio_config radio_cfg; + clock_config clk_cfg; +} adrv9002_config; + +static adrv9002_config lte_defaults(void) +{ + radio_config radio_config; + radio_config.ssi_lanes = 2; + radio_config.ddr = true; // needs logic + radio_config.short_strobe = true; + radio_config.lvds = true; + radio_config.adc_rate_mode = 3; + radio_config.fdd = false; + + tx_radio_channel_config tx_config[2]; + int i; + for(i = 0; i < CHANNEL_COUNT; i++) { + tx_config[i].enabled = true; + tx_config[i].sample_rate_hz = 61440000; + tx_config[i].frequency_offset_correction_enable = false; + tx_config[i].analog_filter_power_mode = 2; + tx_config[i].channel_bandwidth_hz = 38000000; + tx_config[i].elb_type = 0; + tx_config[i].orx_enabled = false; + + radio_config.tx_config[i] = tx_config[i]; + } + + rx_radio_channel_config rx_config[2]; + for(i = 0; i < CHANNEL_COUNT; i++) { + rx_config[i].enabled = true; + rx_config[i].sample_rate_hz = 61440000; + rx_config[i].frequency_offset_correction_enable = false; + rx_config[i].analog_filter_power_mode = 2; + rx_config[i].channel_bandwidth_hz = 38000000; + rx_config[i].adc_high_performance_mode = true; + rx_config[i].analog_filter_biquad = false; // got from default cfg + rx_config[i].analog_filter_bandwidth_hz = 18000000; // got from default cfg + rx_config[i].nco_enable = false; + rx_config[i].nco_frequency_hz = 0; + rx_config[i].rf_port = 0; + + radio_config.rx_config[i] = rx_config[i]; + } + adrv9002_config cfg; + cfg.radio_cfg = radio_config; + + clock_config clock_config; + clock_config.device_clock_frequency_khz = 38400; + clock_config.device_clock_output_enable = true; + clock_config.device_clock_output_divider = 2; + clock_config.clock_pll_high_performance_enable = false; + clock_config.clock_pll_power_mode = 2; + clock_config.processor_clock_divider = 1; + + cfg.clk_cfg = clock_config; + return cfg; +} + +static adrv9002_config lte_lvs_3072_MHz_10(void) +{ + rx_radio_channel_config rx1; + rx1.enabled = 1; + rx1.adc_high_performance_mode = true; + rx1.frequency_offset_correction_enable = false; + rx1.analog_filter_power_mode = 2; // High power/performance + rx1.analog_filter_biquad = false; + rx1.channel_bandwidth_hz = 18000000; + rx1.sample_rate_hz = 30720000; + rx1.nco_enable = false; + rx1.nco_frequency_hz = 0; + rx1.rf_port = 0; // RX-A + rx1.analog_filter_bandwidth_hz = 0; // TODO: not used? + + // Copy rx1 to rx2 + rx_radio_channel_config rx2 = rx1; + rx2.rf_port = 0; // RX-B + + // TX side + tx_radio_channel_config tx1; + tx1.enabled = 1; + tx1.sample_rate_hz = 30720000; + tx1.frequency_offset_correction_enable = false; + tx1.analog_filter_power_mode = 2; // High power/performance + tx1.channel_bandwidth_hz = 18000000; + tx1.orx_enabled = true; + tx1.elb_type = 2; + + // Copy tx1 to tx2 + tx_radio_channel_config tx2 = tx1; + + radio_config r_cfg; + r_cfg.adc_rate_mode = 3; // High Performance + r_cfg.fdd = false; + r_cfg.lvds = true; + r_cfg.ssi_lanes = 2; + r_cfg.ddr = true; + r_cfg.adc_rate_mode = 3; // High Performance + r_cfg.short_strobe = true; + r_cfg.rx_config[0] = rx1; + r_cfg.rx_config[1] = rx2; + r_cfg.tx_config[0] = tx1; + r_cfg.tx_config[1] = tx2; + + clock_config clk_cfg; + clk_cfg.device_clock_frequency_khz = 38400; + clk_cfg.clock_pll_high_performance_enable = true; + clk_cfg.clock_pll_power_mode = 2; // High power/performance + clk_cfg.processor_clock_divider = 1; + clk_cfg.device_clock_output_divider = 0; // TODO: not used? + clk_cfg.device_clock_output_enable = 0; + + adrv9002_config adrv_cfg; + adrv_cfg.clk_cfg = clk_cfg; + adrv_cfg.radio_cfg = r_cfg; + + return adrv_cfg; +} +/*---------------------------------------------------------------------------*/ #ifndef ENOTSUPP #define ENOTSUPP 524 @@ -127,6 +394,8 @@ struct plugin_private { /* adc */ const char *adc_name[NUM_MAX_ADC]; int n_adcs; + /* profile generator */ + int current_preset; }; #define dialog_box_message(widget, title, level, msg) { \ @@ -927,6 +1196,1168 @@ static void load_profile(GtkFileChooser *chooser, gpointer data) gtk_file_chooser_set_filename(chooser, "(None)"); } +static void profile_gen_append_debug_info(gpointer data, char *string) +{ + struct plugin_private *priv = data; + GtkLabel *info_label; + char message[BUFSIZ]; + + info_label = GTK_LABEL(gtk_builder_get_object(priv->builder, "label_profile_debug")); + sprintf(message, "%s%s", gtk_label_get_text(info_label), g_locale_to_utf8(string, -1, NULL, NULL, NULL)); + gtk_label_set_text(info_label, message); +} + +static void profile_gen_set_debug_info(gpointer data, char *string) +{ + struct plugin_private *priv = data; + gtk_label_set_text(GTK_LABEL(gtk_builder_get_object(priv->builder, "label_profile_debug")), + g_locale_to_utf8(string, -1, NULL, NULL, NULL)); +} + +static void profile_gen_save_type_changed(GtkComboBoxText *self, struct plugin_private *priv) +{ + // 0 - stream image + // 1 - profile + bool save_type; + char filename[64]; + + save_type = atoi(gtk_combo_box_get_active_id(GTK_COMBO_BOX(self))); + sprintf(filename, "adrv9002%s", save_type ? ".json" : ".stream"); + GtkFileChooser *chooser = GTK_FILE_CHOOSER(gtk_builder_get_object(priv->builder, "save_panel")); + gtk_file_chooser_set_current_name(chooser, filename); +} + +static void profile_gen_save_dialog_show(GtkButton *self, gpointer data) +{ + struct plugin_private *priv = data; + GtkFileChooser *chooser; + + chooser = GTK_FILE_CHOOSER(gtk_builder_get_object(priv->builder, "save_panel")); + gtk_file_chooser_set_action(chooser, GTK_FILE_CHOOSER_ACTION_SAVE); + gtk_file_chooser_set_do_overwrite_confirmation(chooser, TRUE); + + gtk_file_chooser_set_current_folder(chooser, getenv("HOME")); + profile_gen_save_type_changed(GTK_COMBO_BOX_TEXT(gtk_builder_get_object(priv->builder, "cb_save_type")), priv); + + gtk_widget_show(GTK_WIDGET(chooser)); +} + +static void profile_gen_update_orx(GtkComboBox *self, struct plugin_private *priv) +{ + int chann; + char widget_str[25]; + bool tdd_en, tx_en; + + tdd_en = atoi(gtk_combo_box_get_active_id( + GTK_COMBO_BOX(gtk_builder_get_object(priv->builder, "cb_radio_duplex")))) == 0; + for(chann = 0; chann < CHANNEL_COUNT; chann++) { + sprintf(widget_str, "cb_tx_chan%d_en", chann + 1); + tx_en = gtk_toggle_button_get_active( + GTK_TOGGLE_BUTTON(gtk_builder_get_object(priv->builder, widget_str))); + + sprintf(widget_str, "frame_radio_orx%d", chann + 1); + gtk_widget_set_sensitive(GTK_WIDGET(gtk_builder_get_object(priv->builder, widget_str)), + tx_en && tdd_en); + + // deactivate if disabled + if(!(tx_en && tdd_en)) { + sprintf(widget_str, "cb_radio_orx%d_en", chann + 1); + gtk_toggle_button_set_active( + GTK_TOGGLE_BUTTON(gtk_builder_get_object(priv->builder, widget_str)), false); + } + } +} + +char *extract_value_between(const char *str, const char *begin, const char *end) +{ + // if end is empty it will stop at the end of str + const char *i1 = strstr(str, begin); + if(i1 != NULL) { + const size_t pl1 = strlen(begin); + const char *i2 = strcmp(end, "") != 0 ? strstr(i1 + pl1, end) : NULL; + const size_t mlen = strcmp(end, "") != 0 ? i2 - (i1 + pl1) : (int)(strlen(i1) - pl1); + char *ret = malloc(mlen + 1); + + if(ret) { + memcpy(ret, i1 + pl1, mlen); + ret[mlen] = '\0'; + return ret; + } + } + + return NULL; +} + +static int profile_gen_get_ssi_lanes_from_device(gpointer data, uint8_t *ssi_lanes) +{ + struct plugin_private *priv = data; + char message[BUFSIZ]; + char profile_config[512]; + int ret = 0; + + ret = iio_device_attr_read(priv->adrv9002, "profile_config", profile_config, sizeof(profile_config)); + if(ret < 0) { + sprintf(message, "\nFailed to get device attr read %s! error code: %d", "profile_config", ret); + goto iio_error; + } + + char *ssi_interface = extract_value_between(profile_config, "SSI interface:", ""); + if(ssi_interface == NULL) { + sprintf(message, "\nFailed to get SSI interface!"); + ret = -ENODEV; + goto iio_error; + } + if(strstr(ssi_interface, "CMOS/LVDS") != NULL) { + *ssi_lanes = 1; + } else if(strstr(ssi_interface, "LVDS") != NULL) { + *ssi_lanes = 2; + } else if(strstr(ssi_interface, "CMOS") != NULL) { + *ssi_lanes = 4; + } else { + sprintf(message, "\nFailed to get SSI interface! got '%s' instead", ssi_interface); + ret = -EINVAL; + goto iio_error; + } + free(ssi_interface); + +iio_error: + profile_gen_set_debug_info(data, message); + + return ret; +} + +static int profile_gen_config_get_from_device(struct adrv9002_config *cfg, gpointer data) +{ + struct adrv9002_config default_cfg = lte_lvs_3072_MHz_10(); + struct plugin_private *priv = data; + int ret = 0; + char message[BUFSIZ]; + + radio_config radio_config; + char buf[1024]; + char profile_config[512]; + ret = iio_device_attr_read(priv->adrv9002, "profile_config", profile_config, sizeof(profile_config)); + if(ret < 0) { + sprintf(message, "\nFailed to get device attr read %s! error code: %d", "profile_config", ret); + goto iio_error; + } + + // radio_config.ssi_lanes + profile_gen_get_ssi_lanes_from_device(data, &radio_config.ssi_lanes); + + // radio_config.ddr + radio_config.ddr = default_cfg.radio_cfg.ddr; // TODO + + // radio_config.short_strobe + radio_config.short_strobe = default_cfg.radio_cfg.short_strobe; // TODO + + // radio_config.lvds + radio_config.lvds = radio_config.ssi_lanes == 2; + + // radio_config.adc_rate_mode + radio_config.adc_rate_mode = default_cfg.radio_cfg.adc_rate_mode; // TODO + + char *duplex_mode = extract_value_between(profile_config, "Duplex Mode:", "\n"); + if(duplex_mode == NULL) { + sprintf(message, "\nFailed to get Duplex Mode!"); + ret = -ENODEV; + goto iio_error; + } + if(strstr(duplex_mode, "FDD") != NULL) { + radio_config.fdd = true; + } else if(strstr(duplex_mode, "TDD") != NULL) { + radio_config.fdd = false; + } else { + sprintf(message, "\nFailed to get Duplex Mode! got '%s' instead", duplex_mode); + ret = -EINVAL; + goto iio_error; + } + free(duplex_mode); + + // radio_config.tx_config + tx_radio_channel_config tx_config[2]; + + int chann; + char chann_str[32]; + for(chann = 0; chann < CHANNEL_COUNT; chann++) { + sprintf(chann_str, "voltage%d", chann); + struct iio_channel *tx = iio_device_find_channel(priv->adrv9002, chann_str, true); + if(tx == NULL) { + sprintf(message, "\nFailed to find channel: %s!", chann_str); + ret = -EINVAL; + goto iio_error; + } + + // tx.enabled + ret = iio_channel_attr_read(tx, "en", buf, sizeof(buf)); + if(ret < 0) { + sprintf(message, "\nFailed to get channel: %s attr: %s! error code: %d", chann_str, "en", + ret); + goto iio_error; + } + tx_config[chann].enabled = atoi(buf); + + // tx.sample_rate_hz + ret = iio_channel_attr_read(tx, "sampling_frequency", buf, sizeof(buf)); + if(ret < 0) { + sprintf(message, "\nFailed to get channel: %s attr: %s! error code: %d", chann_str, + "sampling_frequency", ret); + goto iio_error; + } + tx_config[chann].sample_rate_hz = atoi(buf); + + // tx.frequency_offset_correction_enable + tx_config[chann].frequency_offset_correction_enable = + default_cfg.radio_cfg.tx_config[chann].frequency_offset_correction_enable; // TODO + + // tx.analog_filter_power_mode + tx_config[chann].analog_filter_power_mode = + default_cfg.radio_cfg.tx_config[chann].analog_filter_power_mode; // TODO + + // tx.channel_bandwidth_hz + ret = iio_channel_attr_read(tx, "rf_bandwidth", buf, sizeof(buf)); + if(ret < 0) { + sprintf(message, "\nFailed to get channel: %s attr: %s! error code: %d", chann_str, + "rf_bandwidth", ret); + goto iio_error; + } + tx_config[chann].channel_bandwidth_hz = atoi(buf); + + // tx.elb_type + tx_config[chann].elb_type = default_cfg.radio_cfg.tx_config[chann].elb_type; // TODO + + radio_config.tx_config[chann] = tx_config[chann]; + } + + // radio_config.rx_config + rx_radio_channel_config rx_config[2]; + + for(chann = 0; chann < CHANNEL_COUNT; chann++) { + sprintf(chann_str, "voltage%d", chann); + struct iio_channel *rx = iio_device_find_channel(priv->adrv9002, chann_str, false); + if(rx == NULL) { + sprintf(message, "\nFailed to find channel %s!", chann_str); + ret = -EINVAL; + goto iio_error; + } + + // rx.enabled + ret = iio_channel_attr_read(rx, "en", buf, sizeof(buf)); + if(ret < 0) { + sprintf(message, "\nFailed to get channel: %s attr: %s! error code: %d", chann_str, "en", + ret); + goto iio_error; + } + rx_config[chann].enabled = atoi(buf); + + // rx.sample_rate_hz + ret = iio_channel_attr_read(rx, "sampling_frequency", buf, sizeof(buf)); + if(ret < 0) { + sprintf(message, "\nFailed to get channel: %s attr: %s! error code: %d", chann_str, + "sampling_frequency", ret); + goto iio_error; + } + rx_config[chann].sample_rate_hz = atoi(buf); + + // rx.frequency_offset_correction_enable + rx_config[chann].frequency_offset_correction_enable = + default_cfg.radio_cfg.rx_config[chann].frequency_offset_correction_enable; // TODO + + // rx.analog_filter_power_mode + rx_config[chann].analog_filter_power_mode = + default_cfg.radio_cfg.rx_config[chann].analog_filter_power_mode; // TODO + + // rx.channel_bandwidth_hz + ret = iio_channel_attr_read(rx, "rf_bandwidth", buf, sizeof(buf)); + if(ret < 0) { + sprintf(message, "\nFailed to get channel: %s attr: %s! error code: %d", chann_str, + "rf_bandwidth", ret); + goto iio_error; + } + rx_config[chann].channel_bandwidth_hz = atoi(buf); + + // rx.adc_high_performance_mode + ret = iio_device_debug_attr_read(priv->adrv9002, chann == 0 ? "rx0_adc_type" : "rx1_adc_type", + buf, sizeof(buf)); + if(ret < 0) { + sprintf(message, "\nFailed to get channel: %s attr: %s! error code: %d", chann_str, + chann == 0 ? "rx0_adc_type" : "rx1_adc_type", ret); + goto iio_error; + } + rx_config[chann].adc_high_performance_mode = strstr(buf, "HP") != NULL; + + // rx.analog_filter_biquad + rx_config[chann].analog_filter_biquad = + default_cfg.radio_cfg.rx_config[chann].analog_filter_biquad; // TODO + + // rx.analog_filter_bandwidth_hz + rx_config[chann].analog_filter_bandwidth_hz = + default_cfg.radio_cfg.rx_config[chann].analog_filter_bandwidth_hz; // TODO + + // rx.nco_enable + rx_config[chann].nco_enable = default_cfg.radio_cfg.rx_config[chann].nco_enable; // TODO + + // rx.nco_frequency_hz + ret = iio_channel_attr_read(rx, "nco_frequency", buf, sizeof(buf)); + if(ret < 0) { + if(ret == -ENOTSUPP) { + rx_config[chann].nco_frequency_hz = 0; + } else { + sprintf(message, "\nFailed to get channel: %s attr: %s! error code: %d", chann_str, + "nco_frequency", ret); + goto iio_error; + } + } else { + rx_config[chann].nco_frequency_hz = atoi(buf); + } + + // rx.rf_port + rx_config[chann].rf_port = default_cfg.radio_cfg.rx_config[chann].rf_port; // TODO + + radio_config.rx_config[chann] = rx_config[chann]; + + // tx.orx_enabled + ret = iio_channel_attr_read(rx, "orx_en", buf, sizeof(buf)); + if(ret < 0) { + if(ret == -ENODEV) { + tx_config[chann].orx_enabled = + default_cfg.radio_cfg.tx_config[chann].orx_enabled; // Temporary fix + } else { + sprintf(message, "\nFailed to get channel: %s attr: %s! error code: %d", chann_str, + "orx_en", ret); + goto iio_error; + } + } else { + tx_config[chann].orx_enabled = atoi(buf); + } + } + + cfg->radio_cfg = radio_config; + + // clock_config + clock_config clock_config; + + // clock_config.device_clock_frequency_khz + char *device_clock = extract_value_between(profile_config, "Device clk(Hz): ", "\n"); + if(device_clock == NULL) { + sprintf(message, "\nFailed to get Device clk!"); + ret = -ENODEV; + goto iio_error; + } + clock_config.device_clock_frequency_khz = atoi(device_clock) / 1000; // convert Hz to kHz + free(device_clock); + + // clock_config.device_clock_output_divider + char *device_divider = extract_value_between(profile_config, "ARM Power Saving Clk Divider: ", "\n"); + if(device_divider == NULL) { + sprintf(message, "\nFailed to get Saving Clk Divider!"); + ret = -EINVAL; + goto iio_error; + } + clock_config.device_clock_output_divider = atoi(device_divider); + free(device_divider); + + // clock_config.clock_pll_high_performance_enable + clock_config.clock_pll_high_performance_enable = default_cfg.clk_cfg.clock_pll_high_performance_enable; // TODO + + // clock_config.clock_pll_power_mode + clock_config.clock_pll_power_mode = default_cfg.clk_cfg.clock_pll_power_mode; // TODO + + // clock_config.processor_clock_divider + clock_config.processor_clock_divider = default_cfg.clk_cfg.processor_clock_divider; // TODO + + cfg->clk_cfg = clock_config; + + return 0; +iio_error: + profile_gen_set_debug_info(data, message); + return ret == 0 ? -1 : ret; +} + +static void populate_combo_box(GtkComboBoxText *combo_box, char **list, guint len, bool has_entry, char *entry_default) +{ + gtk_combo_box_text_remove_all(combo_box); + + if(len) { + bool present = false; + guint i; + for(i = 0; i < len; ++i) { + gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo_box), list[i]); + if(strcmp(gtk_combo_box_text_get_active_text(combo_box), list[i]) == 0) { + present = true; + } + } + + // if current value is not found in list, set it to list[0] + if(!present) { + gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN(GTK_COMBO_BOX(combo_box)))), list[0]); + } + } + + gtk_widget_set_sensitive(GTK_WIDGET(gtk_bin_get_child(GTK_BIN(combo_box))), has_entry); + if(has_entry) { + gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN(combo_box))), entry_default); + } +} + +static void set_all_cb_to_same_text(char *cb_name_list[], guint len, char *text, gpointer data) +{ + struct plugin_private *priv = data; + + guint i; + for(i = 0; i < len; i++) { + GtkComboBoxText *obj = GTK_COMBO_BOX_TEXT(gtk_builder_get_object(priv->builder, cb_name_list[i])); + gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN(obj))), text); + } +} + +static int profile_gen_config_set_live_device(struct adrv9002_config *cfg, gpointer data, bool reset_preset) +{ + struct plugin_private *priv = data; + int ret = profile_gen_config_get_from_device(cfg, data); + + if(ret != 0) + return ret; + if(!reset_preset) + return 0; + + char widget_str[256]; + char value[256]; + int chann = 0; + char str_value[25]; + + // Radio + // ssi_lanes + switch(cfg->radio_cfg.ssi_lanes) { + case 1: + sprintf(str_value, "CMOS/LVDS"); + break; + + case 2: + sprintf(str_value, "LVDS"); + break; + case 4: + sprintf(str_value, "CMOS"); + break; + default: + profile_gen_append_debug_info(priv, "\nFailed to get ssi_lanes!"); + sprintf(str_value, "failed to read"); + break; + } + gtk_label_set_label(GTK_LABEL(gtk_builder_get_object(priv->builder, "label_radio_ssi")), str_value); + + // duplex mode + sprintf(str_value, "%d", cfg->radio_cfg.fdd); + gtk_combo_box_set_active_id(GTK_COMBO_BOX(gtk_builder_get_object(priv->builder, "cb_radio_duplex")), str_value); + + // RX and TX + size_t ch_type; + char *ch_types[2] = {"rx", "tx"}; + for(ch_type = 0; ch_type < ARRAY_SIZE(ch_types); ch_type++) { + for(chann = 0; chann < CHANNEL_COUNT; chann++) { + // channel_bandwidth_hz + sprintf(widget_str, "cb_%s_chan%d_bw", ch_types[ch_type], chann + 1); + sprintf(value, "%d", cfg->radio_cfg.rx_config[chann].channel_bandwidth_hz); + populate_combo_box(GTK_COMBO_BOX_TEXT(gtk_builder_get_object(priv->builder, widget_str)), NULL, 0, TRUE, + value); + + // sample_rate_hz + sprintf(widget_str, "cb_%s_chan%d_interface", ch_types[ch_type], chann + 1); + sprintf(value, "%d", cfg->radio_cfg.rx_config[chann].sample_rate_hz); + populate_combo_box(GTK_COMBO_BOX_TEXT(gtk_builder_get_object(priv->builder, widget_str)), NULL, 0, TRUE, + value); + + // enabled + sprintf(widget_str, "cb_%s_chan%d_en", ch_types[ch_type], chann + 1); + if(reset_preset) { + gtk_toggle_button_set_active( + GTK_TOGGLE_BUTTON(gtk_builder_get_object(priv->builder, widget_str)), + cfg->radio_cfg.rx_config[chann].enabled); + } + + // frequency_offset_correction_enable + sprintf(widget_str, "cb_%s_chan%d_correction", ch_types[ch_type], chann + 1); + if(reset_preset) { + gtk_toggle_button_set_active( + GTK_TOGGLE_BUTTON(gtk_builder_get_object(priv->builder, widget_str)), + cfg->radio_cfg.rx_config[chann].frequency_offset_correction_enable); + } + } + } + + // RX specific + // rf_port + for(chann = 0; chann < CHANNEL_COUNT; chann++) { + sprintf(widget_str, "cb_rx_chan%d_rf_port", chann + 1); + sprintf(value, "%d", cfg->radio_cfg.rx_config[chann].rf_port); + if(reset_preset) { + gtk_combo_box_set_active_id(GTK_COMBO_BOX(gtk_builder_get_object(priv->builder, widget_str)), + value); + } + } + + // update duplex state + profile_gen_update_orx(GTK_COMBO_BOX(gtk_builder_get_object(priv->builder, "cb_radio_duplex")), priv); + + return 0; +} + +static int get_index_of_string(char **list, guint len, char *string) +{ + guint i; + for(i = 0; i < len; i++) { + if(strcmp(list[i], string) == 0) { + return i; + } + } + return -1; +} + +static int profile_gen_config_set_LTE(struct adrv9002_config *cfg, gpointer data, bool reset_preset) +{ + struct plugin_private *priv = data; + char widget_str[256] = ""; + char value[256] = ""; + char str_value[25] = ""; + char *value_list[10]; + int value_count = 0; + guint chann; + struct adrv9002_config lte_default_config = lte_defaults(); + cfg = <e_default_config; + size_t ch_type; + const char *ch_types[2] = {"rx", "tx"}; + + // col 1 is sample rate and col 2 is bandwidth + guint freq_table_len = 6; + char *freq_table[2][6] = {{"1920000", "3840000", "7680000", "12360000", "30720000", "61440000"}, + {"1008000", "2700000", "4500000", "90000000", "18000000", "38000000"}}; + + // get current device SSI Interface + profile_gen_get_ssi_lanes_from_device(data, &cfg->radio_cfg.ssi_lanes); + + // overwrite with LTE default config + cfg = <e_default_config; + cfg->radio_cfg.ssi_lanes = cfg->radio_cfg.ssi_lanes; + + if(cfg->radio_cfg.ssi_lanes == 1) { // 1 (CMOS/LVDS) + value_count = 0; + } else if(cfg->radio_cfg.ssi_lanes == 2) { // 2 (LVDS) + int j; + value_count = 6; + for(j = 0; j < value_count; j++) { + value_list[j] = freq_table[0][j]; + } + } else if(cfg->radio_cfg.ssi_lanes == 4) { // 4 (CMOS) + value_list[0] = freq_table[0][0]; + value_count = 1; + } + + // sample_rate_hz + for(ch_type = 0; ch_type < ARRAY_SIZE(ch_types); ch_type++) { + for(chann = 0; chann < CHANNEL_COUNT; chann++) { + sprintf(widget_str, "cb_%s_chan%d_interface", ch_types[ch_type], chann + 1); + populate_combo_box(GTK_COMBO_BOX_TEXT(gtk_builder_get_object(priv->builder, widget_str)), + value_list, value_count, false, NULL); + if(reset_preset) { + // default value from config + sprintf(value, "%d", + ch_type ? cfg->radio_cfg.tx_config[chann].sample_rate_hz + : cfg->radio_cfg.rx_config[chann].sample_rate_hz); + gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN(GTK_COMBO_BOX( + gtk_builder_get_object(priv->builder, widget_str))))), + value); + } + } + } + + // channel_bandwidth_hz + for(ch_type = 0; ch_type < ARRAY_SIZE(ch_types); ch_type++) { + for(chann = 0; chann < CHANNEL_COUNT; chann++) { + sprintf(widget_str, "cb_%s_chan%d_interface", ch_types[ch_type], chann + 1); + char *current_sample_rate = gtk_combo_box_text_get_active_text( + GTK_COMBO_BOX_TEXT(gtk_builder_get_object(priv->builder, widget_str))); + sprintf(value, "%d", + atoi(freq_table[1][get_index_of_string(freq_table[0], freq_table_len, + current_sample_rate)])); + value_list[0] = value; + value_count = 1; + + sprintf(widget_str, "cb_%s_chan%d_bw", ch_types[ch_type], chann + 1); + populate_combo_box(GTK_COMBO_BOX_TEXT(gtk_builder_get_object(priv->builder, widget_str)), + value_list, value_count, false, NULL); + if(reset_preset) { + // default value from config + sprintf(value, "%d", + ch_type ? cfg->radio_cfg.tx_config[chann].channel_bandwidth_hz + : cfg->radio_cfg.rx_config[chann].channel_bandwidth_hz); + gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN(GTK_COMBO_BOX( + gtk_builder_get_object(priv->builder, widget_str))))), + value); + } + } + } + + // Radio + // ssi_lanes + switch(cfg->radio_cfg.ssi_lanes) { + case 1: + sprintf(str_value, "CMOS/LVDS"); + break; + + case 2: + sprintf(str_value, "LVDS"); + break; + case 4: + sprintf(str_value, "CMOS"); + break; + default: + profile_gen_set_debug_info(priv, "\nFailed to get ssi_lanes!"); + sprintf(str_value, "failed to read"); + break; + } + gtk_label_set_label(GTK_LABEL(gtk_builder_get_object(priv->builder, "label_radio_ssi")), str_value); + + // duplex mode + sprintf(str_value, "%d", cfg->radio_cfg.fdd); + if(reset_preset) { + gtk_combo_box_set_active_id(GTK_COMBO_BOX(gtk_builder_get_object(priv->builder, "cb_radio_duplex")), + str_value); + } + + // RX and TX + for(ch_type = 0; ch_type < ARRAY_SIZE(ch_types); ch_type++) { + for(chann = 0; chann < CHANNEL_COUNT; chann++) { + // enabled + sprintf(widget_str, "cb_%s_chan%d_en", ch_types[ch_type], chann + 1); + if(reset_preset) { + gtk_toggle_button_set_active( + GTK_TOGGLE_BUTTON(gtk_builder_get_object(priv->builder, widget_str)), + cfg->radio_cfg.rx_config[chann].enabled); + } + + // frequency_offset_correction_enable + sprintf(widget_str, "cb_%s_chan%d_correction", ch_types[ch_type], chann + 1); + if(reset_preset) { + gtk_toggle_button_set_active( + GTK_TOGGLE_BUTTON(gtk_builder_get_object(priv->builder, widget_str)), + cfg->radio_cfg.rx_config[chann].frequency_offset_correction_enable); + } + } + } + + // RX specific + // ft_port + for(chann = 0; chann < CHANNEL_COUNT; chann++) { + sprintf(widget_str, "cb_rx_chan%d_rf_port", chann + 1); + sprintf(value, "%d", cfg->radio_cfg.rx_config[chann].rf_port); + if(reset_preset) { + gtk_combo_box_set_active_id(GTK_COMBO_BOX(gtk_builder_get_object(priv->builder, widget_str)), + value); + } + } + + // update duplex state + profile_gen_update_orx(GTK_COMBO_BOX(gtk_builder_get_object(priv->builder, "cb_radio_duplex")), priv); + + return 0; +} + +static int profile_gen_config_init(struct adrv9002_config *cfg, gpointer data) +{ + struct plugin_private *priv = data; + int preset = + atoi(gtk_combo_box_get_active_id(GTK_COMBO_BOX(gtk_builder_get_object(priv->builder, "cb_preset")))); + bool reset_preset = priv->current_preset != preset; + priv->current_preset = preset; + + switch(preset) { + case 0: + return profile_gen_config_set_live_device(cfg, data, reset_preset); + + case 1: + return profile_gen_config_set_LTE(cfg, data, reset_preset); + default: + printf("\nInvalid preset!"); + return -1; + } +} + +static int profile_gen_config_get_default(struct adrv9002_config *cfg, gpointer data) +{ + struct plugin_private *priv = data; + int preset = + atoi(gtk_combo_box_get_active_id(GTK_COMBO_BOX(gtk_builder_get_object(priv->builder, "cb_preset")))); + + if(preset == 0) { + return profile_gen_config_get_from_device(cfg, data); + } else if(preset == 1) { + struct adrv9002_config tmp_cfg = lte_defaults(); + cfg = &tmp_cfg; + return 0; + } else { + printf("\nInvalid preset!"); + return -1; + } +} + +static int profile_gen_config_populate_from_ui(struct adrv9002_config *cfg, gpointer data) +{ + struct plugin_private *priv = data; + char widget_str[256]; + guint chann = 0; + + // Radio Config + sprintf(widget_str, "label_radio_ssi"); + char interface[25]; + sprintf(interface, "%s", gtk_label_get_label(GTK_LABEL(gtk_builder_get_object(priv->builder, widget_str)))); + + if(strcmp(interface, "CMOS/LVDS") == 0) + cfg->radio_cfg.ssi_lanes = 1; + else if(strcmp(interface, "LVDS") == 0) + cfg->radio_cfg.ssi_lanes = 2; + else if(strcmp(interface, "CMOS") == 0) + cfg->radio_cfg.ssi_lanes = 4; + else { + profile_gen_set_debug_info(priv, "\nFailed to get ssi_lanes!"); + return -1; + } + + sprintf(widget_str, "cb_radio_duplex"); + cfg->radio_cfg.fdd = + atoi(gtk_combo_box_get_active_id(GTK_COMBO_BOX(gtk_builder_get_object(priv->builder, widget_str)))); + + // RX + for(chann = 0; chann < CHANNEL_COUNT; chann++) { + sprintf(widget_str, "cb_rx_chan%d_bw", chann + 1); + cfg->radio_cfg.rx_config[chann].channel_bandwidth_hz = + (uint32_t)atoi(gtk_combo_box_text_get_active_text( + GTK_COMBO_BOX_TEXT(gtk_builder_get_object(priv->builder, widget_str)))); + + sprintf(widget_str, "cb_rx_chan%d_interface", chann + 1); + cfg->radio_cfg.rx_config[chann].sample_rate_hz = (uint32_t)atoi(gtk_combo_box_text_get_active_text( + GTK_COMBO_BOX_TEXT(gtk_builder_get_object(priv->builder, widget_str)))); + + sprintf(widget_str, "cb_rx_chan%d_correction", chann + 1); + cfg->radio_cfg.rx_config[chann].frequency_offset_correction_enable = gtk_toggle_button_get_active( + GTK_TOGGLE_BUTTON(gtk_builder_get_object(priv->builder, widget_str))); + + sprintf(widget_str, "cb_rx_chan%d_en", chann + 1); + cfg->radio_cfg.rx_config[chann].enabled = gtk_toggle_button_get_active( + GTK_TOGGLE_BUTTON(gtk_builder_get_object(priv->builder, widget_str))); + + sprintf(widget_str, "cb_rx_chan%d_rf_port", chann + 1); + cfg->radio_cfg.rx_config[chann].rf_port = (uint8_t)atoi( + gtk_combo_box_get_active_id(GTK_COMBO_BOX(gtk_builder_get_object(priv->builder, widget_str)))); + } + + // TX + for(chann = 0; chann < CHANNEL_COUNT; chann++) { + sprintf(widget_str, "cb_tx_chan%d_bw", chann + 1); + cfg->radio_cfg.tx_config[chann].channel_bandwidth_hz = + (uint32_t)atoi(gtk_combo_box_text_get_active_text( + GTK_COMBO_BOX_TEXT(gtk_builder_get_object(priv->builder, widget_str)))); + + sprintf(widget_str, "cb_tx_chan%d_interface", chann + 1); + cfg->radio_cfg.tx_config[chann].sample_rate_hz = (uint32_t)atoi(gtk_combo_box_text_get_active_text( + GTK_COMBO_BOX_TEXT(gtk_builder_get_object(priv->builder, widget_str)))); + + sprintf(widget_str, "cb_tx_chan%d_en", chann + 1); + cfg->radio_cfg.tx_config[chann].enabled = gtk_toggle_button_get_active( + GTK_TOGGLE_BUTTON(gtk_builder_get_object(priv->builder, widget_str))); + + sprintf(widget_str, "cb_radio_orx%d_en", chann + 1); + cfg->radio_cfg.tx_config[chann].orx_enabled = gtk_toggle_button_get_active( + GTK_TOGGLE_BUTTON(gtk_builder_get_object(priv->builder, widget_str))); + + sprintf(widget_str, "cb_tx_chan%d_correction", chann + 1); + cfg->radio_cfg.tx_config[chann].frequency_offset_correction_enable = gtk_toggle_button_get_active( + GTK_TOGGLE_BUTTON(gtk_builder_get_object(priv->builder, widget_str))); + } + + return 0; +} + +static int profile_gen_config_get_current(struct adrv9002_config *cfg, gpointer data) +{ + int ret = profile_gen_config_get_default(cfg, data); + if(ret < 0) + return ret; + + ret = profile_gen_config_populate_from_ui(cfg, data); + if(ret < 0) + return ret; + + return 0; +} + +static void profile_gen_preset_update(GtkComboBoxText *self, struct plugin_private *priv) +{ + if(gtk_combo_box_get_active(GTK_COMBO_BOX(self)) == -1) + return; + + profile_gen_config_init(NULL, priv); +} + +static void profile_gen_on_ch_interface_changed(GtkComboBoxText *self, struct plugin_private *priv) +{ + // if statement is only true if changed by user or cleared combobox, since comboboxes with entries are always on + // index -1 + if(gtk_combo_box_get_active(GTK_COMBO_BOX(self)) == -1) + return; + + char *cb_name_list[4] = {"cb_rx_chan1_interface", "cb_rx_chan2_interface", "cb_tx_chan1_interface", + "cb_tx_chan2_interface"}; + + set_all_cb_to_same_text(cb_name_list, ARRAY_SIZE(cb_name_list), gtk_combo_box_text_get_active_text(self), priv); + + profile_gen_preset_update(self, priv); +} + +static char *profile_gen_config_to_str(struct adrv9002_config *cfg) +{ + cJSON *config, *radio_cfg, *clk_cfg, *rx_config, *tx_config, *tmp_object; + int chann; + + config = cJSON_CreateObject(); + + // radio_cfg + radio_cfg = cJSON_CreateObject(); + cJSON_AddItemToObject(config, "radio_cfg", radio_cfg); + cJSON_AddNumberToObject(radio_cfg, "ssi_lanes", cfg->radio_cfg.ssi_lanes); + cJSON_AddNumberToObject(radio_cfg, "ddr", cfg->radio_cfg.ddr); + cJSON_AddNumberToObject(radio_cfg, "short_strobe", cfg->radio_cfg.short_strobe); + cJSON_AddNumberToObject(radio_cfg, "lvds", cfg->radio_cfg.lvds); + cJSON_AddNumberToObject(radio_cfg, "adc_rate_mode", cfg->radio_cfg.adc_rate_mode); + cJSON_AddNumberToObject(radio_cfg, "fdd", cfg->radio_cfg.fdd); + + // radio_cfg.rx_config + cJSON_AddItemToObject(radio_cfg, "rx_config", rx_config = cJSON_CreateArray()); + for(chann = 0; chann < CHANNEL_COUNT; chann++) { + cJSON_AddItemToArray(rx_config, tmp_object = cJSON_CreateObject()); + cJSON_AddNumberToObject(tmp_object, "enabled", cfg->radio_cfg.rx_config[chann].enabled); + cJSON_AddNumberToObject(tmp_object, "adc_high_performance_mode", + cfg->radio_cfg.rx_config[chann].adc_high_performance_mode); + cJSON_AddNumberToObject(tmp_object, "frequency_offset_correction_enable", + cfg->radio_cfg.rx_config[chann].frequency_offset_correction_enable); + cJSON_AddNumberToObject(tmp_object, "analog_filter_power_mode", + cfg->radio_cfg.rx_config[chann].analog_filter_power_mode); + cJSON_AddNumberToObject(tmp_object, "analog_filter_biquad", + cfg->radio_cfg.rx_config[chann].analog_filter_biquad); + cJSON_AddNumberToObject(tmp_object, "analog_filter_bandwidth_hz", + cfg->radio_cfg.rx_config[chann].analog_filter_bandwidth_hz); + cJSON_AddNumberToObject(tmp_object, "channel_bandwidth_hz", + cfg->radio_cfg.rx_config[chann].channel_bandwidth_hz); + cJSON_AddNumberToObject(tmp_object, "sample_rate_hz", cfg->radio_cfg.rx_config[chann].sample_rate_hz); + cJSON_AddNumberToObject(tmp_object, "nco_enable", cfg->radio_cfg.rx_config[chann].nco_enable); + cJSON_AddNumberToObject(tmp_object, "nco_frequency_hz", + cfg->radio_cfg.rx_config[chann].nco_frequency_hz); + cJSON_AddNumberToObject(tmp_object, "rf_port", cfg->radio_cfg.rx_config[chann].rf_port); + } + + // radio_cfg.tx_config + cJSON_AddItemToObject(radio_cfg, "tx_config", tx_config = cJSON_CreateArray()); + for(chann = 0; chann < CHANNEL_COUNT; chann++) { + cJSON_AddItemToArray(tx_config, tmp_object = cJSON_CreateObject()); + cJSON_AddNumberToObject(tmp_object, "enabled", cfg->radio_cfg.tx_config[chann].enabled); + cJSON_AddNumberToObject(tmp_object, "sample_rate_hz", cfg->radio_cfg.tx_config[chann].sample_rate_hz); + cJSON_AddNumberToObject(tmp_object, "frequency_offset_correction_enable", + cfg->radio_cfg.tx_config[chann].frequency_offset_correction_enable); + cJSON_AddNumberToObject(tmp_object, "analog_filter_power_mode", + cfg->radio_cfg.tx_config[chann].analog_filter_power_mode); + cJSON_AddNumberToObject(tmp_object, "channel_bandwidth_hz", + cfg->radio_cfg.tx_config[chann].channel_bandwidth_hz); + cJSON_AddNumberToObject(tmp_object, "orx_enabled", cfg->radio_cfg.tx_config[chann].orx_enabled); + cJSON_AddNumberToObject(tmp_object, "elb_type", cfg->radio_cfg.tx_config[chann].elb_type); + } + + // clk_cfg + clk_cfg = cJSON_CreateObject(); + cJSON_AddItemToObject(config, "clk_cfg", clk_cfg); + cJSON_AddNumberToObject(clk_cfg, "device_clock_frequency_khz", cfg->clk_cfg.device_clock_frequency_khz); + cJSON_AddNumberToObject(clk_cfg, "device_clock_output_enable", cfg->clk_cfg.device_clock_output_enable); + cJSON_AddNumberToObject(clk_cfg, "device_clock_output_divider", cfg->clk_cfg.device_clock_output_divider); + cJSON_AddNumberToObject(clk_cfg, "clock_pll_high_performance_enable", + cfg->clk_cfg.clock_pll_high_performance_enable); + cJSON_AddNumberToObject(clk_cfg, "clock_pll_power_mode", cfg->clk_cfg.clock_pll_power_mode); + cJSON_AddNumberToObject(clk_cfg, "processor_clock_divider", cfg->clk_cfg.processor_clock_divider); + + char *json_str = cJSON_Print(config); + cJSON_Delete(config); + + return json_str; +} + +static int profile_gen_write_config_to_file(gchar *filename, struct adrv9002_config *cfg) +{ + char *json_str = profile_gen_config_to_str(cfg); + FILE *file = fopen(filename, "w"); + + if(file == NULL) { + return 1; + } + + fputs(json_str, file); + fclose(file); + return 0; +} + +static char *profile_gen_cli_get_cmd(void) +{ + FILE *file; + char *command; + command = malloc(sizeof(char) * 24); + sprintf(command, "adrv9002-iio-cli"); + + // Open the command for reading + file = popen(command, "r"); + if(file == NULL) { + goto err; + } + fflush(file); + char out[BUFSIZ]; + int ret = fread(out, sizeof(char), sizeof(out), file); + pclose(file); + + if(ret == 0) { + goto err; + } + + return command; + +err: + return NULL; +} + +static int profile_gen_save_to_file(gchar *filename, struct adrv9002_config *cfg, gpointer data, bool file_type) +{ + // run profile gen cli command + FILE *file; + int ret = 0; + char command[BUFSIZ]; + char *config_filename = g_build_filename(getenv("TEMP") ?: P_tmpdir, "adrv9002_config.json", NULL); + + profile_gen_write_config_to_file(config_filename, cfg); + + if(file_type) { // profile + sprintf(command, "%s --config %s --profile %s", profile_gen_cli_get_cmd(), config_filename, filename); + } else { // stream image + sprintf(command, "%s --config %s --stream %s", profile_gen_cli_get_cmd(), config_filename, filename); + } + + // Open the command for reading + file = popen(command, "r"); + if(file == NULL) { + profile_gen_set_debug_info(data, "\nFailed to run command!"); + ret = 1; + goto err; + } + + // Read the output + char out[1024], message[BUFSIZ] = "\0"; + while(fgets(out, sizeof(out), file) != NULL) { + strcat(message, out); + } + remove(config_filename); + pclose(file); + + profile_gen_set_debug_info(data, message); + + return 0; +err: + return ret; +} + +static void profile_gen_save_dialog_response(GtkDialog *dialog, gint response_id, gpointer data) +{ + struct plugin_private *priv = data; + GtkFileChooser *chooser = GTK_FILE_CHOOSER(gtk_builder_get_object(priv->builder, "save_panel")); + struct adrv9002_config cfg = lte_lvs_3072_MHz_10(); + gchar *filename = gtk_file_chooser_get_filename(chooser); + + int ret = profile_gen_config_get_current(&cfg, data); + if(ret < 0 || response_id != GTK_RESPONSE_ACCEPT) { + gtk_widget_hide(GTK_WIDGET(chooser)); + return; + } + + // 0 if stream image + // 1 if profile + bool file_type = + atoi(gtk_combo_box_get_active_id(GTK_COMBO_BOX(gtk_builder_get_object(priv->builder, "cb_save_type")))); + profile_gen_save_to_file(filename, &cfg, data, file_type); + + gtk_widget_hide(GTK_WIDGET(chooser)); +} + +static char *profile_gen_cli_get_api(void) +{ + // run profile gen cli command + FILE *file; + char out[BUFSIZ], command[BUFSIZ]; + + sprintf(command, "%s --version", profile_gen_cli_get_cmd()); + + // open the command for reading + file = popen(command, "r"); + if(file == NULL) { + return NULL; + } + + // read the output + fflush(file); + int ret = fread(out, sizeof(char), sizeof(out), file); + pclose(file); + if(ret == 0) { + return NULL; + } + + // command output may contain characters after the API version + char *version = extract_value_between(out, "Profile generator API: ", "\n"); + if(version == NULL) { + return extract_value_between(out, "Profile generator API: ", ""); + } + + return version; +} + +static bool profile_gen_check_api(gpointer data) +{ + struct plugin_private *priv = data; + char version[256], message[BUFSIZ]; + + if(iio_device_debug_attr_read(priv->adrv9002, "api_version", version, sizeof(version)) < 0) { + sprintf(message, "\nCould not read API version!"); + profile_gen_set_debug_info(data, message); + goto err; + } + + char *supported_version = profile_gen_cli_get_api(); + if(supported_version == NULL) { + sprintf(message, "\nFailed to get profile generator API!"); + profile_gen_set_debug_info(data, message); + goto err; + } + + if(strcmp(version, supported_version) != 0) { + sprintf(message, "\nOnly API version %s is supported, the device uses %s!", supported_version, version); + profile_gen_set_debug_info(data, message); + free(supported_version); + goto err; + } + + free(supported_version); + return true; +err: + return false; +} + +static void profile_gen_load_config_to_device(GtkButton *self, gpointer data) +{ + struct plugin_private *priv = data; + struct adrv9002_config cfg = lte_lvs_3072_MHz_10(); + char message[BUFSIZ] = "\0", fail_message[BUFSIZ]; + int ret; + + // get config struct + ret = profile_gen_config_get_current(&cfg, data); + if(ret < 0) { + return; + } + + // check api version + ret = profile_gen_check_api(priv); + if(!ret) { + return; + } + + // run profile gen cli command + FILE *file; + char command[BUFSIZ]; + char *config_filename = g_build_filename(getenv("TEMP") ?: P_tmpdir, "adrv9002_config.json", NULL); + char *profile_filename = g_build_filename(getenv("TEMP") ?: P_tmpdir, "adrv9002_profile.json", NULL); + char *stream_filename = g_build_filename(getenv("TEMP") ?: P_tmpdir, "adrv9002_stream.json", NULL); + + profile_gen_write_config_to_file(config_filename, &cfg); + sprintf(command, "%s --config %s --profile %s --stream %s", profile_gen_cli_get_cmd(), config_filename, + profile_filename, stream_filename); + + // open the command for reading + file = popen(command, "r"); + if(file == NULL) { + profile_gen_set_debug_info(data, "\nFailed to run command!"); + return; + } + + // read the output + char out[BUFSIZ]; + while(fgets(out, sizeof(out), file) != NULL) { + strcat(message, out); + } + remove(config_filename); + pclose(file); + + if(ret == 0) { + strcat(message, "\nFailed to read the command output!"); + goto err; + } + + // load profile and stream to device + char *buf; + ssize_t size; + + // write profile + buf = read_file(profile_filename, &size); + remove(profile_filename); + if(!buf) { + strcat(message, "\nFailed to read the generated profile file!"); + goto err; + } + iio_context_set_timeout(priv->ctx, 30000); + ret = iio_device_attr_write_raw(priv->adrv9002, "profile_config", buf, size); + if(ret < 0) { + sprintf(fail_message, "\nFailed to write the generated profile to device with error code: %d", ret); + strcat(message, fail_message); + goto err; + } + free(buf); + + // write stream image + buf = read_file(stream_filename, &size); + remove(stream_filename); + if(!buf) { + strcat(message, "\nFailed to read the generated stream image file!"); + goto err; + } + iio_context_set_timeout(priv->ctx, 30000); + ret = iio_device_attr_write_raw(priv->adrv9002, "stream_config", buf, size); + if(ret < 0) { + sprintf(fail_message, "\nFailed to write the generated stream image to device with error code: %d", + ret); + strcat(message, fail_message); + goto err; + } + free(buf); + + iio_context_set_timeout(priv->ctx, 5000); + strcat(message, "\nSuccessfully loaded profile and stream image to device!"); +err: + profile_gen_set_debug_info(data, message); + return; +} + +static int profile_gen_ui_refresh(GtkButton *self, struct plugin_private *priv) +{ + struct adrv9002_config cfg = lte_lvs_3072_MHz_10(); + priv->current_preset = -1; + if(profile_gen_config_init(&cfg, priv) != 0) { + profile_gen_append_debug_info(priv, "\nFailed to initialize adrv9002 config!"); + return -1; + } + profile_gen_set_debug_info(priv, profile_gen_config_to_str(&cfg)); + + return 0; +} + static void adrv9002_combo_box_init(struct iio_widget *combo, const char *w_str, const char *attr, const char *attr_avail, struct plugin_private *priv, struct iio_channel *chann) @@ -1526,6 +2957,64 @@ static GtkWidget *adrv9002_init(struct osc_plugin *plugin, GtkWidget *notebook, GTK_FILE_CHOOSER(gtk_builder_get_object(priv->builder, "stream_config")), OSC_FILTER_FILE_PATH"/adrv9002"); + if (profile_gen_cli_get_cmd()) { + /* save profile or stream image*/ + g_builder_connect_signal(priv->builder, "btn_save", "clicked", + G_CALLBACK(profile_gen_save_dialog_show), priv); + + g_builder_connect_signal(priv->builder, "save_panel", "response", + G_CALLBACK(profile_gen_save_dialog_response), priv); + + g_builder_connect_signal(priv->builder, "cb_save_type", "changed", + G_CALLBACK(profile_gen_save_type_changed), priv); + + /* set profile*/ + g_builder_connect_signal(priv->builder, "btn_load_to_device", "clicked", + G_CALLBACK(profile_gen_load_config_to_device), priv); + + /* refresh profile */ + g_builder_connect_signal(priv->builder, "btn_refresh_profile", "clicked", + G_CALLBACK(profile_gen_ui_refresh), priv); + + /* init ui */ + profile_gen_ui_refresh(NULL, priv); + + /* update orx state on duplex mode changed */ + g_builder_connect_signal(priv->builder, "cb_radio_duplex", "changed", + G_CALLBACK(profile_gen_update_orx), priv); + + /* update preset signals ==================================================== */ + + char widget_str[25]; + for (i = 0; i < 4; i++) { + sprintf(widget_str, "cb_%s_chan%d_interface", i<2? "tx": "rx", i<2? i + 1: i - 1); + g_builder_connect_signal(priv->builder, widget_str, "changed", + G_CALLBACK(profile_gen_on_ch_interface_changed), priv); + } + + /* ========================================================================== */ + + /* update orx state on tx1 toggled */ + g_builder_connect_signal(priv->builder, "cb_tx_chan1_en", "toggled", + G_CALLBACK(profile_gen_update_orx), priv); + + /* update orx state on tx2 toggled */ + g_builder_connect_signal(priv->builder, "cb_tx_chan2_en", "toggled", + G_CALLBACK(profile_gen_update_orx), priv); + + /* refresh on preset changed */ + g_builder_connect_signal(priv->builder, "cb_preset", "changed", + G_CALLBACK(profile_gen_ui_refresh), priv); + + } else { + gtk_widget_set_sensitive(GTK_WIDGET(gtk_builder_get_object(priv->builder, "boxProfileGen")), false); + gtk_widget_set_sensitive(GTK_WIDGET(gtk_builder_get_object(priv->builder, "labelProfileGen")), false); + + gtk_label_set_text(GTK_LABEL(gtk_builder_get_object(priv->builder, "label_profile_gen_message")), + "Profile generation is only available with profile generator CLI installed!"); + gtk_widget_set_visible(GTK_WIDGET(gtk_builder_get_object(priv->builder, "label_profile_gen_message")), true); + } + /* init temperature label */ temp = iio_device_find_channel(priv->adrv9002, "temp0", false); if (!temp)