aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt7
-rw-r--r--ChangeLog4
-rw-r--r--md5.c276
-rw-r--r--md5.h65
-rw-r--r--md5_helper.c54
-rw-r--r--md5_helper.h33
-rw-r--r--o3000.c46
-rw-r--r--o3000.h4
-rw-r--r--o3000_upgrade.c498
-rw-r--r--o3000_upgrade.h33
10 files changed, 1009 insertions, 11 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index f61faa9..e25cc1f 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -16,8 +16,8 @@ set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_SOURCE_DIR}/cmake)
# library version definition
set(VERSION_MAJOR "2")
-set(VERSION_MINOR "0")
-set(VERSION_RELEASE "3")
+set(VERSION_MINOR "1")
+set(VERSION_RELEASE "0")
set(VERSION_STR "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_RELEASE}")
# pass macros to preprocessor
@@ -37,6 +37,9 @@ add_library( ${LIB_NAME} SHARED
o3000_private.h
o3000_xfer_handler.c
o3000_xfer_handler.h
+ o3000_upgrade.c
+ md5_helper.c
+ md5.c
)
set_target_properties ( ${LIB_NAME} PROPERTIES
diff --git a/ChangeLog b/ChangeLog
index 0884cb0..4831862 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -2,6 +2,10 @@
- ChangeLog O-3000 Host Driver
-------------------------------------------------------------------------------
+Version 2.1.0 SP
+ 2020-05-19
+ * Added firmware update function.
+
Version 2.0.3
2019-09-25 SP
* Added "paranoia check" for wraparound_chunk_size check in handle_transfer().
diff --git a/md5.c b/md5.c
new file mode 100644
index 0000000..8cef19e
--- /dev/null
+++ b/md5.c
@@ -0,0 +1,276 @@
+/*
+ **********************************************************************
+ ** md5.c **
+ ** RSA Data Security, Inc. MD5 Message Digest Algorithm **
+ ** Created: 2/17/90 RLR **
+ ** Revised: 1/91 SRD,AJ,BSK,JT Reference C Version **
+ **********************************************************************
+ */
+
+/*
+ **********************************************************************
+ ** Copyright (C) 1990, RSA Data Security, Inc. All rights reserved. **
+ ** **
+ ** License to copy and use this software is granted provided that **
+ ** it is identified as the "RSA Data Security, Inc. MD5 Message **
+ ** Digest Algorithm" in all material mentioning or referencing this **
+ ** software or this function. **
+ ** **
+ ** License is also granted to make and use derivative works **
+ ** provided that such works are identified as "derived from the RSA **
+ ** Data Security, Inc. MD5 Message Digest Algorithm" in all **
+ ** material mentioning or referencing the derived work. **
+ ** **
+ ** RSA Data Security, Inc. makes no representations concerning **
+ ** either the merchantability of this software or the suitability **
+ ** of this software for any particular purpose. It is provided "as **
+ ** is" without express or implied warranty of any kind. **
+ ** **
+ ** These notices must be retained in any copies of any part of this **
+ ** documentation and/or software. **
+ **********************************************************************
+ */
+
+/* -- include the following line if the md5.h header file is separate -- */
+#include "md5.h"
+
+/* forward declaration */
+static void Transform ();
+
+static unsigned char PADDING[64] = {
+ 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+/* F, G and H are basic MD5 functions: selection, majority, parity */
+#define F(x, y, z) (((x) & (y)) | ((~x) & (z)))
+#define G(x, y, z) (((x) & (z)) | ((y) & (~z)))
+#define H(x, y, z) ((x) ^ (y) ^ (z))
+#define I(x, y, z) ((y) ^ ((x) | (~z)))
+
+/* ROTATE_LEFT rotates x left n bits */
+#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n))))
+
+/* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4 */
+/* Rotation is separate from addition to prevent recomputation */
+#define FF(a, b, c, d, x, s, ac) \
+ {(a) += F ((b), (c), (d)) + (x) + (UINT4)(ac); \
+ (a) = ROTATE_LEFT ((a), (s)); \
+ (a) += (b); \
+ }
+#define GG(a, b, c, d, x, s, ac) \
+ {(a) += G ((b), (c), (d)) + (x) + (UINT4)(ac); \
+ (a) = ROTATE_LEFT ((a), (s)); \
+ (a) += (b); \
+ }
+#define HH(a, b, c, d, x, s, ac) \
+ {(a) += H ((b), (c), (d)) + (x) + (UINT4)(ac); \
+ (a) = ROTATE_LEFT ((a), (s)); \
+ (a) += (b); \
+ }
+#define II(a, b, c, d, x, s, ac) \
+ {(a) += I ((b), (c), (d)) + (x) + (UINT4)(ac); \
+ (a) = ROTATE_LEFT ((a), (s)); \
+ (a) += (b); \
+ }
+
+void MD5Init (mdContext)
+MD5_CTX *mdContext;
+{
+ mdContext->i[0] = mdContext->i[1] = (UINT4)0;
+
+ /* Load magic initialization constants.
+ */
+ mdContext->buf[0] = (UINT4)0x67452301;
+ mdContext->buf[1] = (UINT4)0xefcdab89;
+ mdContext->buf[2] = (UINT4)0x98badcfe;
+ mdContext->buf[3] = (UINT4)0x10325476;
+}
+
+void MD5Update (mdContext, inBuf, inLen)
+MD5_CTX *mdContext;
+unsigned char *inBuf;
+unsigned int inLen;
+{
+ UINT4 in[16];
+ int mdi;
+ unsigned int i, ii;
+
+ /* compute number of bytes mod 64 */
+ mdi = (int)((mdContext->i[0] >> 3) & 0x3F);
+
+ /* update number of bits */
+ if ((mdContext->i[0] + ((UINT4)inLen << 3)) < mdContext->i[0])
+ mdContext->i[1]++;
+ mdContext->i[0] += ((UINT4)inLen << 3);
+ mdContext->i[1] += ((UINT4)inLen >> 29);
+
+ while (inLen--) {
+ /* add new character to buffer, increment mdi */
+ mdContext->in[mdi++] = *inBuf++;
+
+ /* transform if necessary */
+ if (mdi == 0x40) {
+ for (i = 0, ii = 0; i < 16; i++, ii += 4)
+ in[i] = (((UINT4)mdContext->in[ii+3]) << 24) |
+ (((UINT4)mdContext->in[ii+2]) << 16) |
+ (((UINT4)mdContext->in[ii+1]) << 8) |
+ ((UINT4)mdContext->in[ii]);
+ Transform (mdContext->buf, in);
+ mdi = 0;
+ }
+ }
+}
+
+void MD5Final (mdContext)
+MD5_CTX *mdContext;
+{
+ UINT4 in[16];
+ int mdi;
+ unsigned int i, ii;
+ unsigned int padLen;
+
+ /* save number of bits */
+ in[14] = mdContext->i[0];
+ in[15] = mdContext->i[1];
+
+ /* compute number of bytes mod 64 */
+ mdi = (int)((mdContext->i[0] >> 3) & 0x3F);
+
+ /* pad out to 56 mod 64 */
+ padLen = (mdi < 56) ? (56 - mdi) : (120 - mdi);
+ MD5Update (mdContext, PADDING, padLen);
+
+ /* append length in bits and transform */
+ for (i = 0, ii = 0; i < 14; i++, ii += 4)
+ in[i] = (((UINT4)mdContext->in[ii+3]) << 24) |
+ (((UINT4)mdContext->in[ii+2]) << 16) |
+ (((UINT4)mdContext->in[ii+1]) << 8) |
+ ((UINT4)mdContext->in[ii]);
+ Transform (mdContext->buf, in);
+
+ /* store buffer in digest */
+ for (i = 0, ii = 0; i < 4; i++, ii += 4) {
+ mdContext->digest[ii] = (unsigned char)(mdContext->buf[i] & 0xFF);
+ mdContext->digest[ii+1] =
+ (unsigned char)((mdContext->buf[i] >> 8) & 0xFF);
+ mdContext->digest[ii+2] =
+ (unsigned char)((mdContext->buf[i] >> 16) & 0xFF);
+ mdContext->digest[ii+3] =
+ (unsigned char)((mdContext->buf[i] >> 24) & 0xFF);
+ }
+}
+
+/* Basic MD5 step. Transform buf based on in.
+ */
+static void Transform (buf, in)
+UINT4 *buf;
+UINT4 *in;
+{
+ UINT4 a = buf[0], b = buf[1], c = buf[2], d = buf[3];
+
+ /* Round 1 */
+#define S11 7
+#define S12 12
+#define S13 17
+#define S14 22
+ FF ( a, b, c, d, in[ 0], S11, 3614090360U); /* 1 */
+ FF ( d, a, b, c, in[ 1], S12, 3905402710U); /* 2 */
+ FF ( c, d, a, b, in[ 2], S13, 606105819U); /* 3 */
+ FF ( b, c, d, a, in[ 3], S14, 3250441966U); /* 4 */
+ FF ( a, b, c, d, in[ 4], S11, 4118548399U); /* 5 */
+ FF ( d, a, b, c, in[ 5], S12, 1200080426U); /* 6 */
+ FF ( c, d, a, b, in[ 6], S13, 2821735955U); /* 7 */
+ FF ( b, c, d, a, in[ 7], S14, 4249261313U); /* 8 */
+ FF ( a, b, c, d, in[ 8], S11, 1770035416U); /* 9 */
+ FF ( d, a, b, c, in[ 9], S12, 2336552879U); /* 10 */
+ FF ( c, d, a, b, in[10], S13, 4294925233U); /* 11 */
+ FF ( b, c, d, a, in[11], S14, 2304563134U); /* 12 */
+ FF ( a, b, c, d, in[12], S11, 1804603682U); /* 13 */
+ FF ( d, a, b, c, in[13], S12, 4254626195U); /* 14 */
+ FF ( c, d, a, b, in[14], S13, 2792965006U); /* 15 */
+ FF ( b, c, d, a, in[15], S14, 1236535329U); /* 16 */
+
+ /* Round 2 */
+#define S21 5
+#define S22 9
+#define S23 14
+#define S24 20
+ GG ( a, b, c, d, in[ 1], S21, 4129170786U); /* 17 */
+ GG ( d, a, b, c, in[ 6], S22, 3225465664U); /* 18 */
+ GG ( c, d, a, b, in[11], S23, 643717713U); /* 19 */
+ GG ( b, c, d, a, in[ 0], S24, 3921069994U); /* 20 */
+ GG ( a, b, c, d, in[ 5], S21, 3593408605U); /* 21 */
+ GG ( d, a, b, c, in[10], S22, 38016083U); /* 22 */
+ GG ( c, d, a, b, in[15], S23, 3634488961U); /* 23 */
+ GG ( b, c, d, a, in[ 4], S24, 3889429448U); /* 24 */
+ GG ( a, b, c, d, in[ 9], S21, 568446438U); /* 25 */
+ GG ( d, a, b, c, in[14], S22, 3275163606U); /* 26 */
+ GG ( c, d, a, b, in[ 3], S23, 4107603335U); /* 27 */
+ GG ( b, c, d, a, in[ 8], S24, 1163531501U); /* 28 */
+ GG ( a, b, c, d, in[13], S21, 2850285829U); /* 29 */
+ GG ( d, a, b, c, in[ 2], S22, 4243563512U); /* 30 */
+ GG ( c, d, a, b, in[ 7], S23, 1735328473U); /* 31 */
+ GG ( b, c, d, a, in[12], S24, 2368359562U); /* 32 */
+
+ /* Round 3 */
+#define S31 4
+#define S32 11
+#define S33 16
+#define S34 23
+ HH ( a, b, c, d, in[ 5], S31, 4294588738U); /* 33 */
+ HH ( d, a, b, c, in[ 8], S32, 2272392833U); /* 34 */
+ HH ( c, d, a, b, in[11], S33, 1839030562U); /* 35 */
+ HH ( b, c, d, a, in[14], S34, 4259657740U); /* 36 */
+ HH ( a, b, c, d, in[ 1], S31, 2763975236U); /* 37 */
+ HH ( d, a, b, c, in[ 4], S32, 1272893353U); /* 38 */
+ HH ( c, d, a, b, in[ 7], S33, 4139469664U); /* 39 */
+ HH ( b, c, d, a, in[10], S34, 3200236656U); /* 40 */
+ HH ( a, b, c, d, in[13], S31, 681279174U); /* 41 */
+ HH ( d, a, b, c, in[ 0], S32, 3936430074U); /* 42 */
+ HH ( c, d, a, b, in[ 3], S33, 3572445317U); /* 43 */
+ HH ( b, c, d, a, in[ 6], S34, 76029189U); /* 44 */
+ HH ( a, b, c, d, in[ 9], S31, 3654602809U); /* 45 */
+ HH ( d, a, b, c, in[12], S32, 3873151461U); /* 46 */
+ HH ( c, d, a, b, in[15], S33, 530742520U); /* 47 */
+ HH ( b, c, d, a, in[ 2], S34, 3299628645U); /* 48 */
+
+ /* Round 4 */
+#define S41 6
+#define S42 10
+#define S43 15
+#define S44 21
+ II ( a, b, c, d, in[ 0], S41, 4096336452U); /* 49 */
+ II ( d, a, b, c, in[ 7], S42, 1126891415U); /* 50 */
+ II ( c, d, a, b, in[14], S43, 2878612391U); /* 51 */
+ II ( b, c, d, a, in[ 5], S44, 4237533241U); /* 52 */
+ II ( a, b, c, d, in[12], S41, 1700485571U); /* 53 */
+ II ( d, a, b, c, in[ 3], S42, 2399980690U); /* 54 */
+ II ( c, d, a, b, in[10], S43, 4293915773U); /* 55 */
+ II ( b, c, d, a, in[ 1], S44, 2240044497U); /* 56 */
+ II ( a, b, c, d, in[ 8], S41, 1873313359U); /* 57 */
+ II ( d, a, b, c, in[15], S42, 4264355552U); /* 58 */
+ II ( c, d, a, b, in[ 6], S43, 2734768916U); /* 59 */
+ II ( b, c, d, a, in[13], S44, 1309151649U); /* 60 */
+ II ( a, b, c, d, in[ 4], S41, 4149444226U); /* 61 */
+ II ( d, a, b, c, in[11], S42, 3174756917U); /* 62 */
+ II ( c, d, a, b, in[ 2], S43, 718787259U); /* 63 */
+ II ( b, c, d, a, in[ 9], S44, 3951481745U); /* 64 */
+
+ buf[0] += a;
+ buf[1] += b;
+ buf[2] += c;
+ buf[3] += d;
+}
+
+/*
+ **********************************************************************
+ ** End of md5.c **
+ ******************************* (cut) ********************************
+ */
diff --git a/md5.h b/md5.h
new file mode 100644
index 0000000..1a642c1
--- /dev/null
+++ b/md5.h
@@ -0,0 +1,65 @@
+/*
+ **********************************************************************
+ ** md5.h -- Header file for implementation of MD5 **
+ ** RSA Data Security, Inc. MD5 Message Digest Algorithm **
+ ** Created: 2/17/90 RLR **
+ ** Revised: 12/27/90 SRD,AJ,BSK,JT Reference C version **
+ ** Revised (for MD5): RLR 4/27/91 **
+ ** -- G modified to have y&~z instead of y&z **
+ ** -- FF, GG, HH modified to add in last register done **
+ ** -- Access pattern: round 2 works mod 5, round 3 works mod 3 **
+ ** -- distinct additive constant for each step **
+ ** -- round 4 added, working mod 7 **
+ **********************************************************************
+ */
+
+/*
+ **********************************************************************
+ ** Copyright (C) 1990, RSA Data Security, Inc. All rights reserved. **
+ ** **
+ ** License to copy and use this software is granted provided that **
+ ** it is identified as the "RSA Data Security, Inc. MD5 Message **
+ ** Digest Algorithm" in all material mentioning or referencing this **
+ ** software or this function. **
+ ** **
+ ** License is also granted to make and use derivative works **
+ ** provided that such works are identified as "derived from the RSA **
+ ** Data Security, Inc. MD5 Message Digest Algorithm" in all **
+ ** material mentioning or referencing the derived work. **
+ ** **
+ ** RSA Data Security, Inc. makes no representations concerning **
+ ** either the merchantability of this software or the suitability **
+ ** of this software for any particular purpose. It is provided "as **
+ ** is" without express or implied warranty of any kind. **
+ ** **
+ ** These notices must be retained in any copies of any part of this **
+ ** documentation and/or software. **
+ **********************************************************************
+ */
+
+#ifndef __MD5_H__
+#define __MD5_H__
+
+/* typedef a 32 bit type */
+// typedef unsigned long int UINT4;
+typedef unsigned int UINT4;
+
+/* Data structure for MD5 (Message Digest) computation */
+typedef struct {
+ UINT4 i[2]; /* number of _bits_ handled mod 2^64 */
+ UINT4 buf[4]; /* scratch buffer */
+ unsigned char in[64]; /* input buffer */
+ unsigned char digest[16]; /* actual digest after MD5Final call */
+} MD5_CTX;
+
+void MD5Init ();
+void MD5Update ();
+void MD5Final ();
+
+/*
+ **********************************************************************
+ ** End of md5.h **
+ ******************************* (cut) ********************************
+ */
+
+#endif // __MD5_H__
diff --git a/md5_helper.c b/md5_helper.c
new file mode 100644
index 0000000..895ea3c
--- /dev/null
+++ b/md5_helper.c
@@ -0,0 +1,54 @@
+/**
+* @file md5_helper.c
+* @brief MD5 checksum generation.
+* @author Patrick Roth - roth@stettbacher.ch
+* @date 2010-04-30
+* @copyright 2010 Stettbacher Signal Processing AG
+*
+* @remarks
+*
+* <PRE>
+* This library is free software; you can redistribute it and/or
+* modify it under the terms of the GNU Lesser General Public
+* License as published by the Free Software Foundation; either
+* version 2.1 of the License, or (at your option) any later version.
+*
+* This library is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this library; if not, write to the Free Software
+* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+* </PRE>
+*
+*/
+
+#include <stdio.h>
+
+#include "md5.h"
+
+
+/**
+ * Generate the MD5 checksum.
+ *
+ * @param addr: A pointer to the start address.
+ * @param size: The size to compare.
+ * @param buf: Destination buffer; must provide room for 32+1 chars!
+ * @return A pointer to the first digit of the generated MD5 checksum. The
+ * checksum's length is always 16 bytes.
+ */
+void getMd5ChecksumString(void *addr, ssize_t size, char *buf)
+{
+ MD5_CTX md5;
+
+ MD5Init(&md5);
+ MD5Update(&md5, (unsigned char*)(addr), (unsigned int)size);
+ MD5Final(&md5);
+
+ for(int i = 0; i < 16; i++) {
+ sprintf(buf+i*2, "%02x", md5.digest[i]);
+ }
+ *(buf+32) = '\0';
+}
diff --git a/md5_helper.h b/md5_helper.h
new file mode 100644
index 0000000..fa7c292
--- /dev/null
+++ b/md5_helper.h
@@ -0,0 +1,33 @@
+/**
+* @file md5_helper.h
+* @brief MD5 checksum generation.
+* @author Patrick Roth - roth@stettbacher.ch
+* @date 2010-04-30
+* @copyright 2010 Stettbacher Signal Processing AG
+*
+* @remarks
+*
+* <PRE>
+* This library is free software; you can redistribute it and/or
+* modify it under the terms of the GNU Lesser General Public
+* License as published by the Free Software Foundation; either
+* version 2.1 of the License, or (at your option) any later version.
+*
+* This library is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this library; if not, write to the Free Software
+* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+* </PRE>
+*
+*/
+
+#ifndef __MD5_HELPER_H
+#define __MD5_HELPER_H
+
+void getMd5ChecksumString(void *addr, ssize_t size, char *buf);
+
+#endif /* __MD5_HELPER_H */
diff --git a/o3000.c b/o3000.c
index c68baf7..1ea9f77 100644
--- a/o3000.c
+++ b/o3000.c
@@ -240,6 +240,7 @@ or a transfer may contain several frames.
#include "o3000_private.h"
#include "o3000.h"
#include "o3000_xfer_handler.h"
+#include "o3000_upgrade.h"
#define CAM_IN_EP (1 | LIBUSB_ENDPOINT_IN) ///< endpoint for video data
@@ -867,30 +868,57 @@ int __stdcall o3000_send_xml(int id, const char *msg, int msg_len) {
return (send_xml(session, msg, msg_len));
}
+/**
+ * Check whether all connections are closed.
+ *
+ * @return 0 if all sessions are closed, error code otherwise defined at @ref o3000.h.
+ */
+static int check_sessions(void) {
+ int i;
+
+ for(i = 0; i < MAX_NUM_SESSION; i++) {
+ if(session_table[i] != NULL) {
+ break;
+ }
+ }
+ if (i < MAX_NUM_SESSION) {
+ printf("%s: Session %d is still open\n", __func__, i);
+ return O3000_ERROR_SESSION_STILL_OPEN;
+ }
+ return 0;
+}
/**
* Do firmware update of the device
*
* @param data pointer to firmware update data
- * @param data_len update package lenght in bytes
- * @return 0 on success, -1 on failure
+ * @param data_len firmware update data lenght in bytes
+ * @return 0 on success, error code on failure defined at @ref o3000.h.
*/
int __stdcall o3000_firmware_update(unsigned char *data, int data_len) {
-
- int ret;
+
+ int ret = 0;
// paranoia checks
if(data == NULL) {
- return -1;
+ ret = O3000_ERROR_NOT_VALID_ARGUMENT;
+ printf("%s: Data pointer is NULL. error: %d\n", __func__, ret);
+ return ret;
}
-
-
if(data_len <= 0) {
- return -1;
+ ret = O3000_ERROR_NOT_VALID_ARGUMENT;
+ printf("%s: Length of data is negative. error: %d\n", __func__, ret);
+ return ret;
}
- // checking whether all connection are closed
+ // checking whether all connections are closed
+ if ((ret = check_sessions())) {
+ printf("%s: Cannot proceed to firmware update: error %d\n", __func__, ret);
+ return ret;
+ }
+
ret = firmware_update(data, data_len);
+
return ret;
}
diff --git a/o3000.h b/o3000.h
index 2ec4d07..ac2127e 100644
--- a/o3000.h
+++ b/o3000.h
@@ -53,6 +53,10 @@
#define O3000_ERROR_USB_TRANSFER_TIMEOUT -10 ///< USB transfer timeout
#define O3000_ERROR_USB_EP_HALTED -11 ///< USB endpoint halted
#define O3000_ERROR_LESS_DATA -12 ///< USB received less data than expected
+#define O3000_ERROR_NOT_VALID_ARGUMENT -13 ///< at least one function argument is not valid
+#define O3000_ERROR_SESSION_STILL_OPEN -14 ///< there is at least one session still open
+#define O3000_ERROR_BOOTLOADER_TIMEOUT -15 ///< camera could not start in bootloader mode in given time
+#define O3000_ERROR_USB_INIT_FAILURE -16 ///< failed to initialize the usb
#define O3000_ERROR_OTHER -1000 ///< other (unspecified) error
diff --git a/o3000_upgrade.c b/o3000_upgrade.c
new file mode 100644
index 0000000..b05cf0f
--- /dev/null
+++ b/o3000_upgrade.c
@@ -0,0 +1,498 @@
+/**
+* @file o3000_upgrade.c
+* @brief O-3000 firmware upgrade
+* @author Sophia Papagiannaki - papagiannaki@stettbacher.ch
+* @version 0.1
+* @date 2020-05-12
+* @copyright 2020 Stettbacher Signal Processing AG
+*
+* @remarks
+*
+* <PRE>
+* This library is free software; you can redistribute it and/or
+* modify it under the terms of the GNU Lesser General Public
+* License as published by the Free Software Foundation; either
+* version 2.1 of the License, or (at your option) any later version.
+*
+* This library is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this library; if not, write to the Free Software
+* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+* </PRE>
+*
+*/
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <unistd.h>
+#include <libusb-1.0/libusb.h>
+
+#include "o3000_upgrade.h"
+#include "md5_helper.h"
+#include "o3000.h"
+
+#define BUF_LEN 256
+#define TRANSFER_SIZE 512
+#define MAX_RETRIES 5
+
+#define BL_OUT_EP (2)
+#define XML_IN_EP (2 | LIBUSB_ENDPOINT_IN)
+#define XML_OUT_EP (1)
+
+// table of valid bootloader ids
+const char* bl_ids[] = {
+ "Streaming Camera Bootloader in HS mode", // version without proper product id
+ "Camera O-30xx (HS mode)", // version proper product id
+};
+
+// table of valid application ids
+const char* app_ids[] = {
+ "Streaming Camera in HS mode", // version without proper product id (LEGACY)
+ "Camera O-3010 (HS mode)", // rolling shutter, mono version (LEGACY)
+ "Camera O-3020 (HS mode)", // rolling shutter, color version (LEGACY)
+ "Camera O-3010", // rolling shutter, mono version (without speed mode)
+ "Camera O-3020", // rolling shutter, color version (without speed mode)
+ "Camera O-3110", // global shutter, mono version
+ "Camera O-3120", // global shutter, color version
+};
+
+const uint16_t vid = 0x0483;
+const uint16_t pid = 0xA098;
+
+static struct cam_usb_t {
+ struct libusb_context *ctx; // libusb context
+ struct libusb_device_handle *dev_handle; // the USB device handle
+ struct libusb_device *dev; // the USB device
+ struct libusb_device_descriptor desc;
+ uint32_t transfer_size;
+ struct libusb_transfer *transfer_data_out; ///< libusb specific transfer structure
+ struct libusb_transfer *transfer_xml_out; ///< libusb specific transfer structure
+ struct libusb_transfer *transfer_xml_in; ///< libusb specific transfer structure
+ int data_buf_size; ///< image data buffer size in bytes
+} cam_usb;
+
+enum cam_mode_t {
+ CM_UNKNOWN = 0,
+ CM_BOOTLOADER,
+ CM_APPLICATION,
+};
+
+
+volatile static bool xml_out_xfer_done;
+volatile static bool data_out_xfer_done;
+
+/**
+ * @brief Transfer done callback for XML out
+ *
+ * Note: Transfer done only means submitted, not actually completed!
+ * @param transfer Pointer to XML transfer which has been submitted
+ */
+static void LIBUSB_CALL cb_xml_out_xfer_done(struct libusb_transfer *transfer)
+{
+ if (transfer->status != LIBUSB_TRANSFER_COMPLETED) {
+ printf("%s: XML out transfer failed with status %d\n",__func__, transfer->status);
+ }
+ xml_out_xfer_done = true;
+}
+
+/**
+ * @brief Transfer done callback for data out
+ *
+ * Note: Transfer done only means submitted, not actually completed!
+ * @param transfer Pointer to XML transfer which has been submitted
+ */
+static void LIBUSB_CALL cb_data_out_xfer_done(struct libusb_transfer *transfer)
+{
+ if (transfer->status != LIBUSB_TRANSFER_COMPLETED) {
+ printf("%s: Data out transfer failed with status %d\n",__func__, transfer->status);
+ }
+ data_out_xfer_done = true;
+}
+
+/**
+ Wait until XML transfer has finished.
+*/
+static void wait_for_xml_xfer_done(void)
+{
+ while(!xml_out_xfer_done) {
+ libusb_handle_events(cam_usb.ctx);
+ };
+}
+
+/**
+ Wait until data transfer has finished.
+*/
+static void wait_for_data_xfer_done(void)
+{
+ while(!data_out_xfer_done) {
+ libusb_handle_events(cam_usb.ctx);
+ };
+}
+
+/**
+ * @brief Send XML packet.
+ *
+ * Note: this function accesses the global structure cam_usb!
+ * @param msg Message content
+ * @param msg_len Message content length
+ * @retval 0: success, otherwise: error
+ */
+static int send_xml(char *msg, int msg_len)
+{
+ int retval;
+
+ cam_usb.transfer_xml_out = libusb_alloc_transfer(0);
+ if (!cam_usb.transfer_xml_out)
+ return -1;
+
+ // throw away transfer after use
+ cam_usb.transfer_xml_out->flags = LIBUSB_TRANSFER_FREE_TRANSFER;
+
+ libusb_fill_bulk_transfer(cam_usb.transfer_xml_out, cam_usb.dev_handle, XML_OUT_EP, (unsigned char*)msg,
+ msg_len, cb_xml_out_xfer_done, (unsigned char*)msg, 0);
+
+ retval = libusb_submit_transfer(cam_usb.transfer_xml_out);
+ if(retval) {
+ printf("error in %s",__func__);
+ return -1;
+ }
+ return 0;
+}
+
+/**
+ * @brief Send binary packet.
+ *
+ * Note: this function accesses the global structure cam_usb!
+ * @param data_buf data buffer
+ * @param data_len data length
+ * @retval 0: success, otherwise: error
+ */
+static int send_data(unsigned char *data_buf, int data_len)
+{
+ int retval;
+
+ cam_usb.transfer_data_out = libusb_alloc_transfer(0);
+ if (!cam_usb.transfer_data_out)
+ return -1;
+
+ // throw away transfer after use
+ cam_usb.transfer_data_out->flags = LIBUSB_TRANSFER_FREE_TRANSFER;
+
+ libusb_fill_bulk_transfer(cam_usb.transfer_data_out, cam_usb.dev_handle, BL_OUT_EP, (unsigned char*)data_buf,
+ data_len, cb_data_out_xfer_done, (unsigned char*)data_buf, 0);
+
+ retval = libusb_submit_transfer(cam_usb.transfer_data_out);
+ if(retval) {
+ printf("error in %s",__func__);
+ return -1;
+ }
+ return 0;
+}
+
+
+/**
+
+ Open libusb.
+
+ Note: this function accesses the global structure cam_usb!
+ @retval Camera mode (see @ref cam_mode_t)
+
+*/
+static enum cam_mode_t usb_open(void)
+{
+ int r,i;
+ unsigned char buf[BUF_LEN];
+ enum cam_mode_t mode = CM_UNKNOWN;
+ int retries = MAX_RETRIES;
+ int num_bl_ids;
+ int num_app_ids;
+
+ if (libusb_init(&cam_usb.ctx) < 0) {
+ printf("Error: failed to initialise libusb\n");
+ return mode;
+ }
+
+ // setting up USB logging. That's tricky, as LOG_LEVEL_INFO prints (almost) no log messages,
+ // whereas the next more verbose level LOG_LEVEL_DEBUG will flood the terminal
+ libusb_set_debug(cam_usb.ctx, LIBUSB_LOG_LEVEL_INFO);
+
+ // If the camera has just performed a reset, it might not be ready yet.
+ // We try MAX_RETRIES times, before we give up.
+ while(retries > 0) {
+ if((cam_usb.dev_handle = libusb_open_device_with_vid_pid(cam_usb.ctx, vid, pid)) == NULL) {
+ printf("retry: %d\n",retries);
+ retries--;
+ sleep(1);
+ } else {
+ break;
+ }
+ }
+
+ if(retries <= 0) {
+ printf("Error: Could not find/open device after %d retries\nAre you root?\n",MAX_RETRIES);
+ libusb_exit(cam_usb.ctx);
+ return mode;
+ }
+
+ if((r = libusb_claim_interface(cam_usb.dev_handle, 0)) < 0) {
+ printf("Error: usb_claim_interface error %d\n", r);
+ libusb_close(cam_usb.dev_handle);
+ libusb_exit(cam_usb.ctx);
+ return mode;
+ }
+
+ cam_usb.dev = libusb_get_device(cam_usb.dev_handle);
+
+ r = libusb_get_device_descriptor (cam_usb.dev, &cam_usb.desc);
+ if(r) {
+ printf("Error: libusb_get_device_descriptor error %d\n", r);
+ }
+
+ // determine camera mode
+ r = libusb_get_string_descriptor_ascii(cam_usb.dev_handle, cam_usb.desc.iProduct, buf, BUF_LEN);
+ if(r > 0) {
+
+ // get number of app/bootloader ids
+ num_app_ids = sizeof(app_ids)/sizeof(app_ids[0]);
+ num_bl_ids = sizeof(bl_ids)/sizeof(bl_ids[0]);
+
+ // look for bootloader ids
+ for(i=0; i<num_bl_ids; i++) {
+ if(!strcmp((char*)buf, bl_ids[i])) {
+ // found a valid bootloader id
+ mode = CM_BOOTLOADER;
+ }
+ }
+
+ if(mode == CM_UNKNOWN) {
+ // look for app ids
+ for(i=0; i<num_app_ids; i++) {
+ if(!strcmp((char*)buf, app_ids[i])) {
+ // found a valid app id
+ mode = CM_APPLICATION;
+ }
+ }
+ }
+
+ if(mode == CM_UNKNOWN) {
+ printf("Error: could not make sense out of product %s\n", buf);
+ }
+
+ } else {
+ printf("Error: libusb_get_string_descriptor_ascii error %d\n", r);
+ }
+
+ return mode;
+}
+
+/**
+ * Close usb and exit connection.
+ */
+static void usb_close(void)
+{
+ libusb_release_interface(cam_usb.dev_handle, 0);
+ libusb_close(cam_usb.dev_handle);
+ libusb_exit(cam_usb.ctx);
+
+ // although
+ // the usb_close/usb_open cycle does not seem to work properly
+ // without a short pause. Without the pause, usb_open returns
+ // a cam_mode which in fact is the previous one.
+ sleep(1);
+}
+
+/**
+ * Force camera to start in upgrade mode.
+ *
+ * @return camera mode on starting (see @ref cam_mode_t)
+ */
+static enum cam_mode_t force_into_upgrade_mode() {
+
+ char msg[BUF_LEN];
+ enum cam_mode_t cam_mode = CM_UNKNOWN;
+
+ snprintf(msg, BUF_LEN,
+ "<camera>" \
+ "<upgrade> %s </upgrade>" \
+ "</camera>",
+ "application");
+
+ xml_out_xfer_done = false;
+ send_xml(msg, strlen(msg));
+ wait_for_xml_xfer_done();
+
+ usb_close();
+ cam_mode = usb_open();
+
+ return cam_mode;
+}
+
+/**
+ * Create and send the firmware update package information.
+ *
+ * @param data pointer to firmware update data
+ * @param data_len firmware update data lenght in bytes
+ */
+static void create_and_send_update_info(unsigned char *data, int data_len) {
+
+ char msg[BUF_LEN];
+ char md5[33];
+
+ // compute MD5 digest
+ getMd5ChecksumString(data, data_len, md5);
+
+ snprintf(msg, BUF_LEN,
+ "<camera>" \
+ "<upgrade_info>" \
+ "<section>%s</section>" \
+ "<size>%d</size>" \
+ "<checksum>%s</checksum>" \
+ "</upgrade_info>" \
+ "</camera>",
+ "application", data_len, md5);
+
+ xml_out_xfer_done = false;
+ send_xml(msg, strlen(msg));
+ wait_for_xml_xfer_done();
+}
+
+/**
+ * Send the firmware update package data, close and
+ * open again usb connection.
+ *
+ * @param data pointer to firmware update data
+ * @param data_len firmware update data lenght in bytes
+ * @return camera mode (see @ref cam_mode_t)
+ */
+static enum cam_mode_t send_update_data(unsigned char *data, int data_len) {
+
+ int num_chunks;
+ int last_chunk_size;
+ enum cam_mode_t cam_mode = CM_UNKNOWN;
+
+ // calculate number of chunks to be sent and size of last chunk
+ num_chunks = data_len / TRANSFER_SIZE;
+ last_chunk_size = data_len - (num_chunks * TRANSFER_SIZE);
+
+ // send complete chunks
+ for(int i = 0; i < num_chunks; i++) {
+ data_out_xfer_done = false;
+ send_data(data + i*TRANSFER_SIZE, TRANSFER_SIZE);
+ wait_for_data_xfer_done();
+ }
+
+ // send incomplete chunk (if available)
+ if(last_chunk_size > 0) {
+ data_out_xfer_done = false;
+ send_data(data + num_chunks*TRANSFER_SIZE, last_chunk_size);
+ wait_for_data_xfer_done();
+ }
+
+ usb_close();
+ cam_mode = usb_open();
+
+ return cam_mode;
+}
+
+/**
+ * Try MAX_RETRIES to open camera and check if
+ * it started in bootloader mode.
+ *
+ * @return camera mode (see @ref cam_mode_t)
+ */
+static enum cam_mode_t wait_for_bootloader_mode() {
+
+ int i;
+ enum cam_mode_t cam_mode = CM_UNKNOWN;
+
+ for (i = 0; i > MAX_RETRIES; i++){
+
+ usb_close();
+
+ cam_mode = usb_open();
+
+ if(cam_mode != CM_BOOTLOADER) {
+ continue; // try again
+ }
+ break; // started in bootloader mode so exit loop and return
+ }
+
+ return cam_mode;
+}
+
+/**
+ * Do firmware update of the device.
+ *
+ * Note: Parameters are already checked in o3000_firmware_update.
+ *
+ * @param data pointer to firmware update data
+ * @param data_len firmware update data lenght in bytes
+ * @return 0 on success, error code on failure
+ */
+int firmware_update(unsigned char *data, int data_len) {
+
+ enum cam_mode_t cam_mode = CM_UNKNOWN;
+
+ cam_mode = usb_open();
+
+ if(cam_mode == CM_UNKNOWN) {
+ printf("Error: failed to initialise usb\n");
+ return O3000_ERROR_USB_INIT_FAILURE;
+ }
+
+ /**
+ * If camera started in application mode,
+ * it has to be forced into upgrade mode.
+ */
+ if (cam_mode == CM_APPLICATION) {
+ cam_mode = force_into_upgrade_mode();
+ }
+
+ /**
+ * Now camera should start in bootloader mode.
+ * If not, try MAX_RETRIES times. If still
+ * not in bootloader mode, return error code.
+ */
+ if (cam_mode != CM_BOOTLOADER) {
+
+ cam_mode = wait_for_bootloader_mode();
+
+ if (cam_mode != CM_BOOTLOADER) {
+ printf("Error: Timeout! Camera won't start in bootloader mode\n");
+ usb_close();
+ return O3000_ERROR_BOOTLOADER_TIMEOUT;
+ }
+ }
+
+ /**
+ * Camera is finally in bootloader mode, ready
+ * for the upgrade. So, send xml package first
+ * and then binary data.
+ */
+ create_and_send_update_info(data, data_len);
+
+ cam_mode = send_update_data(data, data_len);
+
+ usb_close();
+
+ if(cam_mode == CM_APPLICATION) {
+ printf("Camera upgrade successful\n");
+ }
+ else if (cam_mode == CM_BOOTLOADER) {
+ printf("Error: Camera is still in bootloader mode\n");
+ return O3000_ERROR_OTHER;
+ }
+ else if (cam_mode == CM_UNKNOWN) {
+ printf("Error: failed to initialise usb\n");
+ return O3000_ERROR_USB_INIT_FAILURE;
+ }
+
+ return 0;
+
+}
diff --git a/o3000_upgrade.h b/o3000_upgrade.h
new file mode 100644
index 0000000..aa898fa
--- /dev/null
+++ b/o3000_upgrade.h
@@ -0,0 +1,33 @@
+/**
+* @file o3000_upgrade.c
+* @brief O-3000 firmware upgrade
+* @author Sophia Papagiannaki - papagiannaki@stettbacher.ch
+* @version 0.1
+* @date 2020-05-12
+* @copyright 2020 Stettbacher Signal Processing AG
+*
+* @remarks
+*
+* <PRE>
+* This library is free software; you can redistribute it and/or
+* modify it under the terms of the GNU Lesser General Public
+* License as published by the Free Software Foundation; either
+* version 2.1 of the License, or (at your option) any later version.
+*
+* This library is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this library; if not, write to the Free Software
+* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+* </PRE>
+*
+*/
+#ifndef __O3000_UPGRADE_H
+#define __O3000_UPGRADE_H
+
+int firmware_update(unsigned char *data, int data_len);
+
+#endif