/*
** $Source: HOG:Other/networking/sana2/src/slip/RCS/device_funcs.c,v $
** $State: Exp $
** $Revision: 37.5 $
** $Date: 92/12/16 13:45:22 $
** $Author: bj $
**
** Amiga SANA-II Example SLIP device driver.
**
** (C) Copyright 1992 Commodore-Amiga, Inc.
**
*/

#include "slip_device.h"

#include <dos/dostags.h>
#include <dos/rdargs.h>
#include <intuition/intuition.h>

#include <clib/exec_protos.h>
#include <clib/dos_protos.h>
#include <clib/utility_protos.h>
#include <clib/timer_protos.h>
#include <clib/alib_stdio_protos.h>
#include <clib/intuition_protos.h>

#include <pragmas/exec_pragmas.h>
#include <pragmas/dos_pragmas.h>
#include <pragmas/utility_pragmas.h>
#include <pragmas/timer_pragmas.h>
#include <pragmas/intuition_pragmas.h>

#include <string.h>
#include "device_protos.h"

/*
** External variables and functions
**
*/

extern struct Library *ExtDeviceBase;
extern VOID (*DevProcEntry)(VOID);
extern ULONG IPToNum(STRPTR ipstr);
extern VOID kprintf(STRPTR fmt, ...);

/*
** Device Open vector
**
** a1 - SANA2 IO Request
** a6 - Pointer to our device base
** d0 - Unit number
** d1 - Flags
**
*/

ULONG ASM DevOpen(REG(a1) struct IOSana2Req *ios2,
		  REG(d0) ULONG s2unit,
		  REG(d1) ULONG s2flags)
{
    struct SLIPDevice *SLIPDevice = SLIPBase;
    struct SLIPDevUnit *sdu;
    struct TagItem *bufftag;
    struct Library *UtilityBase;
    struct BufferManagement *bm;
    ULONG returncode;
    BOOL status = FALSE;

    /* Make sure our open remains single-threaded. */
    ObtainSemaphore(&SLIPDevice->sd_Lock);	/* Enforce single threading since we may need to
    						   Wait() when starting up the Unit process */

    SLIPDevice->sd_Device.lib_OpenCnt++;	/* So we won't expunge ourselves... */

    if(s2unit < SD_MAXUNITS)	/* Legal Unit? */
    {
    	if(sdu = InitSLIPUnit(s2unit))	/* Initialize the Unit */
    	{
	    if(UtilityBase = OpenLibrary("utility.library",37L)) /* For Tag functions */
	    {
	    	/* Allocate a structure to store the pointers to the callback routines. */

	    	if(bm = AllocMem(sizeof(struct BufferManagement),MEMF_CLEAR|MEMF_PUBLIC))
	    	{
	    	    /* Note: I don't complain if I can't find pointers to the callback routines.
	    	    	     This is because there are some programs that may need to open me, but
	    	    	     will never use any device commands that require the callbacks. */

	    	    if(bufftag = FindTagItem(S2_CopyToBuff, (struct TagList *)ios2->ios2_BufferManagement))
	    	    {
	    	        bm->bm_CopyToBuffer = (SANA2_CTB) bufftag->ti_Data;
	    	    }

	    	    if(bufftag = FindTagItem(S2_CopyFromBuff, (struct TagList *)ios2->ios2_BufferManagement))
	    	    {
	    	    	bm->bm_CopyFromBuffer = (SANA2_CFB) bufftag->ti_Data;
	    	    }

	    	    AddTail((struct List *)&sdu->sdu_BuffMgmt,(struct Node *)bm);

		    /* Everything went okay. */
	    	    status = TRUE;
		    returncode = 0;
	    	    SLIPDevice->sd_Device.lib_OpenCnt++;
	    	    SLIPDevice->sd_Device.lib_Flags &=~LIBF_DELEXP;
	    	    sdu->sdu_Unit.unit_OpenCnt++;

		    /* Fix up the initial io request */
                    ios2->ios2_BufferManagement = (VOID *)bm;
	    	    ios2->ios2_Req.io_Error = 0;
	    	    ios2->ios2_Req.io_Message.mn_Node.ln_Type = NT_REPLYMSG;
	    	    ios2->ios2_Req.io_Unit = sdu;
	    	    ios2->ios2_Req.io_Device = SLIPDevice;
	    	}
	    	CloseLibrary(UtilityBase);
	    }
	}
    }
    /* See if something went wrong. */
    if(!status)
    {
    	ios2->ios2_Req.io_Error = IOERR_OPENFAIL;
    	ios2->ios2_Req.io_Unit = (struct Unit *) -1;
    	ios2->ios2_Req.io_Device = (struct Device *) -1;
    	returncode = IOERR_OPENFAIL;
    }
    SLIPDevice->sd_Device.lib_OpenCnt--;
    ReleaseSemaphore(&SLIPDevice->sd_Lock);

    return(returncode);
}

/*
** Device Close vector.
**
** a1 - IOReq
** a6 - Device Pointer
**
*/

BPTR ASM DevClose(REG(a1) struct IOSana2Req *ios2)
{
    struct SLIPDevice *SLIPDevice = SLIPBase;
    struct SLIPDevUnit *sdu;
    BPTR seglist = 0L;

    ObtainSemaphore(&SLIPDevice->sd_Lock);

    sdu = (struct SLIPDevUnit *)ios2->ios2_Req.io_Unit;

    /* Trash the io_Device and io_Unit fields so that any attempt to use this
       request will die immediatly. */

    ios2->ios2_Req.io_Device = (struct Device *) -1;
    ios2->ios2_Req.io_Unit = (struct Unit *) -1;

    /* I always shut the unit process down if the open count drops to zero.
       That way, if I need to expunge, I never have to Wait(). */

    sdu->sdu_Unit.unit_OpenCnt--;
    if(!sdu->sdu_Unit.unit_OpenCnt)
    {
    	ExpungeUnit(sdu);
    }

    SLIPDevice->sd_Device.lib_OpenCnt--;

    ReleaseSemaphore(&SLIPDevice->sd_Lock);

    /* Check to see if we've been asked to expunge. */
    if(SLIPDevice->sd_Device.lib_Flags & LIBF_DELEXP)
    	seglist = DevExpunge();

    return(seglist);
}


/*
** Device Expunge vector
**
** a6 - Device base
**
** Note: You may NEVER EVER Wait() in expunge. Period.
** 	 Don't even *think* about it.
*/

BPTR ASM DevExpunge(VOID)
{
    struct SLIPDevice *SLIPDevice = SLIPBase;
    BPTR seglist;
    ULONG devbase;
    LONG devbasesize;

    if(SLIPDevice->sd_Device.lib_OpenCnt)
    {
    	/* Sorry, we're busy.  We'll expunge later on
    	   if we can. */
    	SLIPDevice->sd_Device.lib_Flags |= LIBF_DELEXP;
    	seglist = (BPTR)0L;
    }
    else
    {
    	/* Free up our library base and function table after
    	   removing ourselves from the library list. */
    	Remove((struct Node *)SLIPDevice);
    	seglist = SLIPDevice->sd_SegList;

    	devbase = (ULONG) SLIPDevice;

    	devbasesize = (ULONG)SLIPDevice->sd_Device.lib_NegSize;
    	devbase = devbase - devbasesize;

    	devbasesize += (ULONG)SLIPDevice->sd_Device.lib_PosSize;

    	FreeMem((APTR)devbase,devbasesize);
    }
    return(seglist);
}

