/*
** $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.
**
*/

#ifndef EOFMODE
# define EOFMODE	1
#endif
#ifndef DEMANDREAD
# define DEMANDREAD	1
#endif

#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 SLIPDevice *ExtDeviceBase;
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(a6) struct SLIPDevice *SLIPDevice,
		  REG(d0) ULONG s2unit,
		  REG(d1) ULONG s2flags)
{
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 TagItem *)ios2->ios2_BufferManagement))
	    	    {
	    	        bm->bm_CopyToBuffer = (SANA2_CTB) bufftag->ti_Data;
	    	    }

	    	    if(bufftag = FindTagItem(S2_CopyFromBuff, (struct TagItem *)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,
		  REG(a6) struct SLIPDevice *SLIPDevice)
{
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(SLIPDevice);

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(REG(a6) struct SLIPDevice *SLIPDevice)
{
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);
	ExtDeviceBase = NULL;
}
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 = SLIPName;

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) &DevProcCEntry;
NPTags[1].ti_Data = (ULONG) SLIPName;      /* Process name */
			/* Rhialto: use opener's priority */
NPTags[2].ti_Data = (ULONG) FindTask(NULL)->tc_Node.ln_Pri;

			/*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 = /* Rhialto */
			sdu->sdu_HwAddr = 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=SLIPName;
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,
		    REG(a6) struct SLIPDevice *SLIPDevice)
{
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_micro; /* Rhialto */

/* Rhialto: huh?
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;
	    break;	/* Rhialto */
	}
	stats = (struct SuperS2PTStats *)stats->ss_Node.mln_Succ;
}
if(!stats->ss_Node.mln_Succ)
{
	if(stats = AllocMem(sizeof(struct SuperS2PTStats),MEMF_CLEAR|MEMF_PUBLIC))
	{
	    stats->ss_PType = ios2->ios2_PacketType;	/* Rhialto */
	    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,
		     REG(a6) struct SLIPDevice *SLIPDevice)
{
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.
** Rhialto: What we want is the config file address as hardware
*8	    address, and the S2_CONFIGINTERFACE address as current address.
*/

VOID GetStationAddress(struct SLIPDevUnit *sdu, struct IOSana2Req *ios2)
{
memset(ios2->ios2_DstAddr, 0, SANA2_MAX_ADDR_BYTES);
memset(ios2->ios2_SrcAddr, 0, SANA2_MAX_ADDR_BYTES);
memcpy(ios2->ios2_SrcAddr, &sdu->sdu_StAddr, 4);
memcpy(ios2->ios2_DstAddr, &sdu->sdu_HwAddr, 4);

TermIO(ios2);
}

/*
** This function handles S2_DEVICEQUERY comands.
** Rhialto: Give the format of the data, and the *correct* size!
*/
VOID DeviceQuery(struct SLIPDevUnit *sdu, struct IOSana2Req *ios2)
{
struct Sana2DeviceQuery *sdq;

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

sdq->DevQueryFormat = 0;
sdq->DeviceLevel = 0;
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 = sizeof(*sdq);
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);
#if DEMANDREAD
	if (sdu->sdu_SerRx->IOSer.io_Message.mn_Node.ln_Type == NT_FREEMSG)
	    QueueSerRequest(sdu);
#endif
	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);
#if DEMANDREAD
	if (sdu->sdu_SerRx->IOSer.io_Message.mn_Node.ln_Type == NT_FREEMSG)
	    QueueSerRequest(sdu);
#endif
	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;

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

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

/* Rhialto: odflags must be in io_SerFlags, not in OpenDevice! */
sdu->sdu_SerTx->io_SerFlags = odflags;

if(!OpenDevice(sdu->sdu_SerDevName,sdu->sdu_SerUnitNum,(struct IORequest *)sdu->sdu_SerTx,0))
{
	/* 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 = odflags;
#if EOFMODE
	sdu->sdu_SerTx->io_TermArray.TermArray0 = 
	sdu->sdu_SerTx->io_TermArray.TermArray1 =
			    0x01010101 * ((unsigned char)SLIP_END);
#endif
	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;
#if EOFMODE
		sdu->sdu_SerRx->io_SerFlags = SERF_EOFMODE|odflags;
		sdu->sdu_SerRx->io_TermArray.TermArray0 = 
		sdu->sdu_SerRx->io_TermArray.TermArray1 =
				    0x01010101 * ((unsigned char)SLIP_END);
#else
		sdu->sdu_SerRx->io_SerFlags = odflags;
#endif
	    	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=SLIPName;
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 List *)&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_Actual;	/* Rhialto: Was 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...*/
#if DEMANDREAD
/* Rhialto: but only if we are sure that we won't drop that packet */
/*ObtainSemaphore(&sdu->sdu_ListLock);*/
/* Semaphore protection of sdu_Rx, sdu_RxOrph, and sdu_Tx is not
* really necessary, since they are only accessed from the
* Unit task, because there are no IMMEDIATE device commands.
*/
if (!IsListEmpty((struct List *)&sdu->sdu_Rx) ||
	!IsListEmpty((struct List *)&sdu->sdu_RxOrph))
	QueueSerRequest(sdu);
else
	sdu->sdu_SerRx->IOSer.io_Message.mn_Node.ln_Type = NT_FREEMSG;
/*ReleaseSemaphore(&sdu->sdu_ListLock);*/
#else
QueueSerRequest(sdu);
#endif
}

/*
** 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.
** Rhialto: without EOF mode, this makes the thing a real CPU hog, 
** as long as input is coming in and read requests are pending.
** And since it runs at pri 5, that is a real disaster.
*/
VOID QueueSerRequest(struct SLIPDevUnit *sdu)
{
#if EOFMODE == 0
sdu->sdu_SerRx->IOSer.io_Command = SDCMD_QUERY;
DoIO((struct IORequest *)sdu->sdu_SerRx);
#endif

if(sdu->sdu_State & SLIPUF_CD)
{
#if EOFMODE
	sdu->sdu_SerRx->IOSer.io_Command = SDCMD_QUERY;
	DoIO((struct IORequest *)sdu->sdu_SerRx);
#endif
	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;
#if EOFMODE
sdu->sdu_SerRx->IOSer.io_Length = SLIP_MTU * 2 + 1;
#else
/* Rhialto: This could lead to requesting more than the buffer
* size! The serial.device has a 16K buffer. (See SDCMD_SETPARAMS)
*/
sdu->sdu_SerRx->IOSer.io_Length = sdu->sdu_SerRx->IOSer.io_Actual;

if (sdu->sdu_SerRx->IOSer.io_Length > (SLIP_MTU*2 + 128))
	sdu->sdu_SerRx->IOSer.io_Length = (SLIP_MTU*2 + 128);
else
/* 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;
#endif

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

/*
** This is the C entry point for the Unit process.
*/

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)
{
			sdu->sdu_NoMore = TRUE;
DoSerial(sdu, ioser);
}
}
if(ioser = (struct IOExtSer *)GetMsg(sdu->sdu_TxPort))
{
sdu->sdu_NoMore = TRUE;
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;
}
