home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Meeting Pearls 3
/
Meeting_Pearls_III.iso
/
Pearls
/
tcp
/
Networking
/
Sana-II
/
spar372.lha
/
Sources
/
sana2_funcs.c
< prev
next >
Wrap
C/C++ Source or Header
|
1993-12-17
|
26KB
|
865 lines
/*
** $Source: dh1:network/parnet/Sana2/Sources/sana2_funcs.c,v $
** $State: Exp $
** $Revision: 37.2 $
** $Date: 93/12/17 22:07:45 $
** $Author: S.A.Pechler $
**
** Amiga SANA-II Example PARnet device driver.
**
** SPAR Sana-II functions.
**
** Based on the Amiga SANA-II Example SLIP device driver code by bj,
** which is (C) Copyright 1992 Commodore-Amiga, Inc.
** the rhslip.device by Olaf Seibert <rhialto@mbfys.kun.nl>, and on
** the agnet.device code by ppessi <Pekka.Pessi@hut.fi>, which is
** Copyright (c) 1993 AmiTCP/IP Group,
** Helsinki University of Technology, Finland.
** All rights reserved.
**
*/
#include "device_protos.h"
/*
**
** PerformIO
**
** PerformIO actually dispatches an io request. It might be called from
** the Unit process, or directly from BeginIO (thus on the caller's schedule)
*/
VOID PerformIO(struct IOSana2Req *ios2)
{
struct SPARDevUnit *sdu;
sdu = (struct SPARDevUnit *)ios2->ios2_Req.io_Unit; /* get the Unit pointer */
ios2->ios2_Req.io_Error = NULL; /* no error so far */
switch(ios2->ios2_Req.io_Command)
{
case CMD_READ: ReadPacket(sdu,ios2);
break;
case CMD_WRITE: debug(("PerformIO: call to CMD_WRITE\n"))
WritePacket(sdu,ios2);
break;
case S2_DEVICEQUERY: debug(("PerformIO: call to S2_DEVICEQUERY\n"))
DeviceQuery(sdu,ios2);
break;
case S2_GETSTATIONADDRESS: debug(("PerformIO: call to S2_GETSTATIONADDRESS\n"))
GetStationAddress(sdu,ios2);
break;
case S2_CONFIGINTERFACE: debug(("PerformIO: call to S2_CONFIGINTERFACE\n"))
ConfigInterface(sdu,ios2);
break;
case S2_BROADCAST: debug(("PerformIO: call to S2_BROADCAST\n"))
BroadcastPacket(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;
debug(("PerformIO: call to not supported function\n"))
TermIO(ios2);
break;
case S2_TRACKTYPE: debug(("PerformIO: call to S2_TRACKTYPE\n"))
TrackType(sdu,ios2);
break;
case S2_UNTRACKTYPE: debug(("PerformIO: call to S2_UNTRACKTYPE\n"))
UnTrackType(sdu,ios2);
break;
case S2_GETTYPESTATS: debug(("PerformIO: call to S2_GETTYPESTATS\n"))
GetTypeStats(sdu,ios2);
break;
case S2_GETSPECIALSTATS: debug(("PerformIO: call to S2_GETSPECIALSTATS\n"))
GetSpecialStats(sdu,ios2);
break;
case S2_GETGLOBALSTATS: debug(("PerformIO: call to S2_GETGLOBALSTATS\n"))
GetGlobalStats(sdu,ios2);
break;
case S2_ONEVENT: debug(("PerformIO: call to S2_ONEVENT\n"))
OnEvent(sdu,ios2);
break;
case S2_READORPHAN: debug(("PerformIO: call to S2_READORPHAN\n"))
ReadOrphan(sdu,ios2);
break;
case S2_ONLINE: debug(("PerformIO: call to S2_ONLINE\n"))
Online(sdu,ios2);
break;
case S2_OFFLINE: debug(("PerformIO: call to S2_OFFLINE\n"))
Offline(sdu,ios2);
break;
default: debug(("PerformIO: call to unknown function\n"))
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 SPAR specific
** statistics.
*/
VOID GetSpecialStats(struct SPARDevUnit *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
** spar device.
*/
VOID GetGlobalStats(struct SPARDevUnit *sdu, struct IOSana2Req *ios2)
{
struct Sana2DeviceStats *stats;
if (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 */
ios2->ios2_Req.io_Error = 0;
ios2->ios2_WireError = 0;
}
else
{
ios2->ios2_Req.io_Error = S2ERR_BAD_ARGUMENT;
ios2->ios2_WireError = S2WERR_NULL_POINTER;
}
TermIO(ios2);
}
/*
** This function returns statistics for a specific
** type of packet that is being tracked.
**
*/
VOID GetTypeStats(struct SPARDevUnit *sdu, struct IOSana2Req *ios2)
{
struct Sana2PacketTypeStats *stats;
struct SuperS2PTStats *sstats;
if (stats = (struct Sana2PacketTypeStats *)ios2->ios2_StatData)
{
ObtainSemaphore(&sdu->sdu_ListLock);
sstats = (struct SuperS2PTStats *)sdu->sdu_Track.mlh_Head;
while(sstats->ss_Node.mln_Succ)
{
/* Is this the desired packet type? */
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;
}
}
else
{
ios2->ios2_Req.io_Error = S2ERR_BAD_ARGUMENT;
ios2->ios2_WireError = S2WERR_NULL_POINTER;
}
TermIO(ios2);
}
/*
** This function adds a packet type to the list
** of those that are being tracked.
*/
VOID TrackType(struct SPARDevUnit *sdu, struct IOSana2Req *ios2)
{
BOOL TypeFound=FALSE;
struct SuperS2PTStats *stats;
ObtainSemaphore(&sdu->sdu_ListLock);
stats = (struct SuperS2PTStats *)sdu->sdu_Track.mlh_Head;
/* Check in the list is this packet is already been tracked */
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;
TypeFound=TRUE;
break;
}
stats = (struct SuperS2PTStats *)stats->ss_Node.mln_Succ;
}
if(!TypeFound) /* Type not in list? */
if(stats = AllocMem(sizeof(struct SuperS2PTStats),MEMF_CLEAR|MEMF_PUBLIC))
{ /* Add packet type to the list */
sdu->sdu_TrackP = TRUE;
stats->ss_PType = ios2->ios2_PacketType;
debug(("TrackType: Added Type: %04lx.\n",ios2->ios2_PacketType))
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 SPARDevUnit *sdu, struct IOSana2Req *ios2)
{
struct SuperS2PTStats *stats;
struct SuperS2PTStats *stats_next;
BOOL TypeFound = FALSE;
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)
{
Remove((struct Node *)stats);
FreeMem(stats,sizeof(struct SuperS2PTStats));
TypeFound = TRUE;
break;
}
stats = stats_next;
}
if(!TypeFound) /* Type not found in list */
{
ios2->ios2_Req.io_Error = S2ERR_BAD_STATE;
ios2->ios2_WireError = S2WERR_NOT_TRACKED;
}
if(!stats->ss_Node.mln_Succ) /* list is empty */
sdu->sdu_TrackP=FALSE; /* reset track flag */
ReleaseSemaphore(&sdu->sdu_ListLock);
TermIO(ios2);
}
/*
** This function is called whenever we receive a packet
** from the PARnet device driver.
**
*/
VOID PacketReceived(struct SPARDevUnit *sdu, ULONG length)
{
struct SuperS2PTStats *stats;
sdu->sdu_Stats.PacketsReceived++;
/* Packet tracking enabled ? */
if(sdu->sdu_TrackP)
{
stats = (struct SuperS2PTStats *)sdu->sdu_Track.mlh_Head;
/* Check if this packet type is on the tracking list */
while(stats->ss_Node.mln_Succ)
{
if( *(UWORD *)(sdu->sdu_RxBuff+SADDR_LEN*2) == stats->ss_PType)
{
/* Found entry for this packet type, updating stats */
stats->ss_Stats.PacketsReceived++;
stats->ss_Stats.BytesReceived+=length;
break;
}
stats = (struct SuperS2PTStats *)stats->ss_Node.mln_Succ;
}
}
}
/*
** This function is called whenever a packet is
** sent to the PARnet device driver.
*/
VOID PacketSent(struct SPARDevUnit *sdu, ULONG length)
{
struct SuperS2PTStats *stats;
sdu->sdu_Stats.PacketsSent++;
/* Packet tracking enabled ? */
if(sdu->sdu_TrackP)
{
stats = (struct SuperS2PTStats *)sdu->sdu_Track.mlh_Head;
/* Check if this packet type is on the tracking list */
while(stats->ss_Node.mln_Succ)
{
if( *(UWORD *)(sdu->sdu_TxBuff+SADDR_LEN*2) == (UWORD)stats->ss_PType)
{
/* Found entry for this packet type, updating stats */
debug(("PacketSent: Tracked packet type: %04lx.\n",stats->ss_PType))
stats->ss_Stats.PacketsSent++;
stats->ss_Stats.BytesSent+=length;
break;
}
stats = (struct SuperS2PTStats *)stats->ss_Node.mln_Succ;
}
}
}
/*
** This function is called whenever a packet that
** is too large is received.
*/
VOID PacketOverrun(struct SPARDevUnit *sdu)
{
sdu->sdu_Stats.Overruns++;
DoEvent(sdu, S2EVENT_RX);
}
/*
** This function is called whenever a packet with
** garbage data is encountered.
*/
VOID ReceivedGarbage(struct SPARDevUnit *sdu)
{
sdu->sdu_Stats.BadData++;
DoEvent(sdu, S2EVENT_RX);
}
/*
** This function is called whenever a packet
** is dropped by the SPAR driver.
*/
VOID PacketDropped(struct SPARDevUnit *sdu)
{
struct SuperS2PTStats *stats;
/* Packet tracking enabled ? */
if(sdu->sdu_TrackP)
{
stats = (struct SuperS2PTStats *)sdu->sdu_Track.mlh_Head;
/* Check if this packet type is on the tracking list */
while(stats->ss_Node.mln_Succ)
{
if( *(UWORD *)(sdu->sdu_TxBuff+SADDR_LEN*2) == (UWORD)stats->ss_PType)
{
/* Found entry for this packet type, updating stats */
stats->ss_Stats.PacketsDropped++;
break;
}
stats = (struct SuperS2PTStats *)stats->ss_Node.mln_Succ;
}
}
}
/*
** This function is called whenever an orphan packet
** is received (Routine from the agnet device).
*/
VOID
ReceivedOrphan(struct SPARDevUnit *sdu)
{
sdu->sdu_Stats.UnknownTypesReceived++;
}
/*
** This function is used to locate an IO request in a linked
** list and abort it if found.
*/
LONG AbortReq(struct MinList *minlist, struct IOSana2Req *ios2)
{
struct Node *node, *next;
LONG 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 SPARDevUnit *sdu, struct IOSana2Req *ios2)
{
UBYTE tempaddr; /* unvalidated hardware address */
/* Note: we may only be configured once. */
if(!(sdu->sdu_State & SPARUF_CONFIG))
{
/* A PARnet network can only handle addresses of 1 octet, so
* only the last octet of the given ethernet address is used (an
* ethernetaddress occupies the first 6 bytes in ios2_SrcAddr) */
tempaddr = ios2->ios2_SrcAddr[0];
debug(("ConfigInterface: Hw.addr. set to :%d (%08lx%04x).\n",(LONG)tempaddr,*(LONG *)ios2->ios2_SrcAddr,*(UWORD *)(ios2->ios2_SrcAddr+4)))
/* check the address before using, take over when it's ok. */
if ((tempaddr!=0) && (tempaddr!=255))
sdu->sdu_StAddr = tempaddr;
sdu->sdu_State |= SPARUF_CONFIG;
if(!(OpenPARnet(sdu)))
{ /* failed to open */
sdu->sdu_State &= ~SPARUF_CONFIG;
ios2->ios2_Req.io_Error = IOERR_OPENFAIL;
ios2->ios2_WireError = S2WERR_GENERIC_ERROR;
}
}
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_BROADCAST commands.
** It is very limited, because PARnet does not support packet broadcast.
** Only ARP broadcasts will be accepted and processed internally.
**
*/
VOID BroadcastPacket(struct SPARDevUnit *sdu, struct IOSana2Req *ios2)
{
struct BufferManagement *bm;
struct ARPframePacket *ARPreqPacket;
/* ARP response packet */
struct ARPframePacket ARPrespPacket = {0,0,ETHERTYPE_ARP,ARPHRD_ETHER,ETHERTYPE_IP,6,4,ARPOP_REPLY,0,0,0,0};
if (sdu->sdu_DestAddr)
{
memset(ios2->ios2_DstAddr, 0, SANA2_MAX_ADDR_BYTES); /* Clear dest.addr. */
ios2->ios2_DstAddr[0]=sdu->sdu_DestAddr; /* fill in destination address */
WritePacket(sdu,ios2); /* Just write it to the other end */
}
else
{
bm =(struct BufferManagement *) ios2->ios2_BufferManagement;
/* Copy the data out of the packet into our temporary buffer. */
if((*bm->bm_CopyFromBuffer)(sdu->sdu_RxBuff+SHDR_LEN,ios2->ios2_Data,ios2->ios2_DataLength))
{
ARPreqPacket=(struct ARPframePacket *)sdu->sdu_RxBuff; /* Pointer to ARP frame */
if ((UWORD)ios2->ios2_PacketType == ETHERTYPE_ARP) /* ARP broadcast? */
{
debug(("ARP Req (%ld): %08lx%08lx%08lx%08lx%08lx%08lx",ios2->ios2_DataLength,*(LONG *)sdu->sdu_RxBuff,*(LONG *)(sdu->sdu_RxBuff+4),*(LONG *)(sdu->sdu_RxBuff+8),*(LONG *)(sdu->sdu_RxBuff+12),*(LONG *)(sdu->sdu_RxBuff+16),*(LONG *)(sdu->sdu_RxBuff+20)))
ARPrespPacket.ar_dstaddr=sdu->sdu_StAddr; /* frame target h/w address (myself) */
/* frame type already filled in
* with ETHERTYPE_ARP as default */
/* copy sender's addresses to target address fields */
ARPrespPacket.ar_tpa=ARPreqPacket->ar_spa;
memcpy(&ARPrespPacket.ar_tha,ARPreqPacket->ar_sha,EADDR_LEN);
/* copy target IP address to sender's IP address field */
ARPrespPacket.ar_spa=ARPreqPacket->ar_tpa;
/* calculate the target hardware address from the last octet of the
* target IP address */
ARPrespPacket.ar_sha[0]=
/* Fill in also hardware source address */
ARPrespPacket.ar_srcaddr= (UBYTE)ARPrespPacket.ar_spa;
/* copy packet to receiver buffer */
memcpy(sdu->sdu_RxBuff,&ARPrespPacket,sizeof(ARPrespPacket));
debug(("Tx ARP resp (%ld): %08lx%08lx%08lx%08lx%08lx%08lx",sizeof(ARPrespPacket),*(LONG *)sdu->sdu_RxBuff,*(LONG *)(sdu->sdu_RxBuff+4),*(LONG *)(sdu->sdu_RxBuff+8),*(LONG *)(sdu->sdu_RxBuff+12),*(LONG *)(sdu->sdu_RxBuff+16),*(LONG *)(sdu->sdu_RxBuff+20)))
GotPacket(sdu,sizeof(ARPrespPacket));
}
else /* non-ARP broadcast packet */
{
debug(("Unsupported broadcast packet type\n"))
PacketDropped(sdu); /* Can't handle address 255 */
}
}
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 function handles S2_GETSTATIONADDRESS commands.
**
** PARnet uses only 1 octet, so only the 1th byte in ios2_SrcAddr will be
** filled.
**
** Rhialto: What we want is the config file address as hardware
** address, and the S2_CONFIGINTERFACE address as current address.
**
*/
VOID GetStationAddress(struct SPARDevUnit *sdu, struct IOSana2Req *ios2)
{
/* Clear address space */
memset(ios2->ios2_DstAddr, 0, SANA2_MAX_ADDR_BYTES);
memset(ios2->ios2_SrcAddr, 0, SANA2_MAX_ADDR_BYTES);
/* Place our current address in first position of Src_Addr.
* And our hardware address (given in spar#.config) in Dst_Addr.
*/
ios2->ios2_SrcAddr[0]=sdu->sdu_StAddr;
ios2->ios2_DstAddr[0]=sdu->sdu_HwAddr;
debug(("GetStationAddr: %08lx%04lx.\n",*(LONG *)ios2->ios2_SrcAddr,*(UWORD *)(ios2->ios2_SrcAddr+4)))
TermIO(ios2);
}
/*
** This function handles S2_DEVICEQUERY comands.
*/
VOID DeviceQuery(struct SPARDevUnit *sdu, struct IOSana2Req *ios2)
{
struct Sana2DeviceQuery *sdq;
sdq = (struct Sana2DeviceQuery *)ios2->ios2_StatData;
sdq->AddrFieldSize = 48; /* six bytes (48 bits) hardware address */
/* this is for Ethernet compatibility, */
/* Spar uses 1 byte h/w addresses. */
sdq->MTU = SPAR_MTU; /* max. trans. unit */
sdq->BPS = SPAR_SPEED; /* PARnet has fixed speed */
sdq->HardwareType = S2WireType_Ethernet; /* fake! */
sdq->DevQueryFormat = NULL;
sdq->DeviceLevel = NULL;
sdq->SizeSupplied = sizeof(struct Sana2DeviceQuery);
TermIO(ios2);
}
/*
** This function is used for handling CMD_WRITE
** commands.
*/
VOID WritePacket(struct SPARDevUnit *sdu, struct IOSana2Req *ios2)
{
/* Make sure that we are online. */
if(sdu->sdu_State & SPARUF_ONLINE)
{
/* Make sure it's a legal length. */
if(ios2->ios2_DataLength <= SPAR_MTU)
{
/* See if our PARnet CMD_WRITE command is busy. If it's not, send
* the IO request to SendPacket. */
if(CheckIO((struct IORequest *)sdu->sdu_ParTx))
{
WaitIO((struct IORequest *)sdu->sdu_ParTx);
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);
}
}
/*
** This routine handles CMD_READ commands. We
** always queue these unless we're offline.
*/
VOID ReadPacket(struct SPARDevUnit *sdu, struct IOSana2Req *ios2)
{
if(sdu->sdu_State & SPARUF_ONLINE)
{
/* Queue it... */
ObtainSemaphore(&sdu->sdu_ListLock);
AddTail((struct List *)&sdu->sdu_Rx,(struct Node *)ios2);
ReleaseSemaphore(&sdu->sdu_ListLock);
}
else
{
/* Sorry, we're offline */
ios2->ios2_Req.io_Error = S2ERR_OUTOFSERVICE;
ios2->ios2_WireError = S2WERR_UNIT_OFFLINE;
TermIO(ios2);
}
}
/*
** This routine handles CMD_READORPHAN commands. We
** always queue these unless we're offline.
**
*/
VOID ReadOrphan(struct SPARDevUnit *sdu, struct IOSana2Req *ios2)
{
if(sdu->sdu_State & SPARUF_ONLINE)
{
/* Queue it...*/
ObtainSemaphore(&sdu->sdu_ListLock);
AddTail((struct List *)&sdu->sdu_RxOrph,(struct Node *)ios2);
ReleaseSemaphore(&sdu->sdu_ListLock);
}
else
{
/* Sorry, we're offline */
ios2->ios2_Req.io_Error = S2ERR_OUTOFSERVICE;
ios2->ios2_WireError = S2WERR_UNIT_OFFLINE;
TermIO(ios2);
}
}
/*
** This routine handles S2_ONEVENT commands.
*/
VOID OnEvent(struct SPARDevUnit *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 & SPARUF_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 & SPARUF_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 gets the current system time and stores
** it in our global statistics structure.
*/
VOID MarkTimeOnline(struct SPARDevUnit *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,NULL))
{
TimerBase = (struct Library *)treq->tr_node.io_Device;
GetSysTime(&sdu->sdu_Stats.LastStart);
CloseDevice((struct IORequest *)treq);
}
FreeMem(treq,sizeof(struct timerequest));
}
}
/*
** This routine handles CMD_ONLINE commands.
*/
VOID Online(struct SPARDevUnit *sdu, struct IOSana2Req *ios2)
{
if(!(sdu->sdu_State & SPARUF_ONLINE))
{
/* We're offline. Try to go online. */
if(OpenPARnet(sdu))
{
if(sdu->sdu_State & SPARUF_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 SPARDevUnit *sdu, struct IOSana2Req *ios2)
{
TermIO(ios2);
if(sdu->sdu_State & SPARUF_ONLINE)
{
/* We're online, so shut everything down. */
ClosePARnet(sdu);
DoOffline(sdu);
DoEvent(sdu,S2EVENT_OFFLINE);
}
}
/*
** This routine is called whenever an "important"
** SANA-II event occurs.
*/
VOID DoEvent(struct SPARDevUnit *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 SPARDevUnit *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);
}