/*
** InitSLIPUnit
**
** Initialize (if needed) a new SLIP device Unit and process.
**
*/

struct SLIPDevUnit *InitSLIPUnit(ULONG s2unit)
{
    struct SLIPDevice *SLIPDevice = SLIPBase;
    struct SLIPDevUnit *sdu;
    struct TagItem NPTags[]={NP_Entry, 0, NP_Name, 0, NP_Priority, SLIP_PRI, TAG_DONE, 0};
    struct MsgPort *replyport;

    /* Check to see if the Unit is already up and running.  If
       it is, just drop through.  If not, try to start it up. */

    if(!SLIPDevice->sd_Units[s2unit])
    {
    	/* Open up dos.library */
    	if(SLIPDevice->sd_DOSBase = OpenLibrary("dos.library",37L))
    	{
    	    /* Allocate a new Unit structure */
            if(sdu = AllocMem(sizeof(struct SLIPDevUnit), MEMF_CLEAR|MEMF_PUBLIC))
            {
            	/* Do some initialization on the Unit structure */

            	NewList(&sdu->sdu_Unit.unit_MsgPort.mp_MsgList);
                sdu->sdu_Unit.unit_MsgPort.mp_Node.ln_Type = NT_MSGPORT;
                sdu->sdu_Unit.unit_MsgPort.mp_Flags = PA_IGNORE;
                sdu->sdu_Unit.unit_MsgPort.mp_Node.ln_Name = "slip.device";

                sdu->sdu_UnitNum = s2unit;
                sdu->sdu_Device = (struct Device *) SLIPDevice;

		/* Try to read in our configuration file */
                if(ReadConfig(sdu))
                {
                    /* Start up the unit process */
                    if(replyport = CreateMsgPort())
                    {
                        SLIPDevice->sd_Startup.Msg.mn_ReplyPort = replyport;
                        SLIPDevice->sd_Startup.Device = (struct Device *) SLIPDevice;
                        SLIPDevice->sd_Startup.Unit = (struct Unit *)sdu;

                        NPTags[0].ti_Data = (ULONG) &DevProcEntry;       /* Assembly entry point for the unit process. */
                        NPTags[1].ti_Data = (ULONG) "slip.device";      /* Process name */

			ExtDeviceBase = (struct Library *)SLIPDevice;

                        if(sdu->sdu_Proc = CreateNewProc(NPTags))
                        {
                            PutMsg(&sdu->sdu_Proc->pr_MsgPort,(struct Message *)&SLIPDevice->sd_Startup);
                            WaitPort(replyport);
                            GetMsg(replyport);
                        }
                        DeleteMsgPort(replyport);
                    }
                }

                if(!sdu->sdu_Proc)
                {
                    /* The Unit process couldn't start for some reason, so free the Unit structure. */
                    FreeMem(sdu,sizeof(struct SLIPDevUnit));
                }
                else
                {
                    /* Set up the Unit structure pointer in the device base */
                    SLIPDevice->sd_Units[s2unit] = (struct Unit *)sdu;
                }
            }
            CloseLibrary(SLIPDevice->sd_DOSBase);
        }
    }
    return((struct SLIPDevUnit *)SLIPDevice->sd_Units[s2unit]);
}

/*
**
** ExpungeUnit
**
** Tells a unit process to go away...
**
** This function is called from the DevClose routine when the open count for a
** unit reaches zero.  This routine signals the unit process to exit and then
** waits for the unit process to acknowledge.  The unit structure is then
** freed.
*/

VOID ExpungeUnit(struct SLIPDevUnit *sdu)
{
    struct SLIPDevice *SLIPDevice = SLIPBase;
    struct Task *unittask;

    unittask = (struct Task *)sdu->sdu_Proc;

    sdu->sdu_Proc = (struct Process *)FindTask(0L);

    Signal(unittask,SIGBREAKF_CTRL_F);
    Wait(SIGBREAKF_CTRL_F);

    SLIPDevice->sd_Units[sdu->sdu_UnitNum] = NULL;

    FreeMem(sdu, sizeof(struct SLIPDevUnit));
}

/*
**
** ReadConfig
**
** Attempt to read in and parse the driver's configuration file.
**
** The files are named by ENV:SANA2/slipX.config where X is the decimal
** representation of the device's unit number.
**
*/

BOOL ReadConfig(struct SLIPDevUnit *sdu)
{
    UBYTE *linebuff,buff[40];
    STRPTR termchar;
    struct RDArgs *rdargs;
    BPTR ConfigFile;
    LONG args[6];
    BOOL status = FALSE;
    ULONG linenum=0;
    UWORD i;

    /* Create the name of our config file.. */
    sprintf(buff,"ENV:SANA2/slip%ld.config",(ULONG)sdu->sdu_UnitNum);

    /* ...and open it. */
    if(ConfigFile = Open(buff,MODE_OLDFILE))
    {
    	/* Here, I use ReadArgs() to do the file parsing for me. */

    	if(linebuff = AllocMem(256,MEMF_CLEAR|MEMF_PUBLIC))
    	{
    	    if(rdargs = AllocDosObject(DOS_RDARGS, NULL))
    	    {
    	    	while(FGets(ConfigFile, linebuff, 255))
    	    	{
                    linenum++;
    	    	    if(linebuff[0] == '#') /* Skip comment lines */
    	    	        continue;

    	    	    rdargs->RDA_Source.CS_Buffer = linebuff;
    	    	    rdargs->RDA_Source.CS_Length = 256;
    	    	    rdargs->RDA_Source.CS_CurChr = 0;

    	    	    /* ReadArgs() requires that the line be null-terminated
    	    	       or funny things happen. */

    	    	    termchar = (STRPTR) linebuff + strlen(linebuff);
    	    	    *termchar = '\n';
    	    	    termchar++;
    	    	    *termchar = 0;

    	    	    for(i = 0; i< 6; i++)
    	    	    	args[i]=0;

		    /* Parse the line...*/

    	    	    if(ReadArgs("SERNAME/A,SERUNIT/A/N,SERBAUD/A/N,IPSTR/A,CD=CARRIERDETECT/S,7WIRE/S",args,rdargs))
    	    	    {
			strcpy(sdu->sdu_SerDevName,(STRPTR)args[0]);
			sdu->sdu_SerUnitNum = *((ULONG *)args[1]);
			sdu->sdu_BaudRate = *((ULONG *)args[2]);
			sdu->sdu_StAddr = IPToNum((STRPTR)args[3]);

			if(args[4])
			    sdu->sdu_State |= SLIPUF_CD;

			if(args[5])
			    sdu->sdu_State |= SLIPUF_7WIRE;

			status = TRUE;
			FreeArgs(rdargs);
			break;
		    }
		    else
                    {
                        struct Library *IntuitionBase;
                        struct EasyStruct es;
                        if(IntuitionBase = OpenLibrary("intuition.library",37L))
                        {
                            es.es_StructSize=sizeof(struct EasyStruct);
                            es.es_Flags=0;
                            es.es_Title="Slip.device";
                            es.es_TextFormat="Error in configuration file on line %ld.";
                            es.es_GadgetFormat="Okay";
                            EasyRequestArgs(NULL, &es, 0, &linenum);
                            CloseLibrary(IntuitionBase);
                        }
                        break;
                    }

		}
		FreeDosObject(DOS_RDARGS,rdargs);
	    }
	    FreeMem(linebuff, 256);
	}
	Close(ConfigFile);
    }
    return(status);
}                 


