From ac1db2c4605427fa7e8929422c647004a4333e59 Mon Sep 17 00:00:00 2001
From: michael-west <michael.west@ettus.com>
Date: Fri, 20 Nov 2015 12:23:09 -0800
Subject: B2xx: Added B205mini support.

- Add support to b200_impl
- New INF file
- Removed references to old 'B205' name
---
 host/utils/b2xx_fx3_utils.cpp | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

(limited to 'host/utils')

diff --git a/host/utils/b2xx_fx3_utils.cpp b/host/utils/b2xx_fx3_utils.cpp
index a3eaf862a..a6896c868 100644
--- a/host/utils/b2xx_fx3_utils.cpp
+++ b/host/utils/b2xx_fx3_utils.cpp
@@ -52,7 +52,8 @@ const static vid_pid_t known_vid_pids[] = {
     {FX3_VID, FX3_DEFAULT_PID},
     {FX3_VID, FX3_REENUM_PID},
     {B200_VENDOR_ID, B200_PRODUCT_ID},
-    {B200_VENDOR_ID, B205_PRODUCT_ID},
+    {B200_VENDOR_ID, B200MINI_PRODUCT_ID},
+    {B200_VENDOR_ID, B205MINI_PRODUCT_ID},
     {B200_VENDOR_NI_ID, B200_PRODUCT_NI_ID},
     {B200_VENDOR_NI_ID, B210_PRODUCT_NI_ID}
 };
@@ -175,7 +176,7 @@ uhd::transport::usb_device_handle::sptr open_device(const boost::uint16_t vid, c
                 vp = known_vid_pid_vector[i];
                 handles = uhd::transport::usb_device_handle::get_device_list(vp.vid, vp.pid);
             }
-           
+
         }
 
         if (handles.size() > 0)
-- 
cgit v1.2.3


From 87860fc3226c8915cb45a4fc39d5e64bf667470d Mon Sep 17 00:00:00 2001
From: Martin Braun <martin.braun@ettus.com>
Date: Wed, 25 Nov 2015 15:47:37 -0800
Subject: octoclock: Now uses internal ihex parser

---
 host/lib/usrp_clock/octoclock/CMakeLists.txt       |   3 -
 host/lib/usrp_clock/octoclock/kk_ihex.h            | 191 ---------------
 host/lib/usrp_clock/octoclock/kk_ihex_license.txt  |  20 --
 host/lib/usrp_clock/octoclock/kk_ihex_read.c       | 261 ---------------------
 host/lib/usrp_clock/octoclock/kk_ihex_read.h       | 119 ----------
 .../octoclock/octoclock_image_loader.cpp           | 119 ++++------
 host/utils/CMakeLists.txt                          |   2 +-
 host/utils/octoclock_firmware_burner.cpp           |  22 +-
 8 files changed, 49 insertions(+), 688 deletions(-)
 delete mode 100644 host/lib/usrp_clock/octoclock/kk_ihex.h
 delete mode 100644 host/lib/usrp_clock/octoclock/kk_ihex_license.txt
 delete mode 100644 host/lib/usrp_clock/octoclock/kk_ihex_read.c
 delete mode 100644 host/lib/usrp_clock/octoclock/kk_ihex_read.h

(limited to 'host/utils')

diff --git a/host/lib/usrp_clock/octoclock/CMakeLists.txt b/host/lib/usrp_clock/octoclock/CMakeLists.txt
index a74cb034f..a54d27c52 100644
--- a/host/lib/usrp_clock/octoclock/CMakeLists.txt
+++ b/host/lib/usrp_clock/octoclock/CMakeLists.txt
@@ -21,10 +21,7 @@
 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
