/* $Id: device_funcs.c,v 38.9 1994/01/25 09:04:06 ppessi Exp $
** $Source: /m/lerppu/u3/projects/Dec93/ohtatcp/amitcp/src/devs/rhslip/RCS/device_funcs.c,v $
** $State: Exp $
** $Revision: 38.9 $
** $Date: 1994/01/25 09:04:06 $
** $Author: ppessi $
**
** Amiga SANA-II Example SLIP device driver.
**
** (C) Copyright 1992 Commodore-Amiga, Inc.
**
*/

#ifndef USEPRIORITY /* add write requests in priority order */
# define USEPRIORITY	1
#endif
#ifndef DEMANDREAD
# define DEMANDREAD	1
#endif

#include "slip_device.h"
#define DEBUG 0
#if DEBUG
#include "syslog.h"
#else
#define debug(x)
#endif

#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"

/* Correct prototype for the CheckIO.
 * (The one in clib/exec_protos.h has wrong return value type: BOOL (16 bits) 
 * instead of a pointer (32 bits)!)
 */
struct IORequest * CheckIO(struct IORequest *req);

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

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

const char SLIPName[] = SLIPDEVNAME;

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

#if DEBUG
    initsyslog();
#endif
    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;

#if DEBUG
	uninitsyslog();
#endif
	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 = (char *)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/(c)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;
    BOOL status = FALSE;
    ULONG linenum=0;

    /* Create the name of our config file.. */
    sprintf(buff,"ENV:SANA2/rhslip%ld.config",(ULONG)sdu->sdu_UnitNum);
    if(ConfigFile = Open(buff,MODE_OLDFILE))
	goto config_open;
#if CSLIP
    sprintf(buff,"ENV:SANA2/cslip%ld.config",(ULONG)sdu->sdu_UnitNum);
    if(ConfigFile = Open(buff,MODE_OLDFILE))
	goto config_open;