/*
**
** BeginIO
**
** This is the dispatch point for the driver's incoming IORequests.
**
*/

#define SLIP_IMMEDIATES 0L

VOID ASM DevBeginIO(REG(a1) struct IOSana2Req *ios2)
{
    ios2->ios2_Req.io_Message.mn_Node.ln_Type = NT_MESSAGE;

    if(ios2->ios2_Req.io_Command < S2_END)
    {
    	if((1L << ios2->ios2_Req.io_Command) & SLIP_IMMEDIATES)
    	{
    	    PerformIO(ios2);
    	}
    	else
    	{
    	    ios2->ios2_Req.io_Flags &= ~IOF_QUICK;
    	    PutMsg((struct MsgPort *)ios2->ios2_Req.io_Unit,(struct Message *)ios2);
    	}
    }
    else
    {
        ios2->ios2_Req.io_Error = IOERR_NOCMD;
        TermIO(ios2);
    }
}

/*
** This routine is used to dispatch an IO request either from BeginIO
** or from the Unit process.
*/
VOID PerformIO(struct IOSana2Req *ios2)
{
    struct SLIPDevUnit *sdu;

    sdu = (struct SLIPDevUnit *)ios2->ios2_Req.io_Unit;

    ios2->ios2_Req.io_Error = 0;

    switch(ios2->ios2_Req.io_Command)
    {
        case CMD_READ:              ReadPacket(sdu,ios2);
                                    break;

        case CMD_WRITE:             WritePacket(sdu,ios2);
                                    break;

        case S2_DEVICEQUERY:        DeviceQuery(sdu,ios2);
                                    break;

        case S2_GETSTATIONADDRESS:  GetStationAddress(sdu,ios2);
                                    break;

        case S2_CONFIGINTERFACE:    ConfigInterface(sdu,ios2);
                                    break;

        case S2_ADDMULTICASTADDRESS:
        case S2_DELMULTICASTADDRESS:
        case S2_MULTICAST:          ios2->ios2_Req.io_Error = S2ERR_NOT_SUPPORTED;
                                    ios2->ios2_WireError = S2WERR_GENERIC_ERROR;
                                    TermIO(ios2);
                                    break;

        case S2_BROADCAST:          WritePacket(sdu,ios2);
                                    break;

        case S2_TRACKTYPE:          TrackType(sdu,ios2);
                                    break;

        case S2_UNTRACKTYPE:        UnTrackType(sdu,ios2);
                                    break;

        case S2_GETTYPESTATS:       GetTypeStats(sdu,ios2);
                                    break;

        case S2_GETSPECIALSTATS:    GetSpecialStats(sdu,ios2);
                                    break;

        case S2_GETGLOBALSTATS:     GetGlobalStats(sdu,ios2);
                                    break;

        case S2_ONEVENT:            OnEvent(sdu,ios2);
                                    break;

        case S2_READORPHAN:         ReadOrphan(sdu,ios2);
                                    break;

        case S2_ONLINE:             Online(sdu,ios2);
                                    break;

        case S2_OFFLINE:            Offline(sdu,ios2);
                                    break;

        default:                    ios2->ios2_Req.io_Error = IOERR_NOCMD;
                                    TermIO(ios2);
                                    break;
    }
}

/*
** This function returns any device specific statistics that
** we may have.  Unfortunately, we don't have and SLIP specific
** statistics.
*/
VOID GetSpecialStats(struct SLIPDevUnit *sdu, struct IOSana2Req *ios2)
{
    struct Sana2SpecialStatHeader *stats;

    stats = (struct Sana2SpecialStatHeader *)ios2->ios2_StatData;

    stats->RecordCountSupplied = 0;
    TermIO(ios2);
}

/*
** This function returns the global statistics for the
** slip device.
*/
VOID GetGlobalStats(struct SLIPDevUnit *sdu, struct IOSana2Req *ios2)
{
    struct Sana2DeviceStats *stats;

    stats = (struct Sana2DeviceStats *)ios2->ios2_StatData;

    stats->PacketsReceived	= sdu->sdu_Stats.PacketsReceived;
    stats->PacketsSent		= sdu->sdu_Stats.PacketsSent;
    stats->BadData		= sdu->sdu_Stats.BadData;
    stats->Overruns		= sdu->sdu_Stats.Overruns;
    stats->UnknownTypesReceived = sdu->sdu_Stats.UnknownTypesReceived;
    stats->Reconfigurations	= sdu->sdu_Stats.Reconfigurations;
    stats->LastStart.tv_secs	= sdu->sdu_Stats.LastStart.tv_secs;
    stats->LastStart.tv_micro	= sdu->sdu_Stats.LastStart.tv_secs;

    ios2->ios2_Req.io_Error = S2ERR_NOT_SUPPORTED;
    ios2->ios2_WireError = S2WERR_GENERIC_ERROR;
    TermIO(ios2);
}

/*
** This function returns statistics for a specific
** type of packet that is being tracked.  Unfortunately,
** SLIP can't differentiate between different packet
** types, which makes packet type tracking essentially
** useless as the numbers would essentially be the same
** as those found via S2_GETGLOBALSTATS.
**
** Just to be thourough, I have arbitrarily picked
** the packet type for SLIP IP packets to be 2048, the
** same as tha used for Ethernet.  This will at least
** allow you to track IP packets.
*/
VOID GetTypeStats(struct SLIPDevUnit *sdu, struct IOSana2Req *ios2)
{
    struct Sana2PacketTypeStats *stats;
    struct SuperS2PTStats *sstats;

    ObtainSemaphore(&sdu->sdu_ListLock);

    stats = (struct Sana2PacketTypeStats *)ios2->ios2_StatData;
    sstats = (struct SuperS2PTStats *)sdu->sdu_Track.mlh_Head;

    while(sstats->ss_Node.mln_Succ)
    {
    	if(ios2->ios2_PacketType == sstats->ss_PType)
    	{
    	    stats->PacketsSent = sstats->ss_Stats.PacketsSent;
    	    stats->PacketsReceived = sstats->ss_Stats.PacketsReceived;
    	    stats->BytesSent = sstats->ss_Stats.BytesSent;
    	    stats->BytesReceived = sstats->ss_Stats.BytesReceived;
    	    stats->PacketsDropped = sstats->ss_Stats.PacketsDropped;
	    break;
	}
	sstats = (struct SuperS2PTStats *)sstats->ss_Node.mln_Succ;
    }
    ReleaseSemaphore(&sdu->sdu_ListLock);
    if(!sstats->ss_Node.mln_Succ)
    {
	ios2->ios2_Req.io_Error = S2ERR_BAD_STATE;
	ios2->ios2_WireError = S2WERR_NOT_TRACKED;
    }
    TermIO(ios2);
}

/*
** This function adds a packet type to the list
** of those that are being tracked.
*/
VOID TrackType(struct SLIPDevUnit *sdu, struct IOSana2Req *ios2)
{
    struct SuperS2PTStats *stats;

    ObtainSemaphore(&sdu->sdu_ListLock);

    stats = (struct SuperS2PTStats *)sdu->sdu_Track.mlh_Head;

    while(stats->ss_Node.mln_Succ)
    {
    	if(ios2->ios2_PacketType == stats->ss_PType)
    	{
	    ios2->ios2_Req.io_Error = S2ERR_BAD_STATE;
	    ios2->ios2_WireError = S2WERR_ALREADY_TRACKED;
	}
	stats = (struct SuperS2PTStats *)stats->ss_Node.mln_Succ;
    }
    if(!stats->ss_Node.mln_Succ)
    {
    	if(stats = AllocMem(sizeof(struct SuperS2PTStats),MEMF_CLEAR|MEMF_PUBLIC))
    	{
    	    if(ios2->ios2_PacketType == 2048)
    	    	sdu->sdu_IPTrack = stats;
    	    AddTail((struct List *)&sdu->sdu_Track,(struct Node *)stats);
    	}
    }
    ReleaseSemaphore(&sdu->sdu_ListLock);

    TermIO(ios2);
}

