From: mbs@adastra.cvl.va.us (Michael B. Smith)
Newsgroups: comp.sys.amiga.programmer
Subject: A serial.device example (LONG) (was Re: SERIAL.DEVICE HEEEEELLLLLPPPPPPP)
Distribution: world
X-NewsSoftware: GRn 1.16f (beta) by Mike Schwartz & Michael B. Smith
Date: 1 Sep 92 19:00:10 EDT
Organization: Private UUCP Node

In article <Vic.00yf@angband.delfax.OCunix.On.Ca> Vic@angband.delfax.OCunix.On.Ca (Vic Lewington) writes:
> In article <u895762.715081138@bruny> u895762@bruny.cc.utas.edu.au (Paul S.E.) writes:
> > I have a problem that has been driving me mad for the past 5 hours.
> > I am trying to read data from serial.device, one character at a time. (i.e. in real time), but the 512 byte buffer prevents me from doing it.
> > HELP, anybody?!
>
>     Having just spent a few hours going nuts myself, here, try this...

[example deleted]

Apologies for posting this to a non-source group. However, I spent a couple
of days looking for a simple, working example, before writing this, and it
wasn't to be found. This may do someone else some good...

Here is a piece of working code that comes with the next release of GRn. No
warranties, and you get what you pay for. It was written to be clear, not
extremely efficient. This requires 2.0+, due to the use of PrintFault().
Remove that, and it should work on 1.3.

This is DICE code, but the only place it should matter is the declaration of
InputHandler(), which is currently:
             __geta4 void InputHandler (void);
I think SAS/C will require this to be:
             void __saveds InputHandler (void);
but I'm not sure...

Flames to NIL:

/*
        GRn-Term.c

        GRn - Gadtools Read News

        Copyright 1992 by Michael B. Smith.

        All Rights Reserved.

        No warranties, express or implied, are available. Use at your own risk.
        The authors have taken pains to attempt to produce quality software,
        but by no means are we liable for anything it does to your system.

        Feel free to modify the source for your own use, but do NOT distribute
        unauthorized sources or binaries as part of GRn. You may distribute
        the source and binaries in unmodified form.
*/

#define _GRNTERM_

#include <stdlib.h>
#include <stdarg.h>

#include <exec/types.h>
#include <devices/serial.h>
#include <dos/dosextens.h>
#include <dos/dostags.h>
#include <clib/dos_protos.h>

#ifndef _GRNTERM_
extern void Abort (void);
extern void panic (const char *, ...);
#endif

static struct MsgPort *NNTP_in_mp  = NULL;
static struct MsgPort *NNTP_out_mp = NULL;

static int NNTP_in_ip  = 0; // input  IO in-process
static int NNTP_out_ip = 0; // output IO in-process

struct IOExtSer *NNTP_in_io  = NULL;
struct IOExtSer *NNTP_out_io = NULL;

int serial_error = 0;

void ShutDownSerialPort (void);
void KillSerialPort     (const int, const char *);
void SetupSerialPort    (const int, const char *, const int);
int reads               (struct IOExtSer *, char *, int);
int writes              (struct IOExtSer *, char *, int);

void ShutDownSerialPort (void)
{
        static int in_sdsp = 0;  // use as a semaphore

        if (in_sdsp)
                return;

        in_sdsp = 1;

        if (NNTP_out_io && NNTP_out_ip) {
                AbortIO (NNTP_out_io);
                WaitIO (NNTP_out_io);
                NNTP_out_ip = 0;
        }
        if (NNTP_in_io && NNTP_in_ip) {
                AbortIO (NNTP_in_io);
                WaitIO (NNTP_in_io);
                NNTP_in_ip = 0;
        }

        if (NNTP_in_io && !serial_error)
                CloseDevice (NNTP_in_io);

        if (NNTP_out_io) {
                DeleteExtIO (NNTP_out_io, sizeof (struct IOExtSer));
                NNTP_out_io = NULL;
        }

        if (NNTP_in_io) {
                DeleteExtIO (NNTP_in_io, sizeof (struct IOExtSer));
                NNTP_in_io = NULL;
        }

        if (NNTP_out_mp) {
                DeletePort (NNTP_out_mp);
                NNTP_out_mp = NULL;
        }

        if (NNTP_in_mp) {
                DeletePort (NNTP_in_mp);
                NNTP_in_mp = NULL;
        }

        in_sdsp = 0;
        return;
}

