The FirmwareUpdater is a mbed firmware update library with HTTP server on cloud.

Dependents:   FirmwareUpdater_TestProgram geigercounter04 firm LPC1768_up_frim

Revision:
1:0305a8120f06
Parent:
0:f9bdb06ab672
Child:
2:a9a32355af69
--- a/FirmwareUpdater.cpp	Wed Nov 03 12:57:51 2010 +0000
+++ b/FirmwareUpdater.cpp	Wed Nov 03 22:03:18 2010 +0000
@@ -1,6 +1,6 @@
 /**
  * =============================================================================
- * Firmware updater (Version 0.0.1)
+ * Firmware updater (Version 0.0.2)
  * =============================================================================
  * Copyright (c) 2010 Shinichiro Nakamura (CuBeatSystems)
  *
@@ -10,7 +10,7 @@
  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  * copies of the Software, and to permit persons to whom the Software is
  * furnished to do so, subject to the following conditions:
- * 
+ *
  * The above copyright notice and this permission notice shall be included in
  * all copies or substantial portions of the Software.
  *
@@ -50,9 +50,10 @@
     /*
      * A file name on the mbed local file system should keep '8 + 3' types of name.
      */
-    if (name.length() > 8) {
-        printf("Invalid firmware name '%s' found. The maximum length is 8.\n", name.c_str());
-        LOG("Invalid firmware name '%s' found. The maximum length is 8.\n", name.c_str());
+    const int PREFIX_MAXLEN = 4;
+    if (name.length() > PREFIX_MAXLEN) {
+        LOG("ERR : Invalid firmware name '%s' found. The maximum length is PREFIX_MAXLEN.\n", name.c_str());
+        error("ERR : Invalid firmware name '%s' found. The maximum length is PREFIX_MAXLEN.\n", name.c_str());
     }
 }
 
@@ -92,36 +93,19 @@
     /*
      * Fetch the version from a local.
      */
-    {
-        std::string file_local = "/local/" + name + EXT_TXT;
-        FILE *fp = fopen(file_local.c_str(), "rb");
-        if (fp == NULL) {
-            LOG("Local firmware version file '%s' open failed.\n", file_local.c_str());
-            return -1;
-        }
-        if (fscanf(fp, "%d", &ver_local) != 1) {
-            LOG("Local firmware version file '%s' is invalid.\n", file_local.c_str());
-            return -2;
-        }
-        fclose(fp);
-        LOG("Local firmware version is %d. (%s)\n", ver_local, file_local.c_str());
+    std::string file_local = "/local/" + name + EXT_TXT;
+    ver_local = readVersionFromFile(file_local.c_str());
+    if (ver_local < 0) {
+        return -1;
     }
+
     /*
      * Fetch the version from a server.
      */
-    {
-        std::string file_server = url + "/" + name + EXT_TXT;
-        HTTPText text;
-        HTTPResult r = client.get(file_server.c_str(), &text);
-        if (r != HTTP_OK) {
-            LOG("Server firmware version file '%s' open failed.\n", file_server.c_str());
-            return -3;
-        }
-        if (sscanf(text.gets(), "%d", &ver_server) != 1) {
-            LOG("Server firmware version file '%s' is invalid.\n", file_server.c_str());
-            return -4;
-        }
-        LOG("Server firmware version is %d. (%s)\n", ver_server, file_server.c_str());
+    std::string file_server = url + "/" + name + EXT_TXT;
+    ver_server = readVersionFromURL(file_server.c_str());
+    if (ver_server < 0) {
+        return -2;
     }
 
     return (ver_local < ver_server) ? 0 : 1;
@@ -137,25 +121,65 @@
      * Fetch the files.
      */
     std::string serv_txt = url + "/" + name + EXT_TXT;
-    std::string serv_bin = url + "/" + name + EXT_BIN;
     std::string file_txttmp = "/local/" + name + EXT_TXTTMP;
-    std::string file_bintmp = "/local/" + name + EXT_BINTMP;
     if (fetch(serv_txt, file_txttmp) != 0) {
+        LOG("ERR : Aborted...\n");
         return -1;
     }
+    std::string serv_bin = url + "/" + name + EXT_BIN;
+    std::string file_bintmp = "/local/" + name + EXT_BINTMP;
     if (fetch(serv_bin, file_bintmp) != 0) {
+        LOG("ERR : Aborted...\n");
         return -2;
     }
+
+    /*
+     * Check the firmware versions.
+     */
+    std::string file_txt = "/local/" + name + EXT_TXT;
+    int ver_old = readVersionFromFile(file_txt.c_str());
+    int ver_new = readVersionFromFile(file_txttmp.c_str());
+    if (ver_old < 0) {
+        LOG("ERR : Could not read the previous firmware version.\n");
+        LOG("ERR : Aborted...\n");
+        return -3;
+    }
+    if (ver_new < 0) {
+        LOG("ERR : Could not read the new firmware version.\n");
+        LOG("ERR : Aborted...\n");
+        return -4;
+    }
+    if (ver_new < ver_old) {
+        LOG("ERR : Ignore the new firmware. (old=%d, new=%d)\n", ver_old, ver_new);
+        LOG("ERR : Aborted...\n");
+        return -5;
+    }
+    LOG("INFO: Firmware updating... (%d -> %d)\n", ver_old, ver_new);
+
+    /*
+     * Cleanup the previous versions.
+     *
+     * Note:
+     *  A file time stamp on mbed is always '12:00 01/01/2008'.
+     *  mbed can't sense updated firmware when the file name is same as previous version.
+     *
+     *  So I decided to cleanup all bin files.
+     *  And the new firmware name is 'name-VERSION.bin'.
+     *  To remove previous versions at first means 'start critical section on the system'.
+     */
+    cleanupAllBinFiles();
+
     /*
      * Copy it.
      */
-    std::string file_txt = "/local/" + name + EXT_TXT;
-    std::string file_bin = "/local/" + name + EXT_BIN;
+    char nbuf[32];
+    snprintf(nbuf, sizeof(nbuf) - 1, "-%d", ver_new);
+    std::string file_bin = "/local/" + name + std::string(nbuf) + EXT_BIN;
     if (copy(file_txttmp, file_txt) != 0) {
-        return -3;
+        return -6;
     }
     if (copy(file_bintmp, file_bin) != 0) {
-        return -4;
+        return -7;
     }
     /*
      * Delete the temporary files.
@@ -187,10 +211,10 @@
     HTTPFile file(local_file.c_str());
     int r = client.get(src_url.c_str(), &file);
     if (r != HTTP_OK) {
-        LOG("Fetch '%s' from '%s' failed.\n", local_file.c_str(), src_url.c_str());
+        LOG("ERR : Fetch '%s' to '%s'.\n", src_url.c_str(), local_file.c_str());
         return -1;
     }
-    LOG("Fetch '%s' from '%s' succeed.\n", local_file.c_str(), src_url.c_str());
+    LOG("INFO: Fetch '%s' to '%s'.\n", src_url.c_str(), local_file.c_str());
     return 0;
 }
 
@@ -203,18 +227,16 @@
  * @return Return 0 if it succeed.
  */
 int FirmwareUpdater::copy(std::string local_file1, std::string local_file2) {
-
-    LOG("File copying... (%s->%s)\n", local_file1.c_str(), local_file2.c_str());
-
+    LOG("INFO: File copying... (%s->%s)\n", local_file1.c_str(), local_file2.c_str());
     FILE *rp = fopen(local_file1.c_str(), "rb");
     if (rp == NULL) {
-        LOG("File '%s' open failed.\n", local_file1.c_str());
+        LOG("ERR : File '%s' open failed.\n", local_file1.c_str());
         return -1;
     }
     remove(local_file2.c_str());
     FILE *wp = fopen(local_file2.c_str(), "wb");
     if (wp == NULL) {
-        LOG("File '%s' open failed.\n", local_file2.c_str());
+        LOG("ERR : File '%s' open failed.\n", local_file2.c_str());
         fclose(rp);
         return -2;
     }
@@ -224,7 +246,7 @@
     }
     fclose(rp);
     fclose(wp);
-    LOG("File copied. (%s->%s)\n", local_file1.c_str(), local_file2.c_str());
+    LOG("INFO: File copied. (%s->%s)\n", local_file1.c_str(), local_file2.c_str());
     return 0;
 }
 
@@ -242,8 +264,80 @@
             va_start(p, format);
             vsnprintf(buf, sizeof(buf) - 1, format, p);
             fprintf(fplog, "%s", buf);
+
+            printf("%s", buf);
+
             va_end(p);
             fclose(fplog);
         }
     }
 }
