Markus Paar
/
firmwareV3
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