Software Update via Ethernet - the mbed application can pull down an updated application binary from a web server and activate that binary. This library works only with the LPC1768, as it relies on the magic-chip boot-loader mechanism.

Dependents:   WattEye X10Svr PUB_SWUpdate

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