diff -uNr els-1.02-1/Makefile els-1.02-2_cjc_2/Makefile
--- els-1.02-1/Makefile	Wed Oct 25 18:14:35 2000
+++ els-1.02-2_cjc_2/Makefile	Thu Feb 15 19:09:49 2001
@@ -78,7 +78,7 @@
 
 
 
-all:	els.o cdetherd cdipserv cdflush cdstty dinc dtrcels cdmknods cddiag
+all:	els.o cdetherd cdscsid cdipserv cdflush cdstty dinc dtrcels cdmknods cddiag
 
 cdmknods: cdmknods.o usr_unittab.o
 	$(CC) $(CFLAGS) cdmknods.o usr_unittab.o -o cdmknods
@@ -87,7 +87,15 @@
 	$(CC) $(CFLAGS) cdipserv.o -o cdipserv
 
 cdetherd: cdetherd.o
-	${CC} $(CFLAGS) cdetherd.o -o cdetherd
+	$(CC) $(CFLAGS) cdetherd.o -o cdetherd
+
+sts.o: sts.c sts.h scsi_iface.h scsi_tools.h
+scsi_iface.o: scsi_iface.c scsi_iface.h
+scsi_tools.o: scsi_tools.c scsi_tools.h scsi_iface.h
+cdscsid.o: cdetherd.c sts.h
+	$(CC) $(CFLAGS) -DSTS_CODE -c cdetherd.c -o cdscsid.o
+cdscsid: cdscsid.o sts.o scsi_iface.o scsi_tools.o
+	$(CC) $(CFLAGS) cdscsid.o sts.o scsi_tools.o scsi_iface.o -o cdscsid
 
 cdflush: cdflush.o
 	$(CC) $(CFLAGS) cdflush.o -o cdflush
@@ -111,24 +119,24 @@
  
 els.o:	els.c els.h ctio.h ker_unittab.o tracer.c
 	@echo building els.o and linking
-	${KCC} $(KFLAGS) -DTRACER -o els_driver.o -c els.c
+	$(KCC) $(KFLAGS) $(TRACING) -o els_driver.o -c els.c
 	$(LD) -r -nostartfiles -o $@ els_driver.o ker_unittab.o
 	@echo
 
 listing: els.c sts_unittab.c
-	${KCC} -r -Wl,-M -nostartfiles -o $@ els_driver.o sts_unittab.o > els.lst
+	$(KCC) -r -Wl,-M -nostartfiles -o $@ els_driver.o sts_unittab.o > els.lst
 
 ker_unittab.o:	sts_unittab.c sts_unittab.h
 	@echo building ker_unittab.o
-	${KCC} ${KFLAGS} -c sts_unittab.c -o ker_unittab.o
+	$(KCC) $(KFLAGS) -c sts_unittab.c -o ker_unittab.o
 	@echo
 usr_unittab.o:	sts_unittab.c sts_unittab.h
 	@echo building usr_unittab.o
-	${CC} $(CFLAGS) -c sts_unittab.c -o usr_unittab.o
+	$(CC) $(CFLAGS) -c sts_unittab.c -o usr_unittab.o
 	@echo
 
 clean:
-	rm -f cdmknods cdelsreset cdetherd cdflush cdipserv cdstty dinc dtrcels cddiag *.o
+	rm -f cdmknods cdelsreset cdetherd cdscsid cdflush cdipserv cdstty dinc dtrcels cddiag *.o
 
 clobber: clean
 	-rm -f els.getty
@@ -143,6 +151,7 @@
 	fi
 	rm -f $(MODDIR)/$(MODULES)
 	rm -f $(SBINDIR)/cdetherd
+	rm -f $(SBINDIR)/cdscsid
 	rm -f $(SBINDIR)/cdipserv
 	rm -f $(SBINDIR)/cdflush
 	rm -f $(SBINDIR)/cdmknods
@@ -152,6 +161,7 @@
 	rm -f $(DMBIN)/cddiag
 	rm -f $(RCDIR)/els
 	rm -f $(MANDIR)/man1/cdetherd.1
+	rm -f $(MANDIR)/man1/cdscsid.1
 	rm -f $(MANDIR)/man1/cdipserv.1
 	rm -f $(MANDIR)/man1/cddiag.1
 	rm -f $(MANDIR)/man1/cdflush.1
@@ -172,6 +182,7 @@
 	install -m 0644 $(MODULES) $(MODDIR)
 	/sbin/depmod -a
 	install -m 755 cdetherd $(SBINDIR)
+	install -m 755 cdscsid $(SBINDIR)
 	install -m 755 cdipserv $(SBINDIR)
 	install -m 755 cdflush $(SBINDIR)
 	install -m 755 cdmknods $(SBINDIR)
@@ -181,6 +192,7 @@
 	install -m 755 cddiag $(DMBIN)
 	install -m 755 els.rc $(RCDIR)/els
 	install -m 0644 cdetherd.1 $(MANDIR)/man1
+	install -m 0644 cdscsid.1 $(MANDIR)/man1
 	install -m 0644 cddiag.1 $(MANDIR)/man1
 	install -m 0644 cdflush.1 $(MANDIR)/man1
 	install -m 0644 cdipserv.1 $(MANDIR)/man1
diff -uNr els-1.02-1/README.STS els-1.02-2_cjc_2/README.STS
--- els-1.02-1/README.STS	Wed Dec 31 18:00:00 1969
+++ els-1.02-2_cjc_2/README.STS	Fri Feb 16 08:31:50 2001
@@ -0,0 +1,234 @@
+            How to Use Your STS With The EtherLite Driver
+
+VERSION:
+	This is my version 2_cjc_2 (Feb 16, 2001) of the STS patch to the
+	EtherLite driver version els-1.02-1 (40002090B.src.rpm 10/29/2000).
+
+
+REQUIREMENTS:
+	- This distribution (Digi's Linux EtherLite Drivers + my patch)
+	- Have the Linux SCSI Generic driver working (either in the kernel
+	  or as a module).
+	- Have both LUNs of your STS detected during kernel startup.
+
+
+HISTORY:
+
+  So, I've been trying to get this one old Motorola STS working under Linux
+  for a long time now.  Finally, I started work on it, and created a tool
+  called "cdscsid" which basically tricks the EtherLite module (els.o) into
+  thinking it's dealing with a "regular" EtherLite device.  Several changes
+  have been made to the els.o driver for compatibility (it didn't recognize
+  the STS product names, obviously).
+
+  All I've had available to test with have been an ancient Motorola STS-S16P
+  and a Central Data ST-1620.  If you have other STS devices, please add
+  them to sts_unittab.c near the top and email me about it so I can add
+  it to my driver as well.
+
+  So, basically, follow all the instructions as you would for a regular
+  EtherLite device, but where it wants an IP address, give the SCSI address
+  for the LUN 0 of your STS.  If your STS is on host 0, channel 0, ID 3,
+  LUN 0, then you want to give "0.0.3.0".
+
+
+STEPS:
+
+	1) verify both your STS's LUNs are visible.  When I do a 
+	   "cat /proc/scsi/scsi" I see this for my STS:
+
+	    Host: scsi0 Channel: 00 Id: 02 Lun: 00
+	       Vendor: MOTOROLA Model: STS-S16P         Rev: V4.1
+	       Type:   Communications                   ANSI SCSI revision: 02
+	    Host: scsi0 Channel: 00 Id: 02 Lun: 01
+	       Vendor: MOTOROLA Model: STS-S16P         Rev: V4.1
+	       Type:   Communications                   ANSI SCSI revision: 02
+
+
+	   Most people with see their "Vendor" as either "CenData" or
+	   "DigiIntl".  Most CenData and DigiIntl devices start with
+	   "ST-" and are followed by a number.
+
+	   If you don't see both LUNs ("Lun: 00" and "Lun: 01") either:
+	     a) edit /etc/lilo.conf (or whatever you use) and add
+		the kernel command line option "max_scsi_luns=2":
+		        append="max_scsi_luns=2"
+		Run "lilo" and reboot.  (I actually use "max_scsi_luns=8")
+	     b) Recompile your kernel to "scan all LUNs".  Install, and
+		reboot.
+
+	2) compile this package ("make")
+	3) insert the els.o module ("insmod ./els.o")
+	4) edit /etc/els.conf to have the els device name, the STS
+	   model name, and the location of the SCSI generic driver:
+		els00 STS-S16P 0.0.2.0
+	5) run "./cdmkmods -auto" to make all the /dev/ttyN* devices,
+	   /dev/dg/els/els00 device, and the /dev/dg/els/elsdiag device.
+	6) run "./cdscsid els00 0.0.2.0" (as in the example above)
+	   and you should see in syslog that it found and initialized
+	   the STS.
+
+
+WARNINGS:
+
+	I've only tested this with a few poor Motorola STS-S8Ps, Moto
+	STS-S16Ps, and a few Digi ST-1620s.  So be warned.
+
+	If you boot with your STS plugged in under Linux 2.2.x, you'll
+	see various linux SCSI warnings:
+		scsi: unknown device type 9
+		resize_dma_pool: unknown device type 9
+
+	These warnings are okay, and I wrote a patch to the linux kernel
+	(against 2.4.0-test5: http://outflux.net/unix/software/sts/) that
+	will get rid of them.  I've had this patch approved, and it's
+	now officially in the 2.4.x kernels.
+
+
+CHANGES between my patches:
+
+   Version 2_cjc_2: (Feb 16, 2001)
+	- used my improved SCSI code from my latest userspace driver.
+	- changed the SCSI addressing mechanism.
+	- made updates to how I pack and unpack FAS packets to maximize
+	  utilized space
+
+   Version 2: (Jan 19, 2001)
+	- merged the cdscsid code into the cdetherd.c file for easier
+	  future porting.
+	- added ALL the STS unit information, thanks to Digi!
+
+
+CHANGES to regular els packages:
+   Version 2_cjc_2: (against els-1.02-1: Feb 16, 2001)
+	Everything from Version 2 below, and in addition:
+
+	Makefile:
+		- added "scsi_iface" and "scsi_tools" sections.
+
+	els.c:
+		- ported to Linux 2.4.x
+			- changed wait_queue variables & initialization
+			- re-initialized tty unit_read/write functions
+		- changed some schedule loops to use ct_delay to reduce
+		  system time usage during timers.
+		- added modules parameters "unit_wait" and "unit_gone"
+		  so that users can tune how long things wait before
+		  timing out.
+		- added module author & description tags.
+		- possibly corrected one or two bugs in how lines are
+		  shutdown and initialized.
+
+	cdutils.h:
+		- changed "NOP" to "CDNOP" to not conflict with scsi.h.
+
+	els.info:
+		- updated the RPM spec file to release "2_cjc_2"
+
+   Version 2: (against els-1.02-1: Jan 19, 2001)
+	Makefile:
+		- added "cdscsid" program and man pages
+		- corrected a few non-serious ${} typos to $()
+	
+	README.STS:
+		- added this file
+
+	cdetherd.c:
+		- merged the cdscsid changes into this file, so that
+		  both cdetherd and cdscsid can be built from the same
+		  source file.
+
+	cdscsid.1:
+		- created this file from the cdetherd.1 man page.
+
+	els.c:
+		- added "Unloaded" kernel message
+		- added "scsiTerminal" initialization kernel message	
+
+	els.info (els.spec):
+		- changed release number to "2"
+		- added SCSI note
+		- added cdscsid executable and manpage to build list
+	
+	release.notes:
+		- Fixed a common grammar error
+
+	sts.c:
+		- All the SCSI handling routines for cdscsid.c.  This file
+		  is from my user-space pty/sts driver.
+		
+	sts.h:
+		- defines I need for dealing with FAS commands and SCSI
+		  commands.  Things in sts.h are sometimes duplicated in
+		  fas.h.  This file is from my user-space pty/sts driver.
+
+	sts_unittab.c:
+		- added all the STS unit information that Digi gave me!
+		- added the 38400 baud settings
+
+	sts_unittab.h:
+		- Added awareness for a maximum baud rate of 38400.
+		- trick els by defining SFC_STD as SFC_ETH
+
+
+   Version 1: (against els-A-7, 08/12/2000)
+	sts.h:
+		- defines I need for dealing with FAS commands and SCSI
+		  commands.  Things in sts.h are sometimes duplicated in
+		  fas.h.  This file is from my user-space pty/sts driver.
+
+	sts.c:
+		- All the SCSI handling routines for cdscsid.c.  This file
+		  is from my user-space pty/sts driver.
+
+	cdscsid.c:
+		- created this file from the cdetherd.c tool.  Most code
+		  changes to cdetherd.c should be in "STS_CODE" defines.
+
+	cdscsid.1
+		- created this file from the cdetherd.1 man page.  Changed
+		  whatever needed changing.  :)
+
+	Makefile:
+		- added "cdscsid" program and man page
+
+	els.h:
+		- Turned off tracing (added 64k to the driver size...)
+
+	els.c:
+		- Fixed a semi-serious bug in the els.o driver itself
+		  where it returns in the middle of processing FAS packets.
+		- Added awareness of the TIOCOUTQ and TIOCINQ ioctls.
+		- Added an "unloaded" note when unloading the module.
+		
+	sts_unittab.c:
+		- Started adding STS model information to the list.
+		- Added awareness for a maximum baud rate of 38400.
+	
+
+WHEN SOMETHING BLOWS UP:
+
+	If stuff blows up (SCSI bus starts resetting, ports don't work)
+	then I recommend editing "sts.h" and "define"ing STS_DEBUG,
+	editing "Makefile" and "define"ing TRACING, and doing a "make clean"
+	and a "make" to turn on tracing and heavy debugging.  Use
+	the "dtrcels" tool to see the els driver's trace information, and
+	check the /tmp/els00.log for stderr from cdscsid.
+
+
+CAVEATS:
+
+	The els.o driver needs to be accepting of STSs.  Right now, I've
+	changed "SFC_STD" to have the same value was "SFC_ETH", and
+	marked all the regular STSs with "SFC_STD", when they shouldn't
+	need to be.  That means that els.c needs to be changed to allow
+	non-SFC_ETH devices to work, and needs to have some of the UNIT_POLL
+	stuff changed for STS devices.  There are some notes in sts.c about
+	it.  Search for "SCSI" in els.c.  ;)
+
+
+Feb 16, 2001
+----
+Kees Cook
+cook@cpoint.net
+http://outflux.net/
diff -uNr els-1.02-1/cdetherd.c els-1.02-2_cjc_2/cdetherd.c
--- els-1.02-1/cdetherd.c	Wed Oct 25 18:14:35 2000
+++ els-1.02-2_cjc_2/cdetherd.c	Fri Feb 16 08:37:51 2001
@@ -5,6 +5,8 @@
  *    Jeff Randall <Jeff_Randall@digi.com>
  *    Bob Rubendunst <Robert_Rubendunst@digi.com>
  *
