FreeCalypso > hg > freecalypso-tools
view loadtools/romload.c @ 898:203c025e71ab
CHANGES: fc-host-tools-r17 released
author | Mychaela Falconia <falcon@freecalypso.org> |
---|---|
date | Sun, 03 Apr 2022 19:09:21 +0000 |
parents | ecea01f65146 |
children |
line wrap: on
line source
/* * This module implements the communication protocol for pushing our * IRAM-loadable code to the Calypso ROM bootloader. */ #include <sys/types.h> #include <sys/ioctl.h> #include <sys/time.h> #include <sys/errno.h> #include <ctype.h> #include <stdint.h> #include <stdio.h> #include <stdlib.h> #include <strings.h> #include <termios.h> #include <unistd.h> #include "../libserial/baudrate.h" #include "srecreader.h" extern int errno; extern char *target_ttydev; extern int target_fd; extern struct baudrate baud_rate_table[]; extern struct baudrate *find_baudrate_by_name(); struct srecreader iramimage; struct baudrate *romload_baud_rate = baud_rate_table; /* 1st entry default */ static int beacon_interval = 13; /* in milliseconds */ static int beacon_timeout; /* ditto */ static u_char beacon_cmd[2] = {'<', 'i'}; static u_char param_cmd[11] = {'<', 'p', 0x00, /* baud rate select code (115200) */ 0x00, /* DPLL setup: leave it off like on power-up, */ /* OsmocomBB does the same thing */ 0x00, 0x04, /* chip select timing (WS) settings */ /* our setting matches both OsmocomBB */ /* and what the ROM runs with */ /* before receiving this command */ 0x22, /* FFFF:F900 register config, low byte */ /* OsmocomBB sends 0x00 here, but I've chosen */ /* 0x22 to match the setting of this register */ /* used by the boot ROM before this command. */ 0x00, 0x01, 0xD4, 0xC0 /* UART timeout */ /* I've chosen the same value as what the */ /* boot ROM runs with before getting this cmd */ }; static u_char write_cmd[1024] = {'<', 'w', 0x01, 0x01}; static u_char cksum_cmd[3] = {'<', 'c'}; static u_char branch_cmd[6] = {'<', 'b'}; static uint32_t write_cmd_endaddr; static unsigned write_cmd_datalen; static unsigned write_block_count; static uint16_t image_cksum; #define MAX_WRITE_LEN 0x3F6 #define INTERMEDIATE_TIMEOUT 500 /* ms to wait for responses */ #define SERIAL_FLUSH_DELAY 200 /* also in ms */ /* * The following function should be called by command line option * parsers upon encountering the -i option. */ set_beacon_interval(arg) char *arg; { int i; i = atoi(arg); if (i < 2 || i > 500) { fprintf(stderr, "invalid -i argument specified\n"); exit(1); } beacon_interval = i; } /* * The following function should be called by command line option * parsers upon encountering the -t option. */ set_romload_timeout(arg) char *arg; { beacon_timeout = atoi(arg); } /* * The following function should be called by command line option * parsers upon encountering the -b option. */ set_romload_baudrate(arg) char *arg; { struct baudrate *br; br = find_baudrate_by_name(arg); if (!br) exit(1); /* error msg already printed */ if (br->bootrom_code < 0) { fprintf(stderr, "baud rate of %s is not supported by the Calypso boot ROM\n", br->name); exit(1); } romload_baud_rate = br; } /* * The following functions alter some of the parameters sent to the * boot ROM in the <p command. */ set_romload_pll_conf(byte) { param_cmd[3] = byte; } set_romload_rhea_cntl(byte) { param_cmd[6] = byte; } static int expect_response(timeout) { char buf[2]; fd_set fds; struct timeval tv; int pass, cc; for (pass = 0; pass < 2; ) { FD_ZERO(&fds); FD_SET(target_fd, &fds); tv.tv_sec = 0; tv.tv_usec = timeout * 1000; cc = select(target_fd+1, &fds, NULL, NULL, &tv); if (cc < 0) { if (errno == EINTR) continue; perror("select"); exit(1); } if (cc < 1) return(-1); cc = read(target_fd, buf + pass, 2 - pass); if (cc <= 0) { perror("read after successful select"); exit(1); } if (pass == 0 && buf[0] != '>') continue; pass += cc; } return(buf[1]); } static send_beacons() { int time_accum; printf("Sending beacons to %s\n", target_ttydev); for (time_accum = 0; ; ) { write(target_fd, beacon_cmd, sizeof beacon_cmd); if (expect_response(beacon_interval) == 'i') return 0; time_accum += beacon_interval; if (beacon_timeout && time_accum >= beacon_timeout) { fprintf(stderr, "Timeout waiting for boot ROM response\n"); exit(1); } } } static int expect_add_response(buf, expect_bytes, timeout) u_char *buf; { fd_set fds; struct timeval tv; int pass, cc; for (pass = 0; pass < expect_bytes; ) { FD_ZERO(&fds); FD_SET(target_fd, &fds); tv.tv_sec = 0; tv.tv_usec = timeout * 1000; cc = select(target_fd+1, &fds, NULL, NULL, &tv); if (cc < 0) { if (errno == EINTR) continue; perror("select"); exit(1); } if (cc < 1) return(-1); cc = read(target_fd, buf + pass, expect_bytes - pass); if (cc <= 0) { perror("read after successful select"); exit(1); } pass += cc; } return(0); } static uint32_t compute_block_cksum() { uint32_t sum; int i, llen; sum = write_cmd_datalen + 5; llen = write_cmd_datalen + 4; for (i = 0; i < llen; i++) sum += write_cmd[i+6]; return sum; } static void send_write_cmd() { int resp; write_cmd[4] = write_cmd_datalen >> 8; write_cmd[5] = write_cmd_datalen & 0xFF; write(target_fd, write_cmd, write_cmd_datalen + 10); /* update our checksum accumulator */ image_cksum += ~compute_block_cksum() & 0xFF; /* collect response */ resp = expect_response(INTERMEDIATE_TIMEOUT); if (resp != 'w') { fprintf(stderr, "Block #%lu: ", write_block_count); if (resp < 0) fprintf(stderr, "No response to <w command\n"); else if (isprint(resp)) fprintf(stderr, "Got >%c in response to <w command; expected >w\n", resp); else fprintf(stderr, "Got > %02X in response to <w command; expected >w\n", resp); exit(1); } putchar('.'); fflush(stdout); write_block_count++; } perform_romload() { int resp; unsigned long rec_count; u_char addresp[2]; static int zero = 0; if (open_srec_file(&iramimage) < 0) exit(1); set_fixed_baudrate("19200"); ioctl(target_fd, FIONBIO, &zero); send_beacons(); printf("Got beacon response, attempting download\n"); usleep(SERIAL_FLUSH_DELAY * 1000); tcflush(target_fd, TCIFLUSH); param_cmd[2] = romload_baud_rate->bootrom_code; write(target_fd, param_cmd, sizeof param_cmd); resp = expect_response(INTERMEDIATE_TIMEOUT); if (resp != 'p') { if (resp < 0) fprintf(stderr, "No response to <p command\n"); else if (isprint(resp)) fprintf(stderr, "Got >%c in response to <p command; expected >p\n", resp); else fprintf(stderr, "Got > %02X in response to <p command; expected >p\n", resp); exit(1); } resp = expect_add_response(addresp, 2, INTERMEDIATE_TIMEOUT); if (resp < 0 || addresp[0] != 0x00 || addresp[1] != 0x04) { fprintf(stderr, "error: extra bytes after >p not received as expected\n"); exit(1); } printf("<p command successful, switching to %s baud\n", romload_baud_rate->name); set_serial_baudrate(romload_baud_rate); usleep(SERIAL_FLUSH_DELAY * 1000); tcflush(target_fd, TCIFLUSH); write_cmd_datalen = 0; write_block_count = 0; image_cksum = 0; for (rec_count = 0; ; ) { if (read_s_record(&iramimage) < 0) exit(1); switch (iramimage.record_type) { case '0': if (iramimage.lineno == 1) continue; fprintf(stderr, "%s: S0 record found in line %d (expected in line 1 only)\n", iramimage.filename, iramimage.lineno); exit(1); case '3': case '7': if (s3s7_get_addr_data(&iramimage) < 0) exit(1); break; default: fprintf(stderr, "%s line %d: S%c record type not supported\n", iramimage.filename, iramimage.lineno, iramimage.record_type); exit(1); } if (iramimage.record_type == '7') break; /* must be S3 */ if (iramimage.datalen < 1) { fprintf(stderr, "%s line %d: S3 record has zero data length\n", iramimage.filename, iramimage.lineno); exit(1); } /* get to work */ if (!rec_count) printf("Sending image payload\n"); if (write_cmd_datalen && (iramimage.addr != write_cmd_endaddr || write_cmd_datalen + iramimage.datalen > MAX_WRITE_LEN)) { send_write_cmd(); write_cmd_datalen = 0; } if (!write_cmd_datalen) { bcopy(iramimage.record + 1, write_cmd + 6, 4); write_cmd_endaddr = iramimage.addr; } bcopy(iramimage.record + 5, write_cmd + 10 + write_cmd_datalen, iramimage.datalen); write_cmd_endaddr += iramimage.datalen; write_cmd_datalen += iramimage.datalen; rec_count++; } /* got S7 */ fclose(iramimage.openfile); if (!rec_count) { fprintf(stderr, "%s line %d: S7 without any preceding S3 data records\n", iramimage.filename, iramimage.lineno); exit(1); } send_write_cmd(); /* last block */ putchar('\n'); /* end line of dots */ /* send <c */ printf("Sending checksum\n"); cksum_cmd[2] = ~image_cksum & 0xFF; write(target_fd, cksum_cmd, sizeof cksum_cmd); resp = expect_response(INTERMEDIATE_TIMEOUT); if (resp != 'c') { if (resp < 0) fprintf(stderr, "No response to <c command\n"); else if (isprint(resp)) fprintf(stderr, "Got >%c in response to <c command; expected >c\n", resp); else fprintf(stderr, "Got > %02X in response to <c command; expected >c\n", resp); exit(1); } resp = expect_add_response(addresp, 1, INTERMEDIATE_TIMEOUT); if (resp < 0) { fprintf(stderr, "error: extra byte after >c not received as expected\n"); exit(1); } printf("<c command successful, sending <b\n"); bcopy(iramimage.record + 1, branch_cmd + 2, 4); write(target_fd, branch_cmd, sizeof branch_cmd); resp = expect_response(INTERMEDIATE_TIMEOUT); if (resp != 'b') { if (resp < 0) fprintf(stderr, "No response to <b command\n"); else if (isprint(resp)) fprintf(stderr, "Got >%c in response to <b command; expected >b\n", resp); else fprintf(stderr, "Got > %02X in response to <b command; expected >b\n", resp); exit(1); } printf("<b command successful: downloaded image should now be running!\n"); return(0); }