void KillSerialPort (const int errno, const char *msg)
{
#ifdef _GRNTERM_
        if (errno)
                PrintFault (errno, msg);
        else {
                PutStr (msg);
                PutStr ("\n");
        }
        ShutDownSerialPort ();
        exit (20);
#else
        ShutDownSerialPort ();
        panic (msg);
        Abort ();
#endif
        /* NOTREACHED */
        return;
}

int SendQuery (struct IOExtSer *iorequest)
{
        int error;

        iorequest->IOSer.io_Command = SDCMD_QUERY;
        if (error = DoIO (iorequest)) {
                PrintFault (error, "SendQuery error");
                return -1;
        }

        return iorequest->IOSer.io_Actual;
}

void SiphonIO (struct IOExtSer *iorequest)
{
        int i;
        char buf [16];

        while ((i = SendQuery (iorequest)) > 0) {
                iorequest->IOSer.io_Command = CMD_READ;
                iorequest->IOSer.io_Data    = buf;
                iorequest->IOSer.io_Length  = i > 16 ? 16 : i;
                iorequest->IOSer.io_Flags   = IOF_QUICK;

                if (i = DoIO (iorequest))
                        PrintFault (i, "SiphonIO error");
        }
        return;
}

void SetupSerialPort (const int eofmode, const char *device, const int unit)
{
        int errno;

        if(!(NNTP_in_mp = (struct MsgPort *) CreatePort (NULL, 0)))
                KillSerialPort (0, "cannot create msgport for input");

        if (!(NNTP_out_mp = (struct MsgPort *) CreatePort (NULL, 0)))
                KillSerialPort (0, "cannot create msgport for output");

        if (!(NNTP_in_io = (struct IOExtSer *) CreateExtIO (NNTP_in_mp, sizeof (struct IOExtSer))))
                KillSerialPort (0, "cannot createExtIO() for input");

        if (!(NNTP_out_io = (struct IOExtSer *) CreateExtIO (NNTP_out_mp, sizeof (struct IOExtSer))))
                KillSerialPort (0, "cannot createExtIO() for output");

        NNTP_in_io->io_SerFlags = SERF_SHARED | SERF_XDISABLED;
        if (eofmode) {
                NNTP_in_io->io_SerFlags |= SERF_EOFMODE;
                NNTP_in_io->io_TermArray.TermArray0 = 0x1615120c;
                NNTP_in_io->io_TermArray.TermArray1 = 0x0a030000;
        }

        if (serial_error = OpenDevice (device, unit, NNTP_in_io, 0)) {
                printf ("device %s unit %ld IoErr = %ld\n", device, unit, IoErr ());
                KillSerialPort (serial_error, "OpenDevice() failed");
        }

        SendQuery (NNTP_in_io);

        NNTP_in_io->io_SerFlags = SERF_SHARED | SERF_XDISABLED;
        if (eofmode) {
                NNTP_in_io->io_SerFlags |= SERF_EOFMODE;
                NNTP_in_io->io_TermArray.TermArray0 = 0x1615120c;
                NNTP_in_io->io_TermArray.TermArray1 = 0x0a030000;
        }
        if (NNTP_in_io->io_RBufLen < 32768)
                NNTP_in_io->io_RBufLen = 32768;

        NNTP_in_io->IOSer.io_Command  = SDCMD_SETPARAMS;

        if (errno = DoIO (NNTP_in_io))
                KillSerialPort (errno, "setparams error");

        SiphonIO (NNTP_in_io);

        memcpy ((void *) NNTP_out_io, (void *) NNTP_in_io, sizeof (struct IOExtSer));
        NNTP_out_io->IOSer.io_Message.mn_ReplyPort = NNTP_out_mp;

        return;
}