#endif
    sprintf(buff,"ENV:SANA2/slip%ld.config",(ULONG)sdu->sdu_UnitNum);

    /* ...and open it. */
    if(ConfigFile = Open(buff,MODE_OLDFILE))
    {
    config_open:
	/* 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))
		{
		    /* ppessi: This is more robust than LONG array */
		    struct {
			STRPTR a_SerDevName;
			ULONG *a_SerUnitNum;
			ULONG *a_BaudRate;
			STRPTR a_IPAddr;
			ULONG *a_MTU;
			ULONG  a_CarrierDetect;
			ULONG  a_7Wire;
			ULONG  a_EofMode;
		    } args[1];

		    const UBYTE template[] = 
		      "SERNAME/A,"
  		      "SERUNIT/A/N,"
		      "SERBAUD/A/N,"
		      "IPSTR/A,"
		      "MTU/K/N,"
		      "CD=CARRIERDETECT/S,7WIRE/S,EOF=EOFMODE/S";

		    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;

		    memset(args, 0, sizeof(*args));

		    /* Parse the line...*/
		    /* ppessi: New parameters */
		    if (ReadArgs(template, (LONG *)args, rdargs))
		    {
			strncpy(sdu->sdu_SerDevName, args->a_SerDevName, sizeof(sdu->sdu_SerDevName));
			sdu->sdu_SerUnitNum = *args->a_SerUnitNum;
			sdu->sdu_BaudRate = *args->a_BaudRate;
			sdu->sdu_StAddr = /* Rhialto */
			    sdu->sdu_HwAddr = IPToNum(args->a_IPAddr);

			if (args->a_CarrierDetect)
			    sdu->sdu_State |= SLIPUF_CD;
			debug(("Config: CD %d\n", sdu->sdu_State & SLIPUF_CD));

			if (args->a_7Wire)
			    sdu->sdu_State |= SLIPUF_7WIRE;
			debug(("Config: 7W %d\n", sdu->sdu_State & SLIPUF_7WIRE));

			if (args->a_EofMode)
			    sdu->sdu_State |= SLIPUF_EOFMODE;
			debug(("Config: EOFMODE %d\n", sdu->sdu_State & SLIPUF_EOFMODE));
		    
			/* ppessi: some SLIP drivers are broken, break this too... */
			if (args->a_MTU && *args->a_MTU < SLIP_SERIAL_BUFSIZE / 2 - BUF_SLOP) 
			{
			    sdu->sdu_MTU = *args->a_MTU;
			} 
			else 
			{
			    /* 1006 byte max */
			    sdu->sdu_MTU = SLIP_MTU; 
			}
			debug(("Config: MTU=%d\n", sdu->sdu_MTU));
		    
			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=(char *)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 any SLIP specific
** statistics.
*/
VOID GetSpecialStats(struct SLIPDevUnit *sdu, struct IOSana2Req *ios2)
{
    struct Sana2SpecialStatHeader *stats;

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

#if CSLIP
    {
	int count;
	struct Sana2SpecialStatRecord *r;

	count = stats->RecordCountMax;
	if (count > 11)
	    count = 11;
	r = (struct Sana2SpecialStatRecord *)(stats + 1);
	r = &r[count - 1];

	switch (count) {
	case 11:
	    r->Type = S2SS_CSLIP_I_TOSSED;
	    r->String = "i_tossed";
	    r->Count = sdu->sdu_slcompress.sls_i_tossed;
	    r--;
	case 10:
	    r->Type = S2SS_CSLIP_I_ERROR;
	    r->String = "i_error";
	    r->Count = sdu->sdu_slcompress.sls_i_error;
	    r--;
	case 9:
	    r->Type = S2SS_CSLIP_I_COMPRESSED;
	    r->String = "i_compressed";
	    r->Count = sdu->sdu_slcompress.sls_i_compressed;
	    r--;
	case 8:
	    r->Type = S2SS_CSLIP_I_UNCOMPRESSED;
	    r->String = "i_uncompressed";
	    r->Count = sdu->sdu_slcompress.sls_i_uncompressed;
	    r--;
	case 7:
	    r->Type = S2SS_CSLIP_I_IP;
	    r->String = "i_ip";
	    r->Count = sdu->sdu_slcompress.sls_i_ip;
	    r--;
	case 6:
	    r->Type = S2SS_CSLIP_O_MISSES;
	    r->String = "o_misses";
	    r->Count = sdu->sdu_slcompress.sls_o_misses;
	    r--;
	case 5:
	    r->Type = S2SS_CSLIP_O_SEARCHES;
	    r->String = "o_searches";
	    r->Count = sdu->sdu_slcompress.sls_o_searches;
	    r--;
	case 4:
	    r->Type = S2SS_CSLIP_O_COMPRESSED;
	    r->String = "o_compressed";
	    r->Count = sdu->sdu_slcompress.sls_o_compressed;
	    r--;
	case 3:
	    r->Type = S2SS_CSLIP_O_UNCOMPRESSED;
	    r->String = "o_uncompressed";
	    r->Count = sdu->sdu_slcompress.sls_o_uncompressed;
	    r--;
	case 2:
	    r->Type = S2SS_CSLIP_O_TCP;
	    r->String = "o_tcp";
	    r->Count = sdu->sdu_slcompress.sls_o_tcp;
	    r--;
	case 1:
	    r->Type = S2SS_CSLIP_O_NONTCP;
	    r->String = "o_nontcp";
	    r->Count = sdu->sdu_slcompress.sls_o_nontcp;
	case 0:
	    break;
	}
	
	stats->RecordCountSupplied = count;
    }
#else
    stats->RecordCountSupplied = 0;
#endif
    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)
{
#if CSLIP
    sl_uncompress_tcp(NULL, TYPE_ERROR, &sdu->sdu_slcompress);
#endif
    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)
{
#if CSLIP
    sl_uncompress_tcp(NULL, TYPE_ERROR, &sdu->sdu_slcompress);
#endif
    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 NOT!
** A6 - The device base.
*/
ULONG ASM DevAbortIO(REG(a1) struct IOSana2Req *ios2,
		     REG(a6) struct SLIPDevice *SLIPDevice)
{
    struct SLIPDevUnit *sdu = (struct SLIPDevUnit *)ios2->ios2_Req.io_Unit;
    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 = sdu->sdu_MTU;			/* ppessi: configureable MTU */
    sdq->BPS = sdu->sdu_BaudRate;
#if CSLIP
    sdq->HardwareType = S2WireType_CSLIP;
#else
    sdq->HardwareType = S2WireType_SLIP;
#endif

    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 <= sdu->sdu_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);
		/* See if modem have dropped carrier */
		if (sdu->sdu_State & SLIPUF_CD) 
		{
		    sdu->sdu_SerTx->IOSer.io_Command = SDCMD_QUERY;
		    if(DoIO((struct IORequest *)sdu->sdu_SerTx) ||
		       (sdu->sdu_SerTx->io_Status & (1 << 5)))
		    {
			/* We're online, so shut everything down. */
			CloseSerial(sdu);
			DoOffline(sdu);
			DoEvent(sdu, S2EVENT_OFFLINE);
			debug(("Carrier Dropped\n"));

			/* Sorry, we're offline */
			ios2->ios2_Req.io_Error = S2ERR_OUTOFSERVICE;
			ios2->ios2_WireError = S2WERR_UNIT_OFFLINE;
			TermIO(ios2);
			return;
		    }
		}
		SendPacket(sdu, ios2);
	    }
	    else
	    {
		/* We'll have to queue the packet for later...*/
		ios2->ios2_Req.io_Flags &= ~IOF_QUICK;
		ObtainSemaphore(&sdu->sdu_ListLock);
#if USEPRIORITY
		Enqueue((struct List *)&sdu->sdu_Tx,(struct Node *)ios2);
#else
		AddTail((struct List *)&sdu->sdu_Tx,(struct Node *)ios2);
#endif
		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);
	DoEvent(sdu, S2EVENT_TX);
    }
}

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))
    {
#if CSLIP
	struct mbuf m;
	unsigned char type;

	m.m_off = sdu->sdu_TxBuff;
	m.m_len = ios2->ios2_DataLength;

	/* Appendix B.1  Living without a framing `type' byte */

	type = sl_compress_tcp(&m, &sdu->sdu_slcompress);
	m.m_off[0] |= type;

	/* Encode the packet in SLIP format */
	framelength=EncodeSLIP(m.m_off,sdu->sdu_TxSLIP,m.m_len);
	PacketSent(sdu,m.m_len);
#else
	/* Encode the packet in SLIP format */
	framelength=EncodeSLIP(sdu->sdu_TxBuff,sdu->sdu_TxSLIP,ios2->ios2_DataLength);
	PacketSent(sdu,ios2->ios2_DataLength);
#endif

	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);
	DoEvent(sdu, S2EVENT_RX);
    }
}

