/* * DIO.C * * (C)Copyright 1987 by Matthew Dillon, All rights reserved * Freely distributable. Donations Welcome. This is NOT shareware, * This is NOT public domain. * * Matthew Dillon * 891 Regal Rd. * Berkeley, Ca. 94708 * * EXEC device driver IO support routines... makes everything easy. * * dfd = dio_open(name, unit, flags, req/NULL) * * open an IO device. Note: in some cases you might have to provide * a request structure with some fields initialized (example, the * console device requires certain fields to be initialized). For * instance, if openning the SERIAL.DEVICE, you would want to give * an IOExtSer structure which is completely blank execept for the * io_SerFlags field. * * The request structure's message and reply ports need not be * initialized. The request structure is no longer needed after * the dio_open(). * * NULL = error, else descriptor (a pointer) returned. * * * dio_close(dfd) * * close an IO device. Any pending asyncronous requests are * AbortIO()'d and then Wait'ed on for completion. * * * dio_closegrp(dfd) * * close EVERY DIO DESCRIPTOR ASSOCIATED WITH THE dio_open() call * that was the parent for this descriptor. That is, you can get * a descriptor using dio_open(), dio_dup() it a couple of times, * then use dio_closegrp() on any ONE of the resulting descriptors * to close ALL of them. * * * dio_ddl(dfd,bool) * * Disable BUF and LEN fields in dio_ctl[_to].. dummy parameters * must still be passed, but they are not loaded into the io_Data * and io_Length fields of the io request. This is for devices * like the AUDIO.DEVICE which has io_Data/io_Length in non-standard * places. * * dio_cact(dfd,bool) * * If an error occurs (io_Error field), the io_Actual field is usually * not modified by the device driver, and thus contains garbage. To * provide a cleaner interface, you can have DIO_CTL() and DIO_CTL_TO() * calls automatically pre-clear this field so if an io_Error does * occur, the field is a definate 0 instead of garbage. * * In most cases you will want to do this. An exception is the * TIMER.DEVICE, which uses the io_Actual field for part of the * timeout structure. * * This flags the particular dio descriptor to do the pre-clear, and * any new descriptors obtained by DIO_DUP()ing this one will also * have the pre-clear flag set. * * * dio_dup(dfd) * * Returns a new channel descriptor referencing the same device. * The new descriptor has it's own signal and IO request structure. * For instance, if you openned the serial device, you might want * to dup the descriptor so you can use one channel to pend an * asyncronous read, and the other channel to write out to the device * and do other things without disturbing the asyncronous read. * * * sig = dio_signal(dfd) * * get the signal number (0..31) used for a DIO descriptor. * This allows you to Wait() for asyncronous requests. Note that * if your Wait() returns, you should double check using dio_isdone() * * dio_flags(dfd, or, ~and) * * Modify the io_Flags field in the request, ORing it with the OR * mask, and ANDing it with ~AND mask. E.G., the AUDIO.DEVICE requires * some flags be put in io_Flags. * * req = dio_ctl_to(dfd, command, buf, len, to) * * Same as DIO_CTL() below, but (A) is always syncronous, and * (B) will attempt to AbortIO()+WaitIO() the request if the * timeout occurs before the IO completes. * * the 'to' argument is in microseconds. * * If timeout occurs before request completes, and DIO aborts the * request, some devices do not have the io_Actual field set * properly. * * req = dio_ctl(dfd, command, buf, len) * * DIO_CTL() is the basis for the entire library. It works as follows: * * (1) If the channel isn't clear (there is an asyncronous IO request * still pending), DIO_CTL() waits for it to complete * * (2) If the command is 0, simply return a pointer to the io * request structure. * * (3) If the DIO_CACT() flag is TRUE, the io_Actual field of the * request is cleared. * * (4) Set the io_Data field to 'buf', and io_Length field to 'len' * If the command is positive, use DoIO(). If the command * negative, take it's absolute value and then do a SendIO(). * (The command is placed in the io_Command field, of course). * * (5) return the IO request structure * * * bool= dio_isdone(dfd) * * return 1 if current channel is clear (done processing), else 0. * e.g. if you did, say, an asyncronous read, and dio_isdone() returns * true, you can now use the data buffer returned and look at the * io_Actual field. * * You need not do a dio_wait() after dio_isdone() returns 1. * * * req = dio_wait(dfd) * * Wait on the current channel for the request to complete and * then return the request structure. (nop if channel is clear) * * * req = dio_abort(dfd) * * Abort the request on the current channel (nop if channel is * clear). Sends an AbortIO() if the channel is active and then * WaitIO()'s the request. * * * MACROS: SEE DIO.H * */ #include #include #include #include #include #include "xmisc.h" #define MPC (MEMF_CLEAR|MEMF_PUBLIC) #define CPORT ior.ior.io_Message.mn_ReplyPort #define MAXREQSIZE 128 /* big enough to hold all Amiga iorequests */ typedef struct IORequest IOR; typedef struct IOStdReq STD; typedef struct MsgPort PORT; typedef struct { STD ior; char filler[MAXREQSIZE-sizeof(STD)]; } MAXIOR; typedef struct { struct _CHAN *list; short refs; } DIO; typedef struct _CHAN { MAXIOR ior; DIO *base; XLIST link; /* doubly linked list */ STD timer; char notclear; char cact; /* automatic io_Actual field clear */ char ddl; UBYTE flagmask; } CHAN; extern CHAN *dio_ctl(), *dio_ctl_to(), *dio_wait(), *dio_abort(); extern PORT *CreatePort(); extern char *AllocMem(); CHAN * dio_open(name, unit, flags, req) char *name; MAXIOR *req; /* not really this big */ { register CHAN *chan; register DIO *dio; register PORT *port; int ret; dio = (DIO *)AllocMem(sizeof(DIO), MPC); if (!dio) goto fail3; chan= (CHAN *)AllocMem(sizeof(CHAN), MPC); if (!chan) goto fail2; if (req) chan->ior = *req; chan->CPORT = CreatePort(NULL,0); if (!chan->CPORT) goto fail1; chan->ior.ior.io_Message.mn_Node.ln_Type = NT_MESSAGE; chan->base = dio; chan->flagmask = 0xF0; dio->refs = 1; if (OpenDevice(name, unit, &chan->ior, flags)) { DeletePort(chan->CPORT); fail1: FreeMem(chan, sizeof(CHAN)); fail2: FreeMem(dio, sizeof(DIO)); fail3: return(NULL); } llink(&dio->list, &chan->link); chan->ior.ior.io_Flags = 0; return(chan); } void dio_dfm(chan,mask) CHAN *chan; { chan->flagmask = mask; } void dio_ddl(chan,n) CHAN *chan; { chan->ddl = n; } void dio_cact(chan,n) CHAN *chan; { chan->cact = n; } void dio_close(chan) register CHAN *chan; { dio_abort(chan); lunlink(&chan->link); if (--chan->base->refs == 0) { FreeMem(chan->base, sizeof(DIO)); CloseDevice(&chan->ior); } if (chan->timer.io_Message.mn_ReplyPort) CloseDevice(&chan->timer); DeletePort(chan->CPORT); FreeMem(chan, sizeof(CHAN)); } void dio_closegroup(chan) register CHAN *chan; { register CHAN *nextc; for (chan = chan->base->list; chan; chan = nextc) { chan = (CHAN *)((char *)chan - ((char *)&chan->link - (char *)chan)); nextc = (CHAN *)chan->link.next; dio_close(chan); } } CHAN * dio_dup(chan) register CHAN *chan; { register CHAN *nc; if (chan) { nc = (CHAN *)AllocMem(sizeof(CHAN), MPC); if (!nc) goto fail2; nc->ior = chan->ior; nc->base = chan->base; nc->CPORT = CreatePort(NULL,0); if (!nc->CPORT) goto fail1; nc->ior.ior.io_Flags = NULL; nc->cact = chan->cact; nc->ddl = chan->ddl; nc->flagmask = chan->flagmask; ++nc->base->refs; llink(&nc->base->list, &nc->link); return(nc); fail1: FreeMem(nc, sizeof(CHAN)); } fail2: return(NULL); } dio_signal(chan) CHAN *chan; { return(chan->CPORT->mp_SigBit); } dio_flags(chan,or,and) long chan; { IOR *ior = (void *)chan; ior->io_Flags = (ior->io_Flags | or) & ~and; } CHAN * dio_ctl_to(chan, com, buf, len, to) register CHAN *chan; char *buf; { register long mask; if (chan->timer.io_Message.mn_ReplyPort == NULL) { chan->timer.io_Message.mn_ReplyPort = chan->CPORT; chan->timer.io_Message.mn_Node.ln_Type = NT_MESSAGE; if (OpenDevice("timer.device", UNIT_VBLANK, &chan->timer, 0)) { puts("Panic: DIO_CTL_TO: No timer.device"); } chan->timer.io_Command = TR_ADDREQUEST; } mask = 1 << chan->CPORT->mp_SigBit; dio_ctl(chan, (com>0)?-com:com, buf, len); /* SendIO the request */ chan->timer.io_Actual = to / 1000000; chan->timer.io_Length = to % 1000000; /* setup timer */ chan->timer.io_Flags = 0; BeginIO(&chan->timer); /* start timer running */ while (Wait(mask)) { /* Wait for something */ if (CheckIO(chan)) /* request done */ break; if (CheckIO(&chan->timer)) { /* timeout? */ dio_abort(chan); break; } } AbortIO(&chan->timer); /* kill the timer */ WaitIO(&chan->timer); /* remove from rp */ return(chan); /* return ior */ } CHAN * dio_ctl(chan, com, buf, len) register CHAN *chan; char *buf; { if (chan->notclear) { /* wait previous req to finish */ WaitIO(chan); chan->notclear = 0; } if (com) { if (chan->cact) chan->ior.ior.io_Actual = 0; /* initialize io_Actual to 0*/ chan->ior.ior.io_Error = 0; /* initialize error to 0 */ if (!chan->ddl) { chan->ior.ior.io_Data = (APTR)buf; /* buffer */ chan->ior.ior.io_Length = len; /* length */ } if (com < 0) { /* asyncronous IO */ chan->ior.ior.io_Command = -com; chan->notclear = 1; chan->ior.ior.io_Flags &= chan->flagmask; BeginIO(chan); } else { /* syncronous IO */ chan->ior.ior.io_Command = com; chan->ior.ior.io_Flags = (chan->ior.ior.io_Flags & chan->flagmask) | IOF_QUICK; BeginIO(chan); if (!(chan->ior.ior.io_Flags & IOF_QUICK)) WaitIO(chan); } } return(chan); } CHAN * dio_isdone(chan) register CHAN *chan; { if (chan->notclear) { /* if not clear */ if (CheckIO(chan)) { /* if done */ WaitIO(chan); /* clear */ chan->notclear = 0; return(chan); /* done */ } return(NULL); /* notdone */ } return(chan); /* done */ } CHAN * dio_wait(chan) register CHAN *chan; { if (chan->notclear) { WaitIO(chan); /* wait and remove from rp */ chan->notclear = 0; } return(chan); } CHAN * dio_abort(chan) register CHAN *chan; { if (chan->notclear) { AbortIO(chan); /* Abort it */ WaitIO(chan); /* wait and remove from rp */ chan->notclear = 0; } return(chan); }