home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Meeting Pearls 3
/
Meeting_Pearls_III.iso
/
Pearls
/
tcp
/
Networking
/
Sana-II
/
spar372.lha
/
Sources
/
device_funcs.c
next >
Wrap
C/C++ Source or Header
|
1993-12-17
|
21KB
|
665 lines
/*
** $Source: dh1:network/parnet/Sana2/Sources/device_funcs.c,v $
** $State: Exp $
** $Revision: 37.2 $
** $Date: 93/12/17 22:06:23 $
** $Author: S.A.Pechler $
**
** Amiga SANA-II Example PARnet device driver.
**
** General Device 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"
/* Our device name */
const char SPARName[] = SPARDEVNAME;
/*
** Device Open vector
**
** a1 - SANA2 IO Request
** a6 - Pointer to our device base
** d0 - Unit number
** d1 - Flags
**
*/
LONG ASM DevOpen(REG(a1) struct IOSana2Req *ios2,
REG(a6) struct SPARDevice *SPARDevice,
REG(d0) ULONG s2unit,
REG(d1) ULONG s2flags)
{
struct SPARDevUnit *sdu; /* our unit structure (it could already have been
* initialized on a previous DevOpen() call) */
struct TagItem *bufftag; /* Tags for the SANA-II buffer management functions */
struct Library *UtilityBase; /* utility.library is used for the Tag functions */
struct BufferManagement *bm; /* Sana-II BufferManagement Buffer Copy functions */
LONG returncode;
BOOL status = FALSE; /* Flag: Initialisation succeeded/failed */
/* Enforce single threading since we may need to Wait() when starting
* up the Unit process. If somebody decides to DoExpunge() before we
* get the semaphore, system is probably blowing up anyways.
*/
ObtainSemaphore(&SPARDevice->sd_Lock);
SPARDevice->sd_Device.lib_OpenCnt++; /* So we won't expunge ourselves... */
if(s2unit < SD_MAXUNITS) /* Legal Unit number? */
{
initsyslog();
if(sdu = InitSPARUnit(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;
SPARDevice->sd_Device.lib_OpenCnt++;
SPARDevice->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 = (struct Unit *)sdu;
ios2->ios2_Req.io_Device = (struct Device *)SPARDevice;
}
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;
}
SPARDevice->sd_Device.lib_OpenCnt--;
ReleaseSemaphore(&SPARDevice->sd_Lock);
return(returncode);
}
/*
** Device Close vector.
**
** a1 - IOReq
** a6 - Device Pointer
**
** There are two different things that might be returned from the Close
** routine. If the device wishes to be unloaded, the Close should return
** the segment list (as given to DevInit). Otherwise close MUST return NULL.
*/
BPTR ASM DevClose(REG(a1) struct IOSana2Req *ios2,
REG(a6) struct SPARDevice *SPARDevice)
{
struct SPARDevUnit *sdu;
BPTR seglist = NULL;
ObtainSemaphore(&SPARDevice->sd_Lock);
sdu = (struct SPARDevUnit *)ios2->ios2_Req.io_Unit;
/* 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);
/* 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;
SPARDevice->sd_Device.lib_OpenCnt--;
ReleaseSemaphore(&SPARDevice->sd_Lock);
/* Check to see if we've been asked to expunge. */
if(SPARDevice->sd_Device.lib_Flags & LIBF_DELEXP)
seglist = DevExpunge(SPARDevice);
return(seglist);
}
/*
** Device Expunge vector
**
** a6 - Device base
**
** There are two different things that might be returned from the Expunge
** routine. If the device is no longer open then Expunge should return the
** segment list (as given to DevInit). Otherwise Expunge should set the
** delayed expunge flag (LIBF_DELEXP) and return NULL.
**
** One other important note: You may NEVER EVER Wait() in expunge. Period.
** This is because Expunge is called from the memory allocator, A Wait()
** call would take too long time to complete.
*/
BPTR ASM DevExpunge(REG(a6) struct SPARDevice *SPARDevice)
{
BPTR seglist;
ULONG devbase;
LONG devbasesize;
if(SPARDevice->sd_Device.lib_OpenCnt)
{
/* Sorry, we're busy. We'll expunge later on if we can. */
SPARDevice->sd_Device.lib_Flags |= LIBF_DELEXP;
seglist = (BPTR)NULL;
}
else
{
/* Free up our library base and function table after
* removing ourselves from the library list. */
Remove((struct Node *)SPARDevice);
seglist = SPARDevice->sd_SegList;
/* Calculate the amount of memory to be FreeMem'ed */
devbase = (ULONG) SPARDevice;
devbasesize = (ULONG)SPARDevice->sd_Device.lib_NegSize;
devbase = devbase - devbasesize;
devbasesize += (ULONG)SPARDevice->sd_Device.lib_PosSize;
uninitsyslog();
FreeMem((APTR)devbase,devbasesize);
ExtDeviceBase = NULL; /* ! */
}
return(seglist);
}
/*
** InitSPARUnit
**
** Initialize (if needed) a new SPAR device Unit and process.
**
*/
struct SPARDevUnit *InitSPARUnit(ULONG s2unit)
{
struct SPARDevice *SPARDevice = SPARBase;
struct SPARDevUnit *sdu;
struct TagItem NPTags[]={NP_Entry, 0, NP_Name, 0, NP_Priority, SPAR_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(!SPARDevice->sd_Units[s2unit])
{
/* Open up dos.library */
if(SPARDevice->sd_DOSBase = OpenLibrary("dos.library",37L))
{
/* Allocate a new Unit structure */
if(sdu = AllocMem(sizeof(struct SPARDevUnit), MEMF_CLEAR|MEMF_PUBLIC))
{
/* Do some initialization on the Unit structure.
* We set the Unit's message port to PA_IGNORE until the
* new process has a change to set it up.
*/
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 *)SPARName;
sdu->sdu_UnitNum = s2unit;
sdu->sdu_Device = (struct Device *) SPARDevice;
sdu->sdu_State = 0; /* status: nothing */
/* Try to read in our configuration file */
if(ReadConfig(sdu))
{
/* Start up the unit process */
if(replyport = CreateMsgPort())
{
SPARDevice->sd_Startup.Msg.mn_ReplyPort = replyport;
SPARDevice->sd_Startup.Device = (struct Device *) SPARDevice;
SPARDevice->sd_Startup.Unit = (struct Unit *)sdu;
NPTags[0].ti_Data = (ULONG) &DevProcCEntry; /* Assembly entry point for the unit process. */
/* Was DevProcEntry in Spar_device.asm */
NPTags[1].ti_Data = (ULONG) SPARName; /* Process name */
/* Rhialto: use opener's priority */
NPTags[2].ti_Data = (ULONG) FindTask(NULL)->tc_Node.ln_Pri;
if(sdu->sdu_Proc = CreateNewProc(NPTags))
{
/* Wait for process setup completion (see DevProcCEntry) */
PutMsg(&sdu->sdu_Proc->pr_MsgPort,(struct Message *)&SPARDevice->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 SPARDevUnit));
else
/* Set up the Unit structure pointer in the device base */
SPARDevice->sd_Units[s2unit] = (struct Unit *)sdu;
}
debug(("InitSPARUnit: opened unit: %d\n",s2unit))
CloseLibrary(SPARDevice->sd_DOSBase);
}
}
debug(("InitSPARUnit: return(%d)\n",SPARDevice->sd_Units[s2unit]))
return((struct SPARDevUnit *)SPARDevice->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 SPARDevUnit *sdu)
{
struct SPARDevice *SPARDevice = SPARBase;
struct Task *unittask;
unittask = (struct Task *)sdu->sdu_Proc;
sdu->sdu_Proc = (struct Process *)FindTask(NULL);
Signal(unittask,SIGBREAKF_CTRL_F);
Wait(SIGBREAKF_CTRL_F);
SPARDevice->sd_Units[sdu->sdu_UnitNum] = NULL;
FreeMem(sdu, sizeof(struct SPARDevUnit));
}
/*
**
** BeginIO
**
** This is the dispatch point for the driver's incoming IORequests.
**
** BeginIO starts all incoming IO. The IO is either queued up for the
** unit task or processed immediately.
**
** IMPORTANT:
** The exec WaitIO() function uses the IORequest node type (LN_TYPE)
** as a flag. If set to NT_MESSAGE, it assumes the request is
** still pending and will wait. If set to NT_REPLYMSG, it assumes the
** request is finished. It's the responsibility of the device driver
** to set the node type to NT_MESSAGE before returning to the user.
*/
/* This define is used to tell which commands should be handled
* immediately (on the caller's schedule).
* In this case, no immediate commands are defined.
*/
#define SPAR_IMMEDIATES NULL
VOID ASM DevBeginIO(REG(a1) struct IOSana2Req *ios2,
REG(a6) struct SPARDevice *SPARDevice)
{
/* set node type: request pending */
ios2->ios2_Req.io_Message.mn_Node.ln_Type = NT_MESSAGE;
/* Check for valid command */
if(ios2->ios2_Req.io_Command < S2_END)
/* check if this command can be handled immediately */
if ((ios2->ios2_Req.io_Flags & IOF_QUICK) &&
((1L << ios2->ios2_Req.io_Command) & SPAR_IMMEDIATES))
PerformIO(ios2); /* Process immediately */
else
{
/* Queue up to the unit task */
ios2->ios2_Req.io_Flags &= ~IOF_QUICK; /* we did NOT complete this quickly */
PutMsg((struct MsgPort *)ios2->ios2_Req.io_Unit,(struct Message *)ios2);
}
else
{ /* Unknown device command */
ios2->ios2_Req.io_Error = IOERR_NOCMD;
TermIO(ios2);
}
}
/*
** TermIO sends the IO request back to the user. It knows not to mark
** the device as inactive if this was an immediate request or if the
** request was started from the server task.
*/
VOID TermIO(struct IOSana2Req *ios2)
{
#ifdef HANDLE_IO_QUICK
/* check if this was an immediaty request */
if(!((1L << ios2->ios2_Req.io_Command) & SPAR_IMMEDIATES))
{
struct SPARDevUnit *sdu;
sdu = (struct SPARDevUnit *)ios2->ios2_Req.io_Unit; /* get the Unit pointer */
/* We may need to turn the active bit off, when IO came not from the
* task */
if (!(sdu->sdu_Unit.unit_flags & UNITB_INTASK))
{
/* The task does not have more work to do */
sdu->sdu_Unit.unit_flags &= ~UNITB_ACTIVE;
}
/*
* Code added from the messydisk.device by Rhialto:
*
* The task may have work to do that came in while we were processing
* in the caller's context.
*/
if (sdu->sdu_Unit.unit_flags & UNITF_WAKETASK)
{ sdu->sdu_Unit.unit_flags &= ~UNITF_WAKETASK;
WakePort(&sdu->sdu_Unit.unit_MsgPort);
}
}
#endif
/* If the quick bit is still set then we don't need to reply
* msg, just return to the user. */
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.
*/
LONG ASM DevAbortIO(REG(a1) struct IOSana2Req *ios2,
REG(a6) struct SPARDevice *SPARDevice)
{
struct SPARDevUnit *sdu = (struct SPARDevUnit *)ios2->ios2_Req.io_Unit;
LONG result = NULL;
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 routine is called whenever a CMD_WRITE request
** has returned from the PARnet driver.
*/
VOID ServiceTxPort(struct SPARDevUnit *sdu, struct IOParReq *IOPar)
{
struct IOSana2Req *ios2;
if(IOPar->io_Error) /* a write timeout occured */
PacketDropped(sdu);
if(sdu->sdu_State & SPARUF_ONLINE)
{
/* Get next CMD_WRITE request (if present) */
ObtainSemaphore(&sdu->sdu_ListLock);
ios2 = (struct IOSana2Req *)RemHead((struct List *)&sdu->sdu_Tx);
ReleaseSemaphore(&sdu->sdu_ListLock);
if(ios2) /* Is a CMD_WRITE request pending? */
{
SendPacket(sdu, ios2);
sdu->sdu_NoMore = TRUE; /* There might be more */
}
}
else
sdu->sdu_NoMore = TRUE;
}
/*
** This routine is called whenever a CMD_READ request
** returns from the PARnet driver.
*/
VOID DoPARnet(struct SPARDevUnit *sdu, struct IOParReq *IOPar)
{
/* Check if received data is valid in size */
/* Not to small? */
if (IOPar->io_Actual < SHDR_LEN) ReceivedGarbage(sdu);
else
{
IOPar->io_Length=IOPar->io_Actual;
/* Not too large? */
if(IOPar->io_Length > SPAR_MTU) PacketOverrun(sdu);
else GotPacket(sdu,IOPar->io_Length);
}
/* Queue up another CMD_READ request...*/
QueueParRequest(sdu);
}
/*
** This is the C entry point for the Unit process.
** A6 has been set up by the assembly stub in spar_device.asm.
*/
VOID ASM DevProcCEntry(VOID)
{
struct Process *proc;
struct SPARDevUnit *sdu;
struct IOParReq *iopar;
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(NULL);
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 SPARDevUnit *)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; /* make it "live" */
/* 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 PARnet 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(InitPARnet(sdu))
sdu->sdu_Proc = proc;
}
/* Initialization done. Reply to our startup message (see InitSparUnit),
* so they can continue */
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;
#ifdef HANDLE_IO_QUICK
/*
* Lock the device. If it fails, we have set a flag such that the
* TermIO wakes us again.
*/
sdu->sdu_Unit.unit_flags |= UNITF_WAKETASK;
if (BSET_ACTIVE(&sdu->sdu_Unit.unit_flags))
continue;
sdu->sdu_Unit.unit_flags |= UNITF_INTASK;
#endif
/* 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;
/* Handle messages from the user (were queued in DevBeginIO) */
if(ios2 = (struct IOSana2Req *)GetMsg((struct MsgPort *)sdu))
{
sdu->sdu_NoMore = TRUE; /* there might be more */
PerformIO(ios2); /* process an io request */
}
/* Handle message from the receiver */
if(iopar = (struct IOParReq *)GetMsg(sdu->sdu_RxPort))
if(sdu->sdu_State & SPARUF_ONLINE)
{
sdu->sdu_NoMore = TRUE; /* there might be more? */
DoPARnet(sdu, iopar);
}
/* Handle message from the transmitter */
if(iopar = (struct IOParReq *)GetMsg(sdu->sdu_TxPort))
{
sdu->sdu_NoMore = TRUE; /* there might be more? */
ServiceTxPort(sdu, iopar);
}
}
}
#ifdef HANDLE_IO_QUICK
sdu->sdu_Unit.unit_flags &= ~(UNITF_ACTIVE | UNITF_INTASK | UNITF_WAKETASK);
#endif
/* No more messages on my port */
/* If we're online, we need to shut everything down. */
if(sdu->sdu_State & SPARUF_ONLINE)
{
ClosePARnet(sdu);
FreeSignal((long)signalbit);
while(bm = (struct BufferManagement *)RemHead((struct List *)&sdu->sdu_BuffMgmt))
FreeMem(bm,sizeof(struct BufferManagement));
}
DeinitPARnet(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((long)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;
}