/*
** This function removes a packet type from the
** list of those that are being tracked.
*/
VOID UnTrackType(struct SLIPDevUnit *sdu, struct IOSana2Req *ios2)
{
    struct SuperS2PTStats *stats;
    struct SuperS2PTStats *stats_next;

    ObtainSemaphore(&sdu->sdu_ListLock);

    stats = (struct SuperS2PTStats *)sdu->sdu_Track.mlh_Head;

    while(stats->ss_Node.mln_Succ)
    {
    	stats_next = (struct SuperS2PTStats *)stats->ss_Node.mln_Succ;
    	if(ios2->ios2_PacketType == stats->ss_PType)
    	{
    	    if(ios2->ios2_PacketType == 2048)
    	        sdu->sdu_IPTrack = NULL;
    	    Remove((struct Node *)stats);
    	    FreeMem(stats,sizeof(struct SuperS2PTStats));
    	    stats = NULL;
	    break;
	}
	stats = stats_next;
    }
    if(stats)
    {
	ios2->ios2_Req.io_Error	= S2ERR_BAD_STATE;
	ios2->ios2_WireError = S2WERR_NOT_TRACKED;
    }
    ReleaseSemaphore(&sdu->sdu_ListLock);

    TermIO(ios2);
}

/*
** This function is called whenever we receive a packet
** from the serial device driver.
**
*/
VOID PacketReceived(struct SLIPDevUnit *sdu, ULONG length)
{
    sdu->sdu_Stats.PacketsReceived++;

    if(sdu->sdu_IPTrack)
    {
    	sdu->sdu_IPTrack->ss_Stats.PacketsReceived++;
    	sdu->sdu_IPTrack->ss_Stats.BytesReceived+=length;
    }
}

/*
** This function is called whenever a packet is
** sent to the serial device driver.
*/
VOID PacketSent(struct SLIPDevUnit *sdu, ULONG length)
{
    sdu->sdu_Stats.PacketsSent++;

    if(sdu->sdu_IPTrack)
    {
    	sdu->sdu_IPTrack->ss_Stats.PacketsSent++;
    	sdu->sdu_IPTrack->ss_Stats.BytesSent+=length;
    }
}

/*
** This function is called whenever a packet that
** is too large is received.
*/
VOID PacketOverrun(struct SLIPDevUnit *sdu)
{
    sdu->sdu_Stats.Overruns++;
    DoEvent(sdu, S2EVENT_RX);
}

/*
** This function is called whenever a packet with
** garbage data is encountered.
*/
VOID ReceivedGarbage(struct SLIPDevUnit *sdu)
{
    sdu->sdu_Stats.BadData++;
    DoEvent(sdu, S2EVENT_RX);
}

/*
** This function is called whenever a packet
** is dropped by the SLIP driver.
*/
VOID PacketDropped(struct SLIPDevUnit *sdu)
{
    if(sdu->sdu_IPTrack)
    {
    	sdu->sdu_IPTrack->ss_Stats.PacketsDropped++;
    }
}

/*
** This function is used to return an IO request
** back to the sender.
*/
VOID TermIO(struct IOSana2Req *ios2)
{
    if(!(ios2->ios2_Req.io_Flags & IOF_QUICK))
    	ReplyMsg((struct Message *)ios2);
}

/*
** The device AbortIO() entry point.
**
** A1 - The IO request to be aborted.
** A3 - The unit pointer
** A6 - The device base.
*/
ULONG ASM DevAbortIO(REG(a1) struct IOSana2Req *ios2, REG(a3) struct SLIPDevUnit *sdu)
{
    ULONG result = 0L;

    ObtainSemaphore(&sdu->sdu_ListLock);
    if(ios2->ios2_Req.io_Message.mn_Node.ln_Type != NT_REPLYMSG)
    {
    	switch(ios2->ios2_Req.io_Command)
    	{
    		case CMD_READ:	result=AbortReq(&sdu->sdu_Rx,ios2);
    				break;

    		case CMD_WRITE: result=AbortReq(&sdu->sdu_Tx,ios2);
    				break;

    		case S2_READORPHAN:	result=AbortReq(&sdu->sdu_RxOrph,ios2);
    					break;

    		case S2_ONEVENT:	result=AbortReq(&sdu->sdu_Events,ios2);
    					break;

    		default:		result=IOERR_NOCMD;
    					break;
	}
    }
    ReleaseSemaphore(&sdu->sdu_ListLock);
    return(result);
}

/*
** This funcion is used to locate an IO request in a linked
** list and abort it if found.
*/
ULONG AbortReq(struct MinList *minlist, struct IOSana2Req *ios2)
{
    struct Node *node, *next;
    ULONG result=IOERR_NOCMD;

    node = (struct Node *)minlist->mlh_Head;

    while(node->ln_Succ)
    {
    	next = node->ln_Succ;

    	if(node == (struct Node *)ios2)
    	{
    	    Remove((struct Node *)ios2);
    	    ios2->ios2_Req.io_Error = IOERR_ABORTED;
    	    TermIO(ios2);
	    result = 0;
	}
	node = next;
    }
    return(result);
}

/*
** This function handles S2_CONFIGINTERFACE commands.
*/
VOID ConfigInterface(struct SLIPDevUnit *sdu, struct IOSana2Req *ios2)
{

    /* Note: we may only be configured once. */
    if(!(sdu->sdu_State & SLIPUF_CONFIG))
    {
        if(OpenSerial(sdu))
        {
            CopyMem(&sdu->sdu_StAddr,&ios2->ios2_SrcAddr,4);
            sdu->sdu_State |= SLIPUF_CONFIG;
        }
    }
    else
    {
    	/* Sorry, we're already configured. */
        ios2->ios2_Req.io_Error = S2ERR_BAD_STATE;
        ios2->ios2_WireError = S2WERR_IS_CONFIGURED;
    }
    TermIO(ios2);
}

/*
** This function handles S2_GETSTATIONADDRESS commands.
**
** We don't really have a hardware address, so we will
** just clear the source address field.
*/

VOID GetStationAddress(struct SLIPDevUnit *sdu, struct IOSana2Req *ios2)
{
    UBYTE i;

    for(i=0; i< SANA2_MAX_ADDR_BYTES; i++)
    	ios2->ios2_SrcAddr[i]=0;

    TermIO(ios2);
}

/*
** This function handles S2_DEVICEQUERY comands.
*/
VOID DeviceQuery(struct SLIPDevUnit *sdu, struct IOSana2Req *ios2)
{
    struct Sana2DeviceQuery *sdq;

    sdq = (struct Sana2DeviceQuery *)ios2->ios2_StatData;

    sdq->AddrFieldSize = 32;			/* 32-bit IP address */
    sdq->MTU = SLIP_MTU;			/* 1006 byte max */
    sdq->BPS = sdu->sdu_BaudRate;
    sdq->HardwareType = S2WireType_SLIP;

    sdq->SizeSupplied = 14;
    TermIO(ios2);
}

