char *ckxv = "Amiga tty I/O, 4D(005), 11 Jul 86"; /* C K I T I O -- Serial and Console I/O support for the Amiga */ /* Author: Jack Rouse Based on the CKUTIO.C module for Unix Contributed to Columbia University for inclusion in C-Kermit. Copyright (C) 1986, Jack J. Rouse, 106 Rubin Ct. Apt. A-4, Cary NC 27511 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. */ #include /* standard I/O stuff */ #undef NULL #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) #ifdef LAT304 #include "lattice/fcntl.h" #include "lattice/signal.h" #endif #include "lattice/ios1.h" /* defines ufbs structure */ char *ckxsys = " Commodore Amiga"; /* system name */ /* external declarations */ extern int speed, mdmtyp, parity, flow; /* external definitions */ char *dftty = SERIALNAME; /* serial device name */ int dfloc = 1; /* serial line is external */ int dfprty = 0; /* default parity is none */ int dfflow = 1; /* default flow control is on */ int backgrd = 0; /* default to foreground */ int ckxech = 0; /* echo in case redirected stdin */ struct Process *CurProc; /* current process */ struct CommandLineInterface *CurCLI; /* current CLI info */ extern struct IntuitionBase *IntuitionBase; /* ptr to Intuition lib */ /* 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 timeropen; /* true iff timer 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 int (*inthdlr)(); /* function to signal break to */ 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 */ /* Exec routines */ APTR AllocMem(); LONG AllocSignal(); struct IORequest *CheckIO(); VOID CloseDevice(), CloseLibrary(); LONG DoIO(); struct MsgPort *FindPort(); struct Task *FindTask(); VOID FreeMem(), FreeSignal(); struct Message *GetMsg(); LONG OpenDevice(); struct Library *OpenLibrary(); VOID PutMsg(), ReplyMsg(); VOID SendIO(); LONG SetSignal(); VOID Signal(); LONG Wait(); LONG WaitIO(); struct Message *WaitPort(); /* Exec support */ struct IORequest *CreateExtIO(); VOID DeleteExtIO(); struct MsgPort *CreatePort(); VOID DeletePort(); struct Task *CreateTask(); VOID DeleteTask(); /* AmigaDOS routines */ VOID Delay(); BPTR Open(); VOID Close(); BPTR Input(), Output(); LONG Read(); LONG WaitForChar(); struct DateStamp *DateStamp(); /* AmigaDOS support (from ckiutl.c) */ struct DosPacket *CreatePacket(); VOID DeletePacket(); #ifdef LAT304 /* translate Unix file handle (0, 1, or 2) to AmigaDOS file handle */ #define DOSFH(n) fileno(&_iob[n]) /* translate Unix file handle (0, 1, or 2) to Lattice file handle */ #define FILENO(n) fileno(&_iob[n]) #else /* Lattice runtime externals */ extern struct UFB _ufbs[]; extern int Enable_Abort; #define DOSFH(n) (_ufbs[n].ufbfh) #define FILENO(n) (n) #endif /* * make note of a serial error and quit */ static Fail(msg) char *msg; { syscleanup(); fprintf(stderr, msg); fprintf(stderr, "\n"); exit(2); } /* * default interrupt handler */ static int defhdlr() { printf("*** BREAK ***\n"); doexit(1); } /* * sysinit -- Amiga specific initialization */ sysinit() { register 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; /* default interrupts to exit handler */ intsigs = BREAKSIGS; inthdlr = defhdlr; #ifdef LAT304 signal(SIGINT, SIG_IGN); #else Enable_Abort = 0; #endif /* 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 = (struct timerequest *) CreateExtIO(serport,(LONG)sizeof(*TimerIOB)); if (TimerIOB == NULL) Fail("no TimerIOB"); if (OpenDevice(TIMERNAME, (LONG)UNIT_VBLANK, TimerIOB, 0L) != 0) Fail("no timer device"); timeropen = TRUE; /* open the Intuition library */ if (!IntuitionBase && (IntuitionBase = (struct IntuitionBase *) OpenLibrary("intuition.library", 0L) ) == NULL ) Fail("can't open Intuition"); /* open the serial device to get configuration */ iob->io_SerFlags = SERF_SHARED; if (OpenDevice(SERIALNAME, 0L, iob, 0L) != 0) Fail("can't open serial.device"); /* set parameters from system defaults */ flow = !(iob->io_SerFlags & SERF_XDISABLED); parity = !(iob->io_SerFlags & SERF_PARTY_ON) ? 0 : (iob->io_SerFlags & SERF_PARTY_ODD) ? 'o' : 'e'; speed = iob->io_Baud; mdmtyp = (iob->io_SerFlags & SERF_7WIRE) != 0; CloseDevice(iob); serialopen = FALSE; return(0); } /* * syscleanup -- Amiga specific cleanup */ syscleanup() { /* close everything */ if (serialopen) CloseDevice(ReadIOB); if (timeropen) CloseDevice(TimerIOB); if (TimerIOB) DeleteExtIO(TimerIOB, (LONG)sizeof(*TimerIOB)); if (WriteIOB) DeleteExtIO(WriteIOB, (LONG)sizeof(*WriteIOB)); if (ReadIOB) DeleteExtIO(ReadIOB, (LONG)sizeof(*ReadIOB)); if (serport) DeletePort(serport); if (conpkt) DeletePacket(conpkt); if (conport) DeletePort(conport); reqres(); if (IntuitionBase) { CloseLibrary(IntuitionBase); IntuitionBase = NULL; } /* reset standard I/O */ if (rawcon > 0) { /* restore Lattice AmigaDOS file handles */ DOSFH(0) = Input(); DOSFH(1) = Output(); DOSFH(2) = saverr; #ifdef LAT304 close(rawcon); #else Close(rawcon); #endif } } /* * 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. */ reqoff() { pushwindow = CurProc->pr_WindowPtr; CurProc->pr_WindowPtr = (APTR)-1; } /* * reqpop -- restore requesters to action at last reqoff */ reqpop() { CurProc->pr_WindowPtr = pushwindow; } /* * reqres -- restore requestors to startup action */ reqres() { CurProc->pr_WindowPtr = savewindow; } /* * KillIO -- terminate an I/O request */ static int KillIO(iob) struct IORequest *iob; { AbortIO(iob); return(WaitIO(iob)); } /* * DoIOQuick -- DoIO with quick IO * This should not be used where waiting is expected since * it cannot be interrupted. */ static int DoIOQuick(iob) register struct IORequest *iob; { register int D7Save; /* V1.1 bug. IO sometimes trashes D7 */ /* do I/O with quick option, wait around if necessary */ iob->io_Flags = IOF_QUICK; if (BeginIO(iob) == 0 && !(iob->io_Flags & IOF_QUICK)) WaitIO(iob); /* return the error, if any */ return((int)iob->io_Error); } /* * 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. */ ttopen(ttname, lcl, modem) char *ttname; int *lcl; int modem; { register struct IOExtSer *iob = ReadIOB; if (serialopen) return(0); /* ignore if already open */ /* verify the serial name */ if (strcmp(ttname, SERIALNAME) != 0) return(-1); /* set open modes */ iob->io_SerFlags = (modem) ? (SERF_SHARED|SERF_7WIRE) : SERF_SHARED; /* open the serial device */ if (OpenDevice(ttname, 0L, iob, 0L) != 0) return(-1); serialopen = TRUE; 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 */ return(0); } /* * StartTimer -- start a timeout */ static VOID StartTimer(secs, micro) LONG secs, micro; { TimerIOB->tr_node.io_Command = TR_ADDREQUEST; TimerIOB->tr_time.tv_secs = secs; TimerIOB->tr_time.tv_micro = micro; SendIO(TimerIOB); } /* * SerialWait -- wait for serial I/O to terminate * return I/O error */ static int SerialWait(iob, timeout) register struct IOExtSer *iob; int timeout; { register int D7Save; /* save register D7 */ register LONG sigs; register struct timerequest *timer = TimerIOB; register LONG waitsigs; /* set up timeout if necessary */ if (timeout > 0) StartTimer((LONG)timeout, 0L); /* wait for completion, timeout, or interrupt */ sigs = 0; waitsigs = (1L << serport->mp_SigBit) | intsigs; for (;;) { if (sigs & intsigs) { /* interrupted */ if (timeout > 0) KillIO(timer); KillIO(iob); testint(sigs); return(-1); } if (CheckIO(iob)) { if (timeout > 0) KillIO(timer); return(WaitIO(iob)); } if (timeout > 0 && CheckIO(timer)) { KillIO(iob); WaitIO(timer); /* restart if XOFF'ed */ iob->IOSer.io_Command = CMD_START; DoIOQuick(iob); return(-1); } sigs = Wait(waitsigs); } } /* * TerminateRead -- wait for queued read to finish */ static int TerminateRead() { if (!pendread) return(0); if (WaitIO(ReadIOB) == 0) queuedser = serbufc; pendread = FALSE; return((int)ReadIOB->IOSer.io_Error); } /* * TerminateWrite -- ensure WriteIOB is ready for reuse */ static int TerminateWrite(timeout) int timeout; { testint(0L); if (!pendwrite) return(0); pendwrite = FALSE; return(SerialWait(WriteIOB, timeout)); } /* * SerialReset -- terminate pending serial and console I/O */ static VOID SerialReset() { if (pendread) { AbortIO(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; return(DoIOQuick(ReadIOB) ? -1 : 0); } /* * ttclos -- close the serial device */ ttclos() { if (!serialopen) return(0); if (ttres() < 0) return(-1); CloseDevice(ReadIOB); serialopen = FALSE; return(0); } /* * tthang -- hang up phone line * Drops DTR by closing serial.device */ tthang() { return((serialopen) ? ttclos() : -1); } /* * ttpkt -- set serial device up for packet transmission * sets serial parameters */ ttpkt(baud, flow) int baud, flow; { extern UBYTE eol; register struct IOExtSer *iob = ReadIOB; int speed; if (!serialopen || pendread) return(-1); /* terminate any pending writes */ TerminateWrite(1); /* fill in parameters */ iob->io_CtlChar = 0x11130000; if (baud >= 0 && (speed = ttsspd(baud)) >= 0) iob->io_Baud = speed; setmem(&iob->io_TermArray, sizeof(struct IOTArray), eol); iob->io_ReadLen = iob->io_WriteLen = 8; iob->io_StopBits = 1; if (flow) iob->io_SerFlags &= ~SERF_XDISABLED; else iob->io_SerFlags |= SERF_XDISABLED; /* if no flow and high baud rate, RAD_BOOGIE is appropriate */ if (!flow && iob->io_Baud > 19200) iob->io_SerFlags |= SERF_RAD_BOOGIE; else iob->io_SerFlags &= ~SERF_RAD_BOOGIE; iob->io_SerFlags &= ~(SERF_EOFMODE|SERF_PARTY_ON|SERF_PARTY_ODD); /* set the parameters */ iob->IOSer.io_Command = SDCMD_SETPARAMS; if (DoIOQuick(iob) != 0) return(-1); return(ttflui()); } /* * ttvt -- set up serial device for connect mode */ ttvt(baud, flow) int baud, flow; { return(ttpkt(baud, flow)); } /* * ttsspd -- verify baud rate */ int ttsspd(speed) { if (speed < 110 || speed > 292000) return(-1); return(max(112, speed)); } /* * ttflui -- flush serial device input buffer */ ttflui() { if (!serialopen || pendread) return(-1); queuedser = -1; ReadIOB->IOSer.io_Command = CMD_CLEAR; return(DoIOQuick(ReadIOB) ? -1 : 0); } static struct IntuiText BodyText = { -1, -1, 0, 4, 4, NULL, "Interrupt Requested", NULL }; static struct IntuiText ContinueText = { -1, -1, 0, 4, 4, NULL, "Continue", NULL }; static struct IntuiText AbortText = { -1, -1, 0, 4, 4, NULL, "Exit C-Kermit", NULL }; /* * test for and catch interrupt */ testint(sigs) LONG sigs; { int (*catch)(); /* test for and reset caught interrupt signals */ if (((sigs | SetSignal(0L, (LONG)BREAKSIGS)) & intsigs) && inthdlr) { if (AutoRequest((struct Window *)NULL, &BodyText, &ContinueText, &AbortText, 0L, 0L, 260L, 55L) ) return; catch = inthdlr; inthdlr = NULL; intsigs = 0; (*catch)(); } } /* * conint -- set console interrupt handler */ conint(newhdlr) int (*newhdlr)(); { testint(0L); /* handle any pending interrupts */ inthdlr = newhdlr; /* set the new handler */ intsigs = BREAKSIGS; /* note signal caught */ } /* * connoi -- disable interrupt trapping */ connoi() { inthdlr = NULL; /* disable interrupts */ intsigs = 0; /* note signal ignored */ testint(0L); /* ignore pending interrupts */ } /* * ttchk -- return number of chars immediately available from serial device */ ttchk() { register struct IOExtSer *read = ReadIOB; if (!serialopen) return(-1); testint(0L); if (pendread && !CheckIO(read)) return(0); if (TerminateRead() != 0) return(-1); read->IOSer.io_Command = SDCMD_QUERY; return((DoIOQuick(read) == 0) ? ((queuedser >= 0) + (int)read->IOSer.io_Actual) : -1); } /* * ttxin -- get n characters from serial device */ ttxin(n, buf) int n; char *buf; { return(ttinl(buf, n, 0, -1)); } /* * ttinc -- read character from serial line */ ttinc(timeout) int timeout; { UBYTE ch; return((ttinl(&ch, 1, timeout, -1) > 0) ? (int)ch : -1); } /* * ttol -- write n chars to serial device */ ttol(buf, n) char *buf; int n; { register int D7Save; register struct IOExtSer *write = WriteIOB; if (!serialopen) return(-1); if (TerminateWrite(0) != 0) return(-1); pendwrite = TRUE; write->IOSer.io_Command = CMD_WRITE; write->IOSer.io_Data = (APTR)buf; write->IOSer.io_Length = n; SendIO(write); return(n); } /* * ttoc -- output single character to serial device */ ttoc(c) char c; { return(ttol(&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 */ ttinl(buf, n, timeout, eol) register char *buf; int n; int timeout; /* timeout in seconds or <= 0 */ int eol; /* end of line character */ { register int D7Save; register struct IOExtSer *read = ReadIOB; register int count; testint(0L); if (!serialopen || pendread || n <= 0) return(-1); /* handle pushback */ if (queuedser >= 0) { *buf = queuedser; queuedser = -1; if (*buf == eol || n == 1) return(1); ++buf; --n; count = 1; } else count = 0; /* set up line terminator */ if (eol >= 0) { /* set up line terminator */ if (eol != *(UBYTE *)&read->io_TermArray) { setmem(&read->io_TermArray, sizeof(struct IOTArray), eol); read->IOSer.io_Command = SDCMD_SETPARAMS; DoIOQuick(read); } read->io_SerFlags |= SERF_EOFMODE; } else read->io_SerFlags &= ~SERF_EOFMODE; /* set up the read */ read->IOSer.io_Command = CMD_READ; read->IOSer.io_Data = (APTR)buf; read->IOSer.io_Length = n; /* perform read quickly if possible */ read->IOSer.io_Flags = IOF_QUICK; BeginIO(read); if (read->IOSer.io_Flags & IOF_QUICK) { read->IOSer.io_Flags = 0; return ((read->IOSer.io_Error == 0) ? (count + (int)read->IOSer.io_Actual) : -1); } /* wait for read to complete */ return ((SerialWait(read, timeout) != 0) ? -1 : count + (int)read->IOSer.io_Actual ); } /* * Sleeper -- perform an interruptible timeout */ static Sleeper(secs, micro) LONG secs, micro; { register int D7Save; register LONG sigs; register LONG waitsigs; register struct timerequest *timer = TimerIOB; if (!timeropen) return(-1); StartTimer(secs, micro); sigs = 0; waitsigs = (1L << serport->mp_SigBit) | intsigs; for (;;) { if (CheckIO(timer)) { WaitIO(timer); return(0); } if (sigs & intsigs) { KillIO(timer); testint(sigs); return(-1); } sigs = Wait(waitsigs); } } /* * sleep -- wait n seconds */ sleep(n) int n; { return(Sleeper((LONG)n, 0L)); } /* * msleep -- wait n milliseconds */ msleep(m) int m; { return(Sleeper((LONG)(m / 1000), (m % 1000) * 1000L)); } /* * rtimer -- reset elapsed time */ rtimer() { DateStamp(&prevtime); } /* * gtimer -- get currently elapsed time in seconds */ gtimer() { 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 */ ztime(s) char **s; { /* * The following date code taken from a USENET article by * Tomas Rokicki(rokicki@Navajo.ARPA) */ static char *months[] = { NULL, "January","February","March","April","May","June", "July","August","September","October","November","December"}; static char buf[32]; long n ; int m, d, y ; struct DateStamp datetime; DateStamp(&datetime); n = datetime.ds_Days - 2251 ; y = (4 * n + 3) / 1461 ; n -= 1461 * y / 4 ; y += 1984 ; m = (5 * n + 2) / 153 ; d = n - (153 * m + 2) / 5 + 1 ; m += 3 ; if (m > 12) { y++ ; m -= 12 ; } sprintf(buf, "%02d:%02d:%02d %s %d, %d", datetime.ds_Minute / 60, datetime.ds_Minute % 60, datetime.ds_Tick / 50, months[m], d, y) ; *s = buf; } /* * congm -- save console modes */ congm() { if (!saverr) saverr = DOSFH(2); return(0); } /* * CreateWindow -- create window and jam it into standard I/O */ int CreateWindow(esc) { if (rawcon > 0) return(0); congm(); #ifdef LAT304 if ((rawcon = open("RAW:0/0/640/200/Kermit", O_RDWR)) <= 0) return(-1); #else if ((rawcon = Open("RAW:0/0/640/200/Kermit", (LONG)MODE_NEWFILE)) == 0) return(-1); #endif 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 */ concb(esc) { if (rawcon) return(0); if (CurCLI && CurProc->pr_CIS != CurCLI->cli_StandardInput) return(0); return(CreateWindow(esc)); } /* * conbin -- put console in raw mode */ conbin(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 */ conoc(c) { putchar(c); fflush(stdout); testint(0L); } /* * conxo -- output x chars to console */ conxo(n, buf) char *buf; int n; { fflush(stdout); write(FILENO(1), buf, n); testint(0L); } /* * conol -- output line to console */ conol(l) char *l; { fputs(l, stdout); fflush(stdout); testint(0L); } /* * conola -- output line array to console */ conola(l) char **l; { for (; **l; ++l) conol(*l); } /* * conoll -- output line with CRLF */ conoll(l) char *l; { conol(l); conxo(2, "\r\n"); } /* * conchk -- returns nonzero if characters available from console */ conchk() { fflush(stdout); testint(0L); return(WaitForChar(DOSFH(0), 0L) != 0); } /* * coninc -- get input character from console */ coninc(timeout) int timeout; { UBYTE ch; fflush(stdout); testint(0L); if (timeout > 0 && !WaitForChar(DOSFH(0), timeout * 1000000L)) return(-1); if (read(FILENO(0), &ch, 1) < 1) return(-1); testint(0L); return((int)ch); } /* * ttsndb -- send a BREAK * flushes queued and active output */ ttsndb() { register int D7Save; if (!serialopen) return(-1); /* flush queued output */ TerminateWrite(1); nttoq = 0; pendwrite = TRUE; WriteIOB->IOSer.io_Command = SDCMD_BREAK; WriteIOB->io_SerFlags &= ~SERF_QUEUEDBRK; SendIO(WriteIOB); return(0); } /* * ttocq -- write char to serial device, queueing if necessary * returns -2 on overrun, -1 on serial error * use only in connect mode */ ttocq(c) char c; { register int i; if (!serialopen) return(-1); if (pendwrite && CheckIO(WriteIOB)) { pendwrite = FALSE; if (WaitIO(write) != 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() { return(nttoq); } /* * conttb -- prepare for contti() usage */ conttb() { /* 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 */ contte() { /* clear any pending ^C, ^D interrupts */ testint(0L); /* 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() { register int D7Save; register int i; register LONG waitsigs; register struct DosPacket *pkt = conpkt; register struct IOExtSer *read = 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 */ read->IOSer.io_Command = CMD_READ; read->IOSer.io_Data = (APTR)&serbufc; read->IOSer.io_Length = 1; SendIO(read); pendread = TRUE; } waitsigs = (1L << serport->mp_SigBit) | (1L << conport->mp_SigBit); for (;;) { if (pendwrite && CheckIO(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 == '\233') { conchar = '\033'; queuedcon = '['; } return((int)conchar); } if (queuedser >= 0) return(-2); if (CheckIO(read)) return((TerminateRead() == 0) ? -2 : -1); Wait(waitsigs); } }