/*
** 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;
	DoEvent(sdu, S2EVENT_RX);
	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 = AllocVec((sdu->sdu_MTU + BUF_SLOP) * 2 + MAX_HDR, MEMF_CLEAR|MEMF_PUBLIC))
		    {
			sdu->sdu_RxBuff = sdu->sdu_TxBuff + sdu->sdu_MTU + BUF_SLOP + MAX_HDR;
			sdu->sdu_RxBuffPtr = sdu->sdu_RxBuff;

			if(sdu->sdu_TxSLIP = AllocVec((sdu->sdu_MTU * 2 + BUF_SLOP) * 2, MEMF_CLEAR|MEMF_PUBLIC))
			{
			    sdu->sdu_RxSLIP = sdu->sdu_TxSLIP + sdu->sdu_MTU * 2 + BUF_SLOP;
			    {
				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)
	FreeVec(sdu->sdu_TxBuff);

    if(sdu->sdu_TxSLIP)
	FreeVec(sdu->sdu_TxSLIP);
}

/*
** 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 = SLIP_SERIAL_BUFSIZE;
	sdu->sdu_SerTx->io_ReadLen = 8;
	sdu->sdu_SerTx->io_WriteLen = 8;
	sdu->sdu_SerTx->io_StopBits = 1;
	sdu->sdu_SerTx->io_SerFlags = odflags;
	sdu->sdu_SerTx->io_TermArray.TermArray0 =
	    sdu->sdu_SerTx->io_TermArray.TermArray1 = 0x01010101 * (UBYTE)SLIP_END;

	if(!DoIO((struct IORequest *)sdu->sdu_SerTx))
	{
	    /* 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. */
			/* ppessi: remove CloseDevice -- use status instead */
			status = FALSE;	
			debug(("No Carrier\n"));
		    }
		}
		else
		{
		    status = FALSE;
		    debug(("QUERY failed %d\n",sdu->sdu_SerTx->io_Error));
		}
	    } 
	    if (status)
	    {
		/* We're now online */
		sdu->sdu_State |= SLIPUF_ONLINE;
		MarkTimeOnline(sdu);

		/* 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(sdu->sdu_State & SLIPUF_EOFMODE)
		  odflags |= SERF_EOFMODE;
		sdu->sdu_SerRx->io_SerFlags = odflags;
		sdu->sdu_SerRx->io_TermArray.TermArray0 =
		    sdu->sdu_SerRx->io_TermArray.TermArray1 = 0x01010101 * (UBYTE)SLIP_END;
		SendIO((struct IORequest *)sdu->sdu_SerRx);
	    }
	}
	else
	{
	    status = FALSE;
	    debug(("SETPARAMS failed %d\n",sdu->sdu_SerTx->io_Error));
	}

	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=(char *)SLIPName;
	    es.es_TextFormat="Couldn't open %s unit %ld.";
	    es.es_GadgetFormat="Okay";
	    EasyRequestArgs(NULL, &es, 0, (APTR)args);
	    CloseLibrary(IntuitionBase);
	}
	status = FALSE;
    }

    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)
{
    if (!CheckIO((struct IORequest *)sdu->sdu_SerRx) && 
	sdu->sdu_SerRx->IOSer.io_Message.mn_Node.ln_Type != NT_FREEMSG) {
	AbortIO((struct IORequest *)sdu->sdu_SerRx);
	WaitIO((struct IORequest *)sdu->sdu_SerRx);
    }
    while(GetMsg(sdu->sdu_RxPort));

    if (!CheckIO((struct IORequest *)sdu->sdu_SerTx)) {
	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);
	}
    }
}

/*
** 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)) > sdu->sdu_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.
     */
    /* Semaphore protection *is* needed due AbortIO //ppessi */
    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)
    {
	short orphan;

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

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

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

	ReleaseSemaphore(&sdu->sdu_ListLock);

	if(ios2)
	{
#if CSLIP
	    /* Appendix B.1  Living without a framing `type' byte */
	    struct mbuf m;
	    unsigned char type;

	    m.m_off = sdu->sdu_RxBuff;
	    m.m_len = length;

	    type = m.m_off[0];

	    if (type & 0x80)
		type = TYPE_COMPRESSED_TCP;
	    else if (type >= 0x70) {
		type = TYPE_UNCOMPRESSED_TCP;
		m.m_off[0] &= ~0x30;
	    } else
		type = TYPE_IP;
	    if (NULL == sl_uncompress_tcp(&m, type, &sdu->sdu_slcompress)) 
	    {
		/* put the ios2 back to queue or they will be lost //ppessi */
		ObtainSemaphore(&sdu->sdu_ListLock);
		if (orphan) 
		    AddHead((struct List *)&sdu->sdu_RxOrph, (struct Node*)ios2);
		else
		    AddHead((struct List *)&sdu->sdu_Rx, (struct Node*)ios2);
		ReleaseSemaphore(&sdu->sdu_ListLock);
		return;
	    }
	    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,m.m_off,m.m_len))
	    {
		ios2->ios2_DataLength = m.m_len;
	    }