int reads (struct IOExtSer *iorequest, char *buf, int len)
{
        int errno;
        int mask;
        //
        // reads:   Get input from "iorequest". We DO NOT HANG. If
        //          no bytes are available, we immediately return.
        //          This is not an error. The caller should deal
        //          with this.
        //
        //          Otherwise, we read UP TO len bytes.
        //
        //          Note that efficiency and high transfer rates will
        //          require len to be much greater than 1!!!
        //
        //          Note that if len == -1, serial.device will build
        //          the result string based on the TermArrays passed
        //          when it was opened.
        //
        if (len >= 0) {
                if ((errno = SendQuery (iorequest)) == 0)
                        return 0;
                if (len > errno)
                        len = errno;
        }

        iorequest->IOSer.io_Command = CMD_READ;
        iorequest->IOSer.io_Data    = buf;
        iorequest->IOSer.io_Length  = len;
        iorequest->IOSer.io_Flags   = IOF_QUICK;
        iorequest->IOSer.io_Actual  = 0;

        BeginIO (iorequest); // in amiga.lib

        if ((iorequest->IOSer.io_Flags & IOF_QUICK) == 0) {

                NNTP_in_ip = 1;
                mask = 1L << iorequest->IOSer.io_Message.mn_ReplyPort->mp_SigBit;
                mask = Wait (mask | SIGBREAKF_CTRL_C);

                if (mask & SIGBREAKF_CTRL_C) {
                        AbortIO (iorequest);
                        WaitIO (iorequest);
                        NNTP_in_ip = 0;

                        // PutStr ("reads: terminated outstanding IO\n");
                        return -1;
                }
                WaitIO (iorequest);
                NNTP_in_ip = 0;
        }

        if (errno = iorequest->IOSer.io_Error) {
                PrintFault (errno, "reads IO error");
                return -1;
        }

        return iorequest->IOSer.io_Actual;
}

int writes (struct IOExtSer *iorequest, char *buf, int len)
{
        int errno;
        //
        //  writes: Output 'len' bytes in 'buf' to 'iorequest'.
        //
        //          Return an error if one occurs.
        //
        //          Much simpler than reads().
        //
        iorequest->IOSer.io_Command = CMD_WRITE;
        iorequest->IOSer.io_Data    = buf;
        iorequest->IOSer.io_Length  = len;
        NNTP_out_ip = 1;

        DoIO (iorequest);
        NNTP_out_ip = 0;

        if (errno = iorequest->IOSer.io_Error) {
                PrintFault (errno, "writes: IO error");
                return -1;
        }

        return iorequest->IOSer.io_Actual;
}

#ifdef DEBUG
void DebugIoRequest (struct IOExtSer * iorequest)
{
        int mask = iorequest->io_SerFlags;

        printf ("CtlChar    0x%lx \n", iorequest->io_CtlChar);
        printf ("RBufLen    %ld   \n", iorequest->io_RBufLen);
        printf ("ExtFlags   0x%lx \n", iorequest->io_ExtFlags);
        printf ("Baud       %ld   \n", iorequest->io_Baud);
        printf ("BrkTime    %ld   \n", iorequest->io_BrkTime);
        printf ("ReadLen    %ld   \n", iorequest->io_ReadLen);
        printf ("WriteLen   %ld   \n", iorequest->io_WriteLen);
        printf ("StopBits   %ld   \n", iorequest->io_StopBits);
        printf ("SerFlags   0x%lx \n", iorequest->io_SerFlags);
        if (mask) {
                printf ("           ");
                if (mask & SERF_XDISABLED)
                        printf ("XDISABLED ");
                if (mask & SERF_EOFMODE)
                        printf ("EOFMODE ");
                if (mask & SERF_SHARED)
                        printf ("SHARED ");
                if (mask & SERF_RAD_BOOGIE)
                        printf ("RAD_BOOGIE ");
                if (mask & SERF_QUEUEDBRK)
                        printf ("QUEUEDBRK ");
                if (mask & SERF_7WIRE)
                        printf ("7WIRE ");
                if (mask & SERF_PARTY_ODD)
                        printf ("PARTY_ODD ");
                if (mask & SERF_PARTY_ON)
                        printf ("PARTY_ON");
                printf ("\n");
        }
        printf ("Status     0x%lx \n", iorequest->io_Status);
        printf ("\n");

        return;
}
#endif

int done = 0;
int ih_done = 0;

__geta4 void InputHandler (void)
{
    int ih_len;
    char ih_inbuf [256];

    struct MsgPort *myport = (struct MsgPort *) CreatePort (NULL, 0);
    struct MsgPort *saveport = NULL;

    // NO ONE touch NNTP_in_io after I've been entered, until I'm done.
    // Under pain of GURU.

    if (!myport) {
            PutStr ("InputHandler: CreatePort() failure\n");

            ih_done = 1;
            done = 3;
            return;
    }

    saveport = NNTP_in_io->IOSer.io_Message.mn_ReplyPort;
    NNTP_in_io->IOSer.io_Message.mn_ReplyPort = myport;

    while (!done) {

            ih_len = reads (NNTP_in_io, ih_inbuf, 256);

            if (ih_len <= 0)
                    continue;

            ih_len = Write (Output (), ih_inbuf, ih_len);
            if (ih_len < 1)
                    PrintFault (IoErr (), "InputHandler Write error");
            Flush (Output ());
    }

    DeletePort (myport);
    NNTP_in_io->IOSer.io_Message.mn_ReplyPort = saveport;

    ih_done = 1;
    return;
}

