[comp.sys.amiga] EXEC DEVICE IO SUPPORT MODULE

dillon@CORY.BERKELEY.EDU.UUCP (04/26/87)

	This module makes it *very* easy to use EXEC devices, such as the
serial.device.  It's generic and can be used with any EXEC device.  It 
allows you to easy open libraries, get multiple io channels for a device,
read, write, do asyncronous operations, etc....

	A couple of notes on the source:

	(1) Aztec users: It must be compiled using 32 bit ints 

	(2) Since I have a symbols file that I include with every
	compilation, every Amiga include file is included when
	I compile my programs.  Therefore, I may have missed
	some #include's that should be in there.  NOTE: DIO.C does
	not #include DIO.H.  DIO.H provides a set of macros to make
	things even *more* easier.

					-Matt


#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create:
#	dio.c
#	dio.h
#	xmisc.h
# This archive created: Sat Apr 25 18:40:11 1987
export PATH; PATH=/bin:/usr/bin:$PATH
echo shar: "extracting 'dio.c'" '(9668 characters)'
if test -f 'dio.c'
then
	echo shar: "will not over-write existing file 'dio.c'"
else
cat << \!Funky!Stuff! > 'dio.c'

/*
 *  DIO.C
 *  MUST BE COMPILED USING 32 BIT INTS
 *  (C)Copyright 1987 by Matthew Dillon.
 *  Freely distributable.  Donations Welcome, I guess.
 *
 *	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_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()
 *
 *
 *  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.
 *
 *
 *  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 <exec/types.h>
#include <exec/io.h>
#include <exec/memory.h>
#include <exec/ports.h>
#include <devices/timer.h>
#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  */
} 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;
    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_cact(chan,n)
CHAN *chan;
{
    if (chan)
	chan->cact = n;
}

void
dio_close(chan)
register CHAN *chan;
{
    if (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->base->refs;
	llink(&nc->base->list, &nc->link);
	return(nc);
fail1:	FreeMem(nc, sizeof(CHAN));
    }
fail2:
    return(NULL);
}


dio_signal(chan)
CHAN *chan;
{
    if (chan)
	return(chan->CPORT->mp_SigBit);
    return(-1);
}


CHAN *
dio_ctl_to(chan, com, buf, len, to)
register CHAN *chan;
char *buf;
{
    register long mask;

    if (!chan)
	return(NULL);
    if (chan->timer.io_Message.mn_ReplyPort == NULL) {
	chan->timer.io_Message.mn_ReplyPort = chan->CPORT;
	chan->timer.io_Command = TR_ADDREQUEST;
	chan->timer.io_Actual = 0;
	OpenDevice("timer.device", UNIT_VBLANK, &chan->timer);
    }
    mask = 1 << chan->CPORT->mp_SigBit;
    dio_ctl(chan, (com>0)?-com:com, buf, len);	/* SendIO the request */
    chan->timer.io_Length = to;     /* setup timer  */
    SendIO(&chan->timer);	    /* start timer  */
    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)
	return(NULL);
    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 */
	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;
	    SendIO(chan);
	} else {			    /* syncronous IO  */
	    chan->ior.ior.io_Command = com;
	    DoIO(chan);
	}
    }
    return(chan);
}


CHAN *
dio_isdone(chan)
register CHAN *chan;
{
    if (!chan)
	return(NULL);
    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)
	return(NULL);
    if (chan->notclear) {
	WaitIO(chan);		/* wait and remove from rp */
	chan->notclear = 0;
    }
    return(chan);
}


CHAN *
dio_abort(chan)
register CHAN *chan;
{
    if (!chan)
	return(NULL);
    if (chan->notclear) {
	AbortIO(chan);		/* Abort it   */
	WaitIO(chan);		/* wait and remove from rp */
	chan->notclear = 0;
    }
    return(chan);
}



!Funky!Stuff!
fi  # end of overwriting check
echo shar: "extracting 'dio.h'" '(4391 characters)'
if test -f 'dio.h'
then
	echo shar: "will not over-write existing file 'dio.h'"
else
cat << \!Funky!Stuff! > 'dio.h'

/*
 * DIO.H
 *
 *  (C)Copyright 1987 Matthew Dillon, All Rights Reserved
 *  Freely distributable.  Donations welcome, I	guess.
 *
 *	Matthew	Dillon
 *	891 Regal Rd.
 *	Berkeley, Ca.	94708
 *
 */

#ifndef	MYLIB_DIO_H
#define	MYLIB_DIO_H
#include <exec/types.h>
#include <exec/io.h>
#include <exec/memory.h>
#include <exec/ports.h>
#include <devices/timer.h>

typedef	struct IORequest IOR;
typedef	struct IOStdReq	 STD;
typedef	struct MsgPort	 PORT;

/*
 *	'to' is in microsections.  The IO request structure
 *  pointer is optional	to dio_open().	If NULL, dio_open()
 *  initializes	it's own IO request (to mostly zero).  You have
 *  to provide an IO request structure,	for instance, if openning
 *  a console device since the window pointer must be passed to
 *  OpenDevice().
 *
 *	each DFD descriptor has	it's own signal.
 *
 *	dio_isdone() returns 1 if the channel is clear,	0 otherwise.
 */

extern long dio_open();		/* dfd	  = dio_open(devname,unit,flags,req)*/
extern long dio_dup();		/* newdfd = dio_dup(dfd)    */
extern STD *dio_ctl();		/* req	= dio_ctl(dfd,com,buf,len)	    */
extern STD *dio_ctl_to();	/* req	= dio_ctl_to(dfd,com,buf,len,to)    */
extern STD *dio_wait();		/* req	= dio_wait(dfd)	    */
extern STD *dio_abort();	/* req	= dio_abort(dfd)    */
extern STD *dio_isdone();	/* req	= dio_isdone(dfd)   */
extern int dio_signal();	/* signm= dio_signal(dfd)   */
extern void dio_close();	/*  dio_close(dfd)	    */
extern void dio_cloesgroup();	/*  dio_closegroup(dfd)	    */
extern void dio_cact();		/*  dio_cact(dfd,bool)	    */



