SWUpdate library to be used with RPC.

Fork of SWUpdate by David Smart

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers SWUpdate.cpp Source File

SWUpdate.cpp

00001 
00002 // Software Update via Ethernet from forum -
00003 // http://mbed.org/forum/mbed/topic/1183/
00004 //
00005 #include "mbed.h"
00006 #include "SWUpdate.h"
00007 //#include "HTTPClient.h"
00008 #include "HTTPText.h"
00009 #include "HTTPFile.h"
00010 #include <stdio.h>
00011 
00012 extern "C" void mbed_reset();
00013 
00014 //#define DEBUG "SWup"
00015 #include <cstdio>
00016 #if (defined(DEBUG) && !defined(TARGET_LPC11U24))
00017 #define DBG(x, ...)  std::printf("[DBG %s %3d] "x"\r\n", DEBUG, __LINE__, ##__VA_ARGS__);
00018 #define WARN(x, ...) std::printf("[WRN %s %3d] "x"\r\n", DEBUG, __LINE__, ##__VA_ARGS__);
00019 #define ERR(x, ...)  std::printf("[ERR %s %3d] "x"\r\n", DEBUG, __LINE__, ##__VA_ARGS__);
00020 #define INFO(x, ...) std::printf("[INF %s %3d] "x"\r\n", DEBUG, __LINE__, ##__VA_ARGS__);
00021 #else
00022 #define DBG(x, ...)
00023 #define WARN(x, ...)
00024 #define ERR(x, ...)
00025 #define INFO(x, ...)
00026 #endif
00027 
00028 static HTTPResult HTTPErrorCode;
00029 
00030 typedef enum {
00031     ok,
00032     no_file,
00033     bad_crc
00034 } Integrity_t;
00035 
00036 static Integrity_t PassesIntegrityCheck(const char * fname, int cksum, int fsize)
00037 {
00038     Integrity_t res = bad_crc;    // assume things go wrong...
00039     int newCksum = 0;
00040     int newFSize = 0;
00041     FILE *fh = fopen(fname, "rb");
00042 
00043     INFO("IntegrityCheck(%s,%d,%d)", fname, cksum, fsize);
00044     if (fh) {
00045         char buf;
00046         while (fread(&buf, 1, 1, fh)) {
00047             newCksum = (newCksum + buf) & 0xFFFF;
00048             newFSize++;
00049         }
00050         fclose(fh);
00051         INFO("      Check(...,%d,%d)", newCksum, newFSize);
00052         if (newCksum == cksum && newFSize == fsize)
00053             res = ok;
00054     } else {
00055         WARN("failed to open %s.", fname);
00056         res = no_file;
00057     }
00058     return res;
00059 }
00060 
00061 /// mytolower exists because not all compiler libraries have this function
00062 ///
00063 /// This takes a character and if it is upper-case, it converts it to
00064 /// lower-case and returns it.
00065 ///
00066 /// @note this only works for characters in the range 'A' - 'Z'.
00067 ///
00068 /// a is the character to convert
00069 /// returns the lower case equivalent to the supplied character.
00070 ///
00071 static char mytolower(char a)
00072 {
00073     if (a >= 'A' && a <= 'Z')
00074         return (a - 'A' + 'a');
00075     else
00076         return a;
00077 }
00078 
00079 /// mystrnicmp exists because not all compiler libraries have this function.
00080 ///
00081 /// Some have strnicmp, others _strnicmp, and others have C++ methods, which
00082 /// is outside the scope of this C-portable set of functions.
00083 ///
00084 /// l is a pointer to the string on the left
00085 /// r is a pointer to the string on the right
00086 /// n is the number of characters to compare
00087 /// returns -1 if l < r
00088 /// returns 0 if l == r
00089 /// returns +1 if l > r
00090 ///
00091 static int mystrnicmp(const char *l, const char *r, size_t n)
00092 {
00093     int result = 0;
00094 
00095     if (n != 0) {
00096         do {
00097             result = mytolower(*l++) - mytolower(*r++);
00098         } while ((result == 0) && (*l != '\0') && (--n > 0));
00099     }
00100     if (result < -1)
00101         result = -1;
00102     else if (result > 1)
00103         result = 1;
00104     return result;
00105 }
00106 
00107 
00108 // Scan the local file system for any .bin files and
00109 // if they don't match the current one, remove them.
00110 //
00111 static bool RemoveOtherBinFiles(const char * name, int ver)
00112 {
00113     char curbin[SW_MAX_FQFN];
00114     DIR *d;
00115     struct dirent *p;
00116     bool noFailed = true;
00117 
00118     snprintf(curbin, SW_MAX_FQFN, "%s%02d.bin", name, (ver % 100));
00119     INFO("Remove bin files excluding {%s}", curbin);
00120     d = opendir("/local/");
00121     // Get a directory handle
00122     if ( d != NULL ) {
00123         // Walk the directory
00124         while ( (p = readdir(d)) != NULL ) {
00125             INFO("  check {%s}", p->d_name);
00126             // if the file is .bin and not curbin
00127             if (0 == mystrnicmp(p->d_name + strlen(p->d_name) - 4, ".bin", 4)
00128                     && (0 != mystrnicmp(p->d_name, curbin, strlen(curbin)))) {
00129                 // remove the file
00130                 char toremove[SW_MAX_FQFN];
00131                 snprintf(toremove, SW_MAX_FQFN, "/local/%s", p->d_name);
00132                 INFO("    removing %s.", toremove);
00133                 if (remove(toremove)) {
00134                     // set flag if it could not be removed
00135                     noFailed = false;
00136                 }
00137             }
00138         }
00139         closedir(d);
00140     }
00141     return noFailed;
00142 }
00143 
00144 HTTPResult SoftwareUpdateGetHTTPErrorCode(void)
00145 {
00146     return HTTPErrorCode;
00147 }
00148 
00149 SWUpdate_T SoftwareUpdate(const char *url, const char * name, Reboot_T action)
00150 {
00151     HTTPClient http;
00152     //http.setTimeout( 15000 );
00153     char fqurl[SW_MAX_URL];    // fully qualified url
00154     char verfn[SW_MAX_FQFN];     // local version file
00155     char fwfn[SW_MAX_FQFN];
00156     char nameroot[7];
00157     uint16_t result = SWUP_OK;    // starting out quite optimistic, for all the things that can go wrong
00158     char buf[50];           // long enough for 3 comma separated numbers...
00159 
00160     INFO("SoftwareUpdate(%s,%s)", url, name);
00161     strncpy(nameroot, name, 6);
00162     nameroot[6] = '\0';
00163     snprintf(verfn, SW_MAX_FQFN, "/local/%s.ver", nameroot);
00164 
00165     /* Read installed version string */
00166     int inst_ver = -1;
00167     FILE *fv = fopen(verfn, "r");
00168     if (fv) {
00169         fscanf(fv, "%d", &inst_ver);
00170         fclose(fv);
00171     }
00172     INFO("  Installed version: %d", inst_ver);
00173 
00174     /* Download latest version string */
00175     HTTPText server_ver("test message");
00176     snprintf(fqurl, SW_MAX_URL, "%s/%s.txt", url, name);
00177     HTTPErrorCode = http.get(fqurl, buf, sizeof(buf));
00178     if (HTTPErrorCode == HTTP_OK) {
00179         int latest_ver = -1;
00180         int cksum = 0;
00181         int fsize = 0;
00182         int parseCount;
00183         parseCount = sscanf(buf, "%d,%d,%d", &latest_ver, &cksum, &fsize);
00184         if (parseCount == 3) {
00185             INFO("        web version: %d", latest_ver);
00186             INFO("           checksum: %d", cksum);
00187             INFO("          file size: %d", fsize);
00188             if (inst_ver != latest_ver) {
00189                 INFO("  Downloading firmware ver %d ...", latest_ver);
00190                 sprintf(fwfn, "/local/%s%02d.BIN", nameroot, (latest_ver % 100));
00191                 snprintf(fqurl, 150, "%s/%s.bin", url, name);
00192 
00193                 HTTPFile latest(fwfn);
00194                 HTTPErrorCode = http.get(fqurl, &latest);
00195                 if (HTTPErrorCode == HTTP_OK) {
00196                     Integrity_t t = PassesIntegrityCheck(fwfn, cksum, fsize);
00197                     if (t == no_file) {
00198                         ERR("  *** No space on file system. ***");
00199                         result |= SWUP_NO_SPACE;
00200                     } else if (t == ok) {
00201                         if (!RemoveOtherBinFiles(nameroot, latest_ver)) {
00202                             ERR("  *** Failed to remove old version(s). ***");
00203                             result |= SWUP_OLD_STUCK;
00204                         }
00205                         INFO("Updating stored version number.");
00206                         fv = fopen(verfn, "w");
00207                         if (fv) {
00208                             int fr = fputs(buf, fv);
00209                             if (fr < 0) {
00210                                 ERR("Failed (%d) to update stored version number.", fr);
00211                                 fclose( fv );
00212                                 result |= SWUP_VER_STUCK;
00213                             } else {
00214                                 fclose( fv );
00215                                 if (action == AUTO_REBOOT) {
00216                                     WARN("Resetting...\n");
00217                                     wait_ms(200);
00218                                     mbed_reset();
00219                                 }
00220                             }
00221                         } else {
00222                             WARN("Failed to update local version info in %s.", verfn);
00223                             result |= SWUP_VWRITE_FAILED;
00224                         }
00225                     } else /* t == bad_crc */ {
00226                         WARN("New file {%s} did not pass integrity check.", fwfn);
00227                         result |= SWUP_INTEGRITY_FAILED;
00228                     }
00229                 } else {
00230                     WARN("Failed to download lastest firmware.");
00231                     result |= SWUP_HTTP_BIN;
00232                 }
00233             } else {
00234                 INFO("Online version is same as installed version.");
00235                 result |= SWUP_SAME_VER;
00236             }
00237         }
00238     } else {
00239         WARN("Failed accessing server. Extended Error Code = %d", HTTPErrorCode);
00240         result |= SWUP_HTTP_VER;
00241     }
00242     return (SWUpdate_T)result;
00243 }