+
+/**
+ * Cleanup all bin files.
+ */
+int FirmwareUpdater::cleanupAllBinFiles(void) {
+    struct dirent *p;
+    DIR *dir = opendir("/local");
+    if (dir == NULL) {
+        return -1;
+    }
+    while ((p = readdir(dir)) != NULL) {
+        char *str = p->d_name;
+        if ((strstr(str, ".bin") != NULL) || (strstr(str, ".BIN") != NULL)) {
+            char buf[BUFSIZ];
+            snprintf(buf, sizeof(buf) - 1, "/local/%s", str);
+            if (remove(buf) == 0) {
+                LOG("INFO: Deleted '%s'.\n", buf);
+            } else {
+                LOG("ERR : Delete '%s' failed.\n", buf);
+            }
+        }
+    }
+    closedir(dir);
+    return 0;
+}
+
+/**
+ * Read a version from a file.
+ *
+ * @param filename file name.
+ * @return A version.
+ */
+int FirmwareUpdater::readVersionFromFile(const char *filename) {
+    int ver;
+    FILE *fp = fopen(filename, "rb");
+    if (fp == NULL) {
+        LOG("ERR : Version file '%s' open failed.\n", filename);
+        return -1;
+    }
+    if (fscanf(fp, "%d", &ver) != 1) {
+        LOG("ERR : Version file '%s' is invalid.\n", filename);
+        return -2;
+    }
+    fclose(fp);
+    LOG("INFO: Version file '%s': Version %d.\n", filename, ver);
+    return ver;
+}
+
+/**
+ * Read a version from a URL.
+ *
+ * @param url URL.
+ * @return A version.
+ */
+int FirmwareUpdater::readVersionFromURL(const char *url) {
+    int ver;
+    HTTPText text;
+    HTTPResult r = client.get(url, &text);
+    if (r != HTTP_OK) {
+        LOG("ERR : Version file '%s' open failed.\n", url);
+        return -1;
+    }
+    if (sscanf(text.gets(), "%d", &ver) != 1) {
+        LOG("ERR : Version file '%s' is invalid.\n", url);
+        return -2;
+    }
+    LOG("INFO: Version file '%s': Version %d.\n", url, ver);
+    return ver;
+}