diff --git a/host/lib/usrp_clock/octoclock/kk_ihex.h b/host/lib/usrp_clock/octoclock/kk_ihex.h
deleted file mode 100644
index 20eba43cc..000000000
--- a/host/lib/usrp_clock/octoclock/kk_ihex.h
+++ /dev/null
@@ -1,191 +0,0 @@
-/*
- * 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
deleted file mode 100644
index 530f413e3..000000000
--- a/host/lib/usrp_clock/octoclock/kk_ihex_license.txt
+++ /dev/null
@@ -1,20 +0,0 @@
-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
deleted file mode 100644
index 964cdd165..000000000
--- a/host/lib/usrp_clock/octoclock/kk_ihex_read.c
+++ /dev/null
@@ -1,261 +0,0 @@
-/*
- * 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
deleted file mode 100644
index 303622b18..000000000
--- a/host/lib/usrp_clock/octoclock/kk_ihex_read.h
+++ /dev/null
@@ -1,119 +0,0 @@
-/*
- * 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 * const ihex,
-                          ihex_address_t address);
-
-// Read a single character
-void ihex_read_byte(struct ihex_state * const ihex, const char byte, 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 * const 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_image_loader.cpp b/host/lib/usrp_clock/octoclock/octoclock_image_loader.cpp
index 8b47da7e5..1d4699f54 100644
--- a/host/lib/usrp_clock/octoclock/octoclock_image_loader.cpp
+++ b/host/lib/usrp_clock/octoclock/octoclock_image_loader.cpp
@@ -17,8 +17,8 @@
 
 #include "octoclock_impl.hpp"
 #include "common.h"
-#include "kk_ihex_read.h"
 
+#include "../../utils/ihex.hpp"
 #include <uhd/device.hpp>
 #include <uhd/image_loader.hpp>
 #include <uhd/transport/udp_simple.hpp>
@@ -34,6 +34,8 @@
 #include <boost/lexical_cast.hpp>
 #include <boost/thread.hpp>
 
+#include <algorithm>
+#include <iterator>
 #include <cstdio>
 #include <cstring>
 #include <fstream>
@@ -54,83 +56,71 @@ using namespace uhd::transport;
 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;
+    std::string                 image_filepath;
     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];
+    std::vector<boost::uint8_t> image;
 } 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(size_t i = 0; i < session.image.size(); i++)
+    {
+        session.crc ^= session.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');
+static void octoclock_read_bin(octoclock_session_t &session)
+{
+    std::ifstream bin_file(session.image_filepath.c_str());
+    if (not bin_file.is_open()) {
+        throw uhd::io_error("Could not read image file.");
     }
-    ihex_end_read(&ihex, outfile); // Closes outfile
 
-    (void)fclose(infile);
+    size_t filesize = fs::file_size(session.image_filepath);
+    session.image.clear();
+    session.image.reserve(filesize);
+
+    std::copy(
+        std::istream_iterator<boost::uint8_t>(bin_file),
+        std::istream_iterator<boost::uint8_t>(),
+        std::back_inserter(session.image)
+    );
+
+    bin_file.close();
 }
 
 static void octoclock_validate_firmware_image(octoclock_session_t &session){
-    if(not fs::exists(session.given_filepath)){
+    if(not fs::exists(session.image_filepath)){
         throw uhd::runtime_error(str(boost::format("Could not find image at path \"%s\"")
-                                     % session.given_filepath));
+                                     % session.image_filepath));
     }
 
-    std::string extension = fs::extension(session.given_filepath);
+    std::string extension = fs::extension(session.image_filepath);
     if(extension == ".bin"){
-        session.actual_filepath = session.given_filepath;
-        session.from_hex = false;
+        octoclock_read_bin(session);
     }
     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;
+        ihex_reader hex_reader(session.image_filepath);
+        session.image = hex_reader.to_vector(OCTOCLOCK_FIRMWARE_MAX_SIZE_BYTES);
     }
     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){
+    if(session.image.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.image.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);
+    session.num_blocks = (session.image.size() % OCTOCLOCK_BLOCK_SIZE)
+                            ? ((session.image.size() / OCTOCLOCK_BLOCK_SIZE) + 1)
+                            : (session.image.size() / OCTOCLOCK_BLOCK_SIZE);
 
     octoclock_calculate_crc(session);
 }
@@ -164,14 +154,15 @@ static void octoclock_setup_session(octoclock_session_t &session,
     }
 
     session.dev_addr = devs[0];
+    session.found = true;
 
     // 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.image_filepath = find_image_path(str(boost::format("octoclock_r%s_fw.hex")
                                                      % session.dev_addr.get("revision","4")
                                                  ));
     }
-    else session.given_filepath = filepath;
+    else session.image_filepath = filepath;
 
     octoclock_validate_firmware_image(session);
 
@@ -231,10 +222,10 @@ static void octoclock_burn(octoclock_session_t &session){
     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);
+    pkt_out.len = htonx<boost::uint16_t>(session.image.size());
     size_t len = 0;
     std::cout << " -- Preparing OctoClock for firmware load..." << std::flush;
-    pkt_out.len = session.size;
+    pkt_out.len = session.image.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)){
@@ -242,14 +233,10 @@ static void octoclock_burn(octoclock_session_t &session){
     }
     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;
@@ -260,14 +247,10 @@ static void octoclock_burn(octoclock_session_t &session){
                   << std::flush;
 
         memset(pkt_out.data, 0, OCTOCLOCK_BLOCK_SIZE);
-        image.read((char*)pkt_out.data, OCTOCLOCK_BLOCK_SIZE);
+        memcpy((char*)pkt_out.data, &session.image[pkt_out.addr], 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.");
         }
     }
@@ -275,7 +258,6 @@ static void octoclock_burn(octoclock_session_t &session){
     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){
@@ -285,7 +267,6 @@ static void octoclock_verify(octoclock_session_t &session){
     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++){
@@ -298,34 +279,22 @@ static void octoclock_verify(octoclock_session_t &session){
                   << std::flush;
 
         memset(image_part, 0, OCTOCLOCK_BLOCK_SIZE);
-        image.read((char*)image_part, OCTOCLOCK_BLOCK_SIZE);
-        cmp_len = image.gcount();
+        memcpy((char*)image_part, &session.image[pkt_out.addr], OCTOCLOCK_BLOCK_SIZE);
+        cmp_len = std::min<size_t>(OCTOCLOCK_BLOCK_SIZE, session.image.size() - size_t(pkt_out.addr));
 
         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;
@@ -360,7 +329,7 @@ bool octoclock_image_loader(const image_loader::image_loader_args_t &image_loade
     std::cout << boost::format("Unit: OctoClock (%s)")
                  % session.dev_addr["addr"]
               << std::endl;
-    std::cout << "Firmware: " << session.given_filepath << std::endl;
+    std::cout << "Firmware: " << session.image_filepath << std::endl;
 
     octoclock_burn(session);
     octoclock_verify(session);
diff --git a/host/utils/CMakeLists.txt b/host/utils/CMakeLists.txt
index 4f56dad0d..8367c0184 100644
--- a/host/utils/CMakeLists.txt
+++ b/host/utils/CMakeLists.txt
@@ -84,7 +84,7 @@ IF(ENABLE_OCTOCLOCK)
 
     SET(octoclock_burner_sources
         octoclock_firmware_burner.cpp
-        ${CMAKE_SOURCE_DIR}/lib/usrp_clock/octoclock/kk_ihex_read.c
+        ${CMAKE_SOURCE_DIR}/lib/utils/ihex.cpp
     )
 
     ADD_DEFINITIONS(-DIHEX_USE_STDBOOL)
diff --git a/host/utils/octoclock_firmware_burner.cpp b/host/utils/octoclock_firmware_burner.cpp
index c338cd818..326985df5 100644
--- a/host/utils/octoclock_firmware_burner.cpp
+++ b/host/utils/octoclock_firmware_burner.cpp
@@ -42,7 +42,7 @@
 #include <uhd/utils/safe_main.hpp>
 
 #include "../lib/usrp_clock/octoclock/common.h"
-#include "kk_ihex_read.h"
+#include "../lib/utils/ihex.hpp"
 
 #define MAX_FIRMWARE_SIZE 1024*120
 #define BLOCK_SIZE 256
@@ -142,7 +142,7 @@ device_addrs_t bootloader_find(const std::string &ip_addr){
     device_addrs_t addrs;
 
     boost::system_time comm_timeout = boost::get_system_time() + boost::posix_time::milliseconds(3000);
-    
+
     while(boost::get_system_time() < comm_timeout){
         UHD_OCTOCLOCK_SEND_AND_RECV(udp_transport, OCTOCLOCK_QUERY_CMD, pkt_out, len, octoclock_data);
         if(UHD_OCTOCLOCK_PACKET_MATCHES(OCTOCLOCK_QUERY_ACK, pkt_out, pkt_in, len) and
@@ -292,22 +292,8 @@ void finalize(udp_simple::sptr udp_transport){
 }
 
 void octoclock_convert_ihex(const std::string &hex_path, const std::string &bin_path){
-    struct ihex_state ihex;
-    ihex_count_t count;
-    char buf[256];
-    FILE* infile = fopen(hex_path.c_str(), "r");
-    FILE* outfile = fopen(bin_path.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);
+    ihex_reader hex_reader(hex_path);
+    hex_reader.to_bin_file(bin_path);
 }
 
 int UHD_SAFE_MAIN(UHD_UNUSED(int argc), UHD_UNUSED(char *argv[])){
-- 
cgit v1.2.3


From 373fec2fb177c50b40733445633a10b01fe62cc5 Mon Sep 17 00:00:00 2001
From: Nicholas Corgan <nick.corgan@ettus.com>
Date: Mon, 14 Dec 2015 07:27:38 -0800
Subject: ihex: Windows fixes

---
 .../lib/usrp_clock/octoclock/octoclock_image_loader.cpp | 17 ++++++++---------
 host/lib/utils/ihex.cpp                                 |  4 ++--
 host/utils/CMakeLists.txt                               |  1 -
 3 files changed, 10 insertions(+), 12 deletions(-)

(limited to 'host/utils')

diff --git a/host/lib/usrp_clock/octoclock/octoclock_image_loader.cpp b/host/lib/usrp_clock/octoclock/octoclock_image_loader.cpp
index 1d4699f54..fdb254024 100644
--- a/host/lib/usrp_clock/octoclock/octoclock_image_loader.cpp
+++ b/host/lib/usrp_clock/octoclock/octoclock_image_loader.cpp
@@ -79,20 +79,18 @@ static void octoclock_calculate_crc(octoclock_session_t &session){
 
 static void octoclock_read_bin(octoclock_session_t &session)
 {
-    std::ifstream bin_file(session.image_filepath.c_str());
+    std::ifstream bin_file(session.image_filepath.c_str(), std::ios::in | std::ios::binary);
     if (not bin_file.is_open()) {
         throw uhd::io_error("Could not read image file.");
     }
 
     size_t filesize = fs::file_size(session.image_filepath);
     session.image.clear();
-    session.image.reserve(filesize);
-
-    std::copy(
-        std::istream_iterator<boost::uint8_t>(bin_file),
-        std::istream_iterator<boost::uint8_t>(),
-        std::back_inserter(session.image)
-    );
+    session.image.resize(filesize);
+    bin_file.read((char*)&session.image[0], filesize);
+    if(size_t(bin_file.gcount()) != filesize) {
+        throw uhd::io_error("Failed to read firmware image.");
+    }
 
     bin_file.close();
 }
@@ -111,7 +109,8 @@ static void octoclock_validate_firmware_image(octoclock_session_t &session){
         ihex_reader hex_reader(session.image_filepath);
         session.image = hex_reader.to_vector(OCTOCLOCK_FIRMWARE_MAX_SIZE_BYTES);
     }
-    else throw uhd::runtime_error(str(boost::format("Invalid extension \"%s\". Extension must be .hex or .bin.")));
+    else throw uhd::runtime_error(str(boost::format("Invalid extension \"%s\". Extension must be .hex or .bin.")
+                                      % extension));
 
     if(session.image.size() > OCTOCLOCK_FIRMWARE_MAX_SIZE_BYTES){
         throw uhd::runtime_error(str(boost::format("The specified firmware image is too large: %d vs. %d")
diff --git a/host/lib/utils/ihex.cpp b/host/lib/utils/ihex.cpp
index bb9b49b32..a29ac3e72 100644
--- a/host/lib/utils/ihex.cpp
+++ b/host/lib/utils/ihex.cpp
@@ -213,7 +213,7 @@ void ihex_reader::to_bin_file(const std::string &bin_filename)
 
 // We need a functor for the cast, a lambda would be perfect...
 int _vector_writer_callback(
-    std::vector<boost::uint8_t> vector,
+    std::vector<boost::uint8_t>& vector,
     unsigned char *buff,
     boost::uint16_t len
 ) {
@@ -229,7 +229,7 @@ std::vector<boost::uint8_t> ihex_reader::to_vector(const size_t size_estimate)
     std::vector<boost::uint8_t> buf;
     buf.reserve(size_estimate == 0 ? DEFAULT_SIZE_ESTIMATE : size_estimate);
 
-    this->read(boost::bind(&_vector_writer_callback, buf, _3, _4));
+    this->read(boost::bind(&_vector_writer_callback, boost::ref(buf), _3, _4));
 
     return buf;
 }
diff --git a/host/utils/CMakeLists.txt b/host/utils/CMakeLists.txt
index 8367c0184..28fecc895 100644
--- a/host/utils/CMakeLists.txt
+++ b/host/utils/CMakeLists.txt
@@ -87,7 +87,6 @@ IF(ENABLE_OCTOCLOCK)
         ${CMAKE_SOURCE_DIR}/lib/utils/ihex.cpp
     )
 
-    ADD_DEFINITIONS(-DIHEX_USE_STDBOOL)
     INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/lib/usrp_clock/octoclock)
     ADD_EXECUTABLE(octoclock_firmware_burner ${octoclock_burner_sources})
     TARGET_LINK_LIBRARIES(octoclock_firmware_burner uhd ${Boost_LIBRARIES})
-- 
cgit v1.2.3


From 29302223406db5f926e11c11c186425b31897bcd Mon Sep 17 00:00:00 2001
From: Nicholas Corgan <nick.corgan@ettus.com>
Date: Mon, 14 Dec 2015 08:01:40 -0800
Subject: Added B205mini VID/PID to udev file

---
 host/utils/uhd-usrp.rules | 1 +
 1 file changed, 1 insertion(+)

(limited to 'host/utils')

diff --git a/host/utils/uhd-usrp.rules b/host/utils/uhd-usrp.rules
index e10c86f13..76bccfefa 100644
--- a/host/utils/uhd-usrp.rules
+++ b/host/utils/uhd-usrp.rules
@@ -24,5 +24,6 @@ SUBSYSTEMS=="usb", ATTRS{idVendor}=="2500", ATTRS{idProduct}=="0002", MODE:="066
 #B200
 SUBSYSTEMS=="usb", ATTRS{idVendor}=="2500", ATTRS{idProduct}=="0020", MODE:="0666"
 SUBSYSTEMS=="usb", ATTRS{idVendor}=="2500", ATTRS{idProduct}=="0021", MODE:="0666"
+SUBSYSTEMS=="usb", ATTRS{idVendor}=="2500", ATTRS{idProduct}=="0022", MODE:="0666"
 SUBSYSTEMS=="usb", ATTRS{idVendor}=="3923", ATTRS{idProduct}=="7813", MODE:="0666"
 SUBSYSTEMS=="usb", ATTRS{idVendor}=="3923", ATTRS{idProduct}=="7814", MODE:="0666"
-- 
cgit v1.2.3