#else
	    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;
	    }
#endif
	    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 ((sdu->sdu_State & SLIPUF_EOFMODE) == 0 || (sdu->sdu_State & SLIPUF_CD))
    {
	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;
    
    if (sdu->sdu_State & SLIPUF_EOFMODE) 
    {
	sdu->sdu_SerRx->IOSer.io_Length = sdu->sdu_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 > (sdu->sdu_MTU * 2 + BUF_SLOP))
	    sdu->sdu_SerRx->IOSer.io_Length = (sdu->sdu_MTU * 2 + BUF_SLOP);
	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;
    }

    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);

#if CSLIP
	/* Initialise the compression stuff */
	sl_compress_init(&sdu->sdu_slcompress, 16, 16);
#endif

	/* 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)
	{
	    int More;

	    signals = Wait(waitmask);

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

	    do {
		More = FALSE;

		if (ios2 = (struct IOSana2Req *)GetMsg((struct MsgPort *)sdu))
		{
		    More = TRUE;
		    PerformIO(ios2);
		}
		if (ioser = (struct IOExtSer *)GetMsg(sdu->sdu_RxPort))
		{
		    More = TRUE;
		    if(sdu->sdu_State & SLIPUF_ONLINE)
		    {
			DoSerial(sdu, ioser);
		    }
		}
		if (ioser = (struct IOExtSer *)GetMsg(sdu->sdu_TxPort))
		{
		    More = TRUE;
		    ServiceTxPort(sdu);
		}
	    } while (More);
	}
	/* 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);
#if CSLIP
	sl_compress_deinit(&sdu->sdu_slcompress);
#endif

	/* 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;
}
