diff options
author | Patrick Roth <roth@stettbacher.ch> | 2021-07-09 07:23:25 +0200 |
---|---|---|
committer | Patrick Roth <roth@stettbacher.ch> | 2021-07-09 07:23:25 +0200 |
commit | ed2c49b25964da760117c04fe94d02d92ad03cd6 (patch) | |
tree | 0be99a4bf80cf4efb5004766308eb7f5eb15e179 | |
parent | Refine comments regarding the relation of o3000_connect(), o3000_disconnect()... (diff) | |
parent | update --> upgrade , more checks added (diff) | |
download | o3000-driver-ed2c49b25964da760117c04fe94d02d92ad03cd6.tar.gz o3000-driver-ed2c49b25964da760117c04fe94d02d92ad03cd6.zip |
Firmware upgrade
See merge request o-3000/driver!1
-rw-r--r-- | CMakeLists.txt | 7 | ||||
-rw-r--r-- | ChangeLog | 4 | ||||
-rw-r--r-- | md5.c | 276 | ||||
-rw-r--r-- | md5.h | 65 | ||||
-rw-r--r-- | md5_helper.c | 54 | ||||
-rw-r--r-- | md5_helper.h | 33 | ||||
-rw-r--r-- | o3000.c | 79 | ||||
-rw-r--r-- | o3000.h | 5 | ||||
-rw-r--r-- | o3000_upgrade.c | 495 | ||||
-rw-r--r-- | o3000_upgrade.h | 33 |
10 files changed, 1047 insertions, 4 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 @@ -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(). @@ -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) ********************************
+ */
@@ -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 */ @@ -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,6 +868,79 @@ int __stdcall o3000_send_xml(int id, const char *msg, int msg_len) { return (send_xml(session, msg, msg_len)); } +/** + * Get the number of open sessions. + * + * @return number of open sessions + */ +static int get_num_open_sessions(void) { + int i, num_sessions = 0; + + for(i = 0; i < MAX_NUM_SESSION; i++) { + if(session_table[i] != NULL) { + num_sessions ++; + } + } + return num_sessions; +} + +/** + * Do firmware upgrade of connected camera device. + * + * Make sure, that all O-3000 sessions are closed before calling this + * function. Further, only one camera must be connected to the host system. + * Otherwise the update process will fail. + * + * @param data pointer to firmware update data binary + * @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_upgrade(unsigned char *data, int data_len) { + + int ret , num_sessions, num_cam; + + // paranoia checks + if(data == NULL) { + printf("%s: Firmware binary not defined!\n", __func__); + return O3000_ERROR_NOT_VALID_ARGUMENT; + } + if(data_len <= 0) { + printf("%s: Invalid firmware binary length of %d bytes!\n", __func__, data_len); + return O3000_ERROR_NOT_VALID_ARGUMENT; + } + + // checking whether all camera sessions are closed + num_sessions = get_num_open_sessions(); + if (num_sessions > 0) { + printf("%s: There're still %d camera session(s) opened. Close all session to perform firmware upgrade.\n", __func__, num_sessions); + return O3000_ERROR_SESSION_STILL_OPEN; + } + + // checking whether only one camera is connected to the system + num_cam = o3000_get_num_cam(); + if(num_cam < 0) { + // forward error + printf("%s: Getting number of connected cameras failed with error code %d\n", __func__, num_cam); + return num_cam; + } + if(num_cam == 0) { + // no camera connected to the system + printf("%s: No camera connected to the host system.\n", __func__); + return O3000_ERROR_NODEV; + } + if(num_cam > 1) { + printf("%s: %d cameras connected to the host system. Make sure only one camera is connected.\n", __func__, num_cam); + return O3000_ERROR_OTHER; + } + + // At this point only one camera is connected and all sessions are closed. Perform upgrade now. + ret = firmware_upgrade(data, data_len); + if(ret != 0) { + printf("%s: Firmware upgrade failed with error code %d.\n", __func__, ret); + } + return ret; +} + /** * Disconnect a currently claimed USB connection. @@ -892,7 +966,7 @@ int __stdcall o3000_disconnect(int id) { o3000_log(session, O3000_LOG_WARNING, "%s: driver is not connected!\n", __func__); return O3000_ERROR_DRV_NOT_CONNECTED; } - + o3000_log(session, O3000_LOG_DEBUG, "%s: disconnect USB device (release interface)\n", __func__); session->cleanup_transfers = TRUE; session->disconnect_dev = TRUE; @@ -906,6 +980,7 @@ int __stdcall o3000_disconnect(int id) { else if(retval != 0) { o3000_log(session, O3000_LOG_ERROR, "%s: releasing interface failed (code %d)\n", __func__, retval); } + return retval; } @@ -1033,7 +1108,7 @@ int __stdcall o3000_connect(int id, int device_nr, char *config_msg, int config_ o3000_log(session, O3000_LOG_ERROR, "%s: sending configuration string failed (code %d)\n", __func__, ret); } } - + /* * Finally enter main-loop */ @@ -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
@@ -88,6 +92,7 @@ int __stdcall o3000_disconnect(int id); int __stdcall o3000_send_xml(int id, const char *msg, int msg_len);
+int __stdcall o3000_firmware_upgrade(unsigned char *data, int data_len);
#if defined(__cplusplus) || defined(c_plusplus)
} // extern "C"
diff --git a/o3000_upgrade.c b/o3000_upgrade.c new file mode 100644 index 0000000..9e461e8 --- /dev/null +++ b/o3000_upgrade.c @@ -0,0 +1,495 @@ +/** +* @file o3000_upgrade.c +* @brief O-3000 firmware upgrade +* @author Patrick Roth - roth@stettbacher.ch +* @author Patrick Brunner - brunner@stettbacher.ch +* @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 +}; + +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("Error: %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("Error: %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: %s: Failed to send XML packet.\n", __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: %s: Failed to send binary data packet.\n", __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: %s: Failed to initialise libusb.\n", __func__); + 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, O3000_VID, O3000_PID)) == NULL) { + printf("retry: %d\n",retries); + retries--; + sleep(1); + } else { + break; + } + } + + if(retries <= 0) { + printf("Error: %s: Could not find/open device after %d retries\nAre you root?\n", __func__, MAX_RETRIES); + libusb_exit(cam_usb.ctx); + return mode; + } + + if((r = libusb_claim_interface(cam_usb.dev_handle, 0)) < 0) { + printf("Error: %s: usb_claim_interface error %d.\n", __func__, 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: %s: libusb_get_device_descriptor error %d.\n", __func__, 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: %s: Could not make sense out of product %s.\n", __func__, buf); + } + + } else { + printf("Error: %s: libusb_get_string_descriptor_ascii error %d.\n", __func__, 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 upgrade of the device. + * + * @param data pointer to firmware update data binary + * @param data_len firmware update data lenght in bytes + * @return 0 on success, error code on failure + */ +int firmware_upgrade(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: %s: Failed to initialise usb.\n", __func__); + 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: %s: Camera is still in bootloader mode.\n", __func__); + return O3000_ERROR_OTHER; + } + else if (cam_mode == CM_UNKNOWN) { + printf("Error: %s: Failed to initialise usb.\n", __func__); + return O3000_ERROR_USB_INIT_FAILURE; + } + + return 0; + +} diff --git a/o3000_upgrade.h b/o3000_upgrade.h new file mode 100644 index 0000000..b88d38c --- /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_upgrade(unsigned char *data, int data_len); + +#endif |