NetBSD/distrib/pc532/floppies/inst-common/download.c
2001-08-20 12:00:46 +00:00

533 lines
12 KiB
C

/* $NetBSD: download.c,v 1.3 2001/08/20 12:00:47 wiz Exp $ */
/*
Hacked by Phil Nelson for use with NetBSD, 10/5/95.
This source is in the public domain except for the makeraw function.
The upload part of this program is taken from Bruce's ROM debugger
code.
For NetBSD, the usages are:
download file
- open "file" and ship it out standard output with the
format expected by the pc532 ROM monitor download
command.
download -r nfile
- standard input was generated by a "download file" command.
Capture it, checking the CRC and store it in "nfile".
Note: This program uses termios.
Date: Tue, 27 Feb 90 11:47:38 pst
From: Bruce Culbertson <culberts@hplwbc.hpl.hp.com>
To: pc532@daver.bungi.com
Subject: Re: ROM Debugger -- download command..
John L. Connin <johnc%manatee%uunet@daver> writes:
> Dave, what data format / protocol does the ROM debugger 'download' command
> expect ??
Here is the download program which I run at the other end of the
serial line -- in my case, on an AT clone. The download protocol
is explained in the comments in the program. I just recently added
the download command to the monitor so let me know if you have
problems with it or if you wish it worked differently.
Bruce Culbertson
----------------------------------------------------------------------
*/
/* MS-DOS Program for downloading to the NSC32000 Monitor. Use this as a
* template for writing downloaders for other OS's. Compile the MS-DOS
* version with the Microsoft C compiler.
*
* Bruce Culbertson 18 February 1990
*/
/* Instructions for use:
*
* machine prompt, command, etc.
* -------------------------------------------------------------------
* 32000 Command (? for help): download <address>
* MS-DOS [exit terminal emulator]
* MS-DOS C> <this program> <file name to download>
* MS-DOS [re-enter terminal emulator]
* 32000 [hit return to get status of download]
* 32000 Command (? for help): ...
*
* At any point you can send control-C (e.g. using your terminal emulator)
* to the 32000 monitor to abort the download and return to the monitor
* prompt.
*/
/* Download protocol:
*
* <start mark> <length> <data> <CRC>
*
* Below, the sending machine is called SRC, receiving machine is DST.
* Eight bit characters are used.
*
* Control-C (0x03) aborts the transfer. This capability is nice
* to have if, for example, length is garbled and the DST expects
* billions of characters. Since any byte of <length>, <data>,
* or <CRC> could be control-C, we need to have a quote character.
* I use ESC (0x1b). Thus, control-C and ESC are sent as
* {0x1b 0x03} and {0x1b 0x1b}, respectively.
*
* Start mark:
* This is a colon. When SRC begins sending, DST loops until it sees
* the start mark. Thus, if spurious characters are sent as the
* user switches from terminal emulator to download program on SRC,
* DST can ignore them.
*
* Length:
* Four bytes, least significant first. The length is number of
* data bytes to be transfered, not including quote characters.
* The two CRC bytes are also not included in the length.
*
* Data:
* A byte is sent as a byte, with quoting if necessary.
*
* CRC:
* Two bytes, least significant first. Use CCITT CRC generator
* polynomial (x^16 + x^12 + x^5 + 1). Compute on data only (not
* length or start) and exclude quotes. (This is the same CRC
* as computed by Minix's CRC command.)
*/
#include <stdio.h>
#include <fcntl.h>
#ifdef MSDOS
# define OPEN_FLAGS (O_RDONLY | O_BINARY)
# define off_t long
# define DEFAULT_PORT 1
long lseek();
int port_num = DEFAULT_PORT;
int port;
#else
# include <unistd.h>
# define OPEN_FLAGS O_RDONLY
#endif
#define CCITT_GEN 0x11021 /* x^16 + x^12 + x^5 + 1 */
#define BUFSZ 0x1000
#define ESC 0x1b
#define CTL_C 0x03
#define START ':'
char buf[BUFSZ];
long write_data(), write_header();
void putch ();
void
usage(name)
char *name;
{
fprintf (stderr, "usage: %s [-r] <file>\n", name);
exit (1);
}
/* Output a character. If it is a CLT_C or ESC, then quote (precede)
* it with a ESC.
*/
void
write_ch (c)
int c;
{
if (c == ESC || c == CTL_C)
putch (ESC);
putch (c);
}
/* Write two CRC bytes, LSB first.
*/
void
write_crc (crc)
long crc;
{
write_ch ((int)((crc >> 0) & 0xff));
write_ch ((int)((crc >> 8) & 0xff));
}
/* Given old CRC and new character, return new CRC. Uses standard
* CCITT CRC generator polynomial.
*/
unsigned long
update_crc (crc, ch)
long crc;
int ch;
{
int i;
for (i = 0x80; i; i >>= 1) {
crc = (crc << 1) | (i & ch? 1: 0);
if (crc & 0x10000) crc ^= CCITT_GEN;
}
return crc;
}
/* Write header. Format is a colon followed by four byte length,
* LSB first. Length is the number of data bytes after quotes are
* removed.
*/
long
write_header (fd)
int fd;
{
long len;
if (0 == (len = lseek (fd, (off_t)0, 2))) {
fprintf (stderr, "file length is zero\n");
exit (1);
}
lseek (fd, (off_t)0, 0);
write_ch (START);
write_ch ((int)((len >> 0) & 0xff));
write_ch ((int)((len >> 8) & 0xff));
write_ch ((int)((len >> 16) & 0xff));
write_ch ((int)((len >> 24) & 0xff));
return len;
}
/* Write data.
*/
long
write_data (fd)
int fd;
{
long len, crc = 0;
char *p;
for (;;) {
len = read (fd, buf, BUFSZ);
if (-1 == len) {
fprintf (stderr, "read failed\n");
exit (1);
}
if (len == 0) break;
for (p = buf; p < buf + len; ++p) {
write_ch (*p);
crc = update_crc (crc, *p);
}
}
return crc;
}
#ifdef MSDOS
/* Write hardware directly since BIOS and DOS are not reliable.
*/
#define COM_WR 0
#define COM_RD 0
#define COM_IER 1
#define COM_CTL 3
#define COM_STAT 5
#define COM_CTL_VAL 3 /* 8 bits, 1 stop, no parity */
#define COM_IER_VAL 0 /* interrupts off */
#define COM_TX_RDY 0x20
int old_control, old_ier;
/* Output a character to the serial port.
*/
void
putch (c)
int c;
{
int stat;
for (;;) {
stat = inp (port + COM_STAT);
if (stat & COM_TX_RDY) break;
}
outp (port + COM_WR, c);
}
/* Initialize serial port and save old values. Assume baud rate
* already set.
*/
init_port()
{
old_control = inp (port + COM_IER);
old_ier = inp (port + COM_CTL);
outp (port + COM_CTL, COM_CTL_VAL);
outp (port + COM_IER, COM_IER_VAL);
}
/* Restore serial port to old configuration.
*/
restore_port()
{
outp (port + COM_CTL, old_control);
outp (port + COM_IER, old_ier);
}
#else
/* dummy routines for Unix! */
void
putch (int c)
{
putchar (c);
}
#include <termios.h>
struct termios oldstate;
struct termios newstate;
int atty;
/* The following function is covered by: */
/*-
* Copyright (c) 1989, 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/*
* Make a pre-existing termios structure into "raw" mode: character-at-a-time
* mode with no characters interpreted, 8-bit data path.
*/
void
makeraw(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;
}
/* End of copyrighted material. */
void
init_port(int fd)
{
atty = isatty(fd);
if (atty) {
tcgetattr (fd, &oldstate);
newstate = oldstate;
makeraw(&newstate);
tcsetattr (fd, TCSANOW, &newstate);
}
}
void
restore_port(int fd)
{
if (atty) {
tcsetattr (fd, TCSANOW, &oldstate);
}
}
int
get_int (val, len)
unsigned long *val;
long len;
{
unsigned char c;
int shift = 0;
*val = 0;
while (len > 0) {
c = getchar ();
if (c == CTL_C) return 0; /* handle control-C */
if (c == ESC) c = getchar (); /* handle quote */
*val = *val + (c<<(8*shift++));
--len;
}
return 1;
}
int
get_file (f, len, crc)
FILE *f;
unsigned long len, *crc;
{
unsigned char c;
while (len > 0) {
c = getchar ();
if (c == CTL_C) return 0; /* handle control-C */
if (c == ESC) c = getchar (); /* handle quote */
fputc(c,f);
*crc = update_crc (*crc, c); /* compute crc, skip quote */
--len;
}
return 1;
}
/* Upload end of the commands for Unix! */
void
upload (name)
char *name;
{
unsigned long crc, adr, len;
unsigned char c;
unsigned long xcrc;
FILE *f;
/* Open the file. */
f = fopen (name, "w");
if (f == NULL) {
printf ("Could not open \"%s\" for writing.\n", name);
exit(1);
}
/* set raw mode for input. */
init_port(0);
/* get start character */
for (;;) {
c = getchar();
if (c == START) break;
if (c == CTL_C) return;
}
/* get len in little endian form */
if (!get_int (&len, 4)) {
fprintf (stderr, "Upload interrupted.\n");
restore_port(0);
exit(2);
}
crc = 0; /* crc on data only */
if (!get_file (f, len, &crc)) { /* get data */
fprintf (stderr, "Upload interrupted.\n");
restore_port(0);
exit(3);
}
/* get crc in little endian */
if (!get_int (&xcrc, 2)) {
fprintf (stderr, "Upload interrupted.\n");
restore_port(0);
exit(4);
}
if (crc == xcrc) /* print status */
printf ("CRC ok, length = %d\n", len);
else
printf ("CRC error, received %d, expected %d, length %d\n",
xcrc, crc, len);
fclose (f);
/* Restore tty parameters. */
restore_port(0);
}
#endif
/* Main program */
int
main (argc, argv)
int argc;
char **argv;
{
int fd;
long crc, len;
#ifdef MSDOS
/* MSDOS argument processing */
if (argc == 3) {
if (1 != sscanf (argv[2], "%d", &port_num)) {
fprintf (stderr, "Bad serial port, use 1 or 2\n");
exit (1);
}
--argc;
}
if (argc != 2) {
fprintf (stderr, "usage: %s <file> [<serial port>]\n", argv[0]);
exit (1);
}
if (port_num == 1) port = 0x3f8;
else if (port_num == 2) port = 0x2f8;
else {
fprintf (stderr, "Bad serial port, use 1 or 2\n");
exit (1);
}
#else
int do_receive = 0;
int ch;
extern int optind;
/* Unix argument processing */
while ((ch = getopt(argc, argv, "r")) != -1) {
switch (ch) {
case 'r': /* Receive end of a download, use "upload()". */
do_receive = 1;
break;
case '?':
default:
usage(argv[0]);
return (1);
}
}
if (argc-1 != optind)
usage(argv[0]);
if (do_receive) {
upload (argv[2]);
exit (0);
}
#endif
if (0 > (fd = open (argv[1], OPEN_FLAGS))) {
fprintf (stderr, "can not open \"%s\" for reading\n", argv[1]);
exit (1);
}
init_port(1);
len = write_header (fd);
crc = write_data (fd);
write_crc (crc);
printf ("Length=%ld CRC=%ld\n", len, crc);
restore_port(1);
exit (0);
}