home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
kermit.columbia.edu
/
kermit.columbia.edu.tar
/
kermit.columbia.edu
/
old
/
ckermit5a190
/
ckitio.c
< prev
next >
Wrap
C/C++ Source or Header
|
1994-10-07
|
43KB
|
1,739 lines
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/* |_o_o|\\ Copyright (c) 1986 The Software Distillery. All Rights Reserved */
/* |. o.| || This program may not be distributed without the permission of */
/* | . | || the authors. */
/* | o | || Dave Baker Ed Burnette Stan Chow Jay Denebeim */
/* | . |// Gordon Keener Jack Rouse John Toebes Doug Walker */
/* ====== BBS:(919)-471-6436 VOICE:(919)-469-4210 */
/* */
/* Contributed to Columbia University for inclusion in C-Kermit. */
/* Permission is granted to any individual or institution to use, copy, or */
/* redistribute this software so long as it is not sold for profit, provided */
/* this copyright notice is retained. */
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
char *ckxv = "Amiga tty I/O $Id: ckitio.c,v 1.14 94/10/04 22:42:58 swalton Exp Locker: swalton $";
/* C K I T I O -- Serial and Console I/O support for the Amiga */
/*
* Author: Jack Rouse, The Software Distillery
* Based on the CKUTIO.C module for Unix
*
* Modified for Manx Aztec C and Version 1.2 and forward of Amiga's OS by
* Stephen Walton of California State University, Northridge,
* srw@csun.edu. Further mods documented in ckiker.upd.
*
$Log: ckitio.c,v $
* Revision 1.14 94/10/04 22:42:58 swalton
* Minor mod to flow control: if flow is not FLO_XONX or FLO_RTSC then
* we use NONE, without error message; this is how ck9tio.c does it.
*
* Revision 1.13 94/09/27 05:28:23 swalton
* ttsspd() was a no-op, somehow. I think the old code was taking advantage
* of the fact that pre-5A versions of C Kermit never called ttsspd, but
* rather always went through either ttpkt() or ttvt(). It works now.
*
* Revision 1.12 94/09/11 09:44:24 swalton
* Fixed timeout in ttinl. There was (again) no Wait() on the timer signal
* bit, so it never really timed out. Evidence: try to receive a file
* with no connection and modem off. It hangs up.
* Deleted ttwmdm() as it is no longer needed. Wrote ttgmdm() so it works
* instead of returning 'not implemented.'
*
* Revision 1.11 94/07/29 12:25:35 swalton
* Changed both timers to use the RKM CreateTimer() and DeleteTimer() routines
* for cleanliness sake. In looking at the code, I also realized that
* Sleeper() should be Wait()'ing on the SigBit in the MsgPort for the
* timer, not the serial port (as it was). Not sure how this could have
* worked at all up until now!
*
* Revision 1.10 94/07/26 16:39:36 swalton
* Added code for local alarm() function to allow use of C Kermit DIAL and
* SCRIPT commands. Now it has a lot of duplicate code for the timer.device,
* which I plan to clean up before release.
* Also added a few strategic debug() calls. May not be very useful with
* above, since doing a LOG DEBUG seems to slow things down enough so that
* DIAL no longer works.
*
* Revision 1.9 93/08/03 08:36:07 swalton
* Many changes thanks to Olaf Barthel:
* 1. Changed include files to Amiga standard.
* 2. Changed signal-handling to use ANSI signal() call. Still can't
* call Aztec Chk_Abort(), though, because it ignores signal().
* 3. Used GetScreenData() on the Workbench screen to find the window
* size to open.
* 4. Deleted DoIOQuick() and changed calls to it to DoIO(), which is
* identical.
* 5.. ttol() rewritten to have a static buffer whose size is checked
* and to handle the pendwrite flag correctly.
*
* Revision 1.8 92/10/30 16:14:46 swalton
* Put in code to attempt to open a 1024 by 1024 console, at John Ata's
* suggestion. This will make a maximum-size window on most Amigas.
*
* Added code to set a global int "v37" to TRUE or FALSE according to the
* version of the ROM Kernel. This is then used in other places to
* conditionally turn on V37 features.
*
* Revision 1.7 92/03/16 13:50:58 swalton
* Support added for CTR/RTS flow control, using the new FLO_ manifest
* constants in version 5A.
*
* Revision 1.6 92/01/15 17:12:35 swalton
* Added Long BREAK support with new ttsndlb() routine.
*
* Added support for multiple devices; the SET LINE command now takes a
* line of the form "device/unit".
*
* Revision 1.5 91/07/18 16:04:57 swalton
* ttinl() now null terminates a received packet correctly.
*
* Revision 1.4 91/05/29 09:08:57 swalton
* 1. Changed function definitions to prototype style. Required adding
* a few forward declarations.
* 2. Removed includes of stdio.h, stdlib.h, and string.h, as they are
* now pulled in by ckcdeb.h, provided we compile with -DCK_ANSILIBS.
*
* Revision 1.3 90/11/19 21:46:54 swalton
* Modifications for compiling with SAS/C Version 5.10, courtesy of
* Larry Rosenman (ler@erami.lonestar.org, ler on BIX)
*
* Revision 1.2 90/11/07 14:42:07 swalton
* Version 1.2--released to world as first beta test version simultaneously
* with release of edit 5A(160).
*
* Revision 1.1 90/07/12 22:30:11 swalton
* Rather extensive changes were made to ckitio.c, mainly to add new functions
* required for the proper operation of C Kermit 5A(149). They are not listed
* in detail here; refer to the parts of the C Kermit interface document
* (file ckasys.doc in the Kermit archive) for the portions labeled *NEW*.
* These will point you at the code revisions.
*
* Revision 1.0 90/04/30 11:54:27 swalton
* Initial revision
*
*/
#include "ckcdeb.h"
#include "ckcker.h"
#include "ckcnet.h"
#include <exec/types.h>
#include <exec/exec.h>
#include <devices/serial.h>
#include <devices/timer.h>
#include <libraries/dos.h>
#include <libraries/dosextens.h>
#define fh_Interact fh_Port
#define fh_Process fh_Type
#include <intuition/intuition.h>
#include <intuition/intuitionbase.h>
#define BREAKSIGS (SIGBREAKF_CTRL_C|SIGBREAKF_CTRL_D)
#include <string.h>
#include <time.h>
#ifdef AZTEC_C
#include <fcntl.h>
#include <signal.h>
char *ckxsys = " Commodore Amiga (Aztec_C)"; /* system name */
#else
#ifdef __SASC
#include <fcntl.h>
#include <signal.h>
#include <ios1.h> /* defines ufbs structure */
char *ckxsys = " Commodore Amiga (SAS/C)"; /* system name */
#endif
#endif
#include <clib/exec_protos.h>
#include <clib/alib_protos.h>
#include <clib/dos_protos.h>
#include <clib/intuition_protos.h>
/* external definitions */
UBYTE *dftty = (UBYTE *) SERIALNAME; /* serial device name */
int dfloc = 1; /* serial line is external */
int dfprty = 0; /* default parity is none */
int ttprty = 0; /* parity in use */
int dfflow = FLO_XONX; /* default flow control is on */
int backgrd = 0; /* default to foreground */
int ckxech = 0; /* echo in case redirected stdin */
int tvtflg = 0;
int ttcarr = 0; /* Carrier detection mode */
int ttnproto = NP_NONE; /* Protocol for network device */
struct Process *CurProc; /* current process */
struct CommandLineInterface *CurCLI; /* current CLI info */
struct IntuitionBase *IntuitionBase; /* ptr to Intuition lib */
short v37; /* Are we version 37? */
/* static definitions */
static struct MsgPort *serport; /* message port for serial comm */
static struct MsgPort *conport; /* console packet port */
static struct timerequest *TimerIOB; /* timer request */
static struct IOExtSer *ReadIOB; /* serial input request */
static struct IOExtSer *WriteIOB; /* serial output request */
static struct DosPacket *conpkt; /* console I/O packet */
static WORD serialopen; /* true iff serial device open */
static WORD pendwrite; /* true iff WriteIOB in use */
static WORD pendread; /* true iff ReadIOB in use */
static WORD pendconsole; /* true when console read pending */
static int queuedser; /* serial pushback char or -1 */
static UBYTE serbufc; /* char buffer for read ahead I/O */
#define NTTOQ 64 /* connect output queue size */
static char ttoq[NTTOQ]; /* connect output queue */
static int nttoq; /* number of chars in ttoq */
static int pttoq; /* next char to output in ttoq */
static int queuedcon; /* contti pushback char or -1 */
static LONG intsigs; /* signals for aborting serial I/O */
static BPTR rawcon; /* file handle for RAW: window */
static BPTR saverr; /* saved stderr file handle */
static APTR savewindow; /* saved process WindowPtr */
static APTR pushwindow; /* pushed process WindowPtr */
static struct DateStamp prevtime; /* saved time value */
/* AmigaDOS support (from ckiutl.c) */
struct DosPacket *CreatePacket(void);
VOID DeletePacket(struct DosPacket *);
#ifdef AZTEC_C
/* translate Unix file handle (0, 1, or 2) to AmigaDOS file handle */
#define DOSFH(n) (_devtab[n].fd)
/* translate Unix file handle (0, 1, or 2) to Aztec file handle */
#define FILENO(n) (n)
extern int Enable_Abort;
#else
/* Lattice runtime externals */
#ifdef __SASC
#define DOSFH(n) (chkufb(n)->ufbfh)
#define FILENO(n) (n)
#endif
#endif
/*
* Under ANSI C, pointer-pointer assignments are illegal without an
* explicit cast. So, we define the following to make such casts short.
*/
#define IOR struct IORequest
/*
* Forward declarations
*/
void reqres(void);
static void testint(long);
#ifdef AZTEC_C
#define Chk_Abort() testint(0L)
#else
void Chk_Abort(void); /* or #define Chk_Abort() testint(0) */
#endif
/*
* make note of a serial error and quit
*/
static void
Fail(char *msg)
{
syscleanup();
fprintf(stderr, msg);
fprintf(stderr, "\n");
exit(2);
}
void
emergency(void) {
(void) syscleanup();
}
/*
* Timer.device routines from RKM, slightly modified.
*/
void
DeleteTimer(struct timerequest *tr) {
struct MsgPort *tp;
if (tr != 0) {
tp = tr->tr_node.io_Message.mn_ReplyPort;
if (tp != 0)
DeletePort(tp);
CloseDevice((struct IORequest *) tr);
DeleteExtIO((struct IORequest *) tr);
}
}
struct timerequest *
CreateTimer(ULONG unit) {
/* return a pointer to a timer request. If any problem, return NULL. */
LONG error;
struct MsgPort *timerport;
struct timerequest *timermsg;
timerport = CreatePort(0, 0);
if (timerport == NULL)
return NULL;
timermsg = (struct timerequest *)
CreateExtIO(timerport, sizeof(struct timerequest));
if (timermsg == NULL) {
DeletePort(timerport);
return NULL;
}
error = OpenDevice((UBYTE *) TIMERNAME, unit,
(struct IORequest *) timermsg, 0L);
if (error != 0) {
DeleteTimer(timermsg);
return NULL;
}
return timermsg;
}
/*
* sysinit -- Amiga specific initialization
*/
int
sysinit(void)
{
struct IOExtSer *iob;
/* set current process info */
CurProc = (struct Process *)FindTask((char *)NULL);
CurCLI = (struct CommandLineInterface *)BADDR(CurProc->pr_CLI);
backgrd = (CurCLI == NULL || CurCLI->cli_Background);
savewindow = CurProc->pr_WindowPtr;
signal(SIGINT, SIG_IGN);
/* allocate console ports and IO blocks */
if ((conport = CreatePort((char *)NULL, 0L)) == NULL)
Fail("no console MsgPort");
if ((conpkt = CreatePacket()) == NULL)
Fail("no console packet");
/* allocate serial ports and IO blocks */
if ((serport = CreatePort((char *)NULL, 0L)) == NULL)
Fail("no serial MsgPort");
iob = (struct IOExtSer *)CreateExtIO(serport,(LONG)sizeof(*iob));
if ((WriteIOB = iob) == NULL) Fail("no WriteIOB");
iob = (struct IOExtSer *)CreateExtIO(serport,(LONG)sizeof(*iob));
if ((ReadIOB = iob) == NULL) Fail("no ReadIOB");
/* open the timer device */
TimerIOB = CreateTimer(UNIT_VBLANK);
if (TimerIOB == NULL) Fail("no TimerIOB");
/* open the Intuition library */
if (!IntuitionBase &&
(IntuitionBase = (struct IntuitionBase *)
OpenLibrary((UBYTE *) "intuition.library", 0L) ) == NULL )
Fail("can't open Intuition");
if (((struct Library *)IntuitionBase)->lib_Version >= 37)
v37 = TRUE;
else
v37 = FALSE;
/* open the serial device to get configuration */
iob->io_SerFlags = SERF_SHARED;
if (OpenDevice((UBYTE *) SERIALNAME, 0L, (IOR *)iob, 0L) != 0)
Fail("can't open serial.device");
/* set parameters from system defaults */
if (!(iob->io_SerFlags & SERF_XDISABLED))
dfflow = FLO_XONX;
else if (iob->io_SerFlags & SERF_7WIRE)
dfflow = FLO_RTSC;
else
dfflow = FLO_NONE;
/*
* Set default (startup) parity from Preferences settings.
*/
if (iob->io_SerFlags & SERF_PARTY_ON) /* Parity is on */
if (iob->io_ExtFlags & SEXTF_MSPON) /* Space or mark */
if (iob->io_ExtFlags & SEXTF_MARK)
dfprty = 'm'; /* Mark parity */
else
dfprty = 's'; /* Space parity */
else /* Even or odd */
if (iob->io_SerFlags & SERF_PARTY_ODD)
dfprty = 'o'; /* Odd parity */
else
dfprty = 'e'; /* Even parity */
else
dfprty = 0; /* No parity. */
ttprty = dfprty;
CloseDevice((IOR *)iob);
serialopen = FALSE;
atexit(emergency);
return(0);
}
unsigned aalarm(unsigned); /* forward declaration */
/*
* syscleanup -- Amiga specific cleanup
*/
syscleanup(void)
{
/* close everything */
aalarm(0);
if (serialopen) CloseDevice((IOR *)ReadIOB);
if (TimerIOB) DeleteTimer(TimerIOB);
if (WriteIOB) DeleteExtIO((IOR *)WriteIOB);
if (ReadIOB) DeleteExtIO((IOR *)ReadIOB);
if (serport) DeletePort(serport);
if (conpkt) DeletePacket(conpkt);
if (conport) DeletePort(conport);
reqres();
if (IntuitionBase)
{
CloseLibrary((struct Library *)IntuitionBase);
IntuitionBase = NULL;
}
/* reset standard I/O */
if (rawcon > 0)
{
/* restore Lattice AmigaDOS file handles */
DOSFH(0) = Input();
DOSFH(1) = Output();
DOSFH(2) = saverr;
Close(rawcon);
rawcon = 0;
}
serialopen = 0;
TimerIOB = WriteIOB = ReadIOB = serport = conpkt = conport = NULL;
return 1;
}
/*
* reqoff -- turn requestors off
* When AmigaDOS encounters an error that user intervention can fix
* (like inserting the correct disk), it normally puts up a requestor.
* The following code disables requestors, causing an error to be
* returned instead.
*/
void
reqoff(void)
{
pushwindow = CurProc->pr_WindowPtr;
CurProc->pr_WindowPtr = (APTR)-1;
}
/*
* reqpop -- restore requesters to action at last reqoff
*/
void
reqpop(void)
{
CurProc->pr_WindowPtr = pushwindow;
}
/*
* reqres -- restore requestors to startup action
*/
void
reqres(void)
{
CurProc->pr_WindowPtr = savewindow;
}
/*
* KillIO -- terminate an I/O request
*/
static int
KillIO(struct IORequest *iob)
{
AbortIO(iob);
return((int)WaitIO(iob));
}
/*
* ttopen -- open the serial device
* If already open, returns 0 immediately.
* Otherwise, the ttname is compare to SERIALNAME and used to
* open the serial device, and, if the value of *lcl is < 0, it is
* reset to 1 indicating local mode. Returns -1 on error.
* timo is the length of time to wait before flunking open; we don't
* need this feature on the Amiga.
*/
int
ttopen(char * ttname, int *lcl, int modem, int timo)
{
struct IOExtSer *iob = ReadIOB;
char *p;
ULONG unit;
static char cttname[50]; /* Current open ttname */
debug(F111,"ttopen entry modem",ttname,modem);
debug(F101," lcl","",*lcl);
if (modem < 0) return -1; /* We don't do networks yet. */
if (serialopen) /* Already have serial device open */
if (strcmp(ttname, cttname) == 0)
return(0); /* Same device - ignore */
else ttclos(0); /* Different device - close */
/* verify the serial name */
#if 0
if (strcmp(ttname, SERIALNAME) != 0) return(-1);
#endif
/* set open modes. We no longer open in shared mode. */
iob->io_SerFlags = (modem > 0 ? SERF_7WIRE : 0);
/* parse device name as device/unit */
if ((p = strchr(ttname, '/')) == NULL)
unit = 0;
else {
if (*(p + strlen(p) - 1) == 's') /* Open in shared mode */
{
iob->io_SerFlags |= SERF_SHARED;
*(p + strlen(p) - 1) = '\0';
}
unit = (ULONG) atoi(p + 1);
*p = '\0';
}
/* open the serial device */
if (OpenDevice((UBYTE *) ttname, unit, (IOR *)iob, 0L) != 0)
return(-1);
serialopen = TRUE;
tvtflg = 0;
pendread = pendwrite = pendconsole = FALSE;
queuedser = -1;
/* fill in the fields of the other IO blocks */
*WriteIOB = *iob;
/* set local mode */
if (*lcl == -1) *lcl = 1; /* always local */
if (p) *p = '/'; /* restore slash */
if (iob->io_SerFlags & SERF_SHARED)
*(p + strlen(p)) = 's'; /* restore suffix if present */
strcpy(cttname, ttname);
debug(F110, "ttopen got device", ttname, 0);
return(0);
}
/*
* StartTimer -- start a timeout
*/
static VOID
StartTimer(LONG secs, LONG micro)
{
TimerIOB->tr_node.io_Command = TR_ADDREQUEST;
TimerIOB->tr_time.tv_secs = secs;
TimerIOB->tr_time.tv_micro = micro;
SendIO((IOR *)TimerIOB);
}
/*
* SerialWait -- wait for serial I/O to terminate
* return I/O error
*/
static int
SerialWait(struct IOExtSer *iob, int timeout)
{
LONG sigs;
struct timerequest *timer = TimerIOB;
LONG waitsigs;
/* set up timeout if necessary */
if (timeout > 0) {
StartTimer((LONG)timeout, 0L);
waitsigs =
(1L << timer->tr_node.io_Message.mn_ReplyPort->mp_SigBit);
} else
waitsigs = 0;
/* wait for completion, timeout, or interrupt */
sigs = 0;
waitsigs |= (1L << serport->mp_SigBit) |
intsigs;
for (;;)
{
if (sigs & intsigs)
{ /* interrupted */
if (timeout > 0) KillIO((IOR *)timer);
KillIO((IOR *)iob);
testint(sigs);
return(-1);
}
if (CheckIO((IOR *)iob))
{
if (timeout > 0) KillIO((IOR *)timer);
return((int)WaitIO((IOR *)iob));
}
if (timeout > 0 && CheckIO((IOR *)timer))
{
KillIO((IOR *)iob);
WaitIO((IOR *)timer);
/* restart if XOFF'ed */
iob->IOSer.io_Command = CMD_START;
DoIO((IOR *)iob);
return(-1);
}
sigs = Wait(waitsigs);
}
}
/*
* TerminateRead -- wait for queued read to finish
*/
static int
TerminateRead(void)
{
if (!pendread) return(0);
if (WaitIO((IOR *)ReadIOB) == 0) queuedser = serbufc;
pendread = FALSE;
return((int)ReadIOB->IOSer.io_Error);
}
/*
* TerminateWrite -- ensure WriteIOB is ready for reuse
*/
static int
TerminateWrite(int timeout)
{
Chk_Abort();
if (!pendwrite) return(0);
pendwrite = FALSE;
if (timeout) {
timeout = WriteIOB->IOSer.io_Length * 80 / WriteIOB->io_Baud;
}
return(SerialWait(WriteIOB, timeout));
}
/*
* SerialReset -- terminate pending serial and console I/O
*/
static void
SerialReset(void)
{
if (pendread)
{
AbortIO((IOR *)ReadIOB); /* should work even if read finished */
TerminateRead();
}
if (pendconsole)
{ /* this does not happen normally */
WaitPort(conport);
GetMsg(conport);
pendconsole = FALSE;
}
if (pendwrite)
TerminateWrite(1);
}
/*
* ttres -- reset serial device
*/
ttres()
{
if (!serialopen) return(-1);
/* reset everything */
SerialReset();
ReadIOB->IOSer.io_Command = CMD_RESET;
tvtflg = 0;
return(DoIO((IOR *)ReadIOB) ? -1 : 0);
}
/*
* ttclos -- close the serial device
*/
int
ttclos(int unit)
{
debug(F101, "ttopen ", "", unit);
if (!serialopen) return(0);
if (ttres() < 0) return(-1);
CloseDevice((IOR *)ReadIOB);
serialopen = FALSE;
tvtflg = 0;
return(0);
}
/*
* tthang -- hang up phone line
* Drops DTR by closing serial.device
*/
int
tthang(void)
{
return((serialopen) ? ttclos(0) : -1);
}
/*
* ttpkt -- set serial device up for packet transmission
* sets serial parameters
*/
int
ttpkt(long speed, int flow, int parity)
{
extern UBYTE eol;
struct IOExtSer *iob = ReadIOB;
debug(F101, "ttpkt speed ", "", speed);
debug(F101, "ttpkt flow ", "", flow);
debug(F101, "ttpkt parity ", "", parity);
if (!serialopen || pendread) return(-1);
/* terminate any pending writes */
TerminateWrite(1);
/* fill in parameters */
iob->io_CtlChar = 0x11130000;
if (speed >= 0 && ttsspd((int) (speed / 10)) >= 0) iob->io_Baud = speed;
iob->io_RBufLen = speed; /* 10 seconds worth of data */
/*
* Notice the dopar(eol) here to set the EOL character with the
* appropriate parity. See also ttinl().
*/
memset(&iob->io_TermArray, dopar(eol), sizeof(struct IOTArray));
iob->io_ReadLen = iob->io_WriteLen = 8;
iob->io_StopBits = 1;
if (flow == FLO_XONX)
iob->io_SerFlags &= ~(SERF_XDISABLED | SERF_7WIRE);
else if (flow == FLO_RTSC)
iob->io_SerFlags |= (SERF_XDISABLED | SERF_7WIRE);
else {
iob->io_SerFlags |= SERF_XDISABLED;
iob->io_SerFlags &= ~SERF_7WIRE;
}
/* if no XON/XOFF flow and high baud rate, RAD_BOOGIE is appropriate */
if (flow != FLO_XONX && iob->io_Baud >= 19200)
iob->io_SerFlags |= SERF_RAD_BOOGIE;
else
iob->io_SerFlags &= ~SERF_RAD_BOOGIE;
/*
* Parity setting. For packet send/receive, we turn off the
* Amiga's internal parity generation and checking, as this code
* does it itself (which makes it bigger and slower...). We
* save the current parity for ttinl().
*/
ttprty = parity;
iob->io_SerFlags &= ~(SERF_EOFMODE|SERF_PARTY_ON|SERF_PARTY_ODD);
iob->io_ExtFlags = 0; /* MUST BE ZERO unless Mark or Space. */
/* set the parameters */
iob->IOSer.io_Command = SDCMD_SETPARAMS;
if (DoIO((IOR *)iob) != 0) return(-1);
tvtflg = 0;
return(ttflui());
}
/*
* ttvt -- set up serial device for connect mode. This is almost the same
* as ttpkt() on the Amiga, except we save the settings and a flag and return
* without doing anything if we've already been called with the same
* values.
*/
int
ttvt(long speed, int flow) {
static long ospeed = -1;
static int oflow = -9;
if (tvtflg != 0 && ospeed == speed && oflow == flow)
return 0;
if (ttpkt(speed, flow, 0) < 0)
return -1;
ospeed = speed; /* Save speed */
oflow = flow; /* and flow control set */
tvtflg = 1; /* and flag we've been called */
return 0;
}
/* T T S S P D -- Set the transmission of tty to ten times the argument */
ttsspd(speed) int speed; {
int s;
struct IOExtSer *iob = ReadIOB;
debug (F101,"ttsspd: speed(cps):","",speed);
if (!serialopen) return(-1);
switch (speed) {
case 5: s = 50; break;
case 7: s = 75; break;
case 11: s = 110; break;
case 13: s = 134; break;
case 15: s = 150; break;
case 30: s = 300; break;
case 60: s = 600; break;
case 120: s = 1200; break;
case 180: s = 1800; break;
case 200: s = 2000; break;
case 240: s = 2400; break;
case 360: s = 3600; break;
case 480: s = 4800; break;
case 720: s = 7200; break;
case 960: s = 9600; break;
case 1440: s = 14400; break;
case 1920: s = 19200; break;
case 3840: s = 38400; break;
case 888: return(-1); /* no 75/1200 split speed */
default: return -1;
}
/* First get a complete copy of current settings. */
iob->IOSer.io_Command = SDCMD_QUERY;
if (DoIO((IOR *)iob) != 0) return(-1);
iob->io_Baud = s;
iob->io_RBufLen = s; /* 10 seconds worth of data */
/* set the parameters */
iob->IOSer.io_Command = SDCMD_SETPARAMS;
if (DoIO((IOR *)iob) != 0) return(-1);
return s;
}
/* T T G S P D - Get speed of currently selected tty line */
/*
Read speed from serial.device, or, if not open, return the value in
the current ReadIOB.
*/
long
ttgspd(void) { /* Get current tty speed */
struct IOExtSer *myread = ReadIOB;
if (!serialopen)
if (myread != NULL) return((long)myread->io_Baud);
else return -1;
Chk_Abort();
if (pendread && !CheckIO((IOR *)myread)) return(0);
if (TerminateRead() != 0) return(-1);
myread->IOSer.io_Command = SDCMD_QUERY;
return((DoIO((IOR *)myread) == 0)
? (long)myread->io_Baud
: -1);
}
/*
* ttflui -- flush serial device input buffer
*/
int
ttflui(void)
{
if (!serialopen || pendread) return(-1);
queuedser = -1;
ReadIOB->IOSer.io_Command = CMD_CLEAR;
return(DoIO((IOR *)ReadIOB) ? -1 : 0);
}
/*
* ttfluo -- flush serial output buffer
*/
int
ttfluo(void)
{
if (!serialopen || pendwrite) return -1;
WriteIOB->IOSer.io_Command = CMD_CLEAR;
return(DoIO((IOR *)WriteIOB) ? -1 : 0);
}
/*
* test for and catch interrupt
*/
static void
testint(LONG sigs)
{
/* test for and reset caught interrupt signals */
if ((sigs | SetSignal(0L, (LONG)BREAKSIGS)) & intsigs) {
raise(SIGINT);
}
}
/*
* conint -- set console interrupt handler and suspend handler.
*/
void
conint(SIGTYP (*newhdlr)(int), SIGTYP (*stophdlr)(int))
{
Chk_Abort(); /* handle any pending interrupts */
signal(SIGINT, newhdlr); /* set the new handler */
intsigs = BREAKSIGS; /* note signal caught */
}
/*
* connoi -- disable interrupt trapping
*/
void
connoi(void)
{
signal(SIGINT, SIG_IGN); /* disable interrupts */
intsigs = 0; /* note signal ignored */
Chk_Abort(); /* ignore pending interrupts */
}
/*
* ttchk -- return number of chars immediately available from serial device
*/
int
ttchk(void)
{
struct IOExtSer *myread = ReadIOB;
if (!serialopen) return(-1);
Chk_Abort();
if (pendread && !CheckIO((IOR *)myread)) return(0);
if (TerminateRead() != 0) return(-1);
myread->IOSer.io_Command = SDCMD_QUERY;
return((DoIO((IOR *)myread) == 0)
? ((queuedser >= 0 ? 1 : 0) + (int)myread->IOSer.io_Actual)
: -1);
}
/*
* ttxin -- get n characters from serial device. This routine should
* only be called when we know that there are at least n characters
* ready to be read.
*/
int
ttxin(int n, CHAR *buf)
{
return(ttinl(buf, n, 0, 0));
}
#ifdef PARSENSE
extern CHAR partab[];
/* P A R C H K -- Check if Kermit packet has parity */
/*
Call with s = pointer to packet, start = packet start character, n = length.
Returns 0 if packet has no parity, -1 on error, or if packet has parity:
'e' for even, 'o' for odd, 'm' for mark. Space parity cannot be sensed.
*/
parchk(s,start,n) CHAR *s, start; int n; {
CHAR s0, s1, s2, s3, sn;
debug(F101,"parchk n","",n);
debug(F101,"parchk start","",start);
debug(F110,"parchk s",s,0);
s0 = s[0] & 0x7f; /* Mark field (usually Ctrl-A) */
if (s0 != start || n < 5) return(-1); /* Not a valid packet */
/* Look at packet control fields, which never have 8th bit set */
/* First check for no parity, most common case. */
if (((s[0] | s[1] | s[2] | s[3] | s[n-2]) & 0x80) == 0)
return(0); /* No parity */
/* Check for mark parity */
if (((s[0] & s[1] & s[2] & s[3] & s[n-2]) & 0x80) == 0x80)
return('m'); /* Mark parity */
/* Packet has some kind of parity */
/* Make 7-bit copies of control fields */
s1 = s[1] & 0x7f; /* LEN */
s2 = s[2] & 0x7f; /* SEQ */
s3 = s[3] & 0x7f; /* TYPE */
sn = s[n-2] & 0x7f; /* CHECK */
/* Check for even parity */
if ((s[0] == partab[s0]) &&
(s[1] == partab[s1]) &&
(s[2] == partab[s2]) &&
(s[3] == partab[s3]) &&
(s[n-2] == partab[sn]))
return('e');
/* Check for odd parity */
if ((s[0] != partab[s0]) &&
(s[1] != partab[s1]) &&
(s[2] != partab[s2]) &&
(s[3] != partab[s3]) &&
(s[n-2] != partab[sn]))
return('o');
/* Otherwise it's probably line noise. Let checksum calculation catch it. */
return(-1);
}
#endif /* PARSENSE */
/*
* ttinc -- read character from serial line
*/
int
ttinc(int timeout)
{
UBYTE ch;
return((ttinl((CHAR *)&ch, 1, timeout, 0) > 0) ? (int)ch : -1);
}
/*
* The following chunk of code is a primitive (very!) alarm() function
* for the Amiga. It is nowhere near general, and it will only work
* with Kermit, most likely. It has three parts:
*
* asignal() is call-compatible with signal(). If the signal is less
* than or equal to _NUMSIG (in <signal.h>), then the vendor-supplied
* signal() is called. If it is equal to _NUMSIG+1, which I define as
* SIGALRM, then it is a new alarm signal. The pointer to the passed
* function is saved and the old one is returned.
*
* aalarm() is the Unix-like call. It is called with a time in seconds,
* which is the time after which the routine passed in the
* signal(SIGALRM, ...) call is to be called. Here we just start a
* timer and return.
*
* check_alarm() sees if the time specified by aalarm() is up yet, and
* calls the saved function if it is.
*/
static void (*savalarm)(int) = SIG_DFL;
static struct timerequest *alarmIOB;
static unsigned savesecs;
static short alarmflag = 0; /* flag that an alarm is pending */
#define SIGALRM (_NUMSIG+1)
void (*asignal(int sig, void (*func)(int)))(int) {
void (*talarm)(int);
debug(F101, "asignal sig", "", sig);
debug(F101, "asignal func", "", func);
if (sig <= _NUMSIG)
return(signal(sig, func));
else if (sig == SIGALRM) {
talarm = savalarm;
savalarm = func;
return(talarm);
}
else {
debug(F100, "asignal called with sig too large", "", 0);
return(SIG_IGN);
}
}
unsigned
aalarm(unsigned secs) {
unsigned t;
debug(F101, "aalarm", "", secs);
t = savesecs;
if (secs == 0) {
if (alarmIOB) {
KillIO((IOR *) alarmIOB);
DeleteTimer(alarmIOB);
alarmIOB = NULL;
}
savesecs = 0;
alarmflag = 0;
return(t);
} else {
alarmIOB = CreateTimer(UNIT_VBLANK);
if (alarmIOB == NULL) {
debug(F100, "CreateExtIO failed in alarm", "", 0);
return(0);
}
alarmIOB->tr_time.tv_secs = savesecs = secs;
alarmIOB->tr_time.tv_micro = 0;
alarmIOB->tr_node.io_Command = TR_ADDREQUEST;
SendIO((IOR *) alarmIOB);
alarmflag = 1;
return(t);
}
}
static void
check_alarm(void) {
if (alarmflag)
if (CheckIO((IOR *) alarmIOB)) {
WaitIO((IOR *) alarmIOB);
alarmflag = 0;
if (savalarm == SIG_IGN)
return;
else if (savalarm == SIG_DFL)
Fail("uncaught alarm seen");
else
(*savalarm)(SIGALRM);
}
}
/*
* ttol -- write n chars to serial device. For small writes, we have
* a small local buffer which allows them to run asynchronously. For
* large writes, we do them synchronously. This seems to be the best
* compromise between speed and code simplicity and size.
*
* Stephen Walton, 23 October 1989
*/
int
ttol(CHAR *buf, int n)
{
struct IOExtSer *mywrite = WriteIOB;
static char outbuf[1024]; /* safe place for output characters */
int s;
int oldn = n;
if (!serialopen) return(-1);
check_alarm();
if ((s = n - sizeof(outbuf)) > 0) {
if (TerminateWrite(1) != 0) return(-1);
mywrite->IOSer.io_Command = CMD_WRITE;
mywrite->IOSer.io_Data = (APTR) buf;
mywrite->IOSer.io_Length = s;
SendIO((IOR *)mywrite);
pendwrite = TRUE;
buf += s;
n -= s;
memcpy(outbuf, buf, n);
if (TerminateWrite(1) != 0) return(-1);
} else {
if (TerminateWrite(1) != 0) return(-1);
memcpy(outbuf, buf, n);
}
mywrite->IOSer.io_Command = CMD_WRITE;
mywrite->IOSer.io_Data = (APTR)outbuf;
mywrite->IOSer.io_Length = n;
SendIO((IOR *)mywrite);
pendwrite = TRUE;
return oldn;
}
/*
* ttoc -- output single character to serial device
*/
int
ttoc(char c)
{
return(ttol((CHAR *) &c, 1));
}
/*
* ttinl -- read from serial device, possibly with timeout and eol character
* reads up to n characters, returning the number of characters read
* if eol > 0, reading the eol character will terminate read
* if timeout > 0, terminates read if timeout elapses
* returns -1 on error, such as timeout or interrupt
*
* Note that this is the single routine which does all character reading
* in Amiga C Kermit, and has some added "features" compared to, say,
* the Unix version. If timeout is 0, this routine waits forever.
* If eol is zero, it is not used.
*
* New for 5A(157) is the start parameter, which is the start-of-packet
* character. Following the Unix example, we just read until eol,
* but return a bad packet if the first character we got doesn't agree
* with start.
*/
int
ttinl(CHAR *buf, int n, int timeout, CHAR eol)
{
unsigned mask;
struct IOExtSer *myread = ReadIOB;
int count;
int nread, i;
Chk_Abort();
check_alarm();
if (!serialopen || pendread || n <= 0) return(-1);
mask = (ttprty ? 0177 : 0377); /* parity stripping mask */
/* handle pushback */
if (queuedser >= 0)
{
*buf = queuedser & mask; /* Strip queued character. */
queuedser = -1;
if (*buf == eol || n == 1) return(1);
++buf;
--n;
count = 1;
}
else
count = 0;
/* set up line terminator */
if (eol > 0)
{
/*
* For reasons which are obscure to me, this batch of
* code generally fails. Normally, this doesn't matter,
* because io_TermArray is set in ttpkt() above, and so
* this code is only executed if eol changes as a result
* of the initial packet negotiation. I found the bug
* by inadvertently not using dopar(eol) in the setting
* of io_TermArray in ttpkt(), which did cause this code
* to be called if parity was MARK or EVEN (since in that
* case dopar(eol) != eol).
*/
if (dopar(eol) != *(UBYTE *)&myread->io_TermArray)
{
memset(&myread->io_TermArray, dopar(eol),
sizeof(struct IOTArray));
myread->IOSer.io_Command = SDCMD_SETPARAMS;
if (DoIO((IOR *)myread) != 0) {
debug(F111, "SETPARAMS fails in ttinl()",
"io_Error", (int) myread->IOSer.io_Error);
myread->io_TermArray.TermArray0 =
myread->io_TermArray.TermArray1 = 0xffffffffu;
return -1;
}
}
myread->io_SerFlags |= SERF_EOFMODE;
}
else
myread->io_SerFlags &= ~SERF_EOFMODE;
/* set up the read */
myread->IOSer.io_Command = CMD_READ;
myread->IOSer.io_Data = (APTR)buf;
myread->IOSer.io_Length = n;
/* perform read quickly if possible */
myread->IOSer.io_Flags = IOF_QUICK;
BeginIO((IOR *)myread);
if (myread->IOSer.io_Flags & IOF_QUICK)
myread->IOSer.io_Flags = 0;
else
/* wait for read to complete if no QUICK. */
if (SerialWait(myread, timeout) != 0)
return -1;
if (myread->IOSer.io_Error != 0)
return -1;
#if COMMENT
if (start != 0 && (buf[0] & mask) != start) /* Bad packet */
return -1;
#endif
/* Strip parity bits if need be. */
nread = (int) myread->IOSer.io_Actual;
if (ttprty)
for (i = 0; i < nread; i++)
buf[i] &= mask;
if (nread > 1)
buf[nread] = '\0'; /* Null terminate */
return(count + nread);
}
/*
* Sleeper -- perform an interruptible timeout
*/
static int
Sleeper(LONG secs, LONG micro)
{
LONG sigs;
LONG waitsigs;
struct timerequest *timer = TimerIOB;
if (!TimerIOB) return(-1);
if (secs == 0 && micro <= 2)
return(0);
StartTimer(secs, micro);
sigs = 0;
waitsigs = (1L << timer->tr_node.io_Message.mn_ReplyPort->mp_SigBit) | intsigs;
for (;;)
{
if (CheckIO((IOR *)timer))
{
WaitIO((IOR *)timer);
return(0);
}
if (sigs & intsigs)
{
KillIO((IOR *)timer);
testint(sigs);
return(-1);
}
sigs = Wait(waitsigs);
}
}
/*
* sleep -- wait n seconds
*/
int
sleep(int n)
{ return(Sleeper((LONG)n, 0L)); }
/*
* msleep -- wait n milliseconds
*/
int
msleep(int m)
{ return(Sleeper((LONG)(m / 1000), (m % 1000) * 1000L)); }
/*
* rtimer -- reset elapsed time
*/
void
rtimer(void)
{ DateStamp(&prevtime); }
/*
* gtimer -- get currently elapsed time in seconds
*/
int
gtimer(void)
{
int x;
struct DateStamp curtime;
DateStamp(&curtime);
x = ((curtime.ds_Days - prevtime.ds_Days ) * 1440 +
(curtime.ds_Minute - prevtime.ds_Minute) ) * 60 +
(curtime.ds_Tick - prevtime.ds_Tick ) / 50;
return((x < 0) ? 0 : x );
}
/*
* ztime -- format current date and time into string
*/
void
ztime(char **s)
{
time_t xclock;
(void) time(&xclock);
*s = asctime(localtime(&xclock));
}
/*
* congm -- save console modes
*/
int
congm(void)
{
if (!saverr) saverr = DOSFH(2);
return(0);
}
/*
* CreateWindow -- create window and jam it into standard I/O
*/
int
CreateWindow(int esc)
{
char rawname[48];
struct Screen s;
if (rawcon > 0) return(0);
congm();
if (GetScreenData(&s, sizeof(s), WBENCHSCREEN, NULL) == 0) {
s.Width = 640;
s.Height = 200;
}
sprintf(rawname, "RAW:0/1/%d/%d/Kermit%s", s.Width, s.Height - 1,
v37? "/ALT0/1/100/30" : "");
rawcon = Open((UBYTE *) rawname, (LONG)MODE_NEWFILE);
if (rawcon == 0)
return(-1);
DOSFH(0) = DOSFH(1) = DOSFH(2) = rawcon;
/* if we create a window, don't abort on errors or echo */
backgrd = FALSE;
ckxech = 1;
return(0);
}
/*
* concb -- put console in single character wakeup mode
*/
int
concb(char esc)
{
if (rawcon) return(0);
if (CurCLI && CurProc->pr_CIS != CurCLI->cli_StandardInput)
return(0);
return(CreateWindow(esc));
}
/*
* conbin -- put console in raw mode
*/
int
conbin(char esc)
{
if (rawcon) return(0);
if (CurCLI && CurProc->pr_CIS != CurCLI->cli_StandardInput)
return(isatty(0) ? 0 : -1);
return(CreateWindow(esc));
}
/*
* conres -- restore console
* we actually restore in syscleanup()
*/
conres()
{
return(0);
}
/*
* conoc -- output character to console
*/
int
conoc(char c)
{
putchar(c);
fflush(stdout);
Chk_Abort();
return c;
}
/*
* conxo -- output x chars to console
*/
int
conxo(int n, char *buf)
{
int retval;
fflush(stdout);
retval = write(FILENO(1), buf, n);
Chk_Abort();
return retval;
}
/*
* conol -- output line to console
*/
int
conol(char *l)
{
int retval;
retval = fputs(l, stdout);
fflush(stdout);
Chk_Abort();
return retval;
}
/*
* conola -- output line array to console
*/
int
conola(char **l)
{
for (; **l; ++l)
if (conol(*l) < 0)
return(-1);
return 0;
}
/*
* conoll -- output line with CRLF
*/
int
conoll(char *l)
{
if (conol(l) < 0)
return -1;
if (conxo(2, "\r\n") < 0)
return -1;
return 0;
}
/*
* conchk -- returns nonzero if characters available from console
*/
int
conchk(void)
{
fflush(stdout);
Chk_Abort();
return(WaitForChar(DOSFH(0), 0L) != 0);
}
/*
* coninc -- get input character from console
*/
int
coninc(int timeout)
{
UBYTE ch;
fflush(stdout);
Chk_Abort();
if (timeout > 0 && !WaitForChar(DOSFH(0), timeout * 1000000L))
return(-1);
if (read(FILENO(0), &ch, 1) < 1) return(-1);
Chk_Abort();
return((int)ch);
}
/*
* T T S C A R R -- Copy desired character mode to global ttcarr for future
* and later use.
*/
ttscarr(carrier) int carrier; {
ttcarr = carrier;
debug(F101, "ttscarr","",ttcarr);
return(ttcarr);
}
static int
sendbreak(long time) {
if (!serialopen) return(-1);
/* flush queued output */
TerminateWrite(1);
nttoq = 0;
pendwrite = TRUE;
WriteIOB->IOSer.io_Command = SDCMD_SETPARAMS;
WriteIOB->io_BrkTime = time;
(void) DoIO((IOR *)WriteIOB);
pendwrite = TRUE;
WriteIOB->IOSer.io_Command = SDCMD_BREAK;
WriteIOB->io_SerFlags &= ~SERF_QUEUEDBRK;
SendIO((IOR *)WriteIOB);
return(0);
}
/*
* ttsndb -- send a BREAK
* flushes queued and active output
*/
int
ttsndb(void)
{
return(sendbreak(275000L));
}
/*
* ttsndlb -- send a long BREAK (1.5 sec)
*/
int
ttsndlb(void) {
return(sendbreak(1500000L));
}
/* T T G M D M -- Get modem signals */
/*
Looks for the modem signals as defined by the BM_??? constants in
ckcdeb.h, and returns those that are on as a bit mask. Returns:
-3 Not implemented
-2 if the line does not have modem control
-1 on error.
>= 0 on success, with a bit mask containing the modem signals that are on.
*/
int
ttgmdm(void) {
struct IOExtSer *myread = ReadIOB;
int z;
UWORD status;
if (!serialopen)
return -1;
Chk_Abort();
if (pendread && !CheckIO((IOR *)myread)) return(0);
if (TerminateRead() != 0) return(-1);
myread->IOSer.io_Command = SDCMD_QUERY;
if (DoIO((IOR *) myread) != 0)
return -1;
status = myread->io_Status;
z = 0;
if (status & (1<<2)) z |= BM_RNG; /* active high */
status = ~status; /* rest are active low */
if (status & (1<<3)) z |= BM_DSR;
if (status & (1<<4)) z |= BM_CTS;
if (status & (1<<5)) z |= BM_DCD;
if (status & (1<<6)) z |= BM_RTS;
if (status & (1<<7)) z |= BM_DTR;
return(z);
}
/*
* ttocq -- write char to serial device, queueing if necessary
* returns -2 on overrun, -1 on serial error
* use only in connect mode
*/
int
ttocq(char c)
{
int i;
if (!serialopen) return(-1);
if (pendwrite && CheckIO((IOR *)WriteIOB))
{
pendwrite = FALSE;
if (WaitIO((IOR *)WriteIOB) != 0) return(-1);
}
if (pendwrite)
{
if (nttoq >= NTTOQ) return(-2); /* overrun */
ttoq[(pttoq + nttoq++) % NTTOQ] = c;
}
else if (nttoq == 0)
return(ttoc(c));
else
{
i = ttoc(ttoq[pttoq]);
ttoq[(pttoq + nttoq) % NTTOQ] = c;
pttoq = (pttoq + 1) % NTTOQ;
if (i < 0) return(-1);
}
return(1);
}
/*
* ttonq -- returns number of characters in serial output queue
*/
int
ttonq(void)
{
return(nttoq);
}
/*
* conttb -- prepare for contti() usage
*/
void
conttb(void)
{
/* flush queued input and output */
queuedcon = -1;
pttoq = nttoq = 0;
}
/*
* contte -- end contti() usage
* this can be called after a tthang, it which case ttres will already
* have done this cleanup
*/
void
contte(void)
{
/* clear any pending ^C, ^D interrupts */
Chk_Abort();
/* terminate any pending I/O */
if (serialopen) SerialReset();
}
/*
* contti -- wait for console or tty input
* returns next console input or -1 when serial input available
*/
int
contti(void)
{
int i;
LONG waitsigs;
struct DosPacket *pkt = conpkt;
struct IOExtSer *myread = ReadIOB;
static UBYTE conchar;
BPTR dosfh = DOSFH(0);
struct FileHandle *fh = (struct FileHandle *)BADDR(dosfh);
if (queuedcon >= 0)
{
conchar = queuedcon;
queuedcon = -1;
return((int)conchar);
}
if (!pendconsole)
{ /* start a console read */
pkt->dp_Port = conport;
pkt->dp_Type = ACTION_READ;
pkt->dp_Arg1 = (LONG)dosfh;
pkt->dp_Arg2 = (LONG)&conchar;
pkt->dp_Arg3 = 1;
PutMsg(fh->fh_Process, pkt->dp_Link);
pendconsole = TRUE;
}
if (queuedser < 0 && !pendread)
{ /* start a serial read */
myread->IOSer.io_Command = CMD_READ;
myread->IOSer.io_Data = (APTR)&serbufc;
myread->IOSer.io_Length = 1;
SendIO((IOR *)myread);
pendread = TRUE;
}
waitsigs = (1L << serport->mp_SigBit) | (1L << conport->mp_SigBit);
for (;;)
{
if (pendwrite && CheckIO((IOR *)WriteIOB))
{
WaitIO((IOR *)WriteIOB);
pendwrite = FALSE;
if (nttoq > 0)
{
i = ttoc(ttoq[pttoq]);
pttoq = (pttoq + 1) % NTTOQ;
--nttoq;
if (i < 0) return(-1);
}
}
/* give the console first chance */
if (GetMsg(conport))
{
pendconsole = FALSE;
if (pkt->dp_Res1 != 1) return(-1);
/* translate CSI to ESC [ */
if (conchar == 0x9B)
{
conchar = 0x1B;
queuedcon = '[';
}
return((int)conchar);
}
if (queuedser >= 0) return(-2);
if (CheckIO((IOR *)myread))
return((TerminateRead() == 0) ? -2 : -1);
Wait(waitsigs);
}
}
/* P S U S P E N D -- Put current process in background. */
/*
* Even though this isn't supported on the Amiga, I return success anyway.
* After all, the user can pop the window to the back and do something
* else any time he wants.
*/
int
psuspend(int foo) {
return 0;
}
/* P R I V _ functions -- all dummy on the Amiga. */
int
priv_ini(void) {
return 0;
}
int
priv_on(void) {
return 0;
}
int
priv_off(void) {
return 0;
}
int
priv_can(void) {
return 0;
}