/* * Copyright 1987 Alan Kent * * Permission is granted to redistribute this code as long * as this message is retained in the code and the code is * not sold without written permission from the author. * * UUCP: {seismo,hplabs,mcvax,ukc,nttlab}!munnari!goanna.oz!ajk * ACSnet: ajk@goanna.oz * ARPA: munnari!goanna.oz!ajk@SEISMO.ARPA */ #include "hd.h" /* initialize library node */ LONG cLibInit ( dev , seg_list ) struct hd_dev *dev; BPTR seg_list; /* I think its BPTR */ { int i; struct hd_unit *unit; struct Process *process; /* easier here than trying to use the automatic initalization */ dev->hd_Dev.dd_Library.lib_Node.ln_Type = NT_DEVICE; dev->hd_Dev.dd_Library.lib_Node.ln_Name = dev_name; dev->hd_Dev.dd_Library.lib_Flags = LIBF_SUMUSED|LIBF_CHANGED; dev->hd_Dev.dd_Library.lib_Version = HD_VERSION; dev->hd_Dev.dd_Library.lib_Revision = HD_REVISION; dev->hd_Dev.dd_Library.lib_IdString = (APTR)dev_id_string; /* Initiate controller. Actually, this is being a bit naughty. */ /* The signals should be allocated in the created subtask, but */ /* I know the subtask does not allocate any signals itself so */ /* any signal we create will be ok. The signal is created for */ /* the interrupt driven controller */ if ( wd_open () < 0 ) return ( NULL ); /* create a single global port on which ALL messages for all devices */ /* and units will arrive. Note that CreatePort allocates a signal. */ /* See the above discussion about signals and wd_open() */ port = CreatePort ( (char*)NULL , (LONG)0 ); if ( port == NULL ) { wd_close (); return ( NULL ); } /* initiate cache */ if ( init_cache () < 0 ) { wd_close (); DeletePort ( port ); return ( NULL ); } /* Create a single task to be shared by all units. */ /* This is to ensure exclusive access to the controller. */ /* Note that the caching at present will only work with a */ /* single unit */ process = CreateProc ( dev_name , (LONG)HD_PRIORITY , (LONG)proc_seg_list >> 2L , (LONG)HD_STACKSIZE ); if ( process == NULL ) { free_cache (); wd_close (); DeletePort ( port ); return ( NULL ); } /* actually, CreateProc returns a pointer to the message port */ /* to use with C, must adjust to start of procedure */ process = PROCPTR ( process ); /* ok, now anything to do with controller is for subtask. */ /* When wd_open was called, it initialized the interrupts to */ /* send an interrupt to this task. Now interrupts should be */ /* sent to the subprocess. */ wd_subtask ( &process->pr_Task ); /* similarly, the port should send message port signals to the task */ port->mp_SigTask = &process->pr_Task; /* initialize all units */ for ( i = 0; i < HD_NUMUNITS; i++ ) { unit = &dev->hd_Unit[i]; unit->hdu_Unit.unit_OpenCnt = 0; unit->hdu_Unit.unit_MsgPort = port; unit->hdu_UnitNum = i; unit->hdu_Task = &process->pr_Task; /* shared */ } return ( (LONG)dev ); } /* actually, cOpen does not need to return anything, but libraries do, */ /* so I am returning the device pointer just to be safe */ struct hd_dev * cOpen ( dev , ior , unit_num , flags ) struct hd_dev *dev; struct IOExtHD *ior; LONG unit_num; ULONG flags; { register struct hd_unit *unit; ior->iohd_TD.iotd_Req.io_Error = 0; /* check for legal unit number */ if ( unit_num < 0 || unit_num >= HD_NUMUNITS ) { ior->iohd_TD.iotd_Req.io_Error = TDERR_BadUnitNum; return ( NULL ); } unit = &dev->hd_Unit[ unit_num ]; /* set up request, unit, open counts, and we no longer want to expunge */ /* the library when all closes are done */ ior->iohd_TD.iotd_Req.io_Unit = &unit->hdu_Unit; dev->hd_Flags &= ~ (ULONG) LIBF_DELEXP; dev->hd_Dev.dd_Library.lib_OpenCnt++; unit->hdu_Unit.unit_OpenCnt++; return ( dev ); } BPTR /* seg list */ cClose ( dev , ior ) struct hd_dev *dev; struct IOExtHD *ior; { ior->iohd_TD.iotd_Req.io_Error = 0; if ( --(ior->iohd_TD.iotd_Req.io_Unit->unit_OpenCnt) == 0 ) { /* expunge unit */ /* well, actually we have only one task for all units so dont */ /* kill the unit task */ } /* clear out ior so that it cant be accidently reused */ ior->iohd_TD.iotd_Req.io_Unit = NULL; ior->iohd_TD.iotd_Req.io_Device = NULL; /* one less person using the device driver */ if ( --(dev->hd_Dev.dd_Library.lib_OpenCnt) <= 0 && ( dev->hd_Flags & LIBF_DELEXP ) ) /* no one has us open anymore and we have a delayed expunge */ /* command pending, so do it! */ return ( cExpunge ( dev ) ); return ( NULL ); } BPTR /* seg list */ cExpunge ( dev ) struct hd_dev *dev; { BPTR seg_list; if ( dev->hd_Dev.dd_Library.lib_OpenCnt > 0 ) { /* someone still has the library open - mark that should be */ /* deleted when everyone has closed us */ dev->hd_Flags |= LIBF_DELEXP; return ( NULL ); } /* stop anyone else from opening the device */ Remove ( dev ); free_cache (); wd_close (); DeletePort ( port ); CloseLibrary ( DOSBase ); seg_list = dev->hd_SegList; FreeMem ( ((char *)dev) - dev->hd_Dev.dd_Library.lib_NegSize , (LONG) dev->hd_Dev.dd_Library.lib_NegSize + (LONG) dev->hd_Dev.dd_Library.lib_PosSize ); return ( seg_list ); } void cBeginIO ( dev , ior ) struct hd_dev *dev; struct IOExtHD *ior; { ior->iohd_TD.iotd_Req.io_Error = 0; /* If some commands can be done very quickly, then a call to */ /* perform_io () could be made. Care must be taken to ensure */ /* that the disk driver task is not in perform_io () at the */ /* same time so some global lock variable should be used. */ /* Also, if the device has been stopped with CMD_STOP, we should */ /* not try to do the command immediately. When the command cannot */ /* be done immediately, the requests must be queued by sending */ /* them to the task. For safty, Disable() and Enable() may need to */ /* be called to safely examine and modify global variables. */ /* In the device driver, the status commands must always work */ /* immediately and cannot do a Wait() as they are called from */ /* interrupts too (and so use the interrupt stack). (Well, thats */ /* what the manual said anyway). Since for a harddisk the code */ /* is so trivial, it was repeated here rather than calling */ /* perform_io(). */ if ( ( ior->iohd_TD.iotd_Req.io_Command & ~(ULONG)TDF_EXTCOM ) > HD_LASTCOMM ) { ior->iohd_TD.iotd_Req.io_Error = IOERR_NOCMD; return; } switch ( (int) ior->iohd_TD.iotd_Req.io_Command ) { case TD_CHANGENUM : ior->iohd_TD.iotd_Req.io_Actual = CHANGE_COUNT; break; case TD_CHANGESTATE : ior->iohd_TD.iotd_Req.io_Actual = 0; break; case TD_PROTSTATUS : ior->iohd_TD.iotd_Req.io_Actual = 0; break; default : /* clear quick flag to say that message is going to be queued */ ior->iohd_TD.iotd_Req.io_Flags &= ~ (ULONG) IOF_QUICK; PutMsg ( ior->iohd_TD.iotd_Req.io_Unit->unit_MsgPort , ior ); break; } } void cAbortIO ( dev , ior ) struct hd_dev *dev; struct IOExtHD *ior; { ior->iohd_TD.iotd_Req.io_Error = 0; /* can any of the commands be aborted? */ }