Diff: FirmwareUpdater.cpp
- Revision:
- 0:48870d877970
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/FirmwareUpdater.cpp Mon Sep 19 10:25:22 2011 +0000
@@ -0,0 +1,374 @@
+/**
+ * =============================================================================
+ * Firmware updater (Version 0.0.2)
+ * =============================================================================
+ * Copyright (c) 2010 Shinichiro Nakamura (CuBeatSystems)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * 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.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ * =============================================================================
+ */
+
+#include "FirmwareUpdater.h"
+
+#include <stdio.h>
+#include <stdarg.h>
+#include "MSCFileSystem.h"
+
+extern "C" void mbed_reset();
+
+const std::string FirmwareUpdater::EXT_BIN = ".bin";
+const std::string FirmwareUpdater::EXT_BINTMP = ".b__";
+const std::string FirmwareUpdater::EXT_TXT = ".txt";
+const std::string FirmwareUpdater::EXT_TXTTMP = ".t__";
+MSCFileSystem fs ("fs");
+LocalFileSystem local ("local");
+
+/**
+ * Create.
+ *
+ * @param url URL for firmware. Do not include a target file name.
+ * @param name An application name. Do not include a extention.
+ * @param log True if logging.
+ */
+FirmwareUpdater::FirmwareUpdater(std::string src_name, std::string dest_name, bool log)
+ : src_name(src_name), dest_name(dest_name), log(log), local("local") {
+ //client.setTimeout(10000);
+
+ /*
+ * A file name on the mbed local file system should keep '8 + 3' types of name.
+ */
+ if (MAXNAMELEN < dest_name.length()) {
+ LOG("ERR : Invalid firmware name '%s' found. The maximum length is %d.\n", dest_name.c_str(), MAXNAMELEN);
+ error("ERR : Invalid firmware name '%s' found. The maximum length is %d.\n", dest_name.c_str(), MAXNAMELEN);
+ }
+}
+
+/**
+ * Dispose.
+ */
+FirmwareUpdater::~FirmwareUpdater() {
+}
+
+/**
+ * Get a URL.
+ *
+ * @return URL.
+ */
+const std::string FirmwareUpdater:: get_src_name() const {
+ return src_name;
+}
+
+/**
+ * Get a name.
+ *
+ * @return name.
+ */
+const std::string FirmwareUpdater:: get_dest_name() const {
+ return dest_name;
+}
+
+/**
+ * Checking a new firmware.
+ * Compare versions of the software between local storage on mbed and on USB-device.
+ *
+ * @return Return 0 if a new firmware exists.
+ */
+int FirmwareUpdater::exist() {
+ int ver_local, ver_USB;
+
+ /*
+ * Fetch the version from a local.
+ */
+ std::string file_local = "/local/" + dest_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_usb = "/fs/" + src_name + EXT_TXT;
+ ver_USB = readVersionFromUSB(file_usb.c_str());
+ if (ver_USB < 0) {
+ return -2;
+ }
+
+ return (ver_local < ver_USB) ? 0 : 1;
+}
+
+/**
+ * Execute update.
+ *
+ * @return Return 0 if it succeed.
+ */
+int FirmwareUpdater::execute() {
+ /*
+ * Fetch the files.
+ */
+ /*
+ std::string usb_txt = "/fs/" + src_name + EXT_TXT;
+ std::string file_txttmp = "/local/" + dest_name + EXT_TXTTMP;
+ if (fetch(usb_txt, file_txttmp) != 0) {
+ LOG("ERR : Aborted...\n");
+ return -1;
+ }
+ std::string usb_bin = "/fs/" + src_name + EXT_BIN;
+ std::string file_bintmp = "/local/" + dest_name + EXT_BINTMP;
+ if (fetch(usb_bin, file_bintmp) != 0) {
+ LOG("ERR : Aborted...\n");
+ return -2;
+ }
+ */
+
+ /*
+ * Check the firmware versions.
+ */
+ std::string dest_file_txt = "/local/" + dest_name + EXT_TXT;
+ std::string src_file_txt = "/fs/" + src_name + EXT_TXT;
+ int ver_old = readVersionFromFile(dest_file_txt.c_str());
+ int ver_new = readVersionFromFile(src_file_txt.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.
+ */
+ char nn[32];
+ createNewBinName(ver_new, nn, sizeof(nn));
+ LOG("INFO: generate src name & dest name ... (%d)", ver_new);
+ std::string src_file_bin = "/fs/" + src_name + EXT_BIN;
+ std::string dest_file_bin = "/local/" + dest_name + EXT_BIN;
+ if (copy(src_file_txt, dest_file_txt) != 0) {
+ return -6;
+ }
+
+ if (copy(src_file_bin, dest_file_bin) != 0) {
+ return -7;
+ }
+ /*
+ * Delete the temporary files.
+ */
+ //remove(file_txttmp.c_str());
+ //remove(file_bintmp.c_str());
+ return 0;
+}
+
+/**
+ * Reset system.
+ */
+void FirmwareUpdater::reset() {
+ mbed_reset();
+}
+
+/**
+ * Fetch a file.
+ *
+ * @param src_url URL of a source file.
+ * @param local_file Local file name.
+ *
+ * @return Return 0 if it succeed.
+ */
+//int FirmwareUpdater::fetch(std::string src_usb, std::string local_file) {
+ /*
+ * Fetch the source file from URL to a temporary file on local.
+ *
+ HTTPFile file(local_file.c_str());
+ int r = client.get(src_url.c_str(), &file);
+ if (r != HTTP_OK) {
+ LOG("ERR : Fetch '%s' to '%s'.\n", src_url.c_str(), local_file.c_str());
+ return -1;
+ }
+ LOG("INFO: Fetched '%s' to '%s'.\n", src_url.c_str(), local_file.c_str());*/
+ //return 0;
+//}
+
+/**
+ * Copy a file.
+ *
+ * @param local_file1 Source file.
+ * @param local_file2 Destination file.
+ *
+ * @return Return 0 if it succeed.
+ */
+int FirmwareUpdater::copy(std::string local_file1, std::string local_file2) {
+ 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("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("ERR : File '%s' open failed.\n", local_file2.c_str());
+ fclose(rp);
+ return -2;
+ }
+ int c;
+ while ((c = fgetc(rp)) != EOF) {
+ fputc(c, wp);
+ }
+ fclose(rp);
+ fclose(wp);
+ LOG("INFO: File copied. (%s->%s)\n", local_file1.c_str(), local_file2.c_str());
+ return 0;
+}
+
+/**
+ * Output a message to a log file.
+ *
+ * @param format ...
+ */
+void FirmwareUpdater::LOG(const char* format, ...) {
+ if (log) {
+ FILE *fplog = fopen("/local/update.log", "a");
+ if (fplog != NULL) {
+ char buf[BUFSIZ];
+ va_list p;
+ va_start(p, format);
+ vsnprintf(buf, sizeof(buf) - 1, format, p);
+ fprintf(fplog, "%s", buf);
+ // printf("%s", buf); /* If you want to check a message from a console. */
+ 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;
+}
+
+/*
+ * Create a new binary file name.
+ *
+ * @param ver Version.
+ * @param buf A pointer to a buffer.
+ * @param siz A size of the buffer.
+ *
+ * @return Return 0 if it succeed.
+ */
+int FirmwareUpdater::createNewBinName(const int ver, char *buf, size_t siz) {
+ if (siz <= dest_name.length()) {
+ return -1;
+ }
+ snprintf(buf, siz - 1, "%s", dest_name.c_str());
+ char nb[32];
+ snprintf(nb, sizeof(nb) - 1, "-%d", ver);
+ if (strlen(buf) + strlen(nb) <= MAXNAMELEN) {
+ strcat(buf, nb);
+ return 0;
+ } else {
+ strcpy(buf + (MAXNAMELEN - strlen(nb)), nb);
+ return 0;
+ }
+}
+
+/**
+ * Read a version from a file on local FileSystem.
+ *
+ * @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 file on USB FileSystem.
+ *
+ * @param filename file name.
+ * @return A version.
+ */
+int FirmwareUpdater::readVersionFromUSB(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;
+}
\ No newline at end of file