/*
 * dio_simple()	and related macros return the !io_Error	field. That
 * is, 0=ERROR,	1=OK
 *
 * dio_actual()	returns	the io_Actual field.
 *
 * NOTE: the io_Actual field may not be	set by the device if an
 * error condition exists.  To make the	io_ctl() and io_ctl_to()
 * call	automatically clear the	io_Actual field	before doing the
 * io operation, use the DIO_CACT() call.  The reason this isn't
 * done	automatically by default is that some devices require
 * parameters to be passed in the io_Actual field (like	the
 * timer.device).
 *
 *  Remember, Asyncronous IO is	done by	sending	-com instead of	com.
 *
 *	CALL			    Syncronous IO   Asyncronous	IO
 *
 *  dio_simple(dfd,com)		    0=ERROR, 1=OK   undefined
 *  dio_actual(dfd,com)		    io_Actual	    undefined
 *  dio_reset(dfd)		    0=ERROR, 1=OK   n/a
 *  dio_update(dfd)		    0=ERROR, 1=OK   n/a
 *  dio_clear(dfd)		    0=ERROR, 1=OK   n/a
 *  dio_stop(dfd)		    0=ERROR, 1=OK   n/a
 *  dio_start(dfd)		    0=ERROR, 1=OK   n/a
 *  dio_flush(dfd)		    0=ERROR, 1=OK   n/a
 *  dio_getreq(dfd)		    returns a ptr to the IO
 *				    request structure
 *  NOTE: If you use the following, you	probably want to have the
 *  device library automatically clear the io_Actual field before
 *  sending the	request	so you get 0 if	an error occurs.  That
 *  is:	dio_cact(dfd,1);
 *
 *
 *  dio_read(dfd,buf,len)	    returns actual bytes read
 *  dio_write(dfd,buf,len)	    returns actual bytes written
 *
 *	The timeout argument for dio_readto() and dio_writeto()
 *	is in MICROSECONDS, up to 2^31uS.
 *
 *  dio_readto(dfd,buf,len,to)	    returns actual bytes read
 *  dio_writeto(dfd,buf,len,to)	    returns actual bytes written
 *
 *	The asyncronous	dio_reada() and	dio_writea() do	not
 *	return anything.
 *
 *  dio_reada(dfd,buf,len)	    begin asyncronous read
 *  dio_writea(dfd,buf,len)	    begin asyncronous write
 */

#define	dio_simple(dfd,com)	(!dio_ctl(dfd,com,0,0)->io_Error)
#define	dio_actual(dfd,com)	( dio_ctl(dfd,com,0,0)->io_Actual)
#define	dio_reset(dfd)		dio_simple(dfd,CMD_RESET)
#define	dio_update(dfd)		dio_simple(dfd,CMD_UPDATE)
#define	dio_clear(dfd)		dio_simple(dfd,CMD_CLEAR)
#define	dio_stop(dfd)		dio_simple(dfd,CMD_STOP)
#define	dio_start(dfd)		dio_simple(dfd,CMD_START)
#define	dio_flush(dfd)		dio_simple(dfd,CMD_FLUSH)
#define	dio_getreq(dfd)		dio_ctl(dfd,0,0,0)

#define	dio_read(dfd,buf,len)	    (dio_ctl(dfd,CMD_READ,buf,len)->io_Actual)
#define	dio_write(dfd,buf,len)	    (dio_ctl(dfd,CMD_WRITE,buf,len)->io_Actual)
#define	dio_readto(dfd,buf,len,to)  (dio_ctl_to(dfd,CMD_READ,buf,len,to)->io_Actual)
#define	dio_writeto(dfd,buf,len,to) (dio_ctl_to(dfd,CMD_WRITE,buf,len,to)->io_Actual)
#define	dio_reada(dfd,buf,len)	    ((void)dio_ctl(dfd,-CMD_READ,buf,len))
#define	dio_writea(dfd,buf,len)	    ((void)dio_ctl(dfd,-CMD_WRITE,buf,len))

#endif


!Funky!Stuff!
fi  # end of overwriting check
echo shar: "extracting 'xmisc.h'" '(644 characters)'
if test -f 'xmisc.h'
then
	echo shar: "will not over-write existing file 'xmisc.h'"
else
cat << \!Funky!Stuff! > 'xmisc.h'

/*
 * XMISC.H
 */

#ifndef	MYLIB_XMISC_H
#define	MYLIB_XMISC_H

#define	GRAPHICS_LIB	    0x0001L
#define	INTUITION_LIB	    0x0002L
#define	EXPANSION_LIB	    0x0004L
#define	DISKFONT_LIB	    0x0008L
#define	TRANSLATOR_LIB	    0x0010L
#define	ICON_LIB	    0x0020L
#define	MATH_LIB	    0x0040L
#define	MATHTRANS_LIB	    0x0080L
#define	MATHIEEEDOUBBAS_LIB 0x0100L
#define	MATHIEEESINGBAS_LIB 0x0200L
#define	LAYERS_LIB	    0x0400L
#define	CLIST_LIB	    0x0800L
#define	POTGO_LIB	    0x1000L
#define	TIMER_LIB	    0x2000L

struct OLI {
    char *name;
    long *var;
};

typedef	struct _XLIST {
    struct _XLIST *next, **prev;
} XLIST;

#endif


!Funky!Stuff!
fi  # end of overwriting check
exit 0
#	End of shell archive