aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Selkirk <paul@psgd.org>2016-06-20 22:46:18 -0400
committerPaul Selkirk <paul@psgd.org>2016-06-20 22:46:18 -0400
commit5a0fb1ce24abe38ada705327868a5601b59e8ec4 (patch)
tree21355ad227847620d0c5454c5af600c6916fa378
parentd24c7c38def470aa0615212a5afd007f091b750a (diff)
E-Kermit, for file upload goodness
-rw-r--r--Makefile8
-rw-r--r--libraries/thirdparty/ekermit/Makefile17
-rw-r--r--libraries/thirdparty/ekermit/stm-kermit.c434
-rw-r--r--libraries/thirdparty/ekermit/stm-kermit.h42
-rw-r--r--projects/ekermit-test/Makefile22
-rw-r--r--projects/ekermit-test/ekermit-test.c167
6 files changed, 690 insertions, 0 deletions
diff --git a/Makefile b/Makefile
index 1bdb5fa..e41b94d 100644
--- a/Makefile
+++ b/Makefile
@@ -43,6 +43,7 @@ export RTOS_DIR = $(MBED_DIR)/rtos
export LIBTFM_DIR = $(LIBS_DIR)/thirdparty/libtfm
export LIBHAL_DIR = $(LIBS_DIR)/libhal
export LIBCLI_DIR = $(LIBS_DIR)/libcli
+export EKERMIT_DIR = $(LIBS_DIR)/thirdparty/ekermit
export LIBS = $(MBED_DIR)/libstmf4.a
@@ -133,6 +134,12 @@ $(LIBHAL_DIR)/libhal.a: $(LIBTFM_DIR)/libtfm.a
$(LIBCLI_DIR)/libcli.a:
$(MAKE) -C $(LIBCLI_DIR)
+$(EKERMIT_DIR)/libekermit.a:
+ $(MAKE) -C $(EKERMIT_DIR)
+
+ekermit-test: $(BOARD_OBJS) $(LIBS) $(EKERMIT_DIR)/libekermit.a
+ $(MAKE) -C projects/ekermit-test
+
libhal-test: $(BOARD_OBJS) $(LIBS) $(LIBHAL_DIR)/libhal.a
$(MAKE) -C projects/libhal-test
@@ -160,3 +167,4 @@ distclean: clean
$(MAKE) -C $(RTOS_DIR) clean
$(MAKE) -C $(LIBHAL_DIR) clean
$(MAKE) -C $(LIBTFM_DIR) clean
+ $(MAKE) -C $(EKERMIT_DIR) clean
diff --git a/libraries/thirdparty/ekermit/Makefile b/libraries/thirdparty/ekermit/Makefile
new file mode 100644
index 0000000..8dd856f
--- /dev/null
+++ b/libraries/thirdparty/ekermit/Makefile
@@ -0,0 +1,17 @@
+EKERMIT = ../../../../thirdparty/ekermit
+
+vpath %.c $(EKERMIT)
+
+OBJS = kermit.o stm-kermit.o
+
+LIB = libekermit.a
+
+CFLAGS += -I$(EKERMIT) -DSTATIC=static -DRECVONLY -DMINSIZE #-DDEBUG
+
+all: $(LIB)
+
+$(LIB): $(OBJS)
+ $(AR) -r $@ $(OBJS)
+
+clean:
+ rm -f $(OBJS) $(LIB)
diff --git a/libraries/thirdparty/ekermit/stm-kermit.c b/libraries/thirdparty/ekermit/stm-kermit.c
new file mode 100644
index 0000000..09eca96
--- /dev/null
+++ b/libraries/thirdparty/ekermit/stm-kermit.c
@@ -0,0 +1,434 @@
+/*
+ * stm-kermit.c
+ * ------------
+ * Interface to the E-Kermit library.
+ *
+ * Copyright (C) 1995, 2011,
+ * Trustees of Columbia University in the City of New York.
+ * All rights reserved.
+ *
+ * Copyright (c) 2016, NORDUnet A/S All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * - Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * - 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.
+ *
+ * - Neither the name of the copyright holders 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 COPYRIGHT HOLDERS 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 COPYRIGHT
+ * HOLDER 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.
+ */
+
+/* This file is based on unixio.c and main.c from the E-Kermit distribution,
+ * with extensive modifications for the Cryptech environment.
+ */
+
+#include <ctype.h>
+#include <string.h>
+
+#include "cdefs.h"
+#include "debug.h"
+#include "platform.h"
+#include "kermit.h"
+
+/* The following symbols, defined in kermit.h, conflict with stm32f429xx.h.
+ * Neither one is used in the file.
+ */
+#undef CR
+#undef SUCCESS
+
+#include "stm-uart.h"
+#include "ringbuf.h"
+#include "stm-kermit.h"
+
+static ringbuf_t uart_ringbuf;
+
+/* current character received from UART */
+static uint8_t uart_rx;
+
+/* Callback for HAL_UART_Receive_IT(). Must be public. */
+void HAL_UART1_RxCpltCallback(UART_HandleTypeDef *huart)
+{
+ ringbuf_write_char(&uart_ringbuf, uart_rx);
+ HAL_UART_Receive_IT(huart, &uart_rx, 1);
+}
+
+static void uart_init(void)
+{
+ ringbuf_init(&uart_ringbuf);
+ HAL_UART_Receive_IT(&huart_mgmt, &uart_rx, 1);
+}
+
+#ifdef DEBUG
+/* This is public because it's prototyped in debug.h */
+void
+dodebug(int fc, UCHAR * label, UCHAR * sval, long nval)
+{
+ if (label == (UCHAR *)"==========")
+ uart_send_string("\r\n");
+ uart_send_integer(HAL_GetTick(), 8);
+ uart_send_char(' ');
+
+ if (!label)
+ label = (UCHAR *)"";
+
+ switch (fc) { /* Function code */
+ case DB_OPN: /* Open debug log */
+ uart_send_string("DEBUG LOG OPEN\r\n");
+ return;
+ case DB_MSG: /* Write a message */
+ uart_send_string((char *)label);
+ if (nval) {
+ uart_send_char(' ');
+ uart_send_integer(nval, 1);
+ }
+ uart_send_string("\r\n");
+ return;
+ case DB_CHR: /* Write label and character */
+ uart_send_string((char *)label);
+ uart_send_string("=[");
+ uart_send_char((uint8_t)nval);
+ uart_send_string("]\r\n");
+ return;
+ case DB_PKT: /* Log a packet */
+ uart_send_string((char *)label);
+ uart_send_char(' ');
+ uart_send_integer(nval, 1);
+ uart_send_char('[');
+ /* It would be great if we could interpret the packet.
+ * For now, just dump the raw data.
+ */
+ for (int i = 0; i < nval; ++i, ++sval) {
+ if (*sval == '\\')
+ uart_send_string("\\\\");
+ else if (isprint(*sval))
+ uart_send_char(*sval);
+ else {
+ uart_send_char('\\');
+ uart_send_hex(*sval, 2);
+ }
+ }
+ uart_send_string("]\r\n");
+ return;
+ case DB_LOG: /* Write label and string or number */
+ uart_send_string((char *)label);
+ if (sval) {
+ uart_send_char('[');
+ uart_send_string((char *)sval);
+ uart_send_char(']');
+ }
+ else {
+ uart_send_char('=');
+ uart_send_integer(nval, 1);
+ }
+ uart_send_string("\r\n");
+ return;
+ case DB_CLS: /* Close debug log */
+ return;
+ }
+}
+#endif
+
+/* R E A D P K T -- Read a Kermit packet from the communications device */
+/*
+ Call with:
+ k - Kermit struct pointer
+ p - pointer to read buffer
+ len - length of read buffer
+
+ When reading a packet, this function looks for start of Kermit packet
+ (k->r_soh), then reads everything between it and the end of the packet
+ (k->r_eom) into the indicated buffer. Returns the number of bytes read, or:
+ 0 - timeout or other possibly correctable error;
+ -1 - fatal error, such as loss of connection, or no buffer to read into.
+*/
+
+static int
+readpkt(struct k_data * k, UCHAR *p, int len)
+{
+ int n;
+ short flag;
+ UCHAR x, c;
+#ifdef DEBUG
+ UCHAR * p2;
+#endif
+
+#ifdef F_CTRLC
+ short ccn;
+ ccn = 0;
+#endif
+
+ flag = n = 0; /* Init local variables */
+
+#ifdef DEBUG
+ p2 = p;
+#endif
+
+ uart_init();
+
+ while (1) {
+ /* wait for the next character */
+ while (ringbuf_read_char(&uart_ringbuf, &x) == 0) { ; }
+
+ c = (k->parity) ? x & 0x7f : x & 0xff; /* Strip parity */
+
+#ifdef F_CTRLC
+ /* In remote mode only: three consecutive ^C's to quit */
+ if (k->remote && c == (UCHAR) 3) {
+ if (++ccn > 2) {
+ debug(DB_MSG,"readpkt ^C^C^C",0,0);
+ return(-1);
+ }
+ } else {
+ ccn = 0;
+ }
+#endif
+
+ if (!flag && c != k->r_soh) /* No start of packet yet */
+ continue; /* so discard these bytes. */
+ if (c == k->r_soh) { /* Start of packet */
+ flag = 1; /* Remember */
+ continue; /* But discard. */
+ } else if (c == k->r_eom /* Packet terminator */
+ || c == '\012' /* 1.3: For HyperTerminal */
+ ) {
+#ifdef DEBUG
+ debug(DB_PKT,"RPKT",p2,n);
+#endif
+ return(n);
+ } else { /* Contents of packet */
+ if (n++ > k->r_maxlen) /* Check length */
+ return(0);
+ else
+ *p++ = x & 0xff;
+ }
+ }
+ debug(DB_MSG,"READPKT FAIL (end)",0,0);
+ return(-1);
+}
+
+/* T X _ D A T A -- Writes n bytes of data to communication device. */
+/*
+ Call with:
+ k = pointer to Kermit struct.
+ p = pointer to data to transmit.
+ n = length.
+ Returns:
+ X_OK on success.
+ X_ERROR on failure to write - i/o error.
+*/
+static int
+tx_data(struct k_data * k, UCHAR *p, int n)
+{
+ return (uart_send_bytes(STM_UART_MGMT, p, n) == HAL_OK) ? X_OK : X_ERROR;
+}
+
+__weak int kermit_openfile(UCHAR * name) { return X_OK; }
+
+/* O P E N F I L E -- Open output file */
+/*
+ Call with:
+ Pointer to filename.
+ Size in bytes.
+ Creation date in format yyyymmdd hh:mm:ss, e.g. 19950208 14:00:00
+ Mode: 1 = read, 2 = create, 3 = append.
+ Returns:
+ X_OK on success.
+ X_ERROR on failure, including rejection based on name, size, or date.
+*/
+static int
+openfile(struct k_data * k, UCHAR * s, int mode)
+{
+ int rc;
+
+ switch (mode) {
+ case 2: /* Write (create) */
+ rc = kermit_openfile(s);
+ debug(DB_LOG,"openfile write ",s,rc);
+ return(rc);
+ default:
+ debug(DB_LOG,"openfile unsupported mode",0,mode);
+ return(X_ERROR);
+ }
+}
+
+__weak int kermit_writefile(UCHAR * s, int n) { return X_OK; }
+
+/* W R I T E F I L E -- Write data to file */
+/*
+ Call with:
+ Kermit struct
+ String pointer
+ Length
+ Returns:
+ X_OK on success
+ X_ERROR on failure, such as i/o error, space used up, etc
+*/
+static int
+writefile(struct k_data * k, UCHAR * s, int n)
+{
+ debug(DB_LOG,"writefile binary",0,k->binary);
+ return kermit_writefile(s, n);
+}
+
+__weak int kermit_closefile(void) { return X_OK; }
+__weak int kermit_cancelfile(void) { return X_OK; }
+
+/* C L O S E F I L E -- Close output file */
+/*
+ Mode = 1 for input file, mode = 2 or 3 for output file.
+
+ For output files, the character c is the character (if any) from the Z
+ packet data field. If it is D, it means the file transfer was canceled
+ in midstream by the sender, and the file is therefore incomplete. This
+ routine should check for that and decide what to do. It should be
+ harmless to call this routine for a file that that is not open.
+*/
+static int
+closefile(struct k_data * k, UCHAR c, int mode)
+{
+ switch (mode) {
+ case 2: /* Closing output file */
+ debug(DB_LOG,"closefile (output) name",k->filename,0);
+ if (c == 'D')
+ return kermit_cancelfile();
+ else
+ return kermit_closefile();
+ default:
+ debug(DB_LOG,"closefile unsupported mode",0,mode);
+ return(X_ERROR);
+ }
+}
+
+static UCHAR o_buf[OBUFLEN+8]; /* File output buffer */
+
+int
+kermit_main(void)
+{
+ struct k_data k; /* Kermit data structure */
+ struct k_response r; /* Kermit response structure */
+ int status, rx_len;
+ UCHAR *inbuf;
+ short r_slot;
+
+ debug(DB_MSG,"==========",0,0);
+ debug(DB_OPN,"debug.log",0,0);
+ debug(DB_MSG,"Initializing...",0,0);
+
+/* Fill in parameters for this run */
+
+ memset(&k, 0, sizeof(k));
+
+ k.remote = 1; /* 0 = local, 1 = remote */
+ k.xfermode = 1; /* 0 = automatic, 1 = manual */
+ k.binary = BINARY; /* 0 = text, 1 = binary */
+ k.parity = P_PARITY; /* Default parity = PAR_NONE */
+ k.bct = 1; /* Block check type */
+
+/* Fill in the i/o pointers */
+
+ k.obuf = o_buf; /* File output buffer */
+ k.obuflen = OBUFLEN; /* File output buffer length */
+
+/* Fill in function pointers */
+
+ k.rxd = readpkt; /* for reading packets */
+ k.txd = tx_data; /* for sending packets */
+ k.openf = openfile; /* for opening files */
+ k.writef = writefile; /* for writing to output file */
+ k.closef = closefile; /* for closing files */
+#ifdef DEBUG
+ k.dbf = dodebug; /* for debugging */
+#endif
+
+/* Initialize Kermit protocol */
+
+ status = kermit(K_INIT, &k, 0, 0, "", &r);
+#ifdef DEBUG
+ debug(DB_LOG,"init status:",0,status);
+ debug(DB_LOG,"version:",k.version,0);
+#endif
+ if (status == X_ERROR)
+ return(FAILURE);
+/*
+ Now we read a packet ourselves and call Kermit with it. Normally, Kermit
+ would read its own packets, but in the embedded context, the device must be
+ free to do other things while waiting for a packet to arrive. So the real
+ control program might dispatch to other types of tasks, of which Kermit is
+ only one. But in order to read a packet into Kermit's internal buffer, we
+ have to ask for a buffer address and slot number.
+
+ To interrupt a transfer in progress, set k.cancel to I_FILE to interrupt
+ only the current file, or to I_GROUP to cancel the current file and all
+ remaining files. To cancel the whole operation in such a way that the
+ both Kermits return an error status, call Kermit with K_ERROR.
+*/
+ while (status != X_DONE) {
+/*
+ Here we block waiting for a packet to come in (unless readpkt times out).
+ Another possibility would be to call inchk() to see if any bytes are waiting
+ to be read, and if not, go do something else for a while, then come back
+ here and check again.
+*/
+ inbuf = getrslot(&k,&r_slot); /* Allocate a window slot */
+ rx_len = k.rxd(&k,inbuf,P_PKTLEN); /* Try to read a packet */
+/*
+ For simplicity, kermit() ACKs the packet immediately after verifying it was
+ received correctly. If, afterwards, the control program fails to handle the
+ data correctly (e.g. can't open file, can't write data, can't close file),
+ then it tells Kermit to send an Error packet next time through the loop.
+*/
+ if (rx_len < 1) { /* No data was read */
+ freerslot(&k,r_slot); /* So free the window slot */
+ if (rx_len < 0) /* If there was a fatal error */
+ return(FAILURE); /* give up */
+
+ /* This would be another place to dispatch to another task */
+ /* while waiting for a Kermit packet to show up. */
+
+ }
+ /* Handle the input */
+
+ switch (status = kermit(K_RUN, &k, r_slot, rx_len, "", &r)) {
+ case X_OK:
+#ifdef DEBUG
+/*
+ This shows how, after each packet, you get the protocol state, file name,
+ date, size, and bytes transferred so far. These can be used in a
+ file-transfer progress display, log, etc.
+*/
+ debug(DB_LOG,"NAME",r.filename ? r.filename : (UCHAR *)"(NULL)",0);
+ debug(DB_LOG,"DATE",r.filedate ? r.filedate : (UCHAR *)"(NULL)",0);
+ debug(DB_LOG,"SIZE",0,r.filesize);
+ debug(DB_LOG,"STATE",0,r.status);
+ debug(DB_LOG,"SOFAR",0,r.sofar);
+#endif
+ /* Maybe do other brief tasks here... */
+ continue; /* Keep looping */
+ case X_DONE:
+ debug(DB_LOG,"X_DONE exiting",0,SUCCESS);
+ break; /* Finished */
+ case X_ERROR:
+ debug(DB_LOG,"X_ERROR exiting",0,FAILURE);
+ return(FAILURE); /* Failed */
+ }
+ }
+ return(SUCCESS);
+}
diff --git a/libraries/thirdparty/ekermit/stm-kermit.h b/libraries/thirdparty/ekermit/stm-kermit.h
new file mode 100644
index 0000000..a2c3436
--- /dev/null
+++ b/libraries/thirdparty/ekermit/stm-kermit.h
@@ -0,0 +1,42 @@
+/*
+ * stm-kermit.h
+ * ------------
+ * Interface to the E-Kermit library.
+ *
+ * Copyright (c) 2016, NORDUnet A/S All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * - Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * - 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.
+ *
+ * - Neither the name of the NORDUnet 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 COPYRIGHT HOLDERS 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 COPYRIGHT
+ * HOLDER 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.
+ */
+
+/* return 0 for success, 1 for failure */
+extern int kermit_main(void);
+
+/* return 0 for success, -1 for failure */
+extern int kermit_openfile(unsigned char *name);
+extern int kermit_writefile(unsigned char *buf, int len);
+extern int kermit_closefile(void);
+extern int kermit_cancelfile(void);
diff --git a/projects/ekermit-test/Makefile b/projects/ekermit-test/Makefile
new file mode 100644
index 0000000..b4fab70
--- /dev/null
+++ b/projects/ekermit-test/Makefile
@@ -0,0 +1,22 @@
+TEST = ekermit-test
+
+OBJS = ../libhal-test/printf.o
+
+BOARD_OBJS += $(TOPLEVEL)/sdram-malloc.o
+
+CFLAGS += -I$(EKERMIT_DIR)
+
+LIBS += $(EKERMIT_DIR)/libekermit.a
+
+all: $(TEST:=.elf)
+
+%.elf: %.o $(BOARD_OBJS) $(OBJS) $(LIBS)
+ $(CC) $(CFLAGS) $^ -o $@ -T$(LDSCRIPT) -g -Wl,-Map=$*.map
+ $(OBJCOPY) -O binary $*.elf $*.bin
+ $(SIZE) $*.elf
+
+clean:
+ rm -f *.o
+ rm -f *.elf
+ rm -f *.bin
+ rm -f *.map
diff --git a/projects/ekermit-test/ekermit-test.c b/projects/ekermit-test/ekermit-test.c
new file mode 100644
index 0000000..3b71a69
--- /dev/null
+++ b/projects/ekermit-test/ekermit-test.c
@@ -0,0 +1,167 @@
+/*
+ * ekermit-test.c
+ * --------------
+ * A test program for the E-Kermit library.
+ *
+ * Copyright (c) 2016, NORDUnet A/S All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * - Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * - 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.
+ *
+ * - Neither the name of the NORDUnet 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 COPYRIGHT HOLDERS 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 COPYRIGHT
+ * HOLDER 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.
+ */
+
+/* This receives one or more files from a kermit client (e.g. C-Kermit),
+ * and stores them in a minimal file system on SDRAM.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+
+#include "stm-init.h"
+#include "stm-sdram.h"
+#include "sdram-malloc.h"
+#include "stm-kermit.h"
+
+/* alignment for file allocations */
+#define ALIGN 16
+
+/* maximum length of file name */
+#define NAMELEN 32
+
+/* maximum number of files */
+#define NFILE 4
+
+typedef struct {
+ char name[NAMELEN];
+ uint8_t *loc;
+ size_t len;
+} filetab_t;
+filetab_t filetab[NFILE], *curfile;
+int nfile;
+
+#ifdef DUMP
+static void hexdump(uint8_t *buf, int len)
+{
+ for ( ; len > 0; buf += 16, len -= 16) {
+ int plen = (len > 16) ? 16 : len;
+ int i;
+ for (i = 0; i < plen; ++i)
+ printf("%02x ", buf[i]);
+ for ( ; i < 16; ++i)
+ printf(" ");
+ printf("| ");
+ for (i = 0; i < plen; ++i)
+ printf("%c", isprint(buf[i]) ? buf[i] : ' ');
+ printf("\n");
+ }
+}
+#endif
+
+/* Override weak function defintions for callouts in stm-kermit.c */
+
+/* Called when kermit receives an F (file) packet
+ */
+int kermit_openfile(unsigned char *name)
+{
+ if (nfile == NFILE) {
+ curfile = NULL;
+ return -1;
+ }
+ curfile = &filetab[nfile];
+ strncpy(curfile->name, (char *)name, sizeof(curfile->name));
+ curfile->loc = sdram_malloc(0);
+ if (curfile->loc == NULL) {
+ curfile = NULL;
+ return -1;
+ }
+ curfile->len = 0;
+ ++nfile;
+ return 0;
+}
+
+/* Called when kermit's receive buffer is full, after some number of D
+ * (data) packets
+ */
+int kermit_writefile(unsigned char *buf, int len)
+{
+ if (curfile == NULL)
+ return -1;
+ uint8_t *writeptr = sdram_malloc(len);
+ if (writeptr == NULL)
+ return -1;
+ memcpy(writeptr, buf, len);
+ curfile->len += len;
+ return 0;
+}
+
+/* Called when kermit receives a Z (EOF) packet
+ */
+int kermit_closefile(void)
+{
+ if (curfile == NULL)
+ return -1;
+ int pad = ALIGN - (curfile->len & (ALIGN - 1));
+ if (sdram_malloc(pad) == NULL)
+ return -1;
+ curfile = NULL;
+ return 0;
+}
+
+/* Called when kermit receives a Z (EOF) packet with a D (discard) flag
+ */
+int kermit_cancelfile(void)
+{
+ if (curfile == NULL)
+ return -1;
+ sdram_free(curfile->loc);
+ memset(curfile, 0, sizeof(*curfile));
+ --nfile;
+ return 0;
+}
+
+/* The main function is pretty simple, because the kermit library does all
+ * the work out of sight.
+ */
+int main(void)
+{
+ stm_init();
+ sdram_init();
+
+ while (1) {
+ memset((void *)filetab, 0, sizeof(filetab));
+ curfile = NULL;
+ nfile = 0;
+ kermit_main();
+ for (int i = 0; i < nfile; ++i) {
+ filetab_t *f = &filetab[i];
+ printf("%d. %-16s %p %d\n", i, f->name, f->loc, f->len);
+#ifdef DUMP
+ hexdump(f->loc, f->len);
+#endif
+ sdram_free(f->loc);
+ }
+ }
+}