int main (int argc, char **argv)
{
        char *device;
        int unit;
        unsigned long pid;
        char *cmd;
        char chr;
        char main_inbuf [128];
        char outbuf [128];
        int len;
        int in_command = 0;
        int last_was_newline = 0;

        if (argc <= 1) {
                device = "serial.device";
                unit = 0;
        }
        else
                if (argc != 3) {
                        PutStr ("GRn-Term <device> <unit>\n");
                        exit (20);
                }

        device = argv [1];
        unit = atoi (argv [2]);

        SetupSerialPort (0, device, unit);

        pid = CreateNewProcTags (
                NP_Entry,       InputHandler,
                NP_Input,       Input(),
                NP_Output,      Output(),
                NP_CloseInput,  DOSFALSE,
                NP_CloseOutput, DOSFALSE,
                NP_Name,        "InputHandler",
                TAG_DONE);

        if (!pid)
                KillSerialPort (0, "CreateNewProc() failed\n");

        PutStr ("\nGRn-Term Initialized\n\n");

        cmd = outbuf;
        while (!done) {

                len = Read (Input (), main_inbuf, 1);

                if (len < 0) {
                        PrintFault (IoErr (), "CONSOLE: input error");
                        continue;
                }
                if (len == 0) {
                        // EOF on input
                        done = 1;
                        Signal ((struct Task *) pid, SIGBREAKF_CTRL_C);
                        continue;
                }

                chr = main_inbuf [0];

                if (cmd == outbuf && chr == '\\')
                        in_command = 1;
                if (!in_command)
                        if (chr == '\n')
                                writes (NNTP_out_io, "\r", 1);
                        else
                                writes (NNTP_out_io, main_inbuf, 1);

                *cmd++ = main_inbuf [0];

                if (chr == '\n') {
                        last_was_newline = 1;
                        *cmd = '\0';
                        cmd = outbuf;

                        if (!in_command)
                                continue;

                        if (strnicmp (outbuf, "\\quit", 5) == 0) {
                                done = 2;
                                Signal ((struct Task *) pid, SIGBREAKF_CTRL_C);
                        }
                        else if (strncmp (outbuf, "\\dial ", 6) == 0) {
                                char *p = outbuf + 6;

                                writes (NNTP_out_io, "\rAT\r", 4);
                                Delay (50);
                                *(p + strlen (p) - 1) = '\r';
                                writes (NNTP_out_io, p, strlen (p));
                        }
                        else if (strncmp (outbuf, "\\hangup", 7) == 0) {
                                Delay (110);
                                writes (NNTP_out_io, "+", 1);
                                Delay (5);
                                writes (NNTP_out_io, "+", 1);
                                Delay (5);
                                writes (NNTP_out_io, "+", 1);
                                Delay (150);
                                writes (NNTP_out_io, "ATH0\r\n", 6);
                                Delay (110);
                        }
#if 0
                        // FIXME: get InputHandler() off the serial port
                        else if (strncmp (outbuf, "\\grn", 4) == 0) {
                                int rslt;

                                ShutDownSerialPort ();
                                rslt = SystemTagList (outbuf + 1, TAG_DONE);
                                if (rslt == -1)
                                        PutStr ("GRn failed\n");
                                else
                                        PutStr ("GRn complete\n");
                                SetupSerialPort (0, device, unit);
                        }
#endif
                        else {
                                PutStr ("Unknown control command '");
                                PutStr (outbuf);
                                PutStr ("'\n");
                        }
                        in_command = 0;
                }
                else
                        last_was_newline = 0;
                *cmd = '\0';
        }

        len = 0;
        while (!ih_done) {
                len++;
                if (len > 10) {
                        len = 0;
                        PutStr ("Waiting on InputHandler to terminate\n");
                }
                Delay (10);
        }

        ShutDownSerialPort ();

        PutStr ("\nGRn-Term closing down\n\n");
}
--
  //   Michael B. Smith
\X/    mbs@adastra.cvl.va.us  -or-  uunet.uu.net!virginia.edu!adastra!mbs