home *** CD-ROM | disk | FTP | other *** search
- /*
- ** $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;
- }
-
-