#include #include #include #include #include #include #include #include #include #include #include #include #define PACKET_SIZE 8 #define LEN(x) (sizeof(x) / sizeof(x[0])) typedef unsigned char uchar; typedef unsigned short ushort; typedef unsigned int uint; typedef unsigned long ulong; typedef unsigned long long ulonglong; enum verbose_level { NONE, DEBUG, WARNING, ERROR }; // in bootloader spam \n or anything? until some buffer is full speed_t baud = 0; char port[32] = {0}; int serial_fd = -1; FILE *file = NULL; const char *flush = "aaaaaaaaaaaaa\n"; int verbose = 0; void help() { puts("help"); exit(EXIT_SUCCESS); } void die() { if (file) fclose(file); if (serial_fd < 0) close(serial_fd); exit(EXIT_FAILURE); } int log_msg(int lvl, const char *fmt, ...) { int ret = 0; va_list args; if (verbose >= lvl) { va_start(args, fmt); ret = vprintf(fmt, args); fflush(stdout); va_end(args); } return ret; } struct baud_pair { const char *b; speed_t c; }; // Conversion table for baud rates const struct baud_pair baud_rates[] = { {"1200", B1200}, {"2400", B2400}, {"4800", B4800}, {"9600", B9600}, {"19200", B19200}, }; int parse_argv(int argc, char *const argv[]) { struct option long_opts[] = { { "baud", required_argument, 0, 'b'}, { "verbose", no_argument, 0, 'v'}, { "port", required_argument, 0, 'p'}, { "help", no_argument, 0, 'h'}, { 0, 0, 0, 0 } }; int long_index = 0; int c; while ((c = getopt_long(argc, argv, "b:p:h", long_opts, &long_index)) != -1) { switch (c) { case 'b': for (int i = 0; i < LEN(baud_rates); i++) { if (!strcmp(baud_rates[i].b, optarg)) { baud = baud_rates[i].c; break; } } if (baud == 0) { fprintf(stderr, "Error: Invalid baud rate '%s'\n", optarg); exit(EXIT_FAILURE); } break; case 'v': verbose = 1; break; case 'p': strncpy(port, optarg, LEN(port)); break; case 'h': help(); break; case '?': help(); break; default: break; } } if (argc <= optind) { fprintf(stderr, "Error: No file was provided to upload\n\n"); help(); } return optind; } int open_tty(const char *port) { int fd = open(port, O_RDWR); if (fd < 0) { fprintf(stderr, "Error: Port '%s' could not be opened\n", port); exit(EXIT_FAILURE); } struct termios term; tcgetattr(fd, &term); cfmakeraw(&term); term.c_cc[VTIME] = 1; term.c_cc[VMIN] = 0; term.c_lflag &= ~OCRNL; cfsetspeed(&term, baud); tcsetattr(fd, TCSANOW, &term); return fd; } ssize_t read_line(int fd, uchar *buf, size_t len) { int ret = 0; ssize_t bytes_read = 0; // The Serial port runs at ~9600 baud. This loop, as inefficient as it is, // won' t ever be a bottleneck for (int i = 0; bytes_read < len; i++) { ssize_t rd = read(fd, &buf[bytes_read], 1); if (rd < 0) { fprintf(stderr, "Error: read from port failed\n%s\n", strerror(errno)); die(); } if (rd == 0) return -1; bytes_read += rd; if (buf[bytes_read - 1] == '\n') break; } buf[bytes_read] = 0; return bytes_read; } void flush_bootloader(int serial_fd) { /* Clean up */ // tcflush(serial_fd, TCIOFLUSH); /* Send empty line to get a ready prompt from bootloader */ write(serial_fd, flush, strlen(flush)); tcdrain(serial_fd); } /* * Get the bootloader in a ready state, cleaning up any previous errors */ void begin_command(int serial_fd) { char buf[32]; int timeout = 1; buf[0] = 0; while (timeout) { /* Comparing should stop after \n even if buf is undefined */ while (strcmp("ready\r\n", buf) != 0) { if (read_line(serial_fd, buf, LEN(buf)) < 0) { timeout = 1; log_msg(DEBUG, "timeout\n%32s\n", buf); flush_bootloader(serial_fd); break; } else { timeout = 0; } } } } void send_packet(int index, uint address, int plen, const uchar *packet) { char obuf[128]; int len = sprintf(obuf, "W %04X %02X ", address, plen); for (int i = 0; i < plen; i++) len += sprintf(&obuf[len], "%02X", packet[i]); obuf[len++] = '\r'; obuf[len++] = '\n'; obuf[len] = '\0'; int cmp = 1; char ibuf[128]; char ebuf[128]; for (int attempt = 1; cmp != 0; attempt++) { log_msg(DEBUG, "Sending packet %d, attempt %d\n", index, attempt); begin_command(serial_fd); write(serial_fd, obuf, len); if((read_line(serial_fd, ibuf, LEN(ibuf)) == -1) || (read_line(serial_fd, ebuf, LEN(ebuf)) == -1)) { continue; } log_msg(DEBUG, "sent: %s", obuf); log_msg(DEBUG, "recv: %s", ibuf); log_msg(DEBUG, "ebuf: %s", ebuf); cmp = strncmp(ibuf, obuf, LEN(ibuf)); if (cmp != 0) { usleep(100); } } } int upload_file(FILE *file, int serial_fd) { int file_size = 0; fseek(file, 0, SEEK_END); file_size = ftell(file); fseek(file, 0, SEEK_SET); int total = (file_size / PACKET_SIZE + ((file_size % PACKET_SIZE) == 0 ? 0 : 1)); printf("Sending %d bytes\n", file_size); printf("Progress: [ %3d%% ]", 0); fflush(stdout); uint address = 0x4000; uchar packet[PACKET_SIZE]; for (int i = 1; !feof(file); i++) { size_t n = fread(packet, sizeof(uchar), LEN(packet), file); if (n > 0) { send_packet(i, address, n, packet); address += n; } printf("\rProgress: [ %3d%% ]", 100 * i / total); fflush(stdout); } return 0; } int main(int argc, char *argv[]) { int ind = parse_argv(argc, argv); serial_fd = open_tty(port); FILE *file = fopen(argv[ind], "r"); if (!file) { fprintf(stderr, "Error: File '%s' could not be opened\n", argv[ind]); close(serial_fd); exit(EXIT_FAILURE); } tcflush(serial_fd, TCIOFLUSH); flush_bootloader(serial_fd); upload_file(file, serial_fd); puts("\ndone"); fclose(file); close(serial_fd); exit(EXIT_SUCCESS); }