/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* |_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.19 1999/09/12 00:42:47 swalton Exp 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.19 1999/09/12 00:42:47 swalton * At some point in version 7A(195), cmdini() was moved to be called before * sysinit(). This meant that concb(esc) could not be called from cmdini() * in ckuus5.c as it was before. So concb(esc) is now called by us from * sysinit() to open the initial Kermit window. * In addition, this is an administrative checkin. Due to some lost RCS * files, the exact changes from version 1.15 to version 1.18 were lost. * * Revision 1.18 1998/04/17 04:13:56 swalton * ttgwsz() added. Now the file transfer display looks nice! * * Revision 1.17 1997/01/16 22:03:07 swalton * Simple change: instead of dying with fatal error if cannot open * serial.device to determine defaults, just skip the initialization. * This allows use of Kermit with, for example, telser.device on a * dial-up Internet link.. * * Revision 1.16 1996/11/29 10:47:22 swalton * Added tgetent() stub to allow code to work with 6.0 release. * * Revision 1.15 1996/11/25 14:59:17 swalton * Changed the name of the variable "rawcon" to "rawconfh" to avoid conflict * with a routine of the same name. * * 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 #include * 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" #undef ULONG #undef USHORT #include #include #include #include #include #include #define fh_Interact fh_Port #define fh_Process fh_Type #include #include #define BREAKSIGS (SIGBREAKF_CTRL_C|SIGBREAKF_CTRL_D) #include #include #ifdef AZTEC_C #include #include char *ckxsys = " Commodore Amiga (Aztec_C)"; /* system name */ #else #ifdef __SASC #include #include #include /* defines ufbs structure */ char *ckxsys = " Commodore Amiga (SAS/C)"; /* system name */ #endif #endif #include #include #include #include /* 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 */ extern int ttnproto; /* Protocol for network device */ extern int tlevel, escape; /* Take level & escape character */ 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 rawconfh; /* 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; extern char uidbuf[]; /* 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) { /* 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); if (tlevel < 0) (void) concb((char) escape); strncpy(uidbuf,"AMIGAUSER",UIDBUFLEN); return(0); } char * whoami() { extern char uidbuf[]; return((char *)uidbuf; } 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 (rawconfh > 0) { /* restore Lattice AmigaDOS file handles */ DOSFH(0) = Input(); DOSFH(1) = Output(); DOSFH(2) = saverr; Close(rawconfh); rawconfh = 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 5760: s = 57600; 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 ), 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 (rawconfh > 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" : ""); rawconfh = Open((UBYTE *) rawname, (LONG)MODE_NEWFILE); if (rawconfh == 0) return(-1); DOSFH(0) = DOSFH(1) = DOSFH(2) = rawconfh; /* 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 (rawconfh) 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 (rawconfh) 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); } /* * ttgwsiz -- get window size */ extern int tt_rows, tt_cols; int ttgwsiz(void) { char t[20]; int n; conol("\x9b\x30\x20\x71"); for (n = 0; n < sizeof(t); n++) { if ((t[n] = coninc(1)) == -1) { t[n] = '\0'; break; } if (t[n] == 'r') { t[++n] = '\0'; break; } } if (sscanf(t, "\x9b" "1;1;%d;%d r", &tt_rows, &tt_cols) != 2) { tt_rows = tt_cols = 0; return -1; } return 0; } /* * 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); } } /* T G E T E N T -- Dummy routine to simulate curses/termcap. */ #ifdef MYCURSES int tgetent(char *s1, char *s2) { return(1); } #endif /* MYCURSES */ /* 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; }