+ * SCSI code Copyright 2000-2001 Kees Cook <cook@cpoint.net>
+ *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2, or (at your option)
@@ -21,6 +23,11 @@
  *
  **************************************************************************** */
 
+/* to build cdscsid, define "STS_CODE" during a gcc run, as in the Makefile
+	#define STS_CODE
+   -Kees 01/19/01
+*/
+
 #include <stdio.h>
 #include <stdarg.h>
 #include <string.h>
@@ -45,6 +52,24 @@
 #include <sys/socket.h>
 #include <netinet/in.h>
 #include <arpa/inet.h>
+#ifdef STS_CODE
+# include <scsi/sg.h>
+# define __USE_XOPEN
+# define __USE_GNU
+# include <stdlib.h>
+# include "sts.h"
+#endif
+
+
+#ifdef STS_CODE
+# define MY_LONGNAME	"scsiTerminal Server"
+# define MY_SHORTNAME	"cdscsid"
+# define MY_SOCKNAME    "SCSI device"
+#else
+# define MY_LONGNAME	"EtherLite"
+# define MY_SHORTNAME	"cdetherd"
+# define MY_SOCKNAME    "socket"
+#endif
 
 #define ELS_TCP_PORT    10001
 
@@ -76,14 +101,18 @@
 void els_terminate();
 void bitmash();
 void err_exit();
+#ifdef STS_CODE
+int make_socket_connection(char * sts_device);
+#else
 int make_socket_connection();
+#endif
 
 extern  char    *optarg;        /* for getopt function */
 
 /* Globals */
 char Logfilename[MAXDEVNAMELEN+RESTOFPATH]; 
 int log_fd = 0;
-int sock_fd;
+int sock_fd = -1;
 int els_fd;
 long curr_time, prev_time;
 /* struct ct_unit unit; */
@@ -92,10 +121,13 @@
 char data_buf[4096];
 char log_file[40];
 els_config els_cfg;
+#ifdef STS_CODE
+sts_state * sts;
+#endif
 
 void els_closelog();
 void els_log_err(int where, const char *format, ...);
-void els_log_msg(int where, char *msg);
+void els_log_msg(int where, const char *fmt, ...);
 void els_log_renamer(char *unit);
 void els_terminate();
 int sock_read(int sock_fd,unsigned char *buf, int wanted);
@@ -188,17 +220,25 @@
 }
 
 
-void els_log_msg(int where, char *msg)
+/* print standard error msg to log file.
+ */
+void els_log_msg(int where, const char *fmt, ...)
 {
+	va_list vaptr;
+
+	va_start(vaptr, fmt);
 	if (!(where&NOSTAMP))
 		print_timestamp();
-	fprintf(stderr,msg);
+	vfprintf(stderr,fmt,vaptr);
+	va_end(vaptr);
 	fflush(stderr);
 	/* It may go to the system console/syslog as well. */
 	if (where&SYSCON) {
-		openlog("cdetherd", LOG_PID, LOG_DAEMON);
-		syslog(LOG_ERR, msg);
-		closelog();
+		char buff[120];
+		va_start(vaptr, fmt);
+		vsprintf(buff,fmt,vaptr);
+		va_end(vaptr);
+		syslog(LOG_INFO, buff);
 	}
 }
 
@@ -224,9 +264,7 @@
 		va_end(vaptr);
 
 		/* syslog has datestamp */
-		openlog("cdetherd", LOG_PID, LOG_DAEMON);
 		syslog(LOG_ERR, buff);
-		closelog();
 	}
 }
 
@@ -246,6 +284,8 @@
 	int i;
 #endif
 