/*
** This function is used for handling CMD_WRITE
** commands.
*/
VOID WritePacket(struct SLIPDevUnit *sdu, struct IOSana2Req *ios2)
{

    /* Make sure that we are online. */
    if(sdu->sdu_State & SLIPUF_ONLINE)
    {
	/* Make sure it's a legal length. */
    	if(ios2->ios2_DataLength <= SLIP_MTU)
    	{
    	    /* See if our serial CMD_WRITE command is busy.  If it's not, send
    	       the IO request to SendPacket. */
    	    if(CheckIO((struct IORequest *)sdu->sdu_SerTx))
    	    {
    	    	WaitIO((struct IORequest *)sdu->sdu_SerTx);
    	    	SendPacket(sdu, ios2);
    	    }
    	    else
    	    {
    	    	/* We'll have to queue the packet for later...*/
    	    	ios2->ios2_Req.io_Flags &= ~IOF_QUICK;
    	    	ObtainSemaphore(&sdu->sdu_ListLock);
    	    	AddTail((struct List *)&sdu->sdu_Tx,(struct Node *)ios2);
    	    	ReleaseSemaphore(&sdu->sdu_ListLock);
    	    }
    	}
    	else
    	{
    	    /* Sorry, the packet is too long! */
    	    ios2->ios2_Req.io_Error = S2ERR_MTU_EXCEEDED;
    	    ios2->ios2_WireError = S2WERR_GENERIC_ERROR;
    	    TermIO(ios2);
    	    DoEvent(sdu,S2EVENT_TX);
    	}
    }
    else
    {
    	/* Sorry, we're offline */
    	ios2->ios2_Req.io_Error = S2ERR_OUTOFSERVICE;
    	ios2->ios2_WireError = S2WERR_UNIT_OFFLINE;
    	TermIO(ios2);
    }
}

VOID SendPacket(struct SLIPDevUnit *sdu, struct IOSana2Req *ios2)
{
    struct IOExtSer *ioser;
    struct BufferManagement *bm;
    ULONG framelength;

    bm =(struct BufferManagement *) ios2->ios2_BufferManagement;

    /* Copy the data out of the packet into our temporary buffer. */
    if((*bm->bm_CopyFromBuffer)(sdu->sdu_TxBuff,ios2->ios2_Data,ios2->ios2_DataLength))
    {
    	PacketSent(sdu,ios2->ios2_DataLength);

	/* Encode the packet in SLIP format */
    	framelength=EncodeSLIP(sdu->sdu_TxBuff,sdu->sdu_TxSLIP,ios2->ios2_DataLength);

    	ioser = sdu->sdu_SerTx;
    	ioser->IOSer.io_Data = sdu->sdu_TxSLIP;
    	ioser->IOSer.io_Length = framelength;
    	ioser->IOSer.io_Command = CMD_WRITE;
    	ioser->IOSer.io_Error = 0;
    	ioser->IOSer.io_Message.mn_Node.ln_Type = 0;

    	/* Send the packet to the serial device driver */
    	SendIO((struct IORequest *)ioser);
    }
    else
    {
    	/* Something went wrong...*/
    	ios2->ios2_Req.io_Error = S2ERR_NO_RESOURCES;
    	ios2->ios2_WireError = S2WERR_BUFF_ERROR;
    	DoEvent(sdu,S2EVENT_BUFF);
    }
    TermIO(ios2);
}

/*
** This routine encodes a packet in SLIP format.
**
** The format is quite simple.
**
** SLIP Encoding:
**
** SLIP_ESC     -> SLIP_ESC SLIP_ESC_ESC
** SLIP_END     -> SLIP_ESC SLIP_ESC_END
**
** The packet is preceded and terminated with a SLIP_END as prescribed by
** rfc 1055.
**
*/
ULONG EncodeSLIP(UBYTE *source, UBYTE *dest, ULONG length)
{
    UBYTE ch;
    UBYTE *current;

    current = dest;

    *current = SLIP_END;
    current++;

    while(length--)
    {
    	ch = *source;
    	source++;

    	if(ch == SLIP_ESC)
    	{
    	    *current = SLIP_ESC;
    	    current++;
    	    ch = SLIP_ESC_ESC;
    	}
    	else if(ch == SLIP_END)
    	{
    	    *current = SLIP_ESC;
    	    current ++;
    	    ch = SLIP_ESC_END;
    	}
	*current = ch;
	current++;
    }
    *current = SLIP_END;
    current++;

    return((ULONG)(current - dest));
}

/*
** This routine handles CMD_READ commands.  We
** always queue these unless we're offline.
*/
VOID ReadPacket(struct SLIPDevUnit *sdu, struct IOSana2Req *ios2)
{

    if(sdu->sdu_State & SLIPUF_ONLINE)
    {
    	/* Queue it... */
    	ObtainSemaphore(&sdu->sdu_ListLock);
    	AddTail((struct List *)&sdu->sdu_Rx,(struct Node *)ios2);
    	ReleaseSemaphore(&sdu->sdu_ListLock);
    }
    else
    {
    	/* Sorry, we're offline */
    	ios2->ios2_Req.io_Error = S2ERR_OUTOFSERVICE;
    	ios2->ios2_WireError = S2WERR_UNIT_OFFLINE;
    	TermIO(ios2);
    }
}

/*
** This routine handles CMD_READORPHAN commands.  We
** always queue these unless we're offline.
**
** Note: These IO requests will never get satisfied.
**       Since SLIP doesn't contain packet type information,
**	 there is no way to identify what type of packets
**	 are being received or sent.  Thus, we simply ignore
**	 the packet type and *assume* that the packets are
**	 IP packets.
*/
VOID ReadOrphan(struct SLIPDevUnit *sdu, struct IOSana2Req *ios2)
{

    if(sdu->sdu_State & SLIPUF_ONLINE)
    {
    	/* Queue it...*/
    	ObtainSemaphore(&sdu->sdu_ListLock);
    	AddTail((struct List *)&sdu->sdu_RxOrph,(struct Node *)ios2);
    	ReleaseSemaphore(&sdu->sdu_ListLock);
    }
    else
    {
    	/* Sorry, we're offline */
    	ios2->ios2_Req.io_Error = S2ERR_OUTOFSERVICE;
    	ios2->ios2_WireError = S2WERR_UNIT_OFFLINE;
    	TermIO(ios2);
    }
}

/*
** This routine initializes our IO requests and buffers
** for serial i/o and SLIP encoding/decoding.
*/
BOOL InitSerial(struct SLIPDevUnit *sdu)
{
    ULONG *clr;
    BOOL status = FALSE;

    for(clr = (ULONG *) &sdu->sdu_SerRx; clr <= (ULONG *) &sdu->sdu_TxSLIP; clr++)
    	*clr = 0L;

    if(sdu->sdu_TxPort = CreateMsgPort())
    {
    	if(sdu->sdu_SerTx = CreateIORequest(sdu->sdu_TxPort,sizeof(struct IOExtSer)))
    	{
    	    if(sdu->sdu_RxPort = CreateMsgPort())
    	    {
    	    	if(sdu->sdu_SerRx = CreateIORequest(sdu->sdu_RxPort,sizeof(struct IOExtSer)))
    	    	{
    	    	    if(sdu->sdu_TxBuff = AllocMem((SLIP_MTU + 128) * 2,MEMF_CLEAR|MEMF_PUBLIC))
    	    	    {
    	    	    	sdu->sdu_RxBuff = sdu->sdu_TxBuff + SLIP_MTU + 128;
    	    	    	sdu->sdu_RxBuffPtr = sdu->sdu_RxBuff;

    	    	    	if(sdu->sdu_TxSLIP = AllocMem((SLIP_MTU*2 + 128) * 2,MEMF_CLEAR|MEMF_PUBLIC))
    	    	    	{
    	    	    	    sdu->sdu_RxSLIP = sdu->sdu_TxSLIP + SLIP_MTU*2 + 128;
    	    	    	    {
    	    	    	    	status = TRUE;
    	    	    	    }
    	    	    	}
    	    	    }
    	    	}
    	    }
    	}
    }
    if(!status)
    	DeinitSerial(sdu);
    return(status);
}

