aboutsummaryrefslogtreecommitdiffstats
path: root/host/lib/usrp_clock
diff options
context:
space:
mode:
Diffstat (limited to 'host/lib/usrp_clock')
-rw-r--r--host/lib/usrp_clock/CMakeLists.txt8
-rw-r--r--host/lib/usrp_clock/octoclock/CMakeLists.txt12
-rw-r--r--host/lib/usrp_clock/octoclock/common.h35
-rw-r--r--host/lib/usrp_clock/octoclock/kk_ihex.h191
-rw-r--r--host/lib/usrp_clock/octoclock/kk_ihex_license.txt20
-rw-r--r--host/lib/usrp_clock/octoclock/kk_ihex_read.c261
-rw-r--r--host/lib/usrp_clock/octoclock/kk_ihex_read.h119
-rw-r--r--host/lib/usrp_clock/octoclock/octoclock_eeprom.cpp23
-rw-r--r--host/lib/usrp_clock/octoclock/octoclock_image_loader.cpp382
-rw-r--r--host/lib/usrp_clock/octoclock/octoclock_impl.cpp8
-rw-r--r--host/lib/usrp_clock/octoclock/octoclock_impl.hpp2
-rw-r--r--host/lib/usrp_clock/usrp_clock_c.cpp176
12 files changed, 1194 insertions, 43 deletions
diff --git a/host/lib/usrp_clock/CMakeLists.txt b/host/lib/usrp_clock/CMakeLists.txt
index 8a58aa9ac..ba0f5fe0a 100644
--- a/host/lib/usrp_clock/CMakeLists.txt
+++ b/host/lib/usrp_clock/CMakeLists.txt
@@ -1,5 +1,5 @@
#
-# Copyright 2011-2014 Ettus Research LLC
+# Copyright 2011-2015 Ettus Research LLC
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -23,4 +23,10 @@ LIBUHD_APPEND_SOURCES(
${CMAKE_CURRENT_SOURCE_DIR}/multi_usrp_clock.cpp
)
+IF(ENABLE_C_API)
+ LIBUHD_APPEND_SOURCES(
+ ${CMAKE_CURRENT_SOURCE_DIR}/usrp_clock_c.cpp
+ )
+ENDIF(ENABLE_C_API)
+
INCLUDE_SUBDIRECTORY(octoclock)
diff --git a/host/lib/usrp_clock/octoclock/CMakeLists.txt b/host/lib/usrp_clock/octoclock/CMakeLists.txt
index e363bb9da..a74cb034f 100644
--- a/host/lib/usrp_clock/octoclock/CMakeLists.txt
+++ b/host/lib/usrp_clock/octoclock/CMakeLists.txt
@@ -1,5 +1,5 @@
#
-# Copyright 2011-2014 Ettus Research LLC
+# Copyright 2013-2015 Ettus Research LLC
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -16,17 +16,17 @@
#
########################################################################
-# This file included, use CMake directory variables
-########################################################################
-
-########################################################################
# Conditionally configure the OctoClock support
########################################################################
-LIBUHD_REGISTER_COMPONENT("OctoClock" ENABLE_OCTOCLOCK ON "ENABLE_LIBUHD" OFF)
+LIBUHD_REGISTER_COMPONENT("OctoClock" ENABLE_OCTOCLOCK ON "ENABLE_LIBUHD" OFF OFF)
IF(ENABLE_OCTOCLOCK)
+ ADD_DEFINITIONS(-DIHEX_USE_STDBOOL)
+
LIBUHD_APPEND_SOURCES(
+ ${CMAKE_CURRENT_SOURCE_DIR}/kk_ihex_read.c
${CMAKE_CURRENT_SOURCE_DIR}/octoclock_eeprom.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/octoclock_image_loader.cpp
${CMAKE_CURRENT_SOURCE_DIR}/octoclock_impl.cpp
${CMAKE_CURRENT_SOURCE_DIR}/octoclock_uart.cpp
)
diff --git a/host/lib/usrp_clock/octoclock/common.h b/host/lib/usrp_clock/octoclock/common.h
index 96acbb30f..5861bc4b1 100644
--- a/host/lib/usrp_clock/octoclock/common.h
+++ b/host/lib/usrp_clock/octoclock/common.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2014 Ettus Research LLC
+ * Copyright 2014-2015 Ettus Research LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -42,11 +42,11 @@ extern "C" {
* only valid C code should go in this section.
*/
-//These values are placed in the octoclock_packet_t.proto_ver field
+// These values are placed in the octoclock_packet_t.proto_ver field
#define OCTOCLOCK_BOOTLOADER_PROTO_VER 1234
-#define OCTOCLOCK_FW_COMPAT_NUM 2
+#define OCTOCLOCK_FW_COMPAT_NUM 3
-//UDP ports assigned for different tasks
+// UDP ports assigned for different tasks
#define OCTOCLOCK_UDP_CTRL_PORT 50000
#define OCTOCLOCK_UDP_GPSDO_PORT 50001
#define OCTOCLOCK_UDP_FW_PORT 50002
@@ -98,11 +98,21 @@ typedef enum {
} ref_t;
typedef enum {
- UP,
- DOWN
+ PREFER_INTERNAL,
+ PREFER_EXTERNAL
} switch_pos_t;
+/*
+ * Some versions of AVR-GCC ignore #pragma pack, so
+ * if AVR-GCC is being used, use __attribute__
+ * instead.
+ */
+#ifdef AVR
+#define __AVR_ALIGNED__ __attribute__((aligned(1)))
+#else
+#define __AVR_ALIGNED__
#pragma pack(push,1)
+#endif
// Structure of values in EEPROM, starting in location 0
typedef struct {
@@ -113,34 +123,37 @@ typedef struct {
uint8_t serial[10];
uint8_t name[10];
uint8_t revision;
-} octoclock_fw_eeprom_t;
+} octoclock_fw_eeprom_t __AVR_ALIGNED__;
typedef struct {
uint8_t external_detected;
uint8_t gps_detected;
uint8_t which_ref;
uint8_t switch_pos;
-} octoclock_state_t;
+} octoclock_state_t __AVR_ALIGNED__;
typedef struct {
uint8_t num_wraps;
uint8_t pos;
-} gpsdo_cache_state_t;
+} gpsdo_cache_state_t __AVR_ALIGNED__;
typedef struct {
uint32_t proto_ver;
uint32_t sequence;
uint8_t code;
union {
- uint16_t len;
+ uint16_t crc;
gpsdo_cache_state_t state;
uint16_t poolsize;
uint16_t addr;
};
uint8_t data[256];
-} octoclock_packet_t;
+ uint16_t len;
+} octoclock_packet_t __AVR_ALIGNED__;
+#ifndef AVR
#pragma pack(pop)
+#endif
#ifdef __cplusplus
}
diff --git a/host/lib/usrp_clock/octoclock/kk_ihex.h b/host/lib/usrp_clock/octoclock/kk_ihex.h
new file mode 100644
index 000000000..20eba43cc
--- /dev/null
+++ b/host/lib/usrp_clock/octoclock/kk_ihex.h
@@ -0,0 +1,191 @@
+/*
+ * kk_ihex.h: A simple library for reading and writing the Intel HEX
+ * or IHEX format. Intended mainly for embedded systems, and thus
+ * somewhat optimised for size at the expense of error handling and
+ * generality.
+ *
+ * USAGE
+ * -----
+ *
+ * The library has been split into read and write parts, which use a
+ * common data structure (`struct ihex_state`), but each can be used
+ * independently. Include the header `kk_ihex_read.h` for reading, and/or
+ * the header `kk_ihex_write.h` for writing (and link with their respective
+ * object files). Both can be used simultaneously - this header defines
+ * the shared data structures and definitions.
+ *
+ *
+ * READING INTEL HEX DATA
+ * ----------------------
+ *
+ * To read data in the Intel HEX format, you must perform the actual reading
+ * of bytes using other means (e.g., stdio). The bytes read must then be
+ * passed to `ihex_read_byte` and/or `ihex_read_bytes`. The reading functions
+ * will then call `ihex_data_read`, at which stage the `struct ihex_state`
+ * structure will contain the data along with its address. See the header
+ * `kk_ihex_read.h` for details and example implementation of `ihex_data_read`.
+ *
+ * The sequence to read data in IHEX format is:
+ * struct ihex_state ihex;
+ * ihex_begin_read(&ihex);
+ * ihex_read_bytes(&ihex, my_input_bytes, length_of_my_input_bytes);
+ * ihex_end_read(&ihex);
+ *
+ *
+ * WRITING BINARY DATA AS INTEL HEX
+ * --------------------------------
+ *
+ * In order to write out data, the `ihex_write_at_address` or
+ * `ihex_write_at_segment` functions are used to set the data location,
+ * and then the binary bytes are written with `ihex_write_byte` and/or
+ * `ihex_write_bytes`. The writing functions will then call the function
+ * `ihex_flush_buffer` whenever the internal write buffer needs to be
+ * cleared - it is up to the caller to provide an implementation of
+ * `ihex_flush_buffer` to do the actual writing. See the header
+ * `kk_ihex_write.h` for details and an example implementation.
+ *
+ * See the declaration further down for an example implementation.
+ *
+ * The sequence to write data in IHEX format is:
+ * struct ihex_state ihex;
+ * ihex_init(&ihex);
+ * ihex_write_at_address(&ihex, 0);
+ * ihex_write_bytes(&ihex, my_data, length_of_my_data);
+ * ihex_end_write(&ihex);
+ *
+ * For outputs larger than 64KiB, 32-bit linear addresses are output. Normally
+ * the initial linear extended address record of zero is NOT written - it can
+ * be forced by setting `ihex->flags |= IHEX_FLAG_ADDRESS_OVERFLOW` before
+ * writing the first byte.
+ *
+ * Gaps in the data may be created by calling `ihex_write_at_address` with the
+ * new starting address without calling `ihex_end_write` in between.
+ *
+ *
+ * The same `struct ihex_state` may be used either for reading or writing,
+ * but NOT both at the same time. Furthermore, a global output buffer is
+ * used for writing, i.e., multiple threads must not write simultaneously
+ * (but multiple writes may be interleaved).
+ *
+ *
+ * CONSERVING MEMORY
+ * -----------------
+ *
+ * For memory-critical use, you can save additional memory by defining
+ * `IHEX_LINE_MAX_LENGTH` as something less than 255. Note, however, that
+ * this limit affects both reading and writing, so the resulting library
+ * will be unable to read lines with more than this number of data bytes.
+ * That said, I haven't encountered any IHEX files with more than 32
+ * data bytes per line. For write only there is no reason to define the
+ * maximum as greater than the line length you'll actually be writing,
+ * e.g., 32 or 16.
+ *
+ * If the write functionality is only occasionally used, you can provide
+ * your own buffer for the duration by defining `IHEX_EXTERNAL_WRITE_BUFFER`
+ * and providing a `char *ihex_write_buffer` which points to valid storage
+ * for at least `IHEX_WRITE_BUFFER_LENGTH` characters from before the first
+ * call to any IHEX write function to until after the last.
+ *
+ * If you are doing both reading and writing, you can define the maximum
+ * output length separately as `IHEX_MAX_OUTPUT_LINE_LENGTH` - this will
+ * decrease the write buffer size, but `struct ihex_state` will still
+ * use the larger `IHEX_LINE_MAX_LENGTH` for its data storage.
+ *
+ * You can also save a few additional bytes by disabling support for
+ * segmented addresses, by defining `IHEX_DISABLE_SEGMENTS`. Both the
+ * read and write modules need to be build with the same option, as the
+ * resulting data structures will not be compatible otherwise. To be honest,
+ * this is a fairly pointless optimisation.
+ *
+ *
+ * Copyright (c) 2013-2015 Kimmo Kulovesi, http://arkku.com/
+ * Provided with absolutely no warranty, use at your own risk only.
+ * Use and distribute freely, mark modified copies as such.
+ */
+
+#ifndef KK_IHEX_H
+#define KK_IHEX_H
+
+#define KK_IHEX_VERSION "2015-08-10"
+
+#include <stdint.h>
+
+#ifdef IHEX_USE_STDBOOL
+#include <stdbool.h>
+typedef bool ihex_bool_t;
+#else
+typedef uint_fast8_t ihex_bool_t;
+#endif
+
+typedef uint_least32_t ihex_address_t;
+typedef uint_least16_t ihex_segment_t;
+typedef int ihex_count_t;
+
+// Maximum number of data bytes per line (applies to both reading and
+// writing!); specify 255 to support reading all possible lengths. Less
+// can be used to limit memory footprint on embedded systems, e.g.,
+// most programs with IHEX output use 32.
+#ifndef IHEX_LINE_MAX_LENGTH
+#define IHEX_LINE_MAX_LENGTH 255
+#endif
+
+enum ihex_flags {
+ IHEX_FLAG_ADDRESS_OVERFLOW = 0x80 // 16-bit address overflow
+};
+typedef uint8_t ihex_flags_t;
+
+typedef struct ihex_state {
+ ihex_address_t address;
+#ifndef IHEX_DISABLE_SEGMENTS
+ ihex_segment_t segment;
+#endif
+ ihex_flags_t flags;
+ uint8_t line_length;
+ uint8_t length;
+ uint8_t data[IHEX_LINE_MAX_LENGTH + 1];
+} kk_ihex_t;
+
+enum ihex_record_type {
+ IHEX_DATA_RECORD,
+ IHEX_END_OF_FILE_RECORD,
+ IHEX_EXTENDED_SEGMENT_ADDRESS_RECORD,
+ IHEX_START_SEGMENT_ADDRESS_RECORD,
+ IHEX_EXTENDED_LINEAR_ADDRESS_RECORD,
+ IHEX_START_LINEAR_ADDRESS_RECORD
+};
+typedef uint8_t ihex_record_type_t;
+
+#ifndef IHEX_DISABLE_SEGMENTS
+
+// Resolve segmented address (if any). It is the author's recommendation that
+// segmented addressing not be used (and indeed the write function of this
+// library uses linear 32-bit addressing unless manually overridden).
+//
+#define IHEX_LINEAR_ADDRESS(ihex) ((ihex)->address + (((ihex_address_t)((ihex)->segment)) << 4))
+//
+// Note that segmented addressing with the above macro is not strictly adherent
+// to the IHEX specification, which mandates that the lowest 16 bits of the
+// address and the index of the data byte must be added modulo 64K (i.e.,
+// at 16 bits precision with wraparound) and the segment address only added
+// afterwards.
+//
+// To implement fully correct segmented addressing, compute the address
+// of _each byte_ with its index in `data` as follows:
+//
+#define IHEX_BYTE_ADDRESS(ihex, byte_index) ((((ihex)->address + (byte_index)) & 0xFFFFU) + (((ihex_address_t)((ihex)->segment)) << 4))
+
+#else // IHEX_DISABLE_SEGMENTS:
+
+#define IHEX_LINEAR_ADDRESS(ihex) ((ihex)->address)
+#define IHEX_BYTE_ADDRESS(ihex, byte_index) ((ihex)->address + (byte_index))
+
+#endif
+
+// The newline string (appended to every output line, e.g., "\r\n")
+#ifndef IHEX_NEWLINE_STRING
+#define IHEX_NEWLINE_STRING "\n"
+#endif
+
+// See kk_ihex_read.h and kk_ihex_write.h for function declarations!
+
+#endif // !KK_IHEX_H
diff --git a/host/lib/usrp_clock/octoclock/kk_ihex_license.txt b/host/lib/usrp_clock/octoclock/kk_ihex_license.txt
new file mode 100644
index 000000000..530f413e3
--- /dev/null
+++ b/host/lib/usrp_clock/octoclock/kk_ihex_license.txt
@@ -0,0 +1,20 @@
+The MIT License (MIT)
+
+Copyright (c) 2013-2015 Kimmo Kulovesi
+
+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.
diff --git a/host/lib/usrp_clock/octoclock/kk_ihex_read.c b/host/lib/usrp_clock/octoclock/kk_ihex_read.c
new file mode 100644
index 000000000..964cdd165
--- /dev/null
+++ b/host/lib/usrp_clock/octoclock/kk_ihex_read.c
@@ -0,0 +1,261 @@
+/*
+ * kk_ihex_read.c: A simple library for reading the Intel HEX (IHEX) format.
+ *
+ * See the header `kk_ihex.h` for instructions.
+ *
+ * Copyright (c) 2013-2015 Kimmo Kulovesi, http://arkku.com/
+ * Provided with absolutely no warranty, use at your own risk only.
+ * Use and distribute freely, mark modified copies as such.
+ *
+ * Modifications Copyright (c) 2015 National Instruments Corp.
+ */
+
+#include "kk_ihex_read.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#define IHEX_START ':'
+
+#define AUTODETECT_ADDRESS (~0UL)
+
+#define ADDRESS_HIGH_MASK ((ihex_address_t) 0xFFFF0000U)
+
+enum ihex_read_state {
+ READ_WAIT_FOR_START = 0,
+ READ_COUNT_HIGH = 1,
+ READ_COUNT_LOW,
+ READ_ADDRESS_MSB_HIGH,
+ READ_ADDRESS_MSB_LOW,
+ READ_ADDRESS_LSB_HIGH,
+ READ_ADDRESS_LSB_LOW,
+ READ_RECORD_TYPE_HIGH,
+ READ_RECORD_TYPE_LOW,
+ READ_DATA_HIGH,
+ READ_DATA_LOW
+};
+
+#define IHEX_READ_RECORD_TYPE_MASK 0x07
+#define IHEX_READ_STATE_MASK 0x78
+#define IHEX_READ_STATE_OFFSET 3
+
+void
+ihex_begin_read (struct ihex_state * const ihex) {
+ ihex->address = 0;
+#ifndef IHEX_DISABLE_SEGMENTS
+ ihex->segment = 0;
+#endif
+ ihex->flags = 0;
+ ihex->line_length = 0;
+ ihex->length = 0;
+}
+
+void
+ihex_read_at_address (struct ihex_state * const ihex, ihex_address_t address) {
+ ihex_begin_read(ihex);
+ ihex->address = address;
+}
+
+#ifndef IHEX_DISABLE_SEGMENTS
+void
+ihex_read_at_segment (struct ihex_state * const ihex, ihex_segment_t segment) {
+ ihex_begin_read(ihex);
+ ihex->segment = segment;
+}
+#endif
+
+void
+ihex_end_read (struct ihex_state * const ihex, FILE* outfile) {
+ uint_fast8_t type = ihex->flags & IHEX_READ_RECORD_TYPE_MASK;
+ uint_fast8_t sum;
+ if ((sum = ihex->length) == 0 && type == IHEX_DATA_RECORD) {
+ return;
+ }
+ {
+ // compute and validate checksum
+ const uint8_t * const eptr = ihex->data + sum;
+ const uint8_t *r = ihex->data;
+ sum += type + (ihex->address & 0xFFU) + ((ihex->address >> 8) & 0xFFU);
+ while (r != eptr) {
+ sum += *r++;
+ }
+ sum = (~sum + 1U) ^ *eptr; // *eptr is the received checksum
+ }
+ if (ihex_data_read(ihex, type, sum, outfile)) {
+ if (type == IHEX_EXTENDED_LINEAR_ADDRESS_RECORD) {
+ ihex->address &= 0xFFFFU;
+ ihex->address |= (((ihex_address_t) ihex->data[0]) << 24) |
+ (((ihex_address_t) ihex->data[1]) << 16);
+#ifndef IHEX_DISABLE_SEGMENTS
+ } else if (type == IHEX_EXTENDED_SEGMENT_ADDRESS_RECORD) {
+ ihex->segment = (ihex_segment_t) ((ihex->data[0] << 8) | ihex->data[1]);
+#endif
+ }
+ }
+ ihex->length = 0;
+ ihex->flags = 0;
+}
+
+void
+ihex_read_byte (struct ihex_state * const ihex, const char byte, FILE* outfile) {
+ uint_fast8_t b = (uint_fast8_t) byte;
+ uint_fast8_t len = ihex->length;
+ uint_fast8_t state = (ihex->flags & IHEX_READ_STATE_MASK);
+ ihex->flags ^= state; // turn off the old state
+ state >>= IHEX_READ_STATE_OFFSET;
+
+ if (b >= '0' && b <= '9') {
+ b -= '0';
+ } else if (b >= 'A' && b <= 'F') {
+ b -= 'A' - 10;
+ } else if (b >= 'a' && b <= 'f') {
+ b -= 'a' - 10;
+ } else if (b == IHEX_START) {
+ // sync to a new record at any state
+ state = READ_COUNT_HIGH;
+ goto end_read;
+ } else {
+ // ignore unknown characters (e.g., extra whitespace)
+ goto save_read_state;
+ }
+
+ if (!(++state & 1)) {
+ // high nybble, store temporarily at end of data:
+ b <<= 4;
+ ihex->data[len] = b;
+ } else {
+ // low nybble, combine with stored high nybble:
+ b = (ihex->data[len] |= b);
+ switch (state >> 1) {
+ default:
+ // remain in initial state while waiting for :
+ return;
+ case (READ_COUNT_LOW >> 1):
+ // data length
+ ihex->line_length = b;
+#if IHEX_LINE_MAX_LENGTH < 255
+ if (b > IHEX_LINE_MAX_LENGTH) {
+ ihex_end_read(ihex);
+ return;
+ }
+#endif
+ break;
+ case (READ_ADDRESS_MSB_LOW >> 1):
+ // high byte of 16-bit address
+ ihex->address &= ADDRESS_HIGH_MASK; // clear the 16-bit address
+ ihex->address |= ((ihex_address_t) b) << 8U;
+ break;
+ case (READ_ADDRESS_LSB_LOW >> 1):
+ // low byte of 16-bit address
+ ihex->address |= (ihex_address_t) b;
+ break;
+ case (READ_RECORD_TYPE_LOW >> 1):
+ // record type
+ if (b & ~IHEX_READ_RECORD_TYPE_MASK) {
+ // skip unknown record types silently
+ return;
+ }
+ ihex->flags = (ihex->flags & ~IHEX_READ_RECORD_TYPE_MASK) | b;
+ break;
+ case (READ_DATA_LOW >> 1):
+ if (len < ihex->line_length) {
+ // data byte
+ ihex->length = len + 1;
+ state = READ_DATA_HIGH;
+ goto save_read_state;
+ }
+ // end of line (last "data" byte is checksum)
+ state = READ_WAIT_FOR_START;
+ end_read:
+ ihex_end_read(ihex, outfile);
+ }
+ }
+save_read_state:
+ ihex->flags |= state << IHEX_READ_STATE_OFFSET;
+}
+
+void
+ihex_read_bytes (struct ihex_state * ihex,
+ const char * data,
+ ihex_count_t count,
+ FILE* outfile) {
+ while (count > 0) {
+ ihex_read_byte(ihex, *data++, outfile);
+ --count;
+ }
+}
+
+ihex_bool_t
+ihex_data_read (struct ihex_state *ihex,
+ ihex_record_type_t type,
+ ihex_bool_t error,
+ FILE* outfile) {
+ unsigned long line_number = 1L;
+ unsigned long file_position = 0L;
+ unsigned long address_offset = 0L;
+ bool debug_enabled = false;
+
+ if (error) {
+ (void) fprintf(stderr, "Checksum error on line %lu\n", line_number);
+ return false;
+ }
+ if ((error = (ihex->length < ihex->line_length))) {
+ (void) fprintf(stderr, "Line length error on line %lu\n", line_number);
+ return false;
+ }
+ if (!outfile) {
+ (void) fprintf(stderr, "Excess data after end of file record\n");
+ return false;
+ }
+ if (type == IHEX_DATA_RECORD) {
+ unsigned long address = (unsigned long) IHEX_LINEAR_ADDRESS(ihex);
+ if (address < address_offset) {
+ if (address_offset == AUTODETECT_ADDRESS) {
+ // autodetect initial address
+ address_offset = address;
+ if (debug_enabled) {
+ (void) fprintf(stderr, "Address offset: 0x%lx\n",
+ address_offset);
+ }
+ } else {
+ (void) fprintf(stderr, "Address underflow on line %lu\n",
+ line_number);
+ return false;
+ }
+ }
+ address -= address_offset;
+ if (address != file_position) {
+ if (debug_enabled) {
+ (void) fprintf(stderr,
+ "Seeking from 0x%lx to 0x%lx on line %lu\n",
+ file_position, address, line_number);
+ }
+ if (outfile == stdout || fseek(outfile, (long) address, SEEK_SET)) {
+ if (file_position < address) {
+ // "seek" forward in stdout by writing NUL bytes
+ do {
+ (void) fputc('\0', outfile);
+ } while (++file_position < address);
+ } else {
+ perror("fseek");
+ return false;
+ }
+ }
+ file_position = address;
+ }
+ if (!fwrite(ihex->data, ihex->length, 1, outfile)) {
+ perror("fwrite");
+ return false;
+ }
+ file_position += ihex->length;
+ } else if (type == IHEX_END_OF_FILE_RECORD) {
+ if (debug_enabled) {
+ (void) fprintf(stderr, "%lu bytes written\n", file_position);
+ }
+ if (outfile != stdout) {
+ (void) fclose(outfile);
+ }
+ outfile = NULL;
+ }
+ return true;
+}
diff --git a/host/lib/usrp_clock/octoclock/kk_ihex_read.h b/host/lib/usrp_clock/octoclock/kk_ihex_read.h
new file mode 100644
index 000000000..5e210fddb
--- /dev/null
+++ b/host/lib/usrp_clock/octoclock/kk_ihex_read.h
@@ -0,0 +1,119 @@
+/*
+ * kk_ihex_read.h: A simple library for reading Intel HEX data. See
+ * the accompanying kk_ihex_write.h for IHEX write support.
+ *
+ *
+ * READING INTEL HEX DATA
+ * ----------------------
+ *
+ * To read data in the Intel HEX format, you must perform the actual reading
+ * of bytes using other means (e.g., stdio). The bytes read must then be
+ * passed to `ihex_read_byte` and/or `ihex_read_bytes`. The reading functions
+ * will then call `ihex_data_read`, at which stage the `struct ihex_state`
+ * structure will contain the data along with its address. See below for
+ * details and example implementation of `ihex_data_read`.
+ *
+ * The sequence to read data in IHEX format is:
+ * struct ihex_state ihex;
+ * ihex_begin_read(&ihex);
+ * ihex_read_bytes(&ihex, my_input_bytes, length_of_my_input_bytes);
+ * ihex_end_read(&ihex);
+ *
+ *
+ * CONSERVING MEMORY
+ * -----------------
+ *
+ * For memory-critical use, you can save additional memory by defining
+ * `IHEX_LINE_MAX_LENGTH` as something less than 255. Note, however, that
+ * this limit affects both reading and writing, so the resulting library
+ * will be unable to read lines with more than this number of data bytes.
+ * That said, I haven't encountered any IHEX files with more than 32
+ * data bytes per line.
+ *
+ *
+ * Copyright (c) 2013-2015 Kimmo Kulovesi, http://arkku.com/
+ * Provided with absolutely no warranty, use at your own risk only.
+ * Use and distribute freely, mark modified copies as such.
+ *
+ * Modifications Copyright (c) 2015 National Instruments Corp.
+ */
+
+#ifndef KK_IHEX_READ_H
+#define KK_IHEX_READ_H
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "kk_ihex.h"
+
+#include <stdio.h>
+
+// Begin reading at address 0
+void ihex_begin_read(struct ihex_state * const ihex);
+
+// Begin reading at `address` (the lowest 16 bits of which will be ignored);
+// this is required only if the high bytes of the 32-bit starting address
+// are not specified in the input data and they are non-zero
+void ihex_read_at_address(struct ihex_state *ihex,
+ ihex_address_t address);
+
+// Read a single character
+void ihex_read_byte(struct ihex_state *ihex, char chr, FILE* outfile);
+
+// Read `count` bytes from `data`
+void ihex_read_bytes(struct ihex_state * ihex,
+ const char * data,
+ ihex_count_t count,
+ FILE* outfile);
+
+// End reading (may call `ihex_data_read` if there is data waiting)
+void ihex_end_read(struct ihex_state *ihex, FILE* outfile);
+
+// Called when a complete line has been read, the record type of which is
+// passed as `type`. The `ihex` structure will have its fields `data`,
+// `line_length`, `address`, and `segment` set appropriately. In case
+// of reading an `IHEX_EXTENDED_LINEAR_ADDRESS_RECORD` or an
+// `IHEX_EXTENDED_SEGMENT_ADDRESS_RECORD` the record's data is not
+// yet parsed - it will be parsed into the `address` or `segment` field
+// only if `ihex_data_read` returns `true`. This allows manual handling
+// of extended addresses by parsing the `ihex->data` bytes.
+//
+// Possible error cases include checksum mismatch (which is indicated
+// as an argument), and excessive line length (in case this has been
+// compiled with `IHEX_LINE_MAX_LENGTH` less than 255) which is indicated
+// by `line_length` greater than `length`. Unknown record types and
+// other erroneous data is usually silently ignored by this minimalistic
+// parser. (It is recommended to compute a hash over the complete data
+// once received and verify that against the source.)
+//
+// Example implementation:
+//
+// ihex_bool_t ihex_data_read(struct ihex_state *ihex,
+// ihex_record_type_t type,
+// ihex_bool_t error) {
+// error = error || (ihex->length < ihex->line_length);
+// if (type == IHEX_DATA_RECORD && !error) {
+// (void) fseek(outfile, IHEX_LINEAR_ADDRESS(ihex), SEEK_SET);
+// (void) fwrite(ihex->data, 1, ihex->length, outfile);
+// } else if (type == IHEX_END_OF_FILE_RECORD) {
+// (void) fclose(outfile);
+// }
+// return !error;
+// }
+//
+ihex_bool_t ihex_data_read(struct ihex_state *ihex,
+ ihex_record_type_t type,
+ ihex_bool_t checksum_mismatch,
+ FILE* outfile);
+
+// Begin reading at `segment`; this is required only if the initial segment
+// is not specified in the input data and it is non-zero.
+//
+#ifndef IHEX_DISABLE_SEGMENTS
+void ihex_read_at_segment(struct ihex_state *ihex, ihex_segment_t segment);
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+#endif // !KK_IHEX_READ_H
diff --git a/host/lib/usrp_clock/octoclock/octoclock_eeprom.cpp b/host/lib/usrp_clock/octoclock/octoclock_eeprom.cpp
index 93c317191..49d1a0442 100644
--- a/host/lib/usrp_clock/octoclock/octoclock_eeprom.cpp
+++ b/host/lib/usrp_clock/octoclock/octoclock_eeprom.cpp
@@ -1,5 +1,5 @@
//
-// Copyright 2014 Ettus Research LLC
+// Copyright 2014-2015 Ettus Research LLC
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
@@ -19,6 +19,7 @@
#include <uhd/usrp_clock/octoclock_eeprom.hpp>
#include <uhd/transport/udp_simple.hpp>
#include <uhd/usrp/mboard_eeprom.hpp>
+#include <uhd/types/byte_vector.hpp>
#include <uhd/types/mac_addr.hpp>
#include <uhd/utils/byteswap.hpp>
#include <boost/assign/list_of.hpp>
@@ -37,26 +38,6 @@ using namespace uhd::usrp_clock;
using namespace uhd::transport;
/***********************************************************************
- * Utility functions
- **********************************************************************/
-
-//! A wrapper around std::copy that takes ranges instead of iterators.
-template<typename RangeSrc, typename RangeDst> inline
-void byte_copy(const RangeSrc &src, RangeDst &dst){
- std::copy(boost::begin(src), boost::end(src), boost::begin(dst));
-}
-
-//! create a string from a byte vector, return empty if invalid ascii
-static const std::string bytes_to_string(const byte_vector_t &bytes){
- std::string out;
- BOOST_FOREACH(boost::uint8_t byte, bytes){
- if (byte < 32 or byte > 127) return out;
- out += byte;
- }
- return out;
-}
-
-/***********************************************************************
* Implementation
**********************************************************************/
void octoclock_eeprom_t::_load(){
diff --git a/host/lib/usrp_clock/octoclock/octoclock_image_loader.cpp b/host/lib/usrp_clock/octoclock/octoclock_image_loader.cpp
new file mode 100644
index 000000000..8b47da7e5
--- /dev/null
+++ b/host/lib/usrp_clock/octoclock/octoclock_image_loader.cpp
@@ -0,0 +1,382 @@
+//
+// Copyright 2015 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include "octoclock_impl.hpp"
+#include "common.h"
+#include "kk_ihex_read.h"
+
+#include <uhd/device.hpp>
+#include <uhd/image_loader.hpp>
+#include <uhd/transport/udp_simple.hpp>
+#include <uhd/types/device_addr.hpp>
+#include <uhd/types/time_spec.hpp>
+#include <uhd/utils/byteswap.hpp>
+#include <uhd/utils/paths.hpp>
+#include <uhd/utils/static.hpp>
+
+#include <boost/cstdint.hpp>
+#include <boost/filesystem.hpp>
+#include <boost/format.hpp>
+#include <boost/lexical_cast.hpp>
+#include <boost/thread.hpp>
+
+#include <cstdio>
+#include <cstring>
+#include <fstream>
+#include <iostream>
+#include <string>
+
+namespace fs = boost::filesystem;
+using namespace uhd;
+using namespace uhd::usrp_clock;
+using namespace uhd::transport;
+
+#define OCTOCLOCK_FIRMWARE_MAX_SIZE_BYTES (1024*120) // Last 8 MB are for bootloader
+#define OCTOCLOCK_BLOCK_SIZE 256
+
+/*
+ * OctoClock burn session
+ */
+typedef struct {
+ bool found;
+ uhd::device_addr_t dev_addr;
+ std::string given_filepath;
+ std::string actual_filepath; // If using a .hex, this is the converted .bin
+ bool from_hex;
+ boost::uint32_t size;
+ boost::uint16_t crc;
+ boost::uint16_t num_blocks;
+ udp_simple::sptr ctrl_xport;
+ udp_simple::sptr fw_xport;
+ boost::uint8_t data_in[udp_simple::mtu];
+} octoclock_session_t;
+
+static void octoclock_calculate_crc(octoclock_session_t &session){
+ std::ifstream ifile(session.actual_filepath.c_str());
+ boost::uint8_t temp_image[OCTOCLOCK_FIRMWARE_MAX_SIZE_BYTES];
+ ifile.read((char*)temp_image, session.size);
+
+ session.crc = 0xFFFF;
+ for(size_t i = 0; i < session.size; i++){
+ session.crc ^= temp_image[i];
+ for(boost::uint8_t j = 0; j < 8; ++j){
+ if(session.crc & 1) session.crc = (session.crc >> 1) ^ 0xA001;
+ else session.crc = (session.crc >> 1);
+ }
+ }
+
+ ifile.close();
+}
+
+static void octoclock_convert_ihex(octoclock_session_t &session){
+ struct ihex_state ihex;
+ ihex_count_t count;
+ char buf[256];
+ FILE* infile = fopen(session.given_filepath.c_str(), "r");
+ FILE* outfile = fopen(session.actual_filepath.c_str(), "w");
+ uint64_t line_number = 1;
+
+ ihex_begin_read(&ihex);
+ while(fgets(buf, 256, infile)){
+ count = ihex_count_t(strlen(buf));
+ ihex_read_bytes(&ihex, buf, count, outfile);
+ line_number += (count && buf[count - 1] == '\n');
+ }
+ ihex_end_read(&ihex, outfile); // Closes outfile
+
+ (void)fclose(infile);
+}
+
+static void octoclock_validate_firmware_image(octoclock_session_t &session){
+ if(not fs::exists(session.given_filepath)){
+ throw uhd::runtime_error(str(boost::format("Could not find image at path \"%s\"")
+ % session.given_filepath));
+ }
+
+ std::string extension = fs::extension(session.given_filepath);
+ if(extension == ".bin"){
+ session.actual_filepath = session.given_filepath;
+ session.from_hex = false;
+ }
+ else if(extension == ".hex"){
+ session.actual_filepath = fs::path(fs::path(uhd::get_tmp_path()) /
+ str(boost::format("octoclock_fw_%d.bin")
+ % time_spec_t::get_system_time().get_full_secs())
+ ).string();
+
+ octoclock_convert_ihex(session);
+ session.from_hex = true;
+ }
+ else throw uhd::runtime_error(str(boost::format("Invalid extension \"%s\". Extension must be .hex or .bin.")));
+
+ session.size = fs::file_size(session.actual_filepath);
+ if(session.size > OCTOCLOCK_FIRMWARE_MAX_SIZE_BYTES){
+ throw uhd::runtime_error(str(boost::format("The specified firmware image is too large: %d vs. %d")
+ % session.size % OCTOCLOCK_FIRMWARE_MAX_SIZE_BYTES));
+ }
+
+ session.num_blocks = (session.size % OCTOCLOCK_BLOCK_SIZE) ? ((session.size / OCTOCLOCK_BLOCK_SIZE) + 1)
+ : (session.size / OCTOCLOCK_BLOCK_SIZE);
+
+ octoclock_calculate_crc(session);
+}
+
+static void octoclock_setup_session(octoclock_session_t &session,
+ const uhd::device_addr_t &args,
+ const std::string &filepath){
+
+ // See if we can find an OctoClock with the given args
+ device_addrs_t devs = octoclock_find(args);
+ if(devs.size() == 0){
+ session.found = false;
+ return;
+ }
+ else if(devs.size() > 1){
+ std::string err_msg = "Could not resolve given args to a single OctoClock device.\n"
+ "Applicable devices:\n";
+
+ BOOST_FOREACH(const uhd::device_addr_t &dev, devs){
+ std::string name = (dev["type"] == "octoclock") ? str(boost::format("OctoClock r%d")
+ % dev.get("revision","4"))
+ : "OctoClock Bootloader";
+ err_msg += str(boost::format(" * %s (addr=%s)\n")
+ % name
+ % dev.get("addr"));
+ }
+
+ err_msg += "\nSpecify one of these devices with the given args to load an image onto it.";
+
+ throw uhd::runtime_error(err_msg);
+ }
+
+ session.dev_addr = devs[0];
+
+ // If no filepath is given, use the default
+ if(filepath == ""){
+ session.given_filepath = find_image_path(str(boost::format("octoclock_r%s_fw.hex")
+ % session.dev_addr.get("revision","4")
+ ));
+ }
+ else session.given_filepath = filepath;
+
+ octoclock_validate_firmware_image(session);
+
+ session.ctrl_xport = udp_simple::make_connected(session.dev_addr["addr"],
+ BOOST_STRINGIZE(OCTOCLOCK_UDP_CTRL_PORT));
+ session.fw_xport = udp_simple::make_connected(session.dev_addr["addr"],
+ BOOST_STRINGIZE(OCTOCLOCK_UDP_FW_PORT));
+}
+
+static void octoclock_reset_into_bootloader(octoclock_session_t &session){
+
+ // Already in bootloader
+ if(session.dev_addr["type"] == "octoclock-bootloader")
+ return;
+
+ octoclock_packet_t pkt_out;
+ pkt_out.sequence = uhd::htonx<boost::uint32_t>(std::rand());
+ const octoclock_packet_t* pkt_in = reinterpret_cast<const octoclock_packet_t*>(session.data_in);
+ size_t len;
+
+ std::cout << " -- Resetting into bootloader..." << std::flush;
+ UHD_OCTOCLOCK_SEND_AND_RECV(session.ctrl_xport, RESET_CMD, pkt_out, len, session.data_in);
+ if(UHD_OCTOCLOCK_PACKET_MATCHES(RESET_ACK, pkt_out, pkt_in, len)){
+
+ // Make sure this device is now in its bootloader
+ boost::this_thread::sleep(boost::posix_time::milliseconds(500));
+ uhd::device_addrs_t octoclocks = uhd::device::find(
+ uhd::device_addr_t(str(boost::format("addr=%s")
+ % session.dev_addr["addr"]
+ )));
+ if(octoclocks.size() == 0){
+ std::cout << "failed." << std::endl;
+ throw uhd::runtime_error("Failed to reset OctoClock.");
+ }
+ else if(octoclocks[0]["type"] != "octoclock-bootloader"){
+ std::cout << "failed." << std::endl;
+ throw uhd::runtime_error("Failed to reset OctoClock.");
+ }
+ else{
+ std::cout << "successful." << std::endl;
+ session.dev_addr = octoclocks[0];
+ }
+ }
+ else{
+ std::cout << "failed." << std::endl;
+ throw uhd::runtime_error("Failed to reset OctoClock.");
+ }
+}
+
+static void octoclock_burn(octoclock_session_t &session){
+
+ // Make sure we're in the bootloader for this
+ octoclock_reset_into_bootloader(session);
+
+ octoclock_packet_t pkt_out;
+ pkt_out.sequence = htonx<boost::uint32_t>(std::rand());
+ const octoclock_packet_t* pkt_in = reinterpret_cast<const octoclock_packet_t*>(session.data_in);
+
+ // Tell OctoClock to prepare for burn
+ pkt_out.len = htonx<boost::uint16_t>(session.size);
+ size_t len = 0;
+ std::cout << " -- Preparing OctoClock for firmware load..." << std::flush;
+ pkt_out.len = session.size;
+ pkt_out.crc = session.crc;
+ UHD_OCTOCLOCK_SEND_AND_RECV(session.fw_xport, PREPARE_FW_BURN_CMD, pkt_out, len, session.data_in);
+ if(UHD_OCTOCLOCK_PACKET_MATCHES(FW_BURN_READY_ACK, pkt_out, pkt_in, len)){
+ std::cout << "successful." << std::endl;
+ }
+ else{
+ std::cout << "failed." << std::endl;
+ if(session.from_hex){
+ fs::remove(session.actual_filepath);
+ }
+ throw uhd::runtime_error("Failed to prepare OctoClock for firmware load.");
+ }
+
+ // Start burning
+ std::ifstream image(session.actual_filepath.c_str(), std::ios::binary);
+ for(size_t i = 0; i < session.num_blocks; i++){
+ pkt_out.sequence++;
+ pkt_out.addr = i * OCTOCLOCK_BLOCK_SIZE;
+
+ std::cout << str(boost::format("\r -- Loading firmware: %d%% (%d/%d blocks)")
+ % int((double(i)/double(session.num_blocks))*100)
+ % i % session.num_blocks)
+ << std::flush;
+
+ memset(pkt_out.data, 0, OCTOCLOCK_BLOCK_SIZE);
+ image.read((char*)pkt_out.data, OCTOCLOCK_BLOCK_SIZE);
+ UHD_OCTOCLOCK_SEND_AND_RECV(session.fw_xport, FILE_TRANSFER_CMD, pkt_out, len, session.data_in);
+ if(not UHD_OCTOCLOCK_PACKET_MATCHES(FILE_TRANSFER_ACK, pkt_out, pkt_in, len)){
+ image.close();
+ std::cout << std::endl;
+ if(session.from_hex){
+ fs::remove(session.actual_filepath);
+ }
+ throw uhd::runtime_error("Failed to load firmware.");
+ }
+ }
+
+ std::cout << str(boost::format("\r -- Loading firmware: 100%% (%d/%d blocks)")
+ % session.num_blocks % session.num_blocks)
+ << std::endl;
+ image.close();
+}
+
+static void octoclock_verify(octoclock_session_t &session){
+
+ octoclock_packet_t pkt_out;
+ pkt_out.sequence = htonx<boost::uint32_t>(std::rand());
+ const octoclock_packet_t* pkt_in = reinterpret_cast<const octoclock_packet_t*>(session.data_in);
+ size_t len = 0;
+
+ std::ifstream image(session.actual_filepath.c_str(), std::ios::binary);
+ boost::uint8_t image_part[OCTOCLOCK_BLOCK_SIZE];
+ boost::uint16_t cmp_len = 0;
+ for(size_t i = 0; i < session.num_blocks; i++){
+ pkt_out.sequence++;
+ pkt_out.addr = i * OCTOCLOCK_BLOCK_SIZE;
+
+ std::cout << str(boost::format("\r -- Verifying firmware load: %d%% (%d/%d blocks)")
+ % int((double(i)/double(session.num_blocks))*100)
+ % i % session.num_blocks)
+ << std::flush;
+
+ memset(image_part, 0, OCTOCLOCK_BLOCK_SIZE);
+ image.read((char*)image_part, OCTOCLOCK_BLOCK_SIZE);
+ cmp_len = image.gcount();
+
+ UHD_OCTOCLOCK_SEND_AND_RECV(session.fw_xport, READ_FW_CMD, pkt_out, len, session.data_in);
+ if(UHD_OCTOCLOCK_PACKET_MATCHES(READ_FW_ACK, pkt_out, pkt_in, len)){
+ if(memcmp(pkt_in->data, image_part, cmp_len)){
+ std::cout << std::endl;
+ image.close();
+ if(session.from_hex){
+ fs::remove(session.actual_filepath);
+ }
+ throw uhd::runtime_error("Failed to verify OctoClock firmware.");
+ }
+ }
+ else{
+ std::cout << std::endl;
+ image.close();
+ if(session.from_hex){
+ fs::remove(session.actual_filepath);
+ }
+ throw uhd::runtime_error("Failed to verify OctoClock firmware.");
+ }
+ }
+
+ image.close();
+ if(session.from_hex){
+ fs::remove(session.actual_filepath);
+ }
+ std::cout << str(boost::format("\r -- Verifying firmware load: 100%% (%d/%d blocks)")
+ % session.num_blocks % session.num_blocks)
+ << std::endl;
+}
+
+static void octoclock_finalize(octoclock_session_t &session){
+
+ octoclock_packet_t pkt_out;
+ pkt_out.sequence = htonx<boost::uint32_t>(std::rand());
+ const octoclock_packet_t* pkt_in = reinterpret_cast<const octoclock_packet_t*>(session.data_in);
+ size_t len = 0;
+
+ std::cout << " -- Finalizing firmware load..." << std::flush;
+ UHD_OCTOCLOCK_SEND_AND_RECV(session.fw_xport, FINALIZE_BURNING_CMD, pkt_out, len, session.data_in);
+ if(UHD_OCTOCLOCK_PACKET_MATCHES(FINALIZE_BURNING_ACK, pkt_out, pkt_in, len)){
+ std::cout << "successful." << std::endl;
+ }
+ else{
+ std::cout << "failed." << std::endl;
+ throw uhd::runtime_error("Failed to finalize OctoClock firmware load.");
+ }
+}
+
+bool octoclock_image_loader(const image_loader::image_loader_args_t &image_loader_args){
+ octoclock_session_t session;
+ octoclock_setup_session(session,
+ image_loader_args.args,
+ image_loader_args.firmware_path
+ );
+ if(!session.found or !image_loader_args.load_firmware) return false;
+
+ std::cout << boost::format("Unit: OctoClock (%s)")
+ % session.dev_addr["addr"]
+ << std::endl;
+ std::cout << "Firmware: " << session.given_filepath << std::endl;
+
+ octoclock_burn(session);
+ octoclock_verify(session);
+ octoclock_finalize(session);
+
+ return true;
+}
+
+UHD_STATIC_BLOCK(register_octoclock_image_loader){
+ std::string recovery_instructions = "Aborting. Your OctoClock firmware is now corrupt. The bootloader\n"
+ "is functional, but the device will not have functional clock distribution.\n"
+ "Run this utility again to restore functionality or refer to:\n\n"
+ "http://files.ettus.com/manual/page_octoclock.html\n\n"
+ "for alternative setups.";
+
+ image_loader::register_image_loader("octoclock",
+ octoclock_image_loader,
+ recovery_instructions);
+}
diff --git a/host/lib/usrp_clock/octoclock/octoclock_impl.cpp b/host/lib/usrp_clock/octoclock/octoclock_impl.cpp
index b98d95725..ef1bc8ca0 100644
--- a/host/lib/usrp_clock/octoclock/octoclock_impl.cpp
+++ b/host/lib/usrp_clock/octoclock/octoclock_impl.cpp
@@ -357,14 +357,14 @@ void octoclock_impl::_get_state(const std::string &oc){
}
uhd::dict<ref_t, std::string> _ref_strings = boost::assign::map_list_of
- (NO_REF, "none")
+ (NO_REF, "none")
(INTERNAL, "internal")
(EXTERNAL, "external")
;
uhd::dict<switch_pos_t, std::string> _switch_pos_strings = boost::assign::map_list_of
- (UP, "Prefer internal")
- (DOWN, "Prefer external")
+ (PREFER_INTERNAL, "Prefer internal")
+ (PREFER_EXTERNAL, "Prefer external")
;
sensor_value_t octoclock_impl::_ext_ref_detected(const std::string &oc){
@@ -410,7 +410,7 @@ boost::uint32_t octoclock_impl::_get_time(const std::string &oc){
}
std::string octoclock_impl::_get_images_help_message(const std::string &addr){
- const std::string image_name = "octoclock_r4_fw.bin";
+ const std::string image_name = "octoclock_r4_fw.hex";
//Check to see if image is in default location
std::string image_location;
diff --git a/host/lib/usrp_clock/octoclock/octoclock_impl.hpp b/host/lib/usrp_clock/octoclock/octoclock_impl.hpp
index ab45cd5f0..453e75ec5 100644
--- a/host/lib/usrp_clock/octoclock/octoclock_impl.hpp
+++ b/host/lib/usrp_clock/octoclock/octoclock_impl.hpp
@@ -31,6 +31,8 @@
#include "common.h"
+uhd::device_addrs_t octoclock_find(const uhd::device_addr_t &hint);
+
/*!
* OctoClock implementation guts
*/
diff --git a/host/lib/usrp_clock/usrp_clock_c.cpp b/host/lib/usrp_clock/usrp_clock_c.cpp
new file mode 100644
index 000000000..220112f37
--- /dev/null
+++ b/host/lib/usrp_clock/usrp_clock_c.cpp
@@ -0,0 +1,176 @@
+/*
+ * Copyright 2015 Ettus Research LLC
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/* C-Interface for multi_usrp_clock */
+
+#include <uhd/utils/static.hpp>
+#include <uhd/usrp_clock/multi_usrp_clock.hpp>
+
+#include <uhd/usrp_clock/usrp_clock.h>
+
+#include <boost/foreach.hpp>
+#include <boost/thread/mutex.hpp>
+
+#include <string.h>
+#include <map>
+
+/****************************************************************************
+ * Registry / Pointer Management
+ ***************************************************************************/
+/* Public structs */
+struct uhd_usrp_clock {
+ size_t usrp_clock_index;
+ std::string last_error;
+};
+
+/* Not public: We use this for our internal registry */
+struct usrp_clock_ptr {
+ uhd::usrp_clock::multi_usrp_clock::sptr ptr;
+ static size_t usrp_clock_counter;
+};
+size_t usrp_clock_ptr::usrp_clock_counter = 0;
+typedef struct usrp_clock_ptr usrp_clock_ptr;
+/* Prefer map, because the list can be discontiguous */
+typedef std::map<size_t, usrp_clock_ptr> usrp_clock_ptrs;
+
+UHD_SINGLETON_FCN(usrp_clock_ptrs, get_usrp_clock_ptrs);
+/* Shortcut for accessing the underlying USRP clock sptr from a uhd_usrp_clock_handle* */
+#define USRP_CLOCK(h_ptr) (get_usrp_clock_ptrs()[h_ptr->usrp_clock_index].ptr)
+
+/****************************************************************************
+ * Generate / Destroy API calls
+ ***************************************************************************/
+static boost::mutex _usrp_clock_find_mutex;
+uhd_error uhd_usrp_clock_find(
+ const char* args,
+ uhd_string_vector_t *devices_out
+){
+ UHD_SAFE_C(
+ boost::mutex::scoped_lock lock(_usrp_clock_find_mutex);
+
+ uhd::device_addrs_t devs = uhd::device::find(std::string(args), uhd::device::CLOCK);
+ devices_out->string_vector_cpp.clear();
+ BOOST_FOREACH(const uhd::device_addr_t &dev, devs){
+ devices_out->string_vector_cpp.push_back(dev.to_string());
+ }
+ )
+}
+
+static boost::mutex _usrp_clock_make_mutex;
+uhd_error uhd_usrp_clock_make(
+ uhd_usrp_clock_handle *h,
+ const char *args
+){
+ UHD_SAFE_C(
+ boost::mutex::scoped_lock lock(_usrp_clock_make_mutex);
+
+ size_t usrp_clock_count = usrp_clock_ptr::usrp_clock_counter;
+ usrp_clock_ptr::usrp_clock_counter++;
+
+ // Initialize USRP Clock
+ uhd::device_addr_t device_addr(args);
+ usrp_clock_ptr P;
+ P.ptr = uhd::usrp_clock::multi_usrp_clock::make(device_addr);
+
+ // Dump into registry
+ get_usrp_clock_ptrs()[usrp_clock_count] = P;
+
+ // Update handle
+ (*h) = new uhd_usrp_clock;
+ (*h)->usrp_clock_index = usrp_clock_count;
+ )
+}
+
+static boost::mutex _usrp_clock_free_mutex;
+uhd_error uhd_usrp_clock_free(
+ uhd_usrp_clock_handle *h
+){
+ UHD_SAFE_C(
+ boost::mutex::scoped_lock lock(_usrp_clock_free_mutex);
+
+ if(!get_usrp_clock_ptrs().count((*h)->usrp_clock_index)){
+ return UHD_ERROR_INVALID_DEVICE;
+ }
+
+ get_usrp_clock_ptrs().erase((*h)->usrp_clock_index);
+ delete *h;
+ *h = NULL;
+ )
+}
+
+uhd_error uhd_usrp_clock_last_error(
+ uhd_usrp_clock_handle h,
+ char* error_out,
+ size_t strbuffer_len
+){
+ UHD_SAFE_C(
+ memset(error_out, '\0', strbuffer_len);
+ strncpy(error_out, h->last_error.c_str(), strbuffer_len);
+ )
+}
+
+uhd_error uhd_usrp_clock_get_pp_string(
+ uhd_usrp_clock_handle h,
+ char* pp_string_out,
+ size_t strbuffer_len
+){
+ UHD_SAFE_C_SAVE_ERROR(h,
+ memset(pp_string_out, '\0', strbuffer_len);
+ strncpy(pp_string_out, USRP_CLOCK(h)->get_pp_string().c_str(), strbuffer_len);
+ )
+}
+
+uhd_error uhd_usrp_clock_get_num_boards(
+ uhd_usrp_clock_handle h,
+ size_t *num_boards_out
+){
+ UHD_SAFE_C_SAVE_ERROR(h,
+ *num_boards_out = USRP_CLOCK(h)->get_num_boards();
+ )
+}
+
+uhd_error uhd_usrp_clock_get_time(
+ uhd_usrp_clock_handle h,
+ size_t board,
+ uint32_t *clock_time_out
+){
+ UHD_SAFE_C_SAVE_ERROR(h,
+ *clock_time_out = USRP_CLOCK(h)->get_time(board);
+ )
+}
+
+uhd_error uhd_usrp_clock_get_sensor(
+ uhd_usrp_clock_handle h,
+ const char* name,
+ size_t board,
+ uhd_sensor_value_handle *sensor_value_out
+){
+ UHD_SAFE_C_SAVE_ERROR(h,
+ delete (*sensor_value_out)->sensor_value_cpp;
+ (*sensor_value_out)->sensor_value_cpp = new uhd::sensor_value_t(USRP_CLOCK(h)->get_sensor(name, board));
+ )
+}
+
+uhd_error uhd_usrp_clock_get_sensor_names(
+ uhd_usrp_clock_handle h,
+ size_t board,
+ uhd_string_vector_handle *sensor_names_out
+){
+ UHD_SAFE_C_SAVE_ERROR(h,
+ (*sensor_names_out)->string_vector_cpp = USRP_CLOCK(h)->get_sensor_names(board);
+ )
+}