diff options
Diffstat (limited to 'commandline/micronucleus.c')
-rw-r--r-- | commandline/micronucleus.c | 427 |
1 files changed, 427 insertions, 0 deletions
diff --git a/commandline/micronucleus.c b/commandline/micronucleus.c new file mode 100644 index 0000000..c6d122a --- /dev/null +++ b/commandline/micronucleus.c @@ -0,0 +1,427 @@ +/* + Created: September 2012 + by ihsan Kehribar <ihsan@kehribar.me> + + 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 <stdio.h> +#include <errno.h> +#include <string.h> +#include <stdlib.h> +#include <time.h> +#include "micronucleus_lib.h" +#include "littleWire_util.h" + +#define FILE_TYPE_INTEL_HEX 1 +#define FILE_TYPE_RAW 2 +#define CONNECT_WAIT 250 /* milliseconds to wait after detecting device on usb bus - probably excessive */ + +/****************************************************************************** +* Global definitions +******************************************************************************/ +unsigned char dataBuffer[65536 + 256]; /* buffer for file data */ +/*****************************************************************************/ + +/****************************************************************************** +* Function prototypes +******************************************************************************/ +static int parseRaw(char *hexfile, unsigned char *buffer, int *startAddr, int *endAddr); +static int parseIntelHex(char *hexfile, unsigned char *buffer, int *startAddr, int *endAddr); /* taken from bootloadHID example from obdev */ +static int parseUntilColon(FILE *fp); /* taken from bootloadHID example from obdev */ +static int parseHex(FILE *fp, int numDigits); /* taken from bootloadHID example from obdev */ +static void printProgress(float progress); +static void setProgressData(char* friendly, int step); +static int progress_step = 0; // current step +static int progress_total_steps = 0; // total steps for upload +static char* progress_friendly_name; // name of progress section +static int dump_progress = 0; // output computer friendly progress info +static int use_ansi = 0; // output ansi control character stuff +static int erase_only = 0; // only erase, dont't write file +static int timeout = 0; +/*****************************************************************************/ + +/****************************************************************************** +* Main function! +******************************************************************************/ +int main(int argc, char **argv) { + int res; + char *file = NULL; + micronucleus *my_device = NULL; + + // parse arguments + int run = 0; + int file_type = FILE_TYPE_INTEL_HEX; + int arg_pointer = 1; + char* usage = "usage: micronucleus [--run] [--dump-progress] [--type intel-hex|raw] [--no-ansi] [--timeout integer] [--erase-only] filename"; + progress_step = 0; + progress_total_steps = 5; // steps: waiting, connecting, parsing, erasing, writing, (running)? + dump_progress = 0; + erase_only = 0; + timeout = 0; // no timeout by default + //#if defined(WIN) + // use_ansi = 0; + //#else + use_ansi = 1; + //#endif + + while (arg_pointer < argc) { + if (strcmp(argv[arg_pointer], "--run") == 0) { + run = 1; + progress_total_steps += 1; + } else if (strcmp(argv[arg_pointer], "--type") == 0) { + arg_pointer += 1; + if (strcmp(argv[arg_pointer], "intel-hex") == 0) { + file_type = FILE_TYPE_INTEL_HEX; + } else if (strcmp(argv[arg_pointer], "raw") == 0) { + file_type = FILE_TYPE_RAW; + } else { + printf("Unknown File Type specified with --type option"); + return EXIT_FAILURE; + } + } else if (strcmp(argv[arg_pointer], "--help") == 0 || strcmp(argv[arg_pointer], "-h") == 0) { + puts(usage); + puts(""); + puts(" --type [intel-hex, raw]: Set upload file type to either intel hex or raw"); + puts(" bytes (intel hex is default)"); + puts(" --dump-progress: Output progress data in computer-friendly form"); + puts(" for driving GUIs"); + puts(" --erase-only: Erase the device without programming. Fills the"); + puts(" program memory with 0xFFFF. Any files are ignored."); + puts(" --run: Ask bootloader to run the program when finished"); + puts(" uploading provided program"); + //#ifndef WIN + puts(" --no-ansi: Don't use ANSI in terminal output"); + //#endif + puts(" --timeout [integer]: Timeout after waiting specified number of seconds"); + puts(" filename: Path to intel hex or raw data file to upload,"); + puts(" or \"-\" to read from stdin"); + return EXIT_SUCCESS; + } else if (strcmp(argv[arg_pointer], "--dump-progress") == 0) { + dump_progress = 1; + } else if (strcmp(argv[arg_pointer], "--no-ansi") == 0) { + use_ansi = 0; + } else if (strcmp(argv[arg_pointer], "--erase-only") == 0) { + erase_only = 1; + progress_total_steps -= 1; + } else if (strcmp(argv[arg_pointer], "--timeout") == 0) { + arg_pointer += 1; + if (sscanf(argv[arg_pointer], "%d", &timeout) != 1) { + printf("Did not understand --timeout value\n"); + return EXIT_FAILURE; + } + } else { + file = argv[arg_pointer]; + } + + arg_pointer += 1; + } + + if (argc < 2) { + puts(usage); + return EXIT_FAILURE; + } + + setProgressData("waiting", 1); + if (dump_progress) printProgress(0.5); + printf("> Please plug in the device ... \n"); + printf("> Press CTRL+C to terminate the program.\n"); + + + time_t start_time, current_time; + time(&start_time); + + while (my_device == NULL) { + delay(100); + my_device = micronucleus_connect(); + + time(¤t_time); + if (timeout && start_time + timeout < current_time) { + break; + } + } + + if (my_device == NULL) { + printf("> Device search timed out\n"); + return EXIT_FAILURE; + } + + printf("> Device is found!\n"); + + // wait for CONNECT_WAIT milliseconds with progress output + float wait = 0.0f; + setProgressData("connecting", 2); + while (wait < CONNECT_WAIT) { + printProgress((wait / ((float) CONNECT_WAIT)) * 0.9f); + wait += 50.0f; + delay(50); + } + + //my_device = micronucleus_connect(); + printProgress(1.0); + + // if (my_device->page_size == 64) { + // printf("> Device looks like ATtiny85!\n"); + // } else if (my_device->page_size == 32) { + // printf("> Device looks like ATtiny45!\n"); + // } else { + // printf("> Unsupported device!\n"); + // return EXIT_FAILURE; + // } + + printf("> Available space for user application: %d bytes\n", my_device->flash_size); + printf("> Suggested sleep time between sending pages: %ums\n", my_device->write_sleep); + printf("> Whole page count: %d\n", my_device->pages); + printf("> Erase function sleep duration: %dms\n", my_device->erase_sleep); + + int startAddress = 1, endAddress = 0; + + if (!erase_only) { + setProgressData("parsing", 3); + printProgress(0.0); + memset(dataBuffer, 0xFF, sizeof(dataBuffer)); + + if (file_type == FILE_TYPE_INTEL_HEX) { + if (parseIntelHex(file, dataBuffer, &startAddress, &endAddress)) { + printf("> Error loading or parsing hex file.\n"); + return EXIT_FAILURE; + } + } else if (file_type == FILE_TYPE_RAW) { + if (parseRaw(file, dataBuffer, &startAddress, &endAddress)) { + printf("> Error loading raw file.\n"); + return EXIT_FAILURE; + } + } + + printProgress(1.0); + + if (startAddress >= endAddress) { + printf("> No data in input file, exiting.\n"); + return EXIT_FAILURE; + } + + if (endAddress > my_device->flash_size) { + printf("> Program file is %d bytes too big for the bootloader!\n", endAddress - my_device->flash_size); + return EXIT_FAILURE; + } + } + + printProgress(1.0); + + setProgressData("erasing", 4); + printf("> Erasing the memory ...\n"); + res = micronucleus_eraseFlash(my_device, printProgress); + + if (res == 1) { // erase disconnection bug workaround + printf(">> Eep! Connection to device lost during erase! Not to worry\n"); + printf(">> This happens on some computers - reconnecting...\n"); + my_device = NULL; + + delay(CONNECT_WAIT); + + int deciseconds_till_reconnect_notice = 50; // notice after 5 seconds + while (my_device == NULL) { + delay(100); + my_device = micronucleus_connect(); + deciseconds_till_reconnect_notice -= 1; + + if (deciseconds_till_reconnect_notice == 0) { + printf(">> (!) Automatic reconnection not working. Unplug and reconnect\n"); + printf(" device usb connector, or reset it some other way to continue.\n"); + } + } + + printf(">> Reconnected! Continuing upload sequence...\n"); + + } else if (res != 0) { + printf(">> Flash erase error %d has occured ...\n", res); + printf(">> Please unplug the device and restart the program.\n"); + return EXIT_FAILURE; + } + printProgress(1.0); + + if (!erase_only) { + printf("> Starting to upload ...\n"); + setProgressData("writing", 5); + res = micronucleus_writeFlash(my_device, endAddress, dataBuffer, printProgress); + if (res != 0) { + printf(">> Flash write error %d has occured ...\n", res); + printf(">> Please unplug the device and restart the program.\n"); + return EXIT_FAILURE; + } + } + + if (run) { + printf("> Starting the user app ...\n"); + setProgressData("running", 6); + printProgress(0.0); + + res = micronucleus_startApp(my_device); + + if (res != 0) { + printf(">> Run error %d has occured ...\n", res); + printf(">> Please unplug the device and restart the program. \n"); + return EXIT_FAILURE; + } + + printProgress(1.0); + } + + printf(">> Micronucleus done. Thank you!\n"); + + return EXIT_SUCCESS; +} +/******************************************************************************/ + +/******************************************************************************/ +static void printProgress(float progress) { + static int last_step; + static int last_integer_total_progress; + + if (dump_progress) { + printf("{status:\"%s\",step:%d,steps:%d,progress:%f}\n", progress_friendly_name, progress_step, progress_total_steps, progress); + } else { + if (last_step == progress_step && use_ansi) { + #ifndef WIN + printf("\033[1F\033[2K"); // move cursor to previous line and erase last update in this progress sequence + #else + printf("\r"); // return carriage to start of line so we can type over existing text + #endif + } + + float total_progress = ((float) progress_step - 1.0f) / (float) progress_total_steps; + total_progress += progress / (float) progress_total_steps; + int integer_total_progress = total_progress * 100.0f; + + if (use_ansi || integer_total_progress >= last_integer_total_progress + 5) { + printf("%s: %d%% complete\n", progress_friendly_name, integer_total_progress); + last_integer_total_progress = integer_total_progress; + } + } + + last_step = progress_step; +} + +static void setProgressData(char* friendly, int step) { + progress_friendly_name = friendly; + progress_step = step; +} +/******************************************************************************/ + +/******************************************************************************/ +static int parseUntilColon(FILE *file_pointer) { + int character; + + do { + character = getc(file_pointer); + } while(character != ':' && character != EOF); + + return character; +} +/******************************************************************************/ + +/******************************************************************************/ +static int parseHex(FILE *file_pointer, int num_digits) { + int iter; + char temp[9]; + + for(iter = 0; iter < num_digits; iter++) { + temp[iter] = getc(file_pointer); + } + temp[iter] = 0; + + return strtol(temp, NULL, 16); +} +/******************************************************************************/ + +/******************************************************************************/ +static int parseIntelHex(char *hexfile, unsigned char *buffer, int *startAddr, int *endAddr) { + int address, base, d, segment, i, lineLen, sum; + FILE *input; + + input = strcmp(hexfile, "-") == 0 ? stdin : fopen(hexfile, "r"); + if (input == NULL) { + printf("> Error opening %s: %s\n", hexfile, strerror(errno)); + return 1; + } + + while (parseUntilColon(input) == ':') { + sum = 0; + sum += lineLen = parseHex(input, 2); + base = address = parseHex(input, 4); + sum += address >> 8; + sum += address; + sum += segment = parseHex(input, 2); /* segment value? */ + if (segment != 0) { /* ignore lines where this byte is not 0 */ + continue; + } + + for (i = 0; i < lineLen; i++) { + d = parseHex(input, 2); + buffer[address++] = d; + sum += d; + } + + sum += parseHex(input, 2); + if ((sum & 0xff) != 0) { + printf("> Warning: Checksum error between address 0x%x and 0x%x\n", base, address); + } + + if(*startAddr > base) { + *startAddr = base; + } + if(*endAddr < address) { + *endAddr = address; + } + } + + fclose(input); + return 0; +} +/******************************************************************************/ + +/******************************************************************************/ +static int parseRaw(char *filename, unsigned char *data_buffer, int *start_address, int *end_address) { + FILE *input; + + input = strcmp(filename, "-") == 0 ? stdin : fopen(filename, "r"); + + if (input == NULL) { + printf("> Error reading %s: %s\n", filename, strerror(errno)); + return 1; + } + + *start_address = 0; + *end_address = 0; + + // read in bytes from file + int byte = 0; + while (1) { + byte = getc(input); + if (byte == EOF) break; + + *data_buffer = byte; + data_buffer += 1; + *end_address += 1; + } + + fclose(input); + return 0; +} +/******************************************************************************/ |