/* * $Id: dlm.c,v 1.7 1995/05/11 21:38:36 kenh Exp $ * * dlm - downloader program for MIT Miniboard * * Written by Ken Hornstein * * Based on the program "dlm20.c" by Fred Martin, Randy Sargent, and Henry Q. * Minksy. * */ #ifndef LINT static char rcsid[] = "$Id: dlm.c,v 1.7 1995/05/11 21:38:36 kenh Exp $"; #endif #include #include #include #include #include #include #include #include /* * Some fixes for slightly bogus systems */ #ifdef BROKEN #ifdef sun #include #endif extern char *optarg; extern int optind, opterr; extern char *sys_errlist[]; #define strerror(x) sys_errlist[x] void cfmakeraw(struct termios *); #endif /* * Function prototypes */ void usage(void); void serial_init(void); void serial_speed(speed_t); void serial_putchar(unsigned char); void serial_putchar_getecho(unsigned char); int serial_getchar(void); int serial_getchar_nowait(void); int gethex(char *, unsigned char **, int *); int sync_with_board(void); /* * Set up a few defaults */ #ifndef DEFAULT_SERIAL_PORT #define DEFAULT_SERIAL_PORT "/dev/tty00" /* Default serial port */ #endif #define SERIAL_PORT_LENGTH 100 char port[SERIAL_PORT_LENGTH]; int bs_feedback = 0; /* If true, expect serial echo during 6811 bootstrap */ int hardware_echo = 1; /* If true, expect serial hardware echo during normal download */ int serial_debug = 0; /* If true, print out serial debugging info */ int bulk_erase = 0; /* If true, bulk erase before download */ int tty_fd; /* File descriptor of serial device */ int lineno; /* Line number of current file */ char twiddle[] = "|/-\\"; /* Totally pointless */ #define TWDIV 8 #define BUFFER_SIZE 255 /* * This is the default bootstrap program used for the serial download. This * program gets loaded into RAM starting at $0000, and then gets executed. * It does the actual work of burning the program into EEPROM. It is important * that each S-record is newline delimited. */ char bootstring[] = "S12300008E00FF4FB71035CE10001F2E80FC1D282086B0A72B860CA72D8D2C20FC7F00EC4A\n" "S123002020058601B700EC8D5F167D00EC27048D102006183C8D5F183808180926E93918FB\n" "S12300408FE700A600202D863E8D298D3B817327F6368D2C8F8D29188F32817227E58170E3\n" "S12300602732815727BC815027B381422741817727CD863F3CCE10001F2E80FCA72F3839B7\n" "S12300808D06368D031632393CCE10001F2E20FCA62F3839188FE10027A9A60043F700ED94\n" "S12300A0B400ED270486168D2486028D2020948606B7103BB7103F8A01B7103B183CBD0008\n" "S12300C0DD18387F103B188F8602CE103FB7103BE7008A01B7103B8D0A7F103B3918CE6F74\n" "S10F00E09A200418CE0B29180926FC39BC\n" "S9030000FC\n"; int main(int argc, char *argv[]) { int opt, i, bytes, total_bytes = 0, address; int tlen = strlen(twiddle); char *b = bootstring; unsigned char *buffer, byte; char input_buffer[BUFFER_SIZE]; FILE *f; strncpy(port, DEFAULT_SERIAL_PORT, SERIAL_PORT_LENGTH); printf("DLM: 6811 File downloader with Intelligent EEPROM burn by " "Ken Hornstein\n"); printf("Based on original DLM program by Fred Martin\n"); while((opt = getopt(argc, argv, "p:db")) != -1) switch (opt) { case 'p': strncpy(port, optarg, SERIAL_PORT_LENGTH); break; case 'd': serial_debug++; break; case 'b': bulk_erase++; break; case 'm': hardware_echo = 1; bs_feedback = 0; break; case 'n': hardware_echo = 0; bs_feedback = 1; break; case 'h': case '?': default: usage(); } if (optind != (argc - 1)) usage(); if ((f = fopen(argv[optind], "r")) == NULL) { fprintf(stderr, "Could not open \"%s\": %s\n", argv[optind], strerror(errno)); exit(1); } serial_init(); serial_speed(B1200); printf("Downloading eeprom loader to RAM at 1200 baud ... "); /* * First byte tells the 68HC11 to accept a program to put into RAM * (which only works if it's in special bootstrap mode) */ serial_putchar(0xff); lineno = 1; /* * Download the bootstrap EEPROM burner */ while(1) { if ((bytes = gethex(b, &buffer, NULL)) == 0) goto nextbootstrap; for (i = 0; i < bytes; i++) { serial_putchar(buffer[i]); if (bs_feedback) if (serial_getchar() != buffer[i]) { fprintf(stderr, "\nData mismatch when " "downloading bootstrap code\n"); exit(1); } printf("\b%c", twiddle[((total_bytes++)/TWDIV)%tlen]); fflush(stdout); } nextbootstrap: lineno++; if ((b = index(b, '\n')) == NULL) break; b++; if (*b == '\0') break; } /* * We have to send 256 bytes during the boostrap phase, so send enough * NULs to fill in the gap */ for(i = total_bytes; i <= 255; i++) { serial_putchar(0x00); printf("\b%c", "X+.+"[(i / TWDIV) % 4]); fflush(stdout); } printf("\n"); /* * Presumably now the bootstrap program is running. Set the serial * port to run at 9600 baud now. */ serial_speed(B9600); if (!sync_with_board()) exit(1); if (bulk_erase) { printf("Bulk erasing EEPROM..."); fflush(stdout); serial_putchar_getecho('B'); serial_putchar_getecho(0); serial_putchar_getecho(0); serial_putchar_getecho(0); serial_putchar_getecho(0xF5); if (serial_getchar() != '>') { printf("Board synchronization error during erase!\n"); exit(1); } else printf("OK\n"); } printf("Sending %s to EEPROM at 9600 baud ... ", argv[optind]); lineno = 1; total_bytes = 0; while (!feof(f)) { if (fgets(input_buffer, BUFFER_SIZE, f) == NULL) goto nextdownload; if ((bytes = gethex(input_buffer, &buffer, &address)) == 0) goto nextdownload; serial_putchar_getecho('P'); serial_putchar_getecho((address >> 8) & 0xff); serial_putchar_getecho(address & 0xff); serial_putchar_getecho(0x00); serial_putchar_getecho(bytes); for(i = 0; i < bytes; i++) { serial_putchar_getecho(buffer[i]); printf("\b%c", twiddle[((total_bytes++)/TWDIV)%tlen]); fflush(stdout); byte = serial_getchar(); if (byte != buffer[i]) printf("Board memory error: wanted 0x%02x, " "got 0x%02x at address 0x%04x\n", buffer[i], byte, address); } if (serial_getchar() != '>') { printf("Board synchronization error.\n"); exit(1); } nextdownload: lineno++; } printf("\nUser program downloaded successfully\n"); exit(0); } /* * Initialize the serial port */ void serial_init() { struct termios tp; if ((tty_fd = open(port, O_RDWR | O_NONBLOCK, 0)) == -1) { fprintf(stderr, "Cannot open serial port \"%s\": %s\n", port, strerror(errno)); exit(1); } fcntl(tty_fd, F_SETFL, fcntl(tty_fd, F_GETFL, NULL) & ~O_NDELAY); tcgetattr(tty_fd, &tp); cfmakeraw(&tp); tp.c_cflag |= CLOCAL; tcsetattr(tty_fd, TCSANOW, &tp); return; } /* * Set the speed on the serial port */ void serial_speed(speed_t speed) { struct termios tp; tcgetattr(tty_fd, &tp); cfsetispeed(&tp, speed); cfsetospeed(&tp, speed); tcsetattr(tty_fd, TCSADRAIN, &tp); return; } /* * Synchronize with the board */ int sync_with_board() { int i; printf("Synchronizing with board ..."); fflush(stdout); /* * Flush out whatever is in the terminal buffer so we get to a known * state */ tcflush(tty_fd, TCIOFLUSH); /* * Send a couple of "s" commands and make sure we get a prompt * character. Keep trying until we are in step with the board. */ serial_putchar('s'); serial_putchar('s'); for (i = 0; i < 20; i++) { int ch, lch; while(1) { ch = serial_getchar_nowait(); if (ch == -1) break; lch = ch; } if (serial_debug) fprintf(stderr, "Current value of lch is: %c\n", lch); if (lch == '>') { tcflush(tty_fd, TCIOFLUSH); break; } serial_putchar('s'); printf("."); fflush(stdout); /* * Wait for 1/10 of a second, and try again */ usleep(100000); } if (i == 20) { printf("failed.\n"); return 0; } else { printf("ok\n"); return 1; } } /* * Take one line of an S-record and convert it into a hex string */ int gethex(char *line, unsigned char **retval, int *address) { int i, checksum = 0, addr; unsigned int bytes, byte; static unsigned char hexbuf[255]; if (line[0] != 'S') { fprintf(stderr, "Line number %d is not an S-record; " "aborting\n", lineno); exit(1); } if (line[1] != '1' && line[1] != '9') { fprintf(stderr, "Line number %d is a type %c S-record.\n" "DLM only handles types 1 and 9. Aborting\n", lineno, line[1]); exit(1); } if (line[1] == '9') return 0; if (sscanf(&line[2], "%2x", &bytes) != 1) { fprintf(stderr, "Non-hex digit in S-record on line %d, " "aborting\n", lineno); exit(1); } checksum += bytes; line += 4; if (sscanf(line, "%4x", &addr) != 1) { fprintf(stderr, "Non-hex digit in S-record on line %d, " "aborting\n", lineno); exit(1); } checksum += (addr & 0xff) + ((addr >> 8) & 0xff); line += 4; if (address) *address = addr; for(i = 0; i < bytes - 3; i++) { if (sscanf(line, "%2x", &byte) != 1) { fprintf(stderr, "Non-hex digit in S-record on line " "%d, aborting\n", lineno); exit(1); } hexbuf[i] = byte; checksum += byte; line += 2; } if (sscanf(line, "%2x", &byte) != 1) { fprintf(stderr, "Non-hex digit in S-record on line " "%d, aborting\n", lineno); exit(1); } if (byte != (~checksum & 0xff)) { fprintf(stderr, "Warning: invalid checksum on line %d " "(given: %02x, computed: %02x)\n", lineno, byte, ~checksum & 0xff); } *retval = hexbuf; return bytes - 3; } /* * Send one byte to the serial device. The tcdrain is so we don't get too * far ahead of ourselves in terms of buffering. */ void serial_putchar(unsigned char c) { if (serial_debug) fprintf(stderr, "Sending 0x%02x to serial port\n", (int) c); /* * Drain before sending the character, not after. This gives us a * chance to do some work while the character is being sent. */ tcdrain(tty_fd); write(tty_fd, &c, 1); return; } /* * Put a character, but get the echo back (so we can make sure we're in sync) * We only really get an echo if we're using the miniboard, since that has * a hardware echo. Don't get the echo if we're using other boards. */ void serial_putchar_getecho(unsigned char c) { int echo; serial_putchar(c); if (hardware_echo) if ((echo = serial_getchar()) != c) { fprintf(stderr, "Serial echo error (wanted %02x, " "got %02x)\n", c, echo); exit(1); } } /* * Get a character from the serial port */ int serial_getchar() { unsigned char c; int s; if ((s = read(tty_fd, &c, 1)) == -1) { perror("read"); exit(1); } else if (s == 0) { if (serial_debug) fprintf(stderr, "Read on serial port returned EOF\n"); return EOF; } else { if (serial_debug) fprintf(stderr, "Read returned 0x%02x\n", c); return c; } } /* * Get a character from the serial port, but do not block getting it */ int serial_getchar_nowait() { int flags; char c; flags = fcntl(tty_fd, F_GETFL, 0); fcntl(tty_fd, F_SETFL, flags | O_NDELAY); if (read(tty_fd, &c, 1) == -1) { if (serial_debug) fprintf(stderr, "read failed with an error of: %s\n", strerror(errno)); fcntl(tty_fd, F_SETFL, flags); return -1; } fcntl(tty_fd, F_SETFL, flags); if (serial_debug) fprintf(stderr, "read returned %c\n", c); return c; } void usage() { printf("Usage: dlm [-h] [-p device] [-b] [-d] [-m] [-n] filename\n\n"); printf("\t-p device Name of serial line device for download\n" "\t\t\t(default: %s)\n", port); printf("\t-h This message\n"); printf("\t-b Bulk erase EEPROM before programming\n"); printf("\t-d Turn on serial port debugging\n"); printf("\t-m Use MiniBoard serial port semantics " "(default)\n"); printf("\t-n Use normal 68HC11 serial port semantics\n"); exit(2); } #ifdef BROKEN /* * Make a pre-existing termios structure into "raw" mode: character-at-a-time * mode with no characters interpreted, 8-bit data path. * Note that this function is Copyright (c) 1989, 1993, The Regents of the * University of California. All rights reserved. */ void cfmakeraw(t) struct termios *t; { t->c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL|IXON); t->c_oflag &= ~OPOST; t->c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN); t->c_cflag &= ~(CSIZE|PARENB); t->c_cflag |= CS8; /* XXX set MIN/TIME */ } #endif