/*
** This routine cleans up our serial i/o requests
** and misc. buffers.
*/
VOID DeinitSerial(struct SLIPDevUnit *sdu)
{
    if(sdu->sdu_SerTx)
    	DeleteIORequest(sdu->sdu_SerTx);

    if(sdu->sdu_TxPort)
    	DeleteMsgPort(sdu->sdu_TxPort);

    if(sdu->sdu_SerRx)
    	DeleteIORequest(sdu->sdu_SerRx);

    if(sdu->sdu_RxPort)
    	DeleteMsgPort(sdu->sdu_RxPort);

    if(sdu->sdu_TxBuff)
    	FreeMem(sdu->sdu_TxBuff,(SLIP_MTU + 128) * 2);

    if(sdu->sdu_TxSLIP)
    	FreeMem(sdu->sdu_TxSLIP,(SLIP_MTU*2 + 128) * 2);
}

/*
** This routine handles S2_ONEVNET commands.
*/
VOID OnEvent(struct SLIPDevUnit *sdu, struct IOSana2Req *ios2)
{

    switch(ios2->ios2_WireError)
    {
    	/* Special case.  We may already be online, in which
    	   case the IO request should return immediately. Otherwise
    	   we queue it for later. */

    	case S2EVENT_ONLINE:
    				if(sdu->sdu_State & SLIPUF_ONLINE)
    				    TermIO(ios2);
    				else
    				{
    				    ObtainSemaphore(&sdu->sdu_ListLock);
    				    AddTail((struct List *)&sdu->sdu_Events,(struct Node *)ios2);
    				    ReleaseSemaphore(&sdu->sdu_ListLock);
    				}
    				break;

	/* Same as with S2EVENT_ONLINE, but the opposite
	   happens. */
	case S2EVENT_OFFLINE:
				if(sdu->sdu_State & SLIPUF_ONLINE)
				{
				    ObtainSemaphore(&sdu->sdu_ListLock);
    				    AddTail((struct List *)&sdu->sdu_Events,(struct Node *)ios2);
    				    ReleaseSemaphore(&sdu->sdu_ListLock);
    				}
				else
    				    TermIO(ios2);
				break;

	/* Just queue everything else. */
	default:
	    			ObtainSemaphore(&sdu->sdu_ListLock);
    				AddTail((struct List *)&sdu->sdu_Events,(struct Node *)ios2);
    				ReleaseSemaphore(&sdu->sdu_ListLock);
				break;
    }
}

/*
** This routine opens the serial device driver and attempts to bring
** the device online.
*/
BOOL OpenSerial(struct SLIPDevUnit *sdu)
{
    BOOL status = TRUE;
    ULONG odflags = 0;

    sdu->sdu_SerRx->IOSer.io_Device = NULL;

    if(sdu->sdu_State & SLIPUF_7WIRE)
    	odflags = SERF_7WIRE;

    if(!OpenDevice(sdu->sdu_SerDevName,sdu->sdu_SerUnitNum,(struct IORequest *)sdu->sdu_SerTx,odflags))
    {
    	/* Set up our serial parameters */
    	sdu->sdu_SerRx->IOSer.io_Device = sdu->sdu_SerTx->IOSer.io_Device;
    	sdu->sdu_SerRx->IOSer.io_Unit = sdu->sdu_SerTx->IOSer.io_Unit;

    	sdu->sdu_SerTx->IOSer.io_Command = SDCMD_SETPARAMS;
    	sdu->sdu_SerTx->io_Baud = sdu->sdu_BaudRate;
    	sdu->sdu_SerTx->io_RBufLen = 16384L;
    	sdu->sdu_SerTx->io_ReadLen = 8;
    	sdu->sdu_SerTx->io_WriteLen = 8;
    	sdu->sdu_SerTx->io_StopBits = 1;
    	sdu->sdu_SerTx->io_SerFlags = SERF_XDISABLED|SERF_RAD_BOOGIE;

    	if(!DoIO((struct IORequest *)sdu->sdu_SerTx))
    	{
    	    /* Assume we're now online */
    	    sdu->sdu_State |= SLIPUF_ONLINE;

    	    /* Are we checking for serial detect? */
    	    if(sdu->sdu_State & SLIPUF_CD)
    	    {
    	    	sdu->sdu_SerTx->IOSer.io_Command = SDCMD_QUERY;
    	    	if(!DoIO((struct IORequest *)sdu->sdu_SerTx))
    	    	{
    	    	    if(sdu->sdu_SerTx->io_Status & (1<<5))
    	    	    {
    	    	    	/* Sorry, no carrier, shut down the serial driver
    	    	    	   and set our state to offline. */
    	    	    	CloseDevice((struct IORequest *)sdu->sdu_SerTx);
    	    	    	sdu->sdu_State &= ~SLIPUF_ONLINE;
    	    	    }
    	    	}
    	    	else
    	    	    status = FALSE;
    	    }
    	    if(status)
    	    {
    	    	/* Queue up the initial CMD_READ command for the
    	    	   serial driver. */
    	    	sdu->sdu_SerRx->IOSer.io_Command = CMD_READ;
    	    	sdu->sdu_SerRx->IOSer.io_Length = 1;
    	    	sdu->sdu_SerRx->IOSer.io_Data = sdu->sdu_RxSLIP;
    	    	SendIO((struct IORequest *)sdu->sdu_SerRx);
    	    }
    	}
    	else
    	    status = FALSE;

    	if(!status)
    	    CloseDevice((struct IORequest *)sdu->sdu_SerTx);
    }
    else
    {
        struct Library *IntuitionBase;
        struct EasyStruct es;
        ULONG args[2];
        args[0]=(ULONG)sdu->sdu_SerDevName;
        args[1]=(ULONG)sdu->sdu_SerUnitNum;
        if(IntuitionBase = OpenLibrary("intuition.library",37L))
        {
            es.es_StructSize=sizeof(struct EasyStruct);
            es.es_Flags=0;
            es.es_Title="Slip.device";
            es.es_TextFormat="Couldn't open %s unit %ld.";
            es.es_GadgetFormat="Okay";
            EasyRequestArgs(NULL, &es, 0, (APTR)args);
            CloseLibrary(IntuitionBase);
        }
        status = FALSE;
    }
    if(sdu->sdu_State & SLIPUF_ONLINE)
    	MarkTimeOnline(sdu);

    return(status);
}

/*
** This routine gets the current system time and stores
** it in our global statistics structure.
*/

VOID MarkTimeOnline(struct SLIPDevUnit *sdu)
{
    register struct Library *TimerBase;
    struct timerequest *treq;

    if(treq = (struct timerequest *)AllocMem(sizeof(struct timerequest),MEMF_PUBLIC|MEMF_CLEAR))
    {
    	if(!OpenDevice("timer.device",UNIT_MICROHZ,(struct IORequest *)treq,0L))
    	{
    	    TimerBase = (struct Library *)treq->tr_node.io_Device;
    	    GetSysTime(&sdu->sdu_Stats.LastStart);
    	    CloseDevice((struct IORequest *)treq);
    	}
    	FreeMem(treq,sizeof(struct timerequest));
    }
}

/*
** This routine aborts any pending activity with the serial
** device driver and then brings the slip driver offline.
*/
VOID CloseSerial(struct SLIPDevUnit *sdu)
{
    AbortIO((struct IORequest *)sdu->sdu_SerRx);
    WaitIO((struct IORequest *)sdu->sdu_SerRx);

    while(GetMsg(sdu->sdu_RxPort));

    AbortIO((struct IORequest *)sdu->sdu_SerTx);
    WaitIO((struct IORequest *)sdu->sdu_SerTx);

    while(GetMsg(sdu->sdu_TxPort));

    CloseDevice((struct IORequest *)sdu->sdu_SerRx);

    sdu->sdu_State &= ~SLIPUF_ONLINE;
}

/*
** This routime handles CMD_ONLINE commands.
*/
VOID Online(struct SLIPDevUnit *sdu, struct IOSana2Req *ios2)
{

    if(!(sdu->sdu_State & SLIPUF_ONLINE))
    {
    	/* We're offline. Try to go online. */
    	if(OpenSerial(sdu))
    	{
    	    if(sdu->sdu_State & SLIPUF_ONLINE)
    	    {
    	    	/* In case someone wants to know...*/
    	    	DoEvent(sdu, S2EVENT_ONLINE);
    	    }
    	    else
    	    {
    	    	/* Sorry, the attempt to go online failed. */
    	    	ios2->ios2_Req.io_Error = S2ERR_OUTOFSERVICE;
    	    	ios2->ios2_WireError = S2WERR_UNIT_OFFLINE;
    	    }
        }
        else
        {
            /* A general problem occured. */
            ios2->ios2_Req.io_Error = S2ERR_NO_RESOURCES;
            ios2->ios2_WireError = S2WERR_GENERIC_ERROR;
        }
    }
    TermIO(ios2);
}

/*
** This routine handles CMD_OFFLINE commands.
*/
VOID Offline(struct SLIPDevUnit *sdu, struct IOSana2Req *ios2)
{
    TermIO(ios2);

    if(sdu->sdu_State & SLIPUF_ONLINE)
    {
    	/* We're online, so shut everything down. */
	CloseSerial(sdu);
	DoOffline(sdu);
	DoEvent(sdu,S2EVENT_OFFLINE);
    }
}

/*
** This routine is called whenever an "important"
** SANA-II event occurs.
*/
VOID DoEvent(struct SLIPDevUnit *sdu, ULONG event)
{
    struct IOSana2Req *ios2;
    struct IOSana2Req *ios2_next;

    ObtainSemaphore(&sdu->sdu_ListLock);

    ios2 = (struct IOSana2Req *)sdu->sdu_Events.mlh_Head;

    while(ios2->ios2_Req.io_Message.mn_Node.ln_Succ)
    {
    	ios2_next = (struct IOSana2Req *)ios2->ios2_Req.io_Message.mn_Node.ln_Succ;

    	/* Is this the event they are looking for? */
    	if(ios2->ios2_WireError == event)
    	{
    	    Remove((struct Node *)ios2);
    	    TermIO(ios2);
    	}
    	ios2 = ios2_next;
    }
    ReleaseSemaphore(&sdu->sdu_ListLock);
}

/*
** This routine is called whenever the device needs to
** be taken offline.  We return any pending CMD_READ's
** or CMD_WRITE's to their senders.
*/
VOID DoOffline(struct SLIPDevUnit *sdu)
{
    struct IOSana2Req *ios2;

    ObtainSemaphore(&sdu->sdu_ListLock);

    while(ios2 = (struct IOSana2Req *)RemHead((struct List *)&sdu->sdu_Rx))
    {
    	ios2->ios2_Req.io_Error = S2ERR_OUTOFSERVICE;
    	ios2->ios2_WireError = S2WERR_UNIT_OFFLINE;
    	TermIO(ios2);
    }

    while(ios2 = (struct IOSana2Req *)RemHead((struct Lust *)&sdu->sdu_Tx))
    {
    	ios2->ios2_Req.io_Error = S2ERR_OUTOFSERVICE;
    	ios2->ios2_WireError = S2WERR_UNIT_OFFLINE;
    	TermIO(ios2);
    }
    ReleaseSemaphore(&sdu->sdu_ListLock);
}

/*
** This routine is called whenever a CMD_WRITE request
** has returned from the serial driver.
*/
VOID ServiceTxPort(struct SLIPDevUnit *sdu)
{
    struct IOSana2Req *ios2;

    /* See if we have any pending CMD_WRITE requests. */
    if(sdu->sdu_State & SLIPUF_ONLINE)
    {
    	ObtainSemaphore(&sdu->sdu_ListLock);
    	ios2 = (struct IOSana2Req *)RemHead((struct List *)&sdu->sdu_Tx);
    	ReleaseSemaphore(&sdu->sdu_ListLock);

    	if(ios2)
    	{
    	    SendPacket(sdu, ios2);
    	    sdu->sdu_NoMore = 1;
    	}
    }
    else
    	sdu->sdu_NoMore = 1;
}

/*
** This routine is called whenever a CMD_READ request
** returns from the serial driver.  It decodes the
** packet data and tries to build complete packets.
*/
VOID DoSerial(struct SLIPDevUnit *sdu, struct IOExtSer *ioSer)
{
    UBYTE *rx_ptr,*packet_ptr;
    UBYTE rx_byte, packet_byte;
    ULONG length;

    packet_ptr = sdu->sdu_RxBuffPtr;
    rx_ptr = sdu->sdu_RxSLIP;

    length = ioSer->IOSer.io_Length;

    while(length--)
    {
    	rx_byte = packet_byte = *rx_ptr;
    	rx_ptr++;

	/* Handle SLIP packet decoding...*/
    	if(sdu->sdu_Escape)
    	{
    	    if(rx_byte == SLIP_ESC_ESC)
    	    	packet_byte = SLIP_ESC;
    	    else if(rx_byte = SLIP_ESC_END)
    	    	packet_byte = SLIP_END;
    	    else
    	    	ReceivedGarbage(sdu);		/* This packet may be hosed */

    	    sdu->sdu_Escape = FALSE;
    	}
    	else if(rx_byte == SLIP_ESC)
    	{
    	    sdu->sdu_Escape = TRUE;
    	    continue;
    	}
    	else if(rx_byte == SLIP_END)
    	{
    	    GotPacket(sdu,(ULONG)(packet_ptr - sdu->sdu_RxBuff));
    	    packet_ptr = sdu->sdu_RxBuff;
    	    continue;
    	}
    	*packet_ptr = packet_byte;
    	packet_ptr++;

    	if(((ULONG)(packet_ptr - sdu->sdu_RxBuff)) > SLIP_MTU)
    	{
    	    packet_ptr = sdu->sdu_RxBuff;
    	    PacketOverrun(sdu);
    	}
    }
    sdu->sdu_RxBuffPtr = packet_ptr;

    /* Queue up another CMD_READ request...*/
    QueueSerRequest(sdu);
}