+	openlog(MY_SHORTNAME, LOG_PID, LOG_DAEMON);
+
 	els_log_renamer(argc >=2 ? argv[1] : "els");
 	/* Remap stderr to log_fd. */
 	if ((log_fd = open(Logfilename, O_WRONLY | O_CREAT,
@@ -253,11 +293,18 @@
 		perror(Logfilename);
 		exit(1);
 	}
+#ifndef STDERR_DEBUG
 	dup2(log_fd, STDERR_FILENO);
+#endif
 	
 	if (argc != 3)  {
-		printf("Usage: %s device ip_addr\n", argv[0]);
-		els_log_err(SYSCON|NOSTAMP, "Usage: %s device ip_addr\n",
+#ifdef STS_CODE
+		printf("Usage: %s els_device sts_device\n", argv[0]);
+		els_log_err(SYSCON|NOSTAMP, "Usage: %s els_device sts_device\n",
+#else
+		printf("Usage: %s els_device ip_addr\n", argv[0]);
+		els_log_err(SYSCON|NOSTAMP, "Usage: %s els_device ip_addr\n",
+#endif
 			    argv[0]);
 		els_closelog();
 		exit(1);
@@ -267,7 +314,8 @@
 	signal (SIGINT, SIG_IGN);
 	signal (SIGQUIT, SIG_IGN);
 	signal (SIGTERM, els_terminate);
-	
+
+#ifndef STDERR_DEBUG
 	if ((pid = fork()) < 0) {
 		els_log_msg(SYSCON, "Cannot fork child process\n");
 		els_closelog();
@@ -279,11 +327,12 @@
 	close(0);
 	close(1);
 /*      close(2); */
+#endif
 	
 	chdir("/");
 	umask(0);
 	
-	els_log_msg(SYSCON, "EtherLite daemon running\n");
+	els_log_msg(SYSCON, MY_LONGNAME " daemon running\n");
 	sleeptime = 2;
 	attempts = 0;
  again:
@@ -303,11 +352,18 @@
 		goto again;
 	}
 	sleeptime = 2;		/* reset on success */
+	/* can leave this because I'm using h/c/id/lun with dots... ;) -Kees */
 	els_cfg.netaddr = inet_addr(argv[2]);
 
  main_loop:
 	attempts = 0;
 	sleeptime = 2;
+#ifdef STS_CODE
+	/* I don't want to loop forever.  If there isn't a scsi device
+           then we should shutdown immediately -Kees */
+	if (make_socket_connection(argv[2]) < 0) {
+		err_exit();
+#else
 	while (make_socket_connection() < 0) {
 		/* loop until connection is made */
 		if (attempts > MAXQUICKTRYS) {
@@ -317,6 +373,7 @@
 		else
 			attempts += 1;
 		sleep(sleeptime);
+#endif
 	}
 	
 	if (first_conn) {
@@ -328,8 +385,14 @@
 		sprintf(log_buf, "Vendor ID: %s, Firmware Version: %s\n", 
 			ptr, els_cfg.fw_ver);
 		els_log_msg(SYSCON, log_buf);
-		sprintf(log_buf, "Model Name: %s, IP Address: %s\n", 
-			els_cfg.product_id, argv[2]);
+		sprintf(log_buf, "Model Name: %s, %s Address: %s\n", 
+			els_cfg.product_id,
+#ifdef STS_CODE
+			"SCSI",
+#else
+			"IP",
+#endif
+			argv[2]);
 		els_log_msg(SYSCON|NOSTAMP, log_buf);
 		if (ioctl(els_fd, ELSADDUNIT, &els_cfg) < 0) {
 			els_log_err(SYSCON, "ELSADDUNIT:%s\n",strerror(errno));
@@ -363,10 +426,17 @@
 			els_log_msg(SYSCON, "select timed out\n");
 			break;
 		}
+#ifdef SELECT_DEBUG
+		fprintf(stderr,"select finished\n");
+#endif
+
 		prev_time = curr_time;
 		curr_time = time(0);
 		
 		if (FD_ISSET(els_fd, &rd_fdset))  {
+#ifdef SELECT_DEBUG
+			fprintf(stderr,"els_fd readable\n");
+#endif
 #if 0
 			/*
 			 * N.B. - Linux's select already adjusts tv.sec for
@@ -391,24 +461,36 @@
 					}
 				}
 #endif
+#ifdef STS_CODE
+				if (sts_write(sts, data_buf, n) < 0)  {
+#else
 				if (write(sock_fd, data_buf, n) < 0)  {
+#endif
 					els_log_err(SYSCON, 
-						    "write on socket:%s\n",
+						    "write on %s:%s\n",
+						    MY_SOCKNAME,
 						    strerror(errno));
 					break;
 				}
 			} else {
-				els_log_err(SYSCON, "read on EtherLite:%s\n",
+				els_log_err(SYSCON,"read on ELS kernel interface:%s\n",
 					    strerror(errno));
 				err_exit();
 			}
 		}
 		if (FD_ISSET(sock_fd, &rd_fdset)) {
+#ifdef SELECT_DEBUG
+			fprintf(stderr,"sock_fd readable\n");
+#endif
 			sock_time.tv_sec = 120;
+#ifdef STS_CODE
+			if ((n = sts_read(sts, data_buf, 4096)) > 0) {
+#else
 			if ((n = read(sock_fd, data_buf, 4096)) > 0) {
+#endif
 #ifdef DBG
-				els_log_msg(SYSCON, "data received from "
-					    "socket: ");
+				els_log_msg(SYSCON, "data received from %s: ",
+					MY_SOCKNAME);
 				ptr = (unsigned char *) data_buf;
 				strcpy(log_buf, "    ");
 				for (i = 0; i < n; i++) {
@@ -421,18 +503,27 @@
 #endif
 				if (write(els_fd, data_buf, n) < 0)  {
 					els_log_err(SYSCON,
-						    "write on EtherLite:%s\n",
+						    "write on %s:%s\n",
+						    MY_LONGNAME,
 						    strerror(errno));
 					err_exit();
 				}
+#ifdef STS_CODE
+			} else if (n < 0) {
+#else
 			} else {
-				els_log_err(SYSCON, "read on socket:%s\n",
-					    strerror(errno));
+#endif
+				els_log_err(SYSCON, "read on %s:%s\n",
+					MY_SOCKNAME,strerror(errno));
 				break;
 			}
 		}
 	}
+#ifdef STS_CODE
+	sts=sts_shutdown(sts);
+#else
 	close(sock_fd);
+#endif
 	
 	els_log_msg(SYSCON, "Unit Offline\n");
 	if (ioctl(els_fd, ELSOFFLINE, 0) < 0) {
@@ -442,8 +533,163 @@
 	goto main_loop;
 }
 
+#ifdef STS_CODE
+int make_socket_connection(char * sts_device) {
+	int host, chan, id, lun;
+	unsigned char extra_len;
+	unsigned char * response;
+	unsigned char * ptr;
+	char * space;
 
+	if (!sts_device) {
+		els_log_err(SYSCON,"make_sock_conn: null sts_device\n");
+		return(-1);
+	}
+	/* FIXME: detect re-initialization calls, and deal with them */
+	if (sts) {
+		els_log_err(SYSCON,"make_sock_conn: already attached\n");
+		return(-1);
+	}
+	/* alloc with cleared memory */
+	if (!(sts=(sts_state *)calloc(1,sizeof(sts_state)))) {
+		els_log_err(SYSCON,"calloc: %s\n",strerror(errno));
+		return(-1);
+	}
+
+	/* process the incoming dot-separated text as host/chan/id/lun */
+	if (sscanf(sts_device,"%d.%d.%d.%d",&host,&chan,&id,&lun)!=4) {
+		els_log_err(SYSCON,"need SCSI address as 'HOST.CHAN.ID.LUN'\n");
+		return(-1);
+	}
 
+	/* find the first lun */
+	if (!(sts->senddev=scsi_open_id(host,chan,id,lun))) {
+		els_log_err(SYSCON,"could not open Send LUN on SCSI host %d "
+				"chan %d id %d lun %d!\n",host,chan,id,lun);
+		return(-1);
+	}
+	/* grab the silly extra text */
+	response = sts->senddev->fullinq;
+	extra_len = response[EXTRA_OFF];
+	ptr=response+VENDOR_START+VENDOR_LEN+PRODUCT_LEN+REV_LEN;
+	extra_len -= ((ptr-response) - 1);
+	if (extra_len > 0)
+			memcpy(sts->sendextra, ptr, extra_len);
+
+	/* store regular device info */
+	strncpy(sts->vendor,sts->senddev->vendor,VENDOR_LEN);
+	strncpy(sts->product,sts->senddev->product,PRODUCT_LEN);
+	strncpy(sts->rev,sts->senddev->version,REV_LEN);
+
+	/* report first one */
+	els_log_msg(SYSCON,"vendor: [%s]  product: [%s]  "
+			"rev: [%s%s%s%s]\n",
+			sts->vendor, sts->product,sts->rev,
+				*sts->sendextra ? " (" : "",
+				sts->sendextra,
+				*sts->sendextra ? ")"  : ""
+			);
+	els_log_msg(SYSCON,"  host: %d chan: %d id: %d lun: %d\n",
+			host, chan, id, lun);
+
+	/* check that this is at least a communications device */
+	if (sts->senddev->sginfo.scsi_type!=TYPE_COMM) {
+		els_log_msg(SYSCON,"Send LUN is not a 'Communication' device!\n");
+		scsi_close(sts->senddev);
+		sts->senddev=NULL;
+		return(-1);
+	}
+	/* look if medium is loaded */
+	if (!scsi_unitready(sts->senddev)) {
+			els_log_err(SYSCON,"Send LUN is not ready -- "
+				"aborting...\n");
+			scsi_close(sts->senddev);
+			return(-1);
+	}
+
+	/* find the second lun */
+	host=sts->senddev->sginfo.host_no;
+	chan=sts->senddev->sginfo.channel;
+	id=sts->senddev->sginfo.scsi_id;
+	lun=sts->senddev->sginfo.lun+1;
+	if (!(sts->recvdev=scsi_open_id(host,chan,id,lun))) {
+		els_log_err(SYSCON,"could not open Recv LUN on SCSI host %d "
+				"chan %d id %d lun %d!\n",host,chan,id,lun);
+		scsi_close(sts->senddev);
+		return(-1);
+	}
+	/* grab the silly extra text */
+	response = sts->recvdev->fullinq;
+	extra_len = response[EXTRA_OFF];
+	ptr=response+VENDOR_START+VENDOR_LEN+PRODUCT_LEN+REV_LEN;
+	extra_len -= ((ptr-response) - 1);
+	if (extra_len > 0)
+			memcpy(sts->recvextra, ptr, extra_len);
+	/* report second one */
+	els_log_msg(SYSCON,"vendor: [%s]  product: [%s]  "
+			"rev: [%s%s%s%s]\n",
+			sts->vendor, sts->product,sts->rev,
+				*sts->recvextra ? " (" : "",
+				sts->recvextra,
+				*sts->recvextra ? ")"  : ""
+			);
+	els_log_msg(SYSCON,"  host: %d chan: %d id: %d lun: %d\n",
+			host, chan, id, lun);
+
+	/* look if medium is loaded */
+	if (!scsi_unitready(sts->recvdev)) {
+			els_log_err(SYSCON,"Recv LUN is not ready -- "
+				"aborting...\n");
+			scsi_close(sts->senddev);
+			scsi_close(sts->recvdev);
+			return(-1);
+	}
+
+
+
+
+	/* FIXME: do any STS inits here, like FAS_GLOBAL */
+
+
+
+
+
+	/* MIMIC EtherLite */
+	ptr=inquiry_data + 16;
+	/* store vendor info */
+	memcpy(ptr,sts->vendor,VENDOR_LEN);
+	/* terminate the scsi text */
+	ptr[VENDOR_LEN]='\0';
+	/* null terminate at the first space */
+	if((space=index(ptr,' '))!=0) *space='\0';
+	/* advance to next area */
+	ptr+=strlen(ptr)+1;
+
+	/* store product info */
+	memcpy(ptr,sts->product,PRODUCT_LEN);
+	/* terminate the scsi text */
+	ptr[PRODUCT_LEN]='\0';
+	/* null terminate at the first space */
+	if((space=index(ptr,' '))!=0) *space='\0';
+	/* advance to next area */
+	ptr+=strlen(ptr)+1;
+ 
+	/* store firmware version info */
+	memcpy(ptr,sts->rev,REV_LEN);
+	/* terminate the scsi text */
+	ptr[REV_LEN]='\0';
+	/* null terminate at the first space */
+	if((space=index(ptr,' '))!=0) *space='\0';
+
+	/* pretend we have sock_fd */
+	sock_fd=sts->recvdev->fd;
+
+	/* start getting info */
+	recv_fas_block(sts);
+    
+	return 1;
+}
+#else
 int make_socket_connection()
 {
 	int inquiry_len;
@@ -459,9 +705,9 @@
 	{	
 		int on = 1;
 		setsockopt(sock_fd, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof(on));
-#if 0
+# if 0
 		setsockopt(sock_fd, SOL_SOCKET, SO_LINGER, &on, sizeof(on));
-#endif
+# endif
 	}
 #endif
 
@@ -512,6 +758,7 @@
 	}
 	return(0);
 }
+#endif
 
 
 int sock_read(int sock_fd, unsigned char *buf, int wanted)
@@ -589,10 +836,16 @@
 {
 	els_log_msg(SYSCON, "Daemon process terminated\n");
 	els_log_msg(SYSCON, "Unit offline\n");
-	if(ioctl(els_fd, ELSOFFLINE, 0) < 0) 
+	if (sock_fd!=-1) {
+	    if(ioctl(els_fd, ELSOFFLINE, 0) < 0) 
 		els_log_err(SYSCON, "ELSOFFLINE:%s\n",strerror(errno));
+#ifdef STS_CODE
+	    sts=sts_shutdown(sts);
+#else
+	    close(sock_fd);
+#endif
+        }
 	close(els_fd);
-	close(sock_fd);
 	close(log_fd);
 	exit(1);
 }
diff -uNr els-1.02-1/cdscsid.1 els-1.02-2_cjc_2/cdscsid.1
--- els-1.02-1/cdscsid.1	Wed Dec 31 18:00:00 1969
+++ els-1.02-2_cjc_2/cdscsid.1	Fri Feb 16 08:39:57 2001
@@ -0,0 +1,70 @@
+.\" cdscsid.1
+.TH CDSCSID 1M "12 August 2000"
+.ds ]W Digi
+.SH NAME
+cdscsid \- makes connection between EtherLite(R) driver and SCSI modules
+.SH SYNOPSIS
+.B cdscsid unit_node [sts_scsi_address]
+.SH AVAILABILITY
+.LP
+Provided by a patched version of the Digi EtherLite Terminal Server(TM)
+software distribution.  Check http://outflux.net/unix/software/sts/
+.SH DESCRIPTION
+.LP
+.B cdscsid
+creates a link to a SCSI Terminal Server and passes information between
+the EtherLite driver and the STS module.
+.B cdscsid 
+supports Digi/Central Data/Motorola scsiTerminalServer products only.
+.LP
+.B cdscsid 
+is normally started on boot time for all EtherLite modules configured
+on the system by the /etc/rc.d/init.d/etherlite script.  When normally
+invoked via cdmknods, it will get the SCSI device for the module it is
+supposed to connect to from the /etc/els.conf file.  After a connection
+is established, information from the module such as module type and
+firmware version is passed to the driver and the module is marked
+Online.
+.LP
+The unit_node argument can be specified as a full path or as a
+partial path.  The directory /dev/ will be prepended to any
+argument that does not start with '/'.
+.LP
+The STS SCSI address is specified
+in dotted decimal notation for the host, channel, ID, and LUN on the command
+line such as:
+.RS
+cdscsid els05 0.0.1.0
+.RE
+.SH "FILES"
+.TP 19
+.SM /dev/els*
+EtherLite module node linkages
+.TP 19
+.SM /etc/els.conf
+Configuration file for the EtherLite Terminal Server module
+driver
+.TP 19
+.SM /tmp/elsn.log
+.SM /tmp/elsn.old
+.SM /tmp/elsn.older
+daemon log files. For example, for unit 3, the current log file is
+named els03.old, the previous one is named els03.old, and the oldest
+one retained is named els03.older.
+.SH "SEE ALSO"
+.BR etherlite (4)
+.BR cdetherd (1)
+.SH "PROVIDED BY"
+.sp
+Kees Cook
+.br
+E-mail: cook@cpoint.net
+.br
+WWW: http://outflux.net/
+.br
+.sp
+
+
+
+
+
diff -uNr els-1.02-1/cdutils.h els-1.02-2_cjc_2/cdutils.h
--- els-1.02-1/cdutils.h	Wed Oct 25 18:14:35 2000
+++ els-1.02-2_cjc_2/cdutils.h	Thu Feb 15 20:13:14 2001
@@ -83,8 +83,8 @@
 #endif
 
 
-
-#define	NOP			3
+/* 'NOP' conflicts with 'NOP' from scsi.h, so I renamed this one -Kees */
+#define	CDNOP			3
 #define	MAXP			7
 #define	NOPOD			0xff
 
diff -uNr els-1.02-1/els.c els-1.02-2_cjc_2/els.c
--- els-1.02-1/els.c	Wed Oct 25 18:14:35 2000
+++ els-1.02-2_cjc_2/els.c	Fri Feb 16 08:37:57 2001
@@ -8,6 +8,7 @@
  *    Zhong Deng <Zhong_Deng@digi.com>
  *    Jeff Randall <Jeff_Randall@digi.com>
  *    Bob Rubendunst <Robert_Rubendunst@digi.com>
+ *    Kees Cook <cook@cpoint.net>
  * 
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -111,6 +112,21 @@
 
 #endif
 
+/* Module Documentation Stuff */
+MODULE_AUTHOR("Zhong Deng / Jeff Randall / Bob Rubendunst / Kees Cook");
+MODULE_DESCRIPTION("Digi EtherLite(R) and SCSI Terminal Server TTY driver");
+
+/* Global timeouts -Kees */
+LOCAL int unit_wait=60;
+#define ELS_UNIT_WAIT unit_wait
+MODULE_PARM(unit_wait,"i");
+MODULE_PARM_DESC(unit_wait,"Seconds to wait during opens & reads for unit to come online");
+
+LOCAL int unit_gone=60;
+#define ELS_UNIT_GONE unit_gone
+MODULE_PARM(unit_gone,"i");
+MODULE_PARM_DESC(unit_gone,"Seconds until unit offlined from lack of UNIT_POLLs");
+
 /* global unit array */
 LOCAL ct_unit		*els_units[MAX_MODULE][MAX_ELS_DEVS];
 
@@ -342,6 +358,7 @@
 			up = NULL;
 		}
 	}
+	printk("Unloaded %s EtherLite(R) Driver\n", ELS_NAME);
 }
 #endif /* MODULE */
 
@@ -355,10 +372,11 @@
 {
 	int	mod, rc;
 	
-	trc(("els_init\n"));
+	trc(("els_init unit_wait=~ unit_gone=~\n",unit_wait,unit_gone));
 	
 	printk("%s EtherLite(R) Driver, Digi International Part Number %s\n",
 		   ELS_NAME, ELS_PART);
+	printk("scsiTerminal Server extensions active (cook@cpoint.net)\n");
 
 #ifdef TRACER
 	printk("(Global Tracing is enabled.)\n");
@@ -476,6 +494,11 @@
 				MOD_DEC_USE_COUNT;
 				return (-ENOMEM);
 			}
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+			init_waitqueue_head(&lp->open_wait);
+			init_waitqueue_head(&lp->close_wait);
+			init_waitqueue_head(&lp->mdmsig_wait);
+#endif
 		}
 		/* diag is unreadable, unwriteable */
 		set_bit(TTY_IO_ERROR, &tty->flags);
@@ -513,9 +536,10 @@
 	 */
 
 	MOD_INC_USE_COUNT;
-	for (i=0; !(up = els_units[mod][pun]) && i < 60 ; i++)
+	for (i=0; !(up = els_units[mod][pun]) && i < ELS_UNIT_WAIT; i++)
 		ct_delay(HZ, TASK_UNINTERRUPTIBLE);
 	if (up == (ct_unit *) NULL) {
+		trc(("els_open % unit DNE\n", tty));
 		/*   MOD_DEC_USE_COUNT; */
 		return (-ENODEV);
 	}
@@ -525,16 +549,18 @@
 	 * before attempting a line open. We need to wait again so that
 	 * short network glitches don't adversly affect port usability.
 	 */
-	for (i = 1; ((up->unit_flags & UNIT_OFFLINE) && (i <= 60)) ; i++) {
+	for (i = 1; ((up->unit_flags & UNIT_OFFLINE) &&
+		     (i <= ELS_UNIT_WAIT)) ; i++) {
 		ct_delay(HZ, TASK_UNINTERRUPTIBLE);
 	}
 	if (up->unit_flags & UNIT_OFFLINE) {
-		trc(("close % UNIT is OFFLINE>\n", tty));
+		trc(("els_open % UNIT is OFFLINE\n", tty));
 		/*  MOD_DEC_USE_COUNT; */
 		return (-ENODEV);
 	}
 	port = DEV2LINE(dev);
 	if (port < 0 || port >= up->num_lines) {
+		trc(("els_open % port DNE\n", tty));
 		/*  MOD_DEC_USE_COUNT; */
 		return (-ENODEV);
 	}
@@ -573,6 +599,11 @@
 		}
 		lp->close_delay = (5 * HZ) / 10;
 		lp->closing_wait = 30 * HZ;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+		init_waitqueue_head(&lp->open_wait);
+		init_waitqueue_head(&lp->close_wait);
+		init_waitqueue_head(&lp->mdmsig_wait);
+#endif
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0)
 		spin_lock_init(&lp->linelock);
 #endif
@@ -627,7 +658,8 @@
 		rc = ctf_init_line(lp);
 		if (rc != OK) {
 			trc(("ctf_init_line() failed\n"));
-			LINE_UNLOCK(lp->linelock, save);
+			/* should this be here? -Kees 
+			LINE_UNLOCK(lp->linelock, save); */
 			goto error;
 		}
 		
@@ -636,7 +668,8 @@
 		trc(("calling ctf_param after asserting DTR & RTS tty=%\n",
 		     tty->termios));
 		ctf_param(lp, tty->termios, FALSE);
-		LINE_UNLOCK(lp->linelock, save);
+		/* should this be here? -Kees 
+		LINE_UNLOCK(lp->linelock, save); */
 	}
 	else
 	{
@@ -822,13 +855,13 @@
 	int save=0;
 	int dev = MINOR(tty->device);
 
-	trc(("els_close tty %\n", tty));
+	trc(("els_close tty % dev=%\n", tty, dev));
 	/* sanity checks */
 	if (!tty)
 		return;		/* happens if cdetherd not running yet. */
 	/* handle global diag/util or ELS unit close */
 	if ((dev == ELS_GLOBAL_MINOR) || ELS_UNIT(dev)) { 
-		trc(("unit close % returns\n", tty));
+		trc(("els_close % returns\n", tty));
 		MOD_DEC_USE_COUNT;		/* R0.11 */
 		return;
 	}
@@ -840,7 +873,7 @@
 	if (tty_hung_up_p(filp)) {
 		if (lp)
 			LINE_UNLOCK(lp->linelock, save);
-		trc(("close done -hung up filp %\n",tty));
+		trc(("els_close done -hung up filp %\n",tty));
 		MOD_DEC_USE_COUNT;
 		return;
 	}
@@ -859,7 +892,7 @@
 
 	if (lp->refcount-- > 1) {
 		LINE_UNLOCK(lp->linelock, save); /* R0.12 */
-		trc(("close % refcount = %\n", tty, lp->refcount));
+		trc(("els_close % refcount = %\n", tty, lp->refcount));
 		MOD_DEC_USE_COUNT;
 		return;
 	}
@@ -905,7 +938,7 @@
 	/* if HUPCL flag is set, drop modem signals */
 	if (!(lp->line_state&LINE_OFFLINE) &&
 	    (tty->termios->c_cflag & HUPCL)) {
-		trc(("close calling ctf_smdm\n"));
+		trc(("els_close calling ctf_smdm\n"));
 		ctf_smdm(lp, TIOCM_RTS | TIOCM_DTR, TIOCMBIC, TRUE);
 	}
   
@@ -924,7 +957,7 @@
 	lp->flags &= ~ASYNC_INITIALIZED; /* insure the circle buf gets reset */
 	set_bit(TTY_IO_ERROR, &tty->flags);
 	tty->closing = 0;
-	trc(("close % els~ port ~d  clearing lp->tty\n",
+	trc(("els_close % els~ port ~d  clearing lp->tty\n",
 	     tty, lp->up->unum, lp->port));
 	lp->tty = (struct tty_struct *) NULL;
 	LINE_UNLOCK(lp->linelock, save);
@@ -941,7 +974,7 @@
 		       ASYNC_CLOSING);
 	wake_up_interruptible(&lp->close_wait);
 	LINE_UNLOCK(lp->linelock, save);
-	trc(("close done % els~ port ~d  line_state=%\n",
+	trc(("els_close done % els~ port ~d  line_state=%\n",
 	     tty, lp->up->unum, lp->port, lp->line_state));
 	MOD_DEC_USE_COUNT;
 }
@@ -1894,6 +1927,17 @@
 			if (!(up->unit_flags & UNIT_OFFLINE))
 				ctm_unit_offline(up);
       
+			/*
+			 * Re-initialize read and select routine for the unit
+			 */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,23)
+			tty->ldisc.poll = unit_select;
+#else
+			tty->ldisc.poll = unit_poll;
+#endif
+			tty->ldisc.read = unit_read;
+			tty->ldisc.write = unit_write;
+
 			strcpy(up->fw_ver, els_cfg.fw_ver);
 			up->netaddr = els_cfg.netaddr;
 			ptr = (u_char *) &up->netaddr;
@@ -1964,8 +2008,13 @@
 		up->max_size_fassend = min(uinfo->max_pkt,
 					   (uinfo->max_line_out));
 		up->unit_flags = UNIT_OFFLINE; /* force init */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+		init_waitqueue_head(&up->read_wait);
+		init_waitqueue_head(&up->write_wait);
+#else
 		up->read_wait = NULL;
 		up->write_wait = NULL;
+#endif
 
 		/*
 		 * Allocate an array of line pointers based on the maximum
@@ -2188,7 +2237,8 @@
 			ttrc(("ctf_timer - ct_systime = %, up->active_time "
 			      "= %\n", ct_systime, up->active_time));
 
-			if (ct_systime - up->active_time > 120) {
+		        /* Unit has gone too long without responding */
+			if (ct_systime - up->active_time > ELS_UNIT_GONE) {
 				printz("timer ctf_offline\n");
 				trc(("timer running ctm_unit_offline()\n"));
 				ctm_unit_offline(up);
@@ -2341,6 +2391,7 @@
 			trc(("ctf_init_line semaphore already clear\n"));
 		wake_up_interruptible(&lp->open_wait);
 		trc(("ctf_enable_fw failed.\n"));
+		lp->line_state |= LINE_OFFLINE; /* go back to offline? -Kees */
 		return (rc);			/* bail w/ error */
 	}
   
@@ -2888,6 +2939,7 @@
 		return (OK);
 	}
  
+	trc(("ctf_wait_send: timer=%\n",timer));
 	if (!timer) {
 		LINE_LOCK(lp->linelock, save);
 		timer = (lp->tty && NONLOCAL(lp)) ? 
@@ -2901,11 +2953,15 @@
 	wait_time = jiffies + timer * HZ;
 	/* If the xmit circular buffer isn't drained, drain it first! */
 	while (lp->xmit_q.cnt) {
+		trc(("ctf_wait_send: xmit q=%\n",lp->xmit_q.cnt));
 		if (lp->line_state&LINE_OBUSY) {
 			while ((lp->line_state & LINE_OBUSY) &&
 			       (jiffies < wait_time)) {
+				/* use ct_delay instead? -Kees
 				schedule();
 				set_current_state(TASK_RUNNING);
+				*/
+				ct_delay(HZ/10,TASK_INTERRUPTIBLE);
 				if (signal_pending(current))
 					goto timeout;
 			}
@@ -2935,8 +2991,11 @@
  
 	/* wait for FAS response to come back */
 	while ((lp->line_state & LINE_WAIT_OUT) && (jiffies < wait_time)) {
+		/* again, use ct_delay instead -Kees
 		schedule();
 		set_current_state(TASK_RUNNING);
+		*/
+		ct_delay(HZ/10, TASK_INTERRUPTIBLE);
 		if (signal_pending(current))
 			break;
 	}
@@ -4383,11 +4442,16 @@
 			ELS_UNLOCK(lp->up->faslock, save);
 			ctm_sendfas(up);
 			
-			wait_time = jiffies + 60 * HZ;
+			/* Let's not spin the CPU here, instead use ct_delay 
+			   Should this be UNINTERRUPTIBLE or not? -Kees */
+			wait_time = jiffies + ELS_UNIT_WAIT * HZ;
 			while ((lp->line_state & LINE_OFFLINE) && 
 			       (jiffies < wait_time)) {
+				ct_delay(HZ/10,TASK_INTERRUPTIBLE);
+				/*
 				schedule();
 				set_current_state(TASK_RUNNING);
+				*/
 			}
 			
 			if (lp->line_state & LINE_OFFLINE) {
@@ -4438,10 +4502,13 @@
 	ELS_UNLOCK(lp->up->faslock, save);
 	ctm_sendfas(up);
 
-	wait_time = jiffies + 60 * HZ;
+	wait_time = jiffies + ELS_UNIT_WAIT * HZ;
 	while (!(lp->line_state & LINE_ENABLED) && (jiffies < wait_time)) {
+		/* use ct_delay instead? -Kees
 		schedule();
 		set_current_state(TASK_RUNNING);
+		*/
+		ct_delay(HZ/10, TASK_INTERRUPTIBLE);
 	}
 
 	while (!(lp->line_state & LINE_ENABLED)) {
diff -uNr els-1.02-1/els.h els-1.02-2_cjc_2/els.h
--- els-1.02-1/els.h	Wed Oct 25 18:14:35 2000
+++ els-1.02-2_cjc_2/els.h	Fri Feb  9 15:01:13 2001
@@ -244,9 +244,15 @@
 	u_char	fas_hist[FASCMDSZ * FASHISTSZ];
 	u_char *fas_histptr;		/* pointer into FAS history buf */
 	struct tty_struct *tty; 	/* associated tty */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+	wait_queue_head_t open_wait;
+        wait_queue_head_t close_wait;
+        wait_queue_head_t mdmsig_wait;
+#else
 	struct wait_queue *open_wait;
 	struct wait_queue *close_wait;
 	struct wait_queue *mdmsig_wait;
+#endif
 	struct termios normaltermios;
 	struct termios callouttermios;
 	struct serial_icounter_struct icount;
@@ -287,8 +293,13 @@
 	ct_pkt *pkt_tail;		/* tail of outgoing queue */
 	ct_pkt *recv_ctpkt;		/* permanent ct_pkt we recv into */
 	u_char	initd;			/* flag to indicate initialized */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+        wait_queue_head_t read_wait;
+        wait_queue_head_t write_wait;
+#else
 	struct wait_queue *read_wait;
 	struct wait_queue *write_wait;
+#endif
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0)
 	spinlock_t	 faslock;
 #endif
diff -uNr els-1.02-1/els.info els-1.02-2_cjc_2/els.info
--- els-1.02-1/els.info	Wed Oct 25 18:14:35 2000
+++ els-1.02-2_cjc_2/els.info	Fri Feb 16 08:30:50 2001
@@ -11,14 +11,14 @@
 #
 name:		els
 version:	1.02
-release:	1
-summary:	Lightweight serial & parallel ports via Ethernet
+release:	2_cjc_2
+summary:	Lightweight serial & parallel ports via Ethernet & SCSI
 group:		Applications/Communications
 copyright:	GPL
 vendor:		Digi International
-url:		http://www.digi.com/
+url:		http://outflux.net/unix/software/sts/
 Source:		http://support.digi.com/support/drivers/linux/%{name}-%{version}-%{release}.tar.gz
-packager:	Bob Rubendunst <Robert_Rubendunst@digi.com>
+packager:	Bob Rubendunst <Robert_Rubendunst@digi.com>, Kees Cook <cook@cpoint.net>
 ExclusiveOS:	Linux
 BuildRoot:	/var/tmp/%{name}-buildroot
 
@@ -27,10 +27,10 @@
 %description 
 This packages contains the device drivers and the supporting
 management applications for the Digi International Etherlite adapter
-family: EL-2, EL-8+, EL-16, and EL-32.  After building and installing
-the binary RPM from the source RPM (as detailed in release.notes),
-Please cd to /usr/src/dg/els/drv/linux and then read release.notes to
-complete installation.
+family: EL-2, EL-8+, EL-16, and EL-32, and SCSI Terminal Servers.
+After building and installing the binary RPM from the source RPM (as
+detailed in release.notes), Please cd to /usr/src/dg/els/drv/linux and
+then read release.notes to complete installation.
 
 
 %prep
@@ -88,10 +88,12 @@
 /usr/bin/dtrcels
 /usr/bin/dinc
 /usr/sbin/cdetherd
+/usr/sbin/cdscsid
 /usr/sbin/cdflush
 /usr/sbin/cdipserv
 /usr/sbin/cdmknods
 /usr/man/man1/*
 /usr/man/man4/*
 %doc release.notes
+%doc README.STS
 %doc COPYING
diff -uNr els-1.02-1/release.notes els-1.02-2_cjc_2/release.notes
--- els-1.02-1/release.notes	Wed Oct 25 18:14:35 2000
+++ els-1.02-2_cjc_2/release.notes	Fri Jan 19 21:44:41 2001
@@ -159,7 +159,7 @@
       script is available at
       http://support.digi.com/support/drivers/linux/ and is part
       number 8006862. If you use this script, then after running the
-      script, skip below to "Your almost there."
+      script, skip below to "You're almost there."
 
       If you do not wish to use the digirpm script, keep in mind you
       need to execute three rpm commands to get the Etherlite driver
@@ -175,7 +175,7 @@
       as /usr/src/drv/els and "make install" should be run as root
       from within the created directory.
 
-      Your almost there!
+      You're almost there!
  
       In order to make the driver available for use, you must cause
       the driver to be loaded.  This can be accomplished on RedHat
diff -uNr els-1.02-1/scsi_iface.c els-1.02-2_cjc_2/scsi_iface.c
--- els-1.02-1/scsi_iface.c	Wed Dec 31 18:00:00 1969
+++ els-1.02-2_cjc_2/scsi_iface.c	Thu Feb 15 20:19:21 2001
@@ -0,0 +1,171 @@
+/*
+ * This library works on the Linux sg device driver
+ *
+ * $Id: scsi_iface.c,v 1.4 2001/02/16 00:29:33 root Exp $
+ *
+ * Copyright (C) 2001 Kees Cook
+ * cook@cpoint.net, http://outflux.net/
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <scsi/sg.h>
+#include <sys/ioctl.h>  /* ioctl */
+
+#define __USE_XOPEN
+#define __USE_GNU
+#include <stdlib.h>
+
+#include "scsi_iface.h"
+
+/* start our scsi command (need to know output size) */
+int start_scsi_cmd (
+			int fd,			/* sg fd */
+			unsigned cmd_len,	/* command length */
+		 	unsigned in_size,	/* input data size */
+		 	unsigned char *i_buff,	/* input buffer */
+		 	unsigned out_size	/* output data size */
+     		      ) {
+		
+  int status = 0;
+  struct sg_header *sg_hd;
+
+  /* safety checks */
+  if (!cmd_len) {
+    fprintf(stderr,"start_scsi_cmd: cmd_len == 0\n");
+    return -1;			/* need a cmd_len != 0 */
+  }
+  if (!i_buff) {
+    fprintf(stderr,"start_scsi_cmd: i_buff == NULL\n");
+    return -1;			/* need an input buffer != NULL */
+  }
+
+  /* keep the packet size under 4k */
+  if (SG_LEN + cmd_len + in_size > MAX_PACKET) {
+    fprintf(stderr,"packet too large\n");
+    return -1;
+  }
+
+  /* generic scsi device header construction */
+  sg_hd = (struct sg_header *) i_buff;
+  /* useful stuff */
+  sg_hd->reply_len = SG_LEN + out_size;
+  sg_hd->twelve_byte = (cmd_len == 12); /* should be set */
+  /* not really used stuff */
+  sg_hd->other_flags = 0;	/* not used */
+  sg_hd->result = 0;
+  sg_hd->pack_id = 0;
+  /* ignored stuff */
+#if     0
+  sg_hd->pack_len = SG_LEN + cmd_len + in_size;	/* not necessary */
+#endif
+
+  /* send command */
+  /* FIXME: handle EAGAIN errno */
+  status = write (fd, i_buff, SG_LEN + cmd_len + in_size);
+  if (status < 0 || status != SG_LEN + cmd_len + in_size) {
+      /* some error happened */
+      fprintf (stderr, "write(sg fd %d) result = 0x%x cmd = 0x%x: ",
+	       fd, sg_hd->result, i_buff[SG_LEN]);
+      perror ("");
+      return status;
+  }
+
+  return 0;
+}
+
+/* get results from last scsi cmd (need output buffer and size) */
+int finish_scsi_cmd (
+			int fd,			/* sg fd */
+		 	unsigned out_size,	/* output data size */
+		 	unsigned char *o_buff	/* output buffer */
+		     ) {
+  int status = 0;
+  struct sg_header *sg_hd;
+
+  /* safety checks */
+  if (SG_LEN + out_size > MAX_PACKET) {
+    fprintf(stderr,"finish_scsi_cmd: packet too large\n");
+    return -1;
+  }
+  if (!o_buff) {
+    fprintf(stderr,"finish_scsi_cmd: o_buff == NULL\n");
+    return -1;
+  }
+  
+  /* retrieve result */
+  /* FIXME: handle EINTR errno */
+  status = read (fd, o_buff, SG_LEN + out_size);
+  if (status < 0 || status != SG_LEN + out_size) {
+      /* some error happened */
+      fprintf (stderr, "read(sg fd %d) result = 0x%x cmd = 0x%x\n",
+	       fd, sg_hd->result, o_buff[SG_LEN]);
+      fprintf (stderr, "read(sg fd %d) sense "
+	       "%x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x\n", fd,
+	       sg_hd->sense_buffer[0], sg_hd->sense_buffer[1],
+	       sg_hd->sense_buffer[2], sg_hd->sense_buffer[3],
+	       sg_hd->sense_buffer[4], sg_hd->sense_buffer[5],
+	       sg_hd->sense_buffer[6], sg_hd->sense_buffer[7],
+	       sg_hd->sense_buffer[8], sg_hd->sense_buffer[9],
+	       sg_hd->sense_buffer[10], sg_hd->sense_buffer[11],
+	       sg_hd->sense_buffer[12], sg_hd->sense_buffer[13],
+	       sg_hd->sense_buffer[14], sg_hd->sense_buffer[15]);
+      if (status < 0)
+	perror ("");
+  }
+
+  /* Look if we got what we expected to get */
+  if (status == SG_LEN + out_size)
+    status = 0;			/* got them all */
+
+  return status;		/* 0 means no error */
+}
+
+/* process a complete scsi cmd. Use the generic scsi interface. */
+int waitfor_scsi_cmd (
+			int fd,			/* sg fd */
+			unsigned cmd_len,	/* command length */
+		 	unsigned in_size,	/* input data size */
+		 	unsigned char *i_buff,	/* input buffer */
+		 	unsigned out_size,	/* output data size */
+		 	unsigned char *o_buff	/* output buffer */
+     		      ) {
+
+  int result;
+  static char buf[MAX_PACKET+SG_LEN];
+
+  if ((result=start_scsi_cmd(fd, cmd_len, in_size, i_buff, out_size))!=0) {
+	fprintf(stderr,"start_scsi_cmd failed\n");
+	return result;
+  }
+
+  if (!o_buff) {
+	memset(buf,0,sizeof(buf));
+	o_buff=buf;
+  }
+
+  if ((result=finish_scsi_cmd(fd, out_size, o_buff))!=0) {
+	fprintf(stderr,"finish_scsi_cmd failed\n");
+	return result;
+  }
+
+  return 0;
+}
diff -uNr els-1.02-1/scsi_iface.h els-1.02-2_cjc_2/scsi_iface.h
--- els-1.02-1/scsi_iface.h	Wed Dec 31 18:00:00 1969
+++ els-1.02-2_cjc_2/scsi_iface.h	Thu Feb 15 20:19:21 2001
@@ -0,0 +1,53 @@
+/*
+ * header file for the SCSI low-level interface
+ *
+ * Copyright (C) 2001 Kees Cook
+ * cook@cpoint.net, http://outflux.net/
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+#ifndef SCSI_IFACE_H
+#define SCSI_IFACE_H
+
+/* for typing convenience */
+#define SG_LEN          sizeof(struct sg_header)
+#define MAX_PACKET      4096 /* could be SG_BIG_BUFF too... */
+
+int start_scsi_cmd(
+                        int fd,                 /* sg fd */
+                        unsigned cmd_len,       /* command length */
+                        unsigned in_size,       /* input data size */
+                        unsigned char *i_buff,  /* input buffer */
+                        unsigned out_size       /* output data size */
+                   );
+
+int finish_scsi_cmd(
+                        int fd,                 /* sg fd */
+                        unsigned out_size,      /* output data size */
+                        unsigned char *o_buff   /* output buffer */
+                    );
+
+int waitfor_scsi_cmd(
+                        int fd,                 /* sg fd */
+                        unsigned cmd_len,       /* command length */
+                        unsigned in_size,       /* input data size */
+                        unsigned char *i_buff,  /* input buffer */
+                        unsigned out_size,      /* output data size */
+                        unsigned char *o_buff   /* output buffer */
+                      );
+
+#endif /* SCSI_IFACE_H */
diff -uNr els-1.02-1/scsi_tools.c els-1.02-2_cjc_2/scsi_tools.c
--- els-1.02-1/scsi_tools.c	Wed Dec 31 18:00:00 1969
+++ els-1.02-2_cjc_2/scsi_tools.c	Fri Feb 16 08:51:29 2001
@@ -0,0 +1,237 @@
+/*
+ * This library implements a cleaner access system to the sg device
+ *
+ * $Id: scsi_tools.c,v 1.14 2001/02/16 14:52:52 root Exp $
+ *
+ * Copyright (C) 2001 Kees Cook
+ * cook@cpoint.net, http://outflux.net/
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include "scsi_tools.h"
+#include "scsi_iface.h"
+
+#if 0
+void printchars(u_char * str, int len) {
+	int i;
+
+	for (i=0;i<len;i++) {
+		printf("%d: %c (0x%02X)\n",i, str[i],str[i]);
+	}
+}
+#endif
+
+/* request vendor brand and model */
+scsi_info * scsi_inquiry (scsi_info * dev) {
+  static u_char cmd[SG_LEN + 18]; /* SCSI command buffer */
+  static u_char Inqbuffer[SG_LEN + INQUIRY_REPLY_LEN];
+  u_char cmdblk[INQUIRY_CMDLEN] = { INQUIRY_CMD,	/* command */
+    0,				/* lun/reserved */
+    0,				/* page code */
+    0,				/* reserved */
+    INQUIRY_REPLY_LEN,		/* allocation length */
+    0
+  };				/* reserved/flag/link */
+
+  if (!dev) {
+	fprintf(stderr,"scsi_inquery got NULL dev?!\n");
+	return NULL;
+  }
+
+  memcpy (cmd + SG_LEN, cmdblk, sizeof (cmdblk));
+
+  /*
+   * +------------------+
+   * | struct sg_header | <- cmd
+   * +------------------+
+   * | copy of cmdblk   | <- cmd + SG_LEN
+   * +------------------+
+   */
+
+  /* clear out inq buffer */
+  memset(Inqbuffer,0,sizeof(Inqbuffer));
+
+  if (waitfor_scsi_cmd (dev->fd, sizeof (cmdblk), 0, cmd,
+		       INQUIRY_REPLY_LEN, Inqbuffer)) {
+      fprintf (stderr, "scsi_inquiry failed\n");
+      return NULL;
+  }
+
+  /* gather device characteristics */
+  if (ioctl(dev->fd, SG_GET_SCSI_ID, &dev->sginfo)<0) {
+	perror("scsi_inquiry: SG_GET_SCSI_ID");
+	return NULL;
+  }
+
+  /* extract null-terminated text from inquery command */
+  strncpy(dev->vendor,Inqbuffer+SG_LEN+VENDOR_START,8);
+  dev->vendor[8]='\0';
+  strncpy(dev->product,Inqbuffer+SG_LEN+VENDOR_START+8,16);
+  dev->product[16]='\0';
+  strncpy(dev->version,Inqbuffer+SG_LEN+VENDOR_START+24,4);
+  dev->version[4]='\0';
+
+  /* save the full inq buffer for vendor-specific stuff */
+  if (!(dev->fullinq=(char*)malloc(INQUIRY_REPLY_LEN+1))) {
+	perror("scsi_inquiry: malloc");
+	return NULL;
+  }
+  memcpy(dev->fullinq,Inqbuffer+SG_LEN,INQUIRY_REPLY_LEN);
+  dev->fullinq[INQUIRY_REPLY_LEN]='\0';
+
+  return dev;
+}
+
+int scsi_unitready (scsi_info * dev) {
+  static u_char cmd[SG_LEN + 18]; /* SCSI command buffer */
+  /* request READY status */
+  static u_char cmdblk[TESTUNITREADY_CMDLEN] = {
+    TESTUNITREADY_CMD,		/* command */
+    0,				/* lun/reserved */
+    0,				/* reserved */
+    0,				/* reserved */
+    0,				/* reserved */
+    0
+  };				/* reserved */
+
+  if (!dev) {
+	fprintf(stderr,"scsi_unitready got NULL dev?!\n");
+	return 0;
+  }
+
+  memcpy (cmd + SG_LEN, cmdblk, sizeof (cmdblk));
+
+  /*
+   * +------------------+
+   * | struct sg_header | <- cmd
+   * +------------------+
+   * | copy of cmdblk   | <- cmd + SG_LEN
+   * +------------------+
+   */
+
+  if (waitfor_scsi_cmd (dev->fd, sizeof (cmdblk), 0, cmd, 0, NULL)) {
+      fprintf (stderr, "scsi_testunitready failed\n");
+      return 0; /* not ready */
+  }
+
+  return
+    *(((struct sg_header *) cmd)->sense_buffer + ADD_SENSECODE) !=
+    NO_MEDIA_SC ||
+    *(((struct sg_header *) cmd)->sense_buffer + ADD_SC_QUALIFIER) !=
+    NO_MEDIA_SCQ;
+}
+
+scsi_info * scsi_open_name(char * filename) {
+	scsi_info * dev;
+	int flags;
+
+	if (!(dev=(scsi_info*)calloc(1,sizeof(scsi_info)))) {
+		perror("scsi_open malloc");
+		return NULL;
+	}
+	if (!filename) {
+		fprintf(stderr,"scsi_open got NULL filename?!\n");
+		return NULL;
+	}
+
+	if ((dev->fd = open (filename, O_RDWR | O_EXCL | O_NONBLOCK))<0) {
+		if (errno != ENOENT && errno != ENXIO) {
+			fprintf(stderr,"scsi_open('%s'): %s\n",filename,
+				strerror(errno));
+		}
+		free(dev);
+		return NULL;
+	}
+	if ((flags = fcntl(dev->fd, F_GETFL))<0) {
+		perror("scsi_open: fcntl(F_GETFL)");
+		goto failed;
+	}
+	if (fcntl(dev->fd, F_SETFL, flags & (~ O_NONBLOCK))<0) {
+		perror("scsi_open: fcntl(F_SETFL)");
+		goto failed;
+	}
+
+	/* perform inquiry */
+	if (!scsi_inquiry(dev)) {
+		fprintf(stderr,"scsi_open: failed inquiry\n");
+		goto failed;
+	}
+
+	dev->filename=strdup(filename);
+
+	return dev;
+failed:
+	scsi_close(dev);
+	return NULL;
+}
+
+scsi_info * scsi_open_id(int host, int chan, int id, int lun) {
+  scsi_info * dev;
+  char sgfile[24];
+  int i;
+
+  for (i=0;i<128;i++) {
+        sprintf(sgfile,"/dev/sg%d",i);
+
+        if ((dev = scsi_open_name(sgfile))!=NULL) {
+		if (	dev->sginfo.host_no==host &&
+			dev->sginfo.channel==chan &&
+			dev->sginfo.scsi_id==id &&
+			dev->sginfo.lun==lun) return dev;
+                scsi_close(dev);
+        }
+  }
+  return NULL;
+}
+
+void scsi_close(scsi_info * dev) {
+  if (!dev) {
+	fprintf(stderr,"scsi_close got NULL dev?!\n");
+	return;
+  }
+  close(dev->fd);
+  if (dev->filename) free(dev->filename);
+  if (dev->fullinq) free(dev->fullinq);
+  free(dev);
+}
+
+/* max str length is 13 */
+char * scsi_type(int type) {
+	switch (type) {
+	case TYPE_DISK: return "Disk";
+	case TYPE_TAPE: return "Tape";
+	case TYPE_PROCESSOR: return "Processor";
+	case TYPE_WORM: return "Worm";
+	case TYPE_ROM: return "CD-Rom";
+	case TYPE_SCANNER: return "Scanner";
+	case TYPE_MOD: return "Mag-optical";
+	case TYPE_MEDIUM_CHANGER: return "Changer";
+	case TYPE_COMM: return "Communication";
+	case TYPE_ENCLOSURE: return "Enclosure";
+	case TYPE_NO_LUN: return "No Lun";
+	default: return "Unknown";
+	}
+}
diff -uNr els-1.02-1/scsi_tools.h els-1.02-2_cjc_2/scsi_tools.h
--- els-1.02-1/scsi_tools.h	Wed Dec 31 18:00:00 1969
+++ els-1.02-2_cjc_2/scsi_tools.h	Fri Feb 16 08:51:29 2001
@@ -0,0 +1,64 @@
+/*
+ * header file for the scsi_tools.c library
+ *
+ * Copyright (C) 2001 Kees Cook
+ * cook@cpoint.net, http://outflux.net/
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+#ifndef SCSI_TOOLS_H
+#define SCSI_TOOLS_H
+#include <scsi/sg.h>
+#include <scsi/scsi.h>
+
+#ifndef TYPE_COMM
+# define TYPE_COMM 0x09
+#endif
+
+/* FIXME: get some of this stuff from <scsi/scsi.h> */
+#define INQUIRY_CMD     0x12
+#define INQUIRY_CMDLEN  6
+#define INQUIRY_REPLY_LEN 96
+#define VENDOR_INFO_LEN 28
+#define VENDOR_START  8 /* Offset in reply data to vendor name */
+
+#define TESTUNITREADY_CMD 0
+#define TESTUNITREADY_CMDLEN 6
+
+#define ADD_SENSECODE 12
+#define ADD_SC_QUALIFIER 13
+#define NO_MEDIA_SC 0x3a
+#define NO_MEDIA_SCQ 0x00
+
+typedef struct scsi_info {
+	int fd;	
+	char * filename;
+	char * fullinq; /* full inquiry buffer */
+	Sg_scsi_id sginfo;
+	char vendor[9];
+	char product[17];
+	char version[5];
+} scsi_info;
+
+scsi_info * scsi_inquiry (scsi_info * dev);
+int scsi_unitready (scsi_info * dev);
+scsi_info * scsi_open_name(char * filename);
+scsi_info * scsi_open_id(int host, int chan, int id, int lun);
+void scsi_close(scsi_info * dev);
+char * scsi_type(int type);
+
+#endif /* SCSI_TOOLS_H */
diff -uNr els-1.02-1/sts.c els-1.02-2_cjc_2/sts.c
--- els-1.02-1/sts.c	Wed Dec 31 18:00:00 1969
+++ els-1.02-2_cjc_2/sts.c	Fri Feb 16 08:44:31 2001
@@ -0,0 +1,628 @@
+/*
+ * SCSI engine for processing FAS commands
+ *
+ * Copyright (C) 2000,2001 Kees Cook
+ * cook@cpoint.net, http://outflux.net/
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <scsi/sg.h>
+#include <sys/ioctl.h>  /* ioctl */
+#include <ctype.h>	/* isprint */
+
+#define __USE_XOPEN
+#define __USE_GNU
+#include <stdlib.h>
+
+#include "scsi_iface.h"
+#include "scsi_tools.h"
+
+#include "sts.h"
+void fas_packet_ret(sts_state * sts, unsigned char * cmd);
+void fas_error(FILE * fd, unsigned int status);
+
+/* this is to have the UNIT_POLL response to have error bits cleared */
+#define UNIT_POLL_HACK
+
+/* helper macros for 16bit Least significant bit first */
+#define GRAB_16BIT(ptr, start) ((ptr)[(start)] + ((ptr)[(start)+1]<<8))
+#define STORE_16BIT(ptr,start,val) \
+			(ptr)[(start)]=((val) & 0xff); \
+			(ptr)[(start)+1]=(((val) & 0xff00) >> 8);
+
+/* helper macros for bit-title manipulations: bit# 0, 1, ... 7 */
+#define IS_SET(byte, bit)	(((byte) & (1 << (bit))) == (1 << (bit)))
+#define SET(byte, bit)		((byte) | (1 << (bit)))
+#define UNSET(byte, bit)	((byte) & (~(1 << (bit))))
+
+
+
+typedef struct fas_opcode_info_t {
+	char * t;		/* text name */
+	unsigned char req_family;/* family of FAS commands it belongs to */
+	unsigned char rsp_family;/* family of FAS commands it belongs to */
+} fas_opcode_info;
+
+fas_opcode_info fas_opcodes[] = {
+        { "FAS_GLOBAL", FAS_GLOBAL, FAS_GLOBAL },
+        { "FAS_ENABLE", FAS_ENABLE, FAS_ENABLE },
+        { "FAS_DISABLE", FAS_ENABLE, FAS_ENABLE },
+        { "FAS_SEND", FAS_SEND, FAS_ENABLE },
+        { "FAS_RECV", FAS_ENABLE, FAS_RECV },
+        { "FAS_IN_TIMERS", FAS_IN_TIMERS, FAS_ENABLE },
+        { "FAS_OUTPUT_CTL", FAS_OUTPUT_CTL, FAS_ENABLE },
+        { "FAS_INPUT_CTL", FAS_OUTPUT_CTL, FAS_ENABLE },
+        { "FAS_SET_MODEM", FAS_SET_MODEM, FAS_ENABLE },
+        { "FAS_STAT_CHG", FAS_STAT_CHG, FAS_STAT_CHG },
+        { "FAS_SEND_BRK", FAS_ENABLE, FAS_ENABLE },
+        { "FAS_SET_PARAMS", FAS_SET_PARAMS, FAS_ENABLE },
+        { "FAS_FLOW_CTL", FAS_FLOW_CTL, FAS_ENABLE },
+        { "FAS_RESERVE", FAS_ENABLE, FAS_ENABLE },
+        { "FAS_RELEASE", FAS_ENABLE, FAS_ENABLE },
+	{ "FAS_RESET", FAS_ENABLE, FAS_ENABLE },	/* 15 */
+	{ "FAS_UNIT_STATS", FAS_UNIT_STATS, FAS_RECV },	/* 16 */
+	{ "FAS_WAIT_SEND", FAS_ENABLE, FAS_ENABLE },	/* 17 */
+	{ "FAS_LINE_STATE", FAS_UNIT_STATS, FAS_RECV },	/* 18 */
+	{ "FAS_LINE_INQUIRY", FAS_ENABLE, FAS_LINE_INQUIRY },	/* 19 */
+	{ "FAS_LPT_TEST", FAS_ENABLE, FAS_ENABLE },	/* 20 */
+	{ "FAS_UNIT_POLL", FAS_UNIT_POLL, FAS_ENABLE },	/* 21 */
+	{ "FAS_UNIT_UNLOCK", FAS_UNIT_UNLOCK, FAS_UNIT_UNLOCK },/* 22 */
+	{ "FAS_UNIT_INQUIRY", FAS_UNIT_INQUIRY, FAS_UNIT_INQUIRY },/* 23 */
+	{ "FAS_EXT_PARAMS", FAS_ENABLE, FAS_ENABLE },	/* 24 */
+	{ "FAS_UNIT_RESET", FAS_ENABLE, FAS_ENABLE },	/* 25 */
+	{ "Unknown", FAS_ENABLE, FAS_ENABLE },		/* 26 */
+	{ "Unknown", FAS_ENABLE, FAS_ENABLE },		/* 27 */
+	{ "Unknown", FAS_ENABLE, FAS_ENABLE },		/* 28 */
+	{ "Unknown", FAS_ENABLE, FAS_ENABLE },		/* 29 */
+	{ "Unknown", FAS_ENABLE, FAS_ENABLE },		/* 30 */
+	{ "Unknown", FAS_ENABLE, FAS_ENABLE },		/* 31 */
+	{ "Unknown", FAS_ENABLE, FAS_ENABLE },		/* 32 */
+	{ "Unknown", FAS_ENABLE, FAS_ENABLE },		/* 33 */
+	{ "Unknown", FAS_ENABLE, FAS_ENABLE },		/* 34 */
+	{ "Unknown", FAS_ENABLE, FAS_ENABLE },		/* 35 */
+	{ "Unknown", FAS_ENABLE, FAS_ENABLE },		/* 36 */
+	{ "Unknown", FAS_ENABLE, FAS_ENABLE },		/* 37 */
+	{ "Unknown", FAS_ENABLE, FAS_ENABLE },		/* 38 */
+	{ "Unknown", FAS_ENABLE, FAS_ENABLE },		/* 39 */
+	{ "Unknown", FAS_ENABLE, FAS_ENABLE },		/* 40 */
+	{ "Unknown", FAS_ENABLE, FAS_ENABLE },		/* 41 */
+	{ "Unknown", FAS_ENABLE, FAS_ENABLE },		/* 42 */
+	{ "Unknown", FAS_ENABLE, FAS_ENABLE },		/* 43 */
+	{ "Unknown", FAS_ENABLE, FAS_ENABLE },		/* 44 */
+	{ "Unknown", FAS_ENABLE, FAS_ENABLE },		/* 45 */
+	{ "Unknown", FAS_ENABLE, FAS_ENABLE },		/* 46 */
+	{ "Unknown", FAS_ENABLE, FAS_ENABLE },		/* 47 */
+	{ "Unknown", FAS_ENABLE, FAS_ENABLE },		/* 48 */
+	{ "Unknown", FAS_ENABLE, FAS_ENABLE },		/* 49 */
+	{ "FAS_CONFIG_LINK", FAS_CONFIG_LINK, FAS_ENABLE },	/* 50 */
+	{ "FAS_MUX_STATS", FAS_UNIT_STATS, FAS_RECV }		/* 51 */
+};
+
+/* adds a FAS command to the doubly linked list of queued cmds */
+void add_cmd(sts_state * sts, char * data, int len) {
+	cmd_list * cmd;
+
+	if (!sts) {
+		fprintf(stderr,"add_cmd: Got null sts\n");
+		return;
+	}
+	if (!data && len!=0) {
+		fprintf(stderr,"add_cmd: Got null data pointer\n");
+		return;
+	}
+	if (!(cmd=malloc(sizeof(cmd_list)))) {
+		perror("add_cmd pointer malloc");
+		return;
+	}
+	if (!(cmd->data=malloc(len))) {
+		perror("add_cmd data malloc");
+		return;
+	}
+	memcpy(cmd->data, data, len);
+	cmd->size=len;
+	cmd->next=NULL;
+	cmd->prev=sts->tail;
+	if (sts->tail) sts->tail->next=cmd;
+	sts->tail=cmd;
+	if (!sts->head) sts->head=cmd;
+}
+
+/* find a properly size cmd in the list, starting with "cmd" */
+cmd_list * find_cmd(sts_state * sts, cmd_list *cmd, int slotsize) {
+	if (!sts) {
+		fprintf(stderr,"find_cmd: Got null sts\n");
+		return NULL;
+	}
+	if (!cmd) cmd=sts->head;
+	for (;cmd;cmd=cmd->next)
+		if (cmd->size<=slotsize) break;
+	return cmd;
+}
+
+/* throws away a cmd in the list */
+void delete_cmd(sts_state * sts, cmd_list * cmd) {
+	if (!sts) {
+		fprintf(stderr,"delete_cmd: Got null sts\n");
+		return;
+	}
+	if (!cmd) {
+		fprintf(stderr,"delete_cmd: Got null cmd\n");
+		return;
+	}
+	if (cmd->prev) cmd->prev->next=cmd->next;
+	if (cmd->next) cmd->next->prev=cmd->prev;
+	if (sts->head == cmd) sts->head=cmd->next;
+	if (sts->tail == cmd) sts->tail=cmd->prev;
+	free(cmd->data);
+	free(cmd);
+}
+
+void printchars(unsigned char * str, int len) {
+	int i;
+
+	for (i=0;i<len;i++) {
+		if (isprint(str[i])) {
+ 		  sts_debug("\t\t%d: 0x%02X (%c)\n",i, str[i], str[i]);
+		}
+		else {
+ 		  sts_debug("\t\t%d: 0x%02X\n",i, str[i]);
+		}
+	}
+}
+
+
+/* FIXME: uuuuh... do I need to specify LUNs or does sg handle that? */
+/* request vendor brand and model */
+unsigned char * Inquiry (int fd) {
+#define INQUIRY_CMD     0x12
+#define INQUIRY_CMDLEN  6
+#define INQUIRY_REPLY_LEN 96
+  unsigned char cmd[MAX_PACKET];
+  static unsigned char Inqbuffer[SG_LEN + INQUIRY_REPLY_LEN];
+  unsigned char cmdblk[INQUIRY_CMDLEN] = { 
+    INQUIRY_CMD,		/* command */
+    0,				/* lun/reserved */
+    0,				/* page code */
+    0,				/* reserved */
+    INQUIRY_REPLY_LEN,		/* allocation length */
+    0				/* reserved/flag/link */
+  };
+
+
+  memcpy (cmd + SG_LEN, cmdblk, sizeof (cmdblk));
+
+#ifdef SCSI_DEBUG
+  sts_debug("scsi inquiry...\n");
+#endif
+  if (waitfor_scsi_cmd (fd, sizeof (cmdblk), 0, cmd,
+		       sizeof (Inqbuffer) - SG_LEN, Inqbuffer)) {
+      fprintf (stderr, "Inquiry failed\n");
+      exit (2);
+  }
+
+  return Inqbuffer+SG_LEN;
+}
+
+
+/* test unit ready: 0 is okay? */
+int TestForMedium (int fd) {
+#define TESTUNITREADY_CMD 0
+#define TESTUNITREADY_CMDLEN 6
+#define ADD_SENSECODE 12
+#define ADD_SC_QUALIFIER 13
+#define NO_MEDIA_SC 0x3a
+#define NO_MEDIA_SCQ 0x00
+  unsigned char cmd[MAX_PACKET];
+  /* request READY status */
+  static unsigned char cmdblk[TESTUNITREADY_CMDLEN] = {
+    TESTUNITREADY_CMD,		/* command */
+    0,				/* lun/reserved */
+    0,				/* reserved */
+    0,				/* reserved */
+    0,				/* reserved */
+    0
+  };				/* reserved */
+
+  memcpy (cmd + SG_LEN, cmdblk, sizeof (cmdblk));
+
+#ifdef SCSI_DEBUG
+  sts_debug("scsi unitready...\n");
+#endif
+  if (waitfor_scsi_cmd (fd, sizeof (cmdblk), 0, cmd, 0, NULL)) {
+      fprintf (stderr, "Test unit ready failed\n");
+      exit (2);
+  }
+
+  return
+    *(((struct sg_header *) cmd)->sense_buffer + ADD_SENSECODE) !=
+    NO_MEDIA_SC ||
+    *(((struct sg_header *) cmd)->sense_buffer + ADD_SC_QUALIFIER) !=
+    NO_MEDIA_SCQ;
+}
+
+/* sts_write needs to act like the standard system write call */
+int sts_write(sts_state * sts, unsigned char * fasblock, int len) {
+#define SENDMSG_CMD	0x0A
+#define SENDMSG_CMDLEN	6
+  static unsigned char sendblk[SENDMSG_CMDLEN] = {
+	SENDMSG_CMD,	/* Op Code */
+	0,		/* Lun/reserved  FIXME? */
+	0,0,0,          /* transfer length (needs to be set) */
+	0		/* Control ?! */
+  };
+  /* FIXME: verify somewhere that MAX_PACKET is large enough
+     for FAS_PKT_MAX + SG_LEN + SENDMSG_CMDLEN */
+  static unsigned char cmd[MAX_PACKET];
+  int result;
+  unsigned char buffer[FAS_PKT_MAX + SG_LEN];
+
+  if (!sts) {
+	fprintf(stderr,"sts_write: got null sts!\n");
+	errno=EBADF;
+	return -1;
+  }
+
+  if (len > FAS_PKT_MAX) {
+	fprintf(stderr,"send_fas_block: block too large: %d > %d\n",
+		len, FAS_PKT_MAX);
+	errno=ENOSPC;
+	return -1;
+  }
+
+  /* read incoming FAS block from ELS and process */
+  len=find_fas_block_size(sts,fasblock,len,
+	cmd + SG_LEN + sizeof(sendblk),FAS_PKT_MAX,
+	FAS_ELS);
+
+  /* set the length bytes */
+  sendblk[2]=((len & 0xff0000) >> 16);
+  sendblk[3]=((len & 0xff00) >> 8);
+  sendblk[4]=(len & 0xff);
+
+  /* put the cmd block into the buffer also */
+  memcpy (cmd + SG_LEN, sendblk, sizeof (sendblk));
+  /* already did this during find_fas_block_size
+  memcpy (cmd + SG_LEN + sizeof(sendblk), fasblock, len -1);
+  */
+
+#if 0
+  /* FIXME: make the report-only var a define here */
+  process_fas_block(sts,cmd+SG_LEN+sizeof(sendblk),len,1);
+#endif
+
+#ifdef SCSI_DEBUG
+  sts_debug("sts_write ...\n");
+  sts_debug("\tSG header\n");
+  printchars(cmd,SG_LEN);
+  sts_debug("\tSCSI cmd\n");
+  printchars(cmd+SG_LEN,sizeof(sendblk));
+  sts_debug("\tSCSI data\n");
+  printchars(cmd+SG_LEN+sizeof(sendblk),len);
+#endif
+  result=waitfor_scsi_cmd(	
+			sts->senddev->fd,
+			SENDMSG_CMDLEN,
+			len,
+			cmd,
+			0,
+			buffer);
+
+  if (result!=0) {
+	fprintf(stderr,"sts_write: SCSI transport failure\n");
+	errno=EIO;
+	return -1;
+  }
+
+  return len;
+}
+
+
+/* send SCSI command to get info from STS */
+int recv_fas_block(sts_state * sts) {
+#define GETMSG_CMD	0x08
+#define GETMSG_CMDLEN	6
+  static unsigned char recvblk[GETMSG_CMDLEN] = {
+	GETMSG_CMD,	/* Op Code */
+	0,		/* Lun/reserved  FIXME? */
+	0,8,0,          /* transfer len: 2048 */
+	0		/* Control ?! */
+  };
+  static unsigned char cmd[MAX_PACKET];
+  int result;
+
+  if (!sts) {
+	fprintf(stderr,"recv_fas_block: got null sts!\n");
+	return 1;
+  }
+
+  memcpy(cmd + SG_LEN, recvblk, sizeof (recvblk));
+
+#ifdef SCSI_DEBUG
+  sts_debug("recv_fas_block ...\n");
+#endif
+  result=start_scsi_cmd(
+			sts->recvdev->fd,
+			GETMSG_CMDLEN,
+			0,
+			cmd,
+			FAS_PKT_MAX);
+
+  if (result!=0) {
+	fprintf(stderr,"recv_fas_block: SCSI transport failure\n");
+  }
+  return result;
+}
+
+/* examine "block" for FAS commands, copying them into "fascmds" */
+int find_fas_block_size(sts_state * sts, char * block, int maxsize,
+  char * fascmds, int bufsize, int fromscsi) {
+  int remaining;          /* how much left to read */
+  unsigned char * ptr;    /* string pointer into block */
+  int cmdat;                      /* debug: start of command */
+  unsigned char * cmd;		/* current fas cmd */
+  int len;                        /* how much data follows FAS_RECV */
+  int size;
+  int sawpktend=0;
+  unsigned char opcode, port, status, follow;
+
+
+  if (!fascmds || (bufsize < maxsize)) {
+	fprintf(stderr,"find_fas_block: problem, incoming buffer too small\n");
+	/* behave like read */
+	errno=EFAULT;
+	return -1;
+  }
+
+  if (!sts) {
+	fprintf(stderr,"find_fas_block_size: got null sts!\n");
+	errno=EINVAL;
+	return -1;
+  }
+  if (!block && maxsize!=0) {
+	fprintf(stderr,"find_fas_block_size: got null block!\n");
+	errno=EINVAL;
+	return -1;
+  }
+
+#if 0
+	if (maxsize>0 && *block!=PKT_END)
+#endif
+	sts_debug("from %s (%d byte%s):\n",
+		fromscsi ? "SCSI bus" : "ELS driver",maxsize,maxsize==1?"":"s");
+
+        cmdat=0;
+        ptr=block;
+	cmd=fascmds;
+        remaining=maxsize;
+        size=0;
+        for (;remaining;cmdat+=8) {
+                opcode=*cmd=*ptr++;
+                if (opcode == PKT_END) {
+#if 0
+			if (cmdat>0)
+#endif
+                        sts_debug("\tat %d: PKT_END\n",cmdat);
+                        /* empty packet? */
+                        if (cmdat == 0) {
+                                sts->flushed=1;
+                        }
+			sawpktend=1;
+			size++; /* got PKT_END... */
+                        break; /* stop processing */
+                }
+                if (opcode == PKT_ERR) {
+                        fprintf(stderr,"process_fas_block: buffer read "
+                                       "overrun\n");
+			/*
+			errno=EFAULT;
+                        return -1;
+			*/
+			/* be nicer: pass what we have */
+			break;
+                }
+                if (remaining < FAS_CMD_LEN) {
+                        fprintf(stderr,"process_fas_block: (opcode 0x%02X) "
+				"buffer read underrun\n",opcode);
+			/*
+			errno=EFAULT;
+                        return -1;
+			*/
+			/* be nicer: pass what we have */
+			break;
+                }
+                memcpy(cmd+1,ptr,FAS_CMD_LEN - 1);
+                ptr+=FAS_CMD_LEN-1;
+                remaining-=FAS_CMD_LEN;
+		size+=FAS_CMD_LEN;
+
+		port=cmd[1];
+		status=cmd[2];
+		follow=cmd[3];
+
+		if (opcode < FAS_CMD_MAX) {
+		   /* handle any weird protocol translations */
+		   if (fromscsi) {
+#ifdef UNIT_POLL_HACK
+			   /* trick ELS into hearing polls by reseting
+			      status flag to 0 ? */
+			   if (opcode == FAS_UNIT_POLL)
+				status=cmd[2]=0;
+#endif
+		   }
+		   else {
+#if 0   /* doesn't help... */
+			/* STS seems to think "0x81" is okay for DTR/RTS
+			   but I don't think so... */
+			if (opcode == FAS_SET_MODEM) {
+				cmd[2]&=(~0x80);
+				cmd[3]&=(~0x80);
+			}
+#endif
+		   }
+			
+		   sts_debug("\tat %d: %s (%04X %04X %04X %04X)\n",
+			cmdat, fas_opcodes[opcode].t, 
+			(cmd[0] << 8)|(cmd[1]),
+			(cmd[2] << 8)|(cmd[3]),
+			(cmd[4] << 8)|(cmd[5]),
+			(cmd[6] << 8)|(cmd[7]));
+
+                   if ((!fromscsi && fas_opcodes[opcode].req_family == FAS_SEND) ||
+		       (fromscsi && fas_opcodes[opcode].rsp_family == FAS_RECV)) {
+	                len=GRAB_16BIT(cmd,fromscsi ? 4 : 2);
+
+                        if (remaining < len) {
+                                   fprintf(stderr,"process_fas_block: %s "
+                                        "data underrun -- expected %d got %d "
+                                        "or less\n", fas_opcodes[opcode].t,
+                                        len,remaining);
+				   errno=EFAULT;
+                                   return -1;
+                         }
+
+                         if (len>0) {
+					size+=len;
+                                        memcpy(cmd+FAS_CMD_LEN,ptr,len);
+                                        ptr+=len;
+                                        remaining-=len;
+
+                                        sts_debug("\tat %d: %s data (%d "
+                                           "byte%s)\n",
+                                           cmdat+FAS_CMD_LEN,
+					   fas_opcodes[opcode].t,
+                                           len, len == 1 ? "" : "s");
+                                        cmdat+=len;
+                                        printchars(cmd+FAS_CMD_LEN,len);
+			                cmd+=len;
+                          }
+			  else {
+                                        sts_debug("\tat %d: %s has no data\n",
+                                           cmdat+FAS_CMD_LEN,
+					   fas_opcodes[opcode].t);
+			  }
+		   }
+		}
+		else {
+			sts_debug("\tat %d: 0x%02X\n", cmdat, opcode);
+		}
+
+		cmd+=FAS_CMD_LEN;
+	}
+
+  if (fromscsi) {
+	/* if it's from the SCSI bus, we don't want the PKT_END */
+	if (sawpktend) size--;
+  }
+  else {
+	/* tack on trailing PKT_END if not at packet max */
+	if (!sawpktend && size>0 && size<FAS_PKT_MAX) {
+		sts_debug("\tat %d: PKT_END (inserted)\n",cmdat);
+		*cmd=PKT_END;
+		size++;
+	}
+  }
+
+  if (size > 0) 
+  	sts_debug("sending %d byte%s to %s\n",size, size == 1 ? "" : "s",
+		!fromscsi ? "SCSI bus" : "ELS driver");
+
+#ifdef STS_DEBUG
+  printchars(fascmds,size);
+#endif
+
+  return size;
+}
+
+/* act like system read call */
+int sts_read(sts_state * sts, char * data_buf, int n) {
+  int result;
+  unsigned char buffer[FAS_PKT_MAX + SG_LEN+1];
+
+  if (!sts) {
+	fprintf(stderr,"sts_read: got null sts!\n");
+	errno=EBADF;
+	return -1;
+  }
+  if (!data_buf) {
+	fprintf(stderr,"sts_read: got null data_buf!\n");
+	errno=EFAULT;
+	return -1;
+  }
+  if (n<FAS_PKT_MAX) {
+	fprintf(stderr,"sts_read: input buffer too small!\n");
+	errno=ENOMEM;
+  }
+
+  /* fill overflow data with PKT_ERRs for safety */
+  memset(buffer+SG_LEN,PKT_ERR,FAS_PKT_MAX+1);
+
+#ifdef SCSI_DEBUG
+  sts_debug("sts_read ...\n");
+#endif
+  result=finish_scsi_cmd(
+			sts->recvdev->fd,
+			FAS_PKT_MAX,
+			buffer);
+
+  if (result!=0) {
+	fprintf(stderr,"sts_read: SCSI transport failure\n");
+	errno=EIO;
+	return -1;
+  }
+  /* immediately re-post the recv request */
+  recv_fas_block(sts);
+
+  return find_fas_block_size(sts,
+	buffer+SG_LEN,FAS_PKT_MAX,
+	data_buf,n,
+	FAS_SCSI);
+}
+
+sts_state * sts_shutdown(sts_state * sts) {
+  int result;
+  unsigned char buffer[FAS_PKT_MAX + SG_LEN];
+
+  if (!sts) {
+	fprintf(stderr,"sts_read: got null sts!\n");
+  }
+  else {
+
+#ifdef SCSI_DEBUG
+	sts_debug("sts_shutdown ...\n");
+#endif
+	result=finish_scsi_cmd(
+			sts->recvdev->fd,
+			FAS_PKT_MAX,
+			buffer);
+
+	if (result!=0) {
+		fprintf(stderr,"sts_shutdown: SCSI transport failure\n");
+	}
+
+	scsi_close(sts->senddev);
+	scsi_close(sts->recvdev);
+	free(sts);
+  }
+
+  return NULL;
+}
+
diff -uNr els-1.02-1/sts.h els-1.02-2_cjc_2/sts.h
--- els-1.02-1/sts.h	Wed Dec 31 18:00:00 1969
+++ els-1.02-2_cjc_2/sts.h	Fri Feb 16 08:44:25 2001
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2000,2001 Kees Cook
+ * cook@cpoint.net, http://outflux.net/
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ */
+#ifndef STS_H
+#define STS_H
+#include "scsi_tools.h"
+
+/* shamelessly stolen from EXT2 headers... */
+/*
+ * Define LOG_DEBUG to produce debug logs to stderr
+ */
+#undef STDERR_DEBUG
+/*
+ * Define STS_DEBUG to produce debug messages
+ */
+#undef	STS_DEBUG
+/*
+ * Define SCSI_DEBUG to produce debug messages for scsi calls (needs STS_DEBUG)
+ */
+#undef SCSI_DEBUG
+/*
+ * Define SELECT_DEBUG to produce debug messages during select loop
+ * (requires STDERR_DEBUG)
+ */
+#undef SELECT_DEBUG
+
+
+/*
+ * Debug code
+ *                                 printf ("sts DEBUG (%s, %d): %s:", \
+ *                                          __FILE__, __LINE__, __FUNCTION__); \
+ */
+#ifdef STS_DEBUG
+# ifndef STDERR_DEBUG
+#  define STDERR_DEBUG
+# endif
+#       define sts_debug(f, a...) { \
+                                  printf (f, ## a); \
+                                  }
+#else
+#       define sts_debug(f, a...)      /**/
+#endif
+
+
+#if 0
+#include "fas.h"
+#endif
+
+/* for typing convenience */
+#define SG_LEN          sizeof(struct sg_header)
+#define MAX_PACKET      4096
+#define FAS_PKT_MAX	2048 /* not true, perhaps? */
+#define FAS_SCSI	1 /* FAS block from SCSI bus */
+#define FAS_ELS		0 /* FAS block from ELS driver */
+#ifndef PKT_END
+#define PKT_END		0x64 /* FAS end marker for SCSI */
+#define PKT_ERR		0xFF /* my marker to show I'm reading too far */
+
+#define FAS_CMD_LEN     8    /* bytes in a FAS_CMD header */
+
+#define FAS_GLOBAL      0x00 /* Sets global firmware options for all lines */
+#define FAS_ENABLE      0x01 /* Enables specific async line,
+                                initializing to defaults; must be
+                                issued before any other command to a line */
+#define FAS_DISABLE     0x02 /* Disables specific async line, turning
+                                off interrupts, etc. */
+#define FAS_SEND        0x03 /* Sends data for output */
+
+#define FAS_RECV        0x04 /* Receives input data */
+#define FAS_IN_TIMERS   0x05 /* Sets custom block transfer parameters */
+#define FAS_OUTPUT_CTL  0x06 /* Flushes, suspends, or resumes output at
+                                a line */
+#define FAS_INPUT_CTL   0x07 /* Flushes, suspends, or resumes input at
+                                a line */
+#define FAS_SET_MODEM   0x08 /* Controls modem output signals */
+#define FAS_STAT_CHG    0x09 /* Monitors modem input signals */
+#define FAS_SEND_BRK    0x0A /* Issues break indication */
+#define FAS_SET_PARAMS  0x0B /* Sets line parameters (e.g., baud rate,
+                                bits per character, stop bits) */
+#define FAS_FLOW_CTL    0x0C /* Sets firmware flow control parameters */
+#define FAS_RESERVE     0x0D /* Requests private use of a line */
+#define FAS_RELEASE     0x0E /* Releases private use of a line */
+/* from EL fas.h ... */
+#define FAS_RESET                       15
+#define FAS_UNIT_STATS          16
+#define FAS_WAIT_SEND           17
+#define FAS_LINE_STATE          18
+#define FAS_LINE_INQUIRY        19
+#define FAS_LPT_TEST            20
+#define FAS_UNIT_POLL           21
+#define FAS_UNIT_UNLOCK         22
+#define FAS_UNIT_INQUIRY        23
+#define FAS_EXT_PARAMS          24
+#define FAS_UNIT_RESET          25
+#define FAS_CONFIG_LINK         50
+#define FAS_MUX_STATS           51
+#define FAS_CMD_MAX		(FAS_MUX_STATS+1)
+#endif
+
+
+/* scsi vendor info offsets */
+#define EXTRA_OFF       4       /* where the extra len is */
+#define VENDOR_START    8       /* Offset in reply data to vendor name */
+#define VENDOR_LEN      8       /* length of vendor string */
+#define PRODUCT_LEN     16      /* length of product string */
+#define REV_LEN         4       /* length of rev string */
+#define EXTRA_LEN       20      /* length of the extra string */
+
+/* cmd list info */
+typedef struct cmd_list_t {
+        unsigned char * data;
+        int size;
+        struct cmd_list_t * prev;
+        struct cmd_list_t * next;
+} cmd_list;
+
+/* sts structure */
+typedef struct sts_state_t {
+        int host, chan, id;      /* SCSI identifiers */
+        char vendor[VENDOR_LEN + 1];   /* SCSI manufacturer string */
+        char product[PRODUCT_LEN + 1]; /* SCSI model string */
+        char rev[REV_LEN + 1];         /* SCSI revision */
+        char sendextra[EXTRA_LEN + 1]; /* SCSI vendor extras */
+        char recvextra[EXTRA_LEN + 1]; /* SCSI vendor extras */
+        scsi_info * senddev;	/* SCSI device for Send LUN */
+	scsi_info * recvdev;    /* SCSI device for Recv LUN */
+        cmd_list * head;        /* head of cmd list */
+        cmd_list * tail;        /* tail of cmd list */
+        int writable;           /* can I post an STS write cmd? */
+        int readable;           /* can I post an STS read cmd? */
+	int flushed;		/* msg recv are clear */
+        int globaled;           /* did we init the sts? */
+	int portsensed;		/* did we finish sensing the number of ports?*/
+	int shutdown;		/* shutdown requested */
+} sts_state;
+
+int recv_fas_block(sts_state * sts);
+int find_fas_block_size(sts_state * sts, char * block, int maxsize,
+  char * fascmds, int bufsize, int fromscsi);
+
+int sts_write(sts_state * sts, unsigned char * data_buf, int len);
+int sts_read(sts_state * sts, char * data_buf, int n);
+sts_state * sts_shutdown(sts_state * sts);
+
+#endif
diff -uNr els-1.02-1/sts_unittab.c els-1.02-2_cjc_2/sts_unittab.c
--- els-1.02-1/sts_unittab.c	Wed Oct 25 18:14:35 2000
+++ els-1.02-2_cjc_2/sts_unittab.c	Fri Feb 16 08:46:55 2001
@@ -54,6 +54,86 @@
  */
 static ctunitinfo ctunittab[] = {
   /* name	#lines	max_pkt max_line_size	ppci	special func */
+  /*
+	I'm still guessing at the meaning of the "max_line_size" values
+	for these devices.  500 seemed to work for the Moto devices,
+	so I left them that way for all of them.
+		-Kees Jan 19, 2001
+  */
+  /*
+	I've redefined "SFC_STD" to be "SFC_ETH", but that meant I needed
+	to add it to each of the bit fields here.  SFC_STD used to be 0,
+	so it was implicit. -Kees Feb 16, 2001
+  */
+  /* Motorola SCSI Terminal Server Units */
+  { "STS-S8P",         8,     2048,   500,  PCC_8_0_38400,     (SFC_STD) },
+  { "STS-S16P",       16,     2048,   500,  PCC_16_0_38400,    (SFC_STD) },
+  /*
+	I've had to set the Moto units to "PCC_*_0_38400" to get them to run
+        correctly.  This does not appear to be due to the ancient
+	firmware in my units.	-Kees Jan 19, 2001
+  */
+
+  /* SCSI Terminal Server Units */
+  { "ST-1000",        16,     2048,   500,  PCC_16_0_57600,    (SFC_STD) },
+  { "ST-1000/ST-1016",16,     2048,   500,  PCC_16_0_57600,    (SFC_STD) },
+  { "ST-1000/SC-1016",16,     2048,   500,  PCC_16_0_57600,    (SFC_STD) },
+  { "ST-1016G",       16,     4096,   500,  PCC_16_0_115200,   (SFC_STD) },
+  { "ST-1616",        16,     4096,   500,  PCC_16_0_115200,   (SFC_STD) },
+  { "ST-1616B",       16,     4096,   500,  PCC_16_0_230400,   (SFC_STD |
+								SFC_NO_SPLIT) },
+  { "ST-1000/ST-1616",16,     4096,   500,  PCC_16_0_115200,   (SFC_STD) },
+  { "ST-1000/ST-1616B",16,    4096,   500,  PCC_16_0_230400,   (SFC_STD |
+								SFC_NO_SPLIT) },
+  { "ST-1002",         3,     2048,   500,  PCC_2_1_57600,     (SFC_STD |
+								SFC_NO_SPLIT) },
+  { "ST-1002/ST-1002F",3,     2048,   500,  PCC_2_1_115200,    (SFC_STD |
+								SFC_NO_SPLIT) },
+  { "ST-1002G",        3,     2048,   500,  PCC_2_1_115200,    (SFC_STD) },
+  { "ST-1002S",        3,     4096,   500,  PCC_2_1_230400,    (SFC_STD |
+								SFC_NO_SPLIT) },
+  { "SP-1003",         3,     4096,   500,  PCC_0_3_57600,     (SFC_STD |
+								SFC_1003) },
+  { "ST-1008",         9,     2048,   500,  PCC_8_1_57600,     (SFC_STD) },
+  { "ST-1008G",        9,     2048,   500,  PCC_8_1_115200,    (SFC_STD) },
+  { "ST-1008S",        9,     4096,   500,  PCC_8_1_230400,    (SFC_STD |
+								SFC_NO_SPLIT) },
+  { "ST-1400A",        4,     2048,   500,  PCC_4_0_115200,    (SFC_STD) },
+  { "ST-1400B",        4,     4096,   500,  PCC_4_0_230400,    (SFC_STD |
+								SFC_NO_SPLIT) },
+  { "ST-1620",        16,     4096,   500,  PCC_16_0_230400,   (SFC_STD |
+								SFC_NO_SPLIT) },
+  { "ST-1622",        16,     4096,   500,  PCC_16_0_115200,   (SFC_STD |
+								SFC_NO_SPLIT) },
+  { "ST-1800A",        8,     2048,   500,  PCC_8_0_115200,    (SFC_STD) },
+  { "ST-1800A+",       9,     2048,   500,  PCC_8_1_115200,    (SFC_STD) },
+  { "ST-1800B",        8,     4096,   500,  PCC_8_0_230400,    (SFC_STD |
+								SFC_NO_SPLIT) },
+  { "ST-1800B+",       9,     4096,   500,  PCC_8_1_230400,    (SFC_STD |
+								SFC_NO_SPLIT) },
+  { "ST-1008/ST-1400", 4,     2048,   500,  PCC_4_0_115200,    (SFC_STD) },
+  { "ST-1000/Sx-1600",16,     4096,   500,  PCC_16_0_115200,   (SFC_STD) },
+  { "ST-1000/ST-1600",16,     4096,   500,  PCC_16_0_115200,   (SFC_STD) },
+  { "ST-1000/SC-1600",16,     4096,   500,  PCC_16_0_115200,   (SFC_STD) },
+  { "ST-1008/ST-1800", 8,     2048,   500,  PCC_8_0_115200,    (SFC_STD) },
+  { "ST-1008/ST-1800P",9,     2048,   500,  PCC_8_1_115200,    (SFC_STD) },
+  { "ST-1000/ST-1610",16,     4096,   500,  PCC_16_0_115200,   (SFC_STD) },
+  { "ST-1008/SM-5008", 8,     2048,   500,  PCC_8_0_115200,    (SFC_STD |
+								SFC_NO_SPLIT) },
+  { "ST-1000/SM-5016",16,     4096,   500,  PCC_16_0_115200,   (SFC_STD |
+								SFC_NO_SPLIT) },
+  { "ST-1008/ST-8008", 9,     2048,   500,  PCC_8_1_57600,     (SFC_STD) },
+  { "ST-1008G/ST-8008",9,     2048,   500,  PCC_8_1_115200,    (SFC_STD) },
+
+  /* I have no idea how to support these.  The PCC code doesn't make sense
+	-Kees Jan 19, 2001
+  { "ST-2001/Sx-2001",126,    4096,   500,  PCC_2001_57600,    (SFC_2000) },
+  { "ST-2001/ST-2126",126,    4096,   500,  PCC_2126_115200,   (SFC_STD) },
+  */
+  { "Sx-4032",        32,     2048,   500,  PCC_32_0_57600,    (SFC_STD) },
+  { "ST-1032",        32,     4096,   500,  PCC_32_0_115200,   (SFC_STD) },
+  { "ST-1032A",       32,     4096,   500,  PCC_32_0_230400,   (SFC_STD |
+								SFC_NO_SPLIT) },
 	
   /* EtherLite(R) Units */
   { "EL-2",	2,	2048,	2304, PCC_2_0_230400,	(SFC_ETH |
@@ -210,6 +290,11 @@
 {
 	
 	switch( ctf_port_type( uinfo, port_num ) ) {
+	case PT_38400_SERIAL:
+		if ((fascode > FAS38400) || fascode == FASNOBAUD)
+			return( !OK );
+		break;
+
 	case PT_57600_SERIAL:
 		if ((fascode > FAS57600) || fascode == FASNOBAUD)
 			return( !OK );
diff -uNr els-1.02-1/sts_unittab.h els-1.02-2_cjc_2/sts_unittab.h
--- els-1.02-1/sts_unittab.h	Wed Oct 25 18:14:35 2000
+++ els-1.02-2_cjc_2/sts_unittab.h	Mon Feb 12 03:38:10 2001
@@ -32,6 +32,10 @@
 #endif
 
 /* Note: These should match the defines in fas.h */
+#ifndef FAS38400
+# define FAS38400		0xc
+#endif
+
 #ifndef FAS57600
 # define FAS57600		0xd
 #endif
@@ -98,6 +102,7 @@
 #define	PCC_4_0_115200		MAKE_PCC_CODE(	4,	0, FAS115200, 0)
 #define	PCC_4_0_230400		MAKE_PCC_CODE(	4,	0, FAS230400, 0)
 #define	PCC_4_0_460800		MAKE_PCC_CODE(	4,	0, FAS460800, 0)
+#define	PCC_8_0_38400		MAKE_PCC_CODE(  8,	0, FAS38400,  0)
 #define	PCC_8_0_57600		MAKE_PCC_CODE(	8,	1, FAS57600,  0)
 #define	PCC_8_0_115200		MAKE_PCC_CODE(	8,	0, FAS115200, 0)
 #define	PCC_8_0_230400		MAKE_PCC_CODE(	8,	0, FAS230400, 0)
@@ -106,6 +111,7 @@
 #define	PCC_8_1_115200		MAKE_PCC_CODE(	8,	1, FAS115200, 0)
 #define	PCC_8_1_230400		MAKE_PCC_CODE(	8,	1, FAS230400, 0)
 #define	PCC_8_1_460800		MAKE_PCC_CODE(	8,	1, FAS460800, 0)
+#define	PCC_16_0_38400		MAKE_PCC_CODE( 16,	0, FAS38400,  0)
 #define	PCC_16_0_57600		MAKE_PCC_CODE( 16,	0, FAS57600,  0)
 #define	PCC_16_0_115200		MAKE_PCC_CODE( 16,	0, FAS115200, 0)
 #define	PCC_16_0_230400		MAKE_PCC_CODE( 16,	0, FAS230400, 0)
@@ -137,13 +143,18 @@
 
 
 /* special function codes (bit masks) */
-#define	SFC_STD		0x00	/* normal units */
+/*#define	SFC_STD		0x00*/	/* normal units */
 #define	SFC_2000	0x01	/* 2000 series units - Not supported */
 #define	SFC_1003	0x02	/* SP-1003 fast parallel unit */
 #define	SFC_NO_SPLIT	0x04	/* can't support split baudrates */
 #define SFC_ETH		0x08	/* EtherLite products */
 #define SFC_PCI		0x10	/* PCI products */
 #define SFC_MILSPEC	0x80	/* Special firmware for military apps */
+/*
+   until the els code admits to knowing what a SCSI unit is, we'll just
+   keep on tricking it -Kees 01/19/2000
+*/
+#define SFC_STD		SFC_ETH
 
 
 
@@ -152,6 +163,7 @@
 #define PT_SERIAL	0x70000000	/* top bit set for serial */
 #define PT_PARALLEL	0x00000000	/* top bit clear for parallel */
 
+#define	PT_38400_SERIAL		(PT_SERIAL | FAS38400)	/* serial to 38.4 */
 #define	PT_57600_SERIAL		(PT_SERIAL | FAS57600)	/* serial to 57.6 */
 #define	PT_115200_SERIAL	(PT_SERIAL | FAS115200)	/* serial to 115.2 */
 #define	PT_230400_SERIAL	(PT_SERIAL | FAS230400)	/* serial to 230.4 */