/*
** This routine is called whenever we think we've got
** a complete packet to satisfy a CMD_READ request.
*/
VOID GotPacket(struct SLIPDevUnit *sdu, ULONG length)
{
    struct IOSana2Req *ios2;
    struct BufferManagement *bm;

    /* We ignore zero-length packets. These occur
       in between legal SLIP packets due to the
       way SLIP packets are framed. */

    if(length)
    {
    	PacketReceived(sdu,length);
	ObtainSemaphore(&sdu->sdu_ListLock);

	ios2 = (struct IOSana2Req *)RemHead((struct List *)&sdu->sdu_Rx);

	if(!ios2)
	    ios2 = (struct IOSana2Req *)RemHead((struct List *)&sdu->sdu_RxOrph);

	ReleaseSemaphore(&sdu->sdu_ListLock);

	if(ios2)
	{
	    bm = (struct BufferManagement *)ios2->ios2_BufferManagement;

	    /* Copy the data into the protocol stack's buffer using its
	       supplied callback routine. */
	    if((*bm->bm_CopyToBuffer)(ios2->ios2_Data,sdu->sdu_RxBuff,length))
	    {
		ios2->ios2_DataLength =	length;
	    }
	    else
	    {
		ios2->ios2_DataLength =	0;
		ios2->ios2_Req.io_Error	= S2ERR_NO_RESOURCES;
		ios2->ios2_WireError = S2WERR_BUFF_ERROR;
		DoEvent(sdu,S2EVENT_BUFF);
	    }
	    TermIO(ios2);
	}
	else
	    PacketDropped(sdu);
    }
    sdu->sdu_Escape = 0;
}

/*
** This routine is called whenever we need to
** get more data from the serial port.  We first
** to a SDCMD_QUERY to see how much data is available,
** if any.
*/
VOID QueueSerRequest(struct SLIPDevUnit *sdu)
{
    sdu->sdu_SerRx->IOSer.io_Command = SDCMD_QUERY;
    DoIO((struct IORequest *)sdu->sdu_SerRx);

    if(sdu->sdu_State & SLIPUF_CD)
    {
    	if(sdu->sdu_SerRx->io_Status & (1<<5))
    	{
    	    /* Oops! We've lost carrier. Go offline. */
    	    CloseSerial(sdu);
    	    DoOffline(sdu);
    	    DoEvent(sdu,S2EVENT_OFFLINE);
    	    return;
    	}
    }
    sdu->sdu_SerRx->IOSer.io_Command = CMD_READ;
    sdu->sdu_SerRx->IOSer.io_Data = sdu->sdu_RxSLIP;
    sdu->sdu_SerRx->IOSer.io_Length = sdu->sdu_SerRx->IOSer.io_Actual;

    /* If the number of bytes available is zero, queue a request
       for one byte. */
    if(!sdu->sdu_SerRx->IOSer.io_Length)
    	sdu->sdu_SerRx->IOSer.io_Length = 1;

    SendIO((struct IORequest *)sdu->sdu_SerRx);
}

/*
** This is the C entry point for the Unit process.
** A6 has been set up by the assembly stub in slip_dev.asm.
*/

VOID ASM DevProcCEntry(VOID)
{
    struct Process *proc;
    struct SLIPDevUnit *sdu;
    struct IOExtSer *ioser;
    struct StartupMessage *sm;
    struct BufferManagement *bm;
    struct IOSana2Req *ios2;
    ULONG waitmask,signals;
    UBYTE signalbit;

    /* Find our Process pointer and wait for our startup
       message to arrive. */

    proc = (struct Process *)FindTask(0L);

    WaitPort(&proc->pr_MsgPort);

    /* Pull the startup message off of our process messageport. */
    sm = (struct StartupMessage *)GetMsg(&proc->pr_MsgPort);

    /* Grab our Unit pointer. */
    sdu = (struct SLIPDevUnit *)sm->Unit;

    /* Attempt to allocate a signal bit for our Unit MsgPort. */
    signalbit = AllocSignal(-1L);
    if(signalbit != -1)
    {
        /* Set up our Unit's MsgPort. */
    	sdu->sdu_Unit.unit_MsgPort.mp_SigBit = signalbit;
    	sdu->sdu_Unit.unit_MsgPort.mp_SigTask = (struct Task *)proc;
    	sdu->sdu_Unit.unit_MsgPort.mp_Flags = PA_SIGNAL;

	/* Initialize our list semaphore */
	InitSemaphore(&sdu->sdu_ListLock);

	/* Initialize our linked lists. */
    	NewList((struct List *)&sdu->sdu_Rx);
    	NewList((struct List *)&sdu->sdu_RxOrph);
    	NewList((struct List *)&sdu->sdu_Tx);
    	NewList((struct List *)&sdu->sdu_Events);
    	NewList((struct List *)&sdu->sdu_BuffMgmt);
    	NewList((struct List *)&sdu->sdu_Track);

	/* Initialize the serial stuff.  If all goes okay,
	   set sdu->sdu_Proc to pointer to our unit process.
	   This will let the Unit init code know that were
	   are okay. */

	if(InitSerial(sdu))
            sdu->sdu_Proc = proc;
    }
    /* Reply to our startup message */
    ReplyMsg((struct Message *)sm);

    /* Check sdu->sdu_Proc to see if everything went okay up
       above. */
    if(sdu->sdu_Proc)
    {
    	waitmask = (1L<<signalbit) | (1L<<sdu->sdu_RxPort->mp_SigBit) |
    		   (1L<<sdu->sdu_TxPort->mp_SigBit) | SIGBREAKF_CTRL_F;

	/* Loop...*/

    	while(TRUE)
    	{
	    signals = Wait(waitmask);

	    /* Have we been signaled to shut down? */
	    if(signals & SIGBREAKF_CTRL_F)
	    	break;

	    /* I use this flag to make sure I don't sit idle
	       with a message sitting on one of my message ports. */

            sdu->sdu_NoMore = TRUE;	/* Make sure we run at least once. */

            while(sdu->sdu_NoMore)
            {
                sdu->sdu_NoMore = FALSE;

                if(ios2 = (struct IOSana2Req *)GetMsg((struct MsgPort *)sdu))
                {
                    sdu->sdu_NoMore = TRUE;
                    PerformIO(ios2);
                }
                if(ioser = (struct IOExtSer *)GetMsg(sdu->sdu_RxPort))
                {
                    if(sdu->sdu_State & SLIPUF_ONLINE)
                    {
                        DoSerial(sdu, ioser);
                    }
                }
                if(ioser = (struct IOExtSer *)GetMsg(sdu->sdu_TxPort))
                {
                    ServiceTxPort(sdu);
                }
            }
        }
        /* If we're online, we need to shut everything down. */
        if(sdu->sdu_State & SLIPUF_ONLINE)
        {
            CloseSerial(sdu);
	    FreeSignal(signalbit);
	    while(bm = (struct BufferManagement *)RemHead((struct List *)&sdu->sdu_BuffMgmt))
	    	FreeMem(bm,sizeof(struct BufferManagement));

	}
        DeinitSerial(sdu);

        /* Signal the other side we're exiting.  Make sure that
           we exit before they wake up by using the same trick
           when replying to Workbench startup messages. */

        Forbid();
        Signal((struct Task *)sdu->sdu_Proc,SIGBREAKF_CTRL_F);
    }
    /* Something went wrong in the init code.  Drop out. */
    else
    {
    	if(signalbit)
    	    FreeSignal(signalbit);
    }
}

/* List init routine. */
VOID NewList(struct List *list)
{
    list->lh_Head = (struct Node *)&list->lh_Tail;
    list->lh_Tail = NULL;
    list->lh_TailPred = (struct Node *)list;
}
