/* * PIPE: device driver. * * Usage: * The writer opens PIPE:somename and begins writing to it. The reader * opens PIPE:samename and begins reading from it. It doesn't matter * who opens PIPE:somename first. Note that if the writer opens the * handle first, writes #include #include #include #include #include #include #include #include #include #include #include typedef struct DosPacket DOSPACKET; typedef struct Process PROC; typedef struct DeviceNode DEVNODE; typedef struct FileHandle FH; typedef unsigned char u_char; #define BUFSIZE 4096 #undef BADDR #define BADDR(x) ((APTR)((long)x << 2)) #define ACTION_FIND_INPUT 1005L #define ACTION_FIND_OUTPUT 1006L #define ACTION_END 1007L #define DOS_FALSE 0 #define DOS_TRUE -1 #define ST_EOF 0x01 /* Handle has been closed */ #define ST_WPEND 0x04 /* pending packet is a write */ #define ST_RPEND 0x08 /* pending packet is a read */ #define ST_CPEND 0x10 /* close pending (writer) */ #define OC_FIRST 1 /* first open, needs to be another */ #define OC_BOTH 2 /* both reader and writer open */ #define OC_LAST 3 /* one closed, one remaining */ #define OC_WAITSECOND 4 /* first open was closed before second was openned */ extern long AbsExecBase; extern DOSPACKET *taskwait(); extern char *AllocMem(); long SysBase; typedef struct _PIPE { struct _PIPE *next, **prev; DOSPACKET *pkt; /* Current pending packet, if any */ char buf[BUFSIZE]; /* Output Buffer */ char *name; /* name (allocated strlen(name)+1) */ short s, e, l; /* FIFO start, end, size */ char state; /* Current state */ char openstate; } PIPE; _main() { PROC *myproc; /* my process */ DOSPACKET *mypkt; /* a pointer to the dos packet sent */ BSTR parmdevname; /* pointer to device name in parmpkt Arg1 */ long parmextra; /* extra info passed in parmpkt Arg2 */ DEVNODE *mynode; /* our device node passed in parmpkt Arg3 */ FH *fh; /* a pointer to our file handle */ PIPE *pipe; /* current PIPE handle */ PIPE *Pipe = NULL;/* linked list base for all pipes */ char *str; u_char *ptr; long run = TRUE; /* handler main loop flag */ int ret; /* nominal packet return value */ int totalcnt = 0;/* total # active pipes */ SysBase = AbsExecBase; myproc = (PROC *)FindTask(0L); /* find myself */ mypkt = taskwait(myproc); /* Wait for startup message */ parmdevname = (BSTR)mypkt->dp_Arg1; /* BSTR name passed to handler */ parmextra = mypkt->dp_Arg2; /* Extra Info passed */ mynode = (DEVNODE *)BADDR(mypkt->dp_Arg3); /* ptr to device node */ /* if taskid NOT installed, every ref creates new */ /* code must be reentrant */ mynode->dn_Task = &myproc->pr_MsgPort; returnpkt(mypkt, myproc, DOS_TRUE, mypkt->dp_Res2); while(run) { mypkt = taskwait(myproc); ret = DOS_TRUE; pipe = (PIPE *)mypkt->dp_Arg1; switch(mypkt->dp_Type) { case ACTION_FIND_INPUT: case ACTION_FIND_OUTPUT: fh = (FH *)BADDR(mypkt->dp_Arg1); /* File handle */ ptr = (u_char *)BADDR(mypkt->dp_Arg3); /* File name */ str = AllocMem(*ptr + 1, 0); if (str == NULL) { ret = DOS_FALSE; goto opfail; } bmov(ptr+1, str, *ptr); str[*ptr] = 0; for (pipe = Pipe; pipe; pipe = pipe->next) { if (strcmp(pipe->name, str) == 0) { FreeMem(str, *ptr + 1); goto openok; } } pipe = (PIPE *)AllocMem(sizeof(PIPE), 0); if (pipe == NULL) { ret = DOS_FALSE; FreeMem(str, *ptr + 1); goto opfail; } ++totalcnt; bzero(pipe, sizeof(*pipe)); pipe->l = BUFSIZE; pipe->name = str; if (Pipe) Pipe->prev = &pipe->next; pipe->next = Pipe; pipe->prev = &Pipe; Pipe = pipe; openok: switch(pipe->openstate) { case 0: pipe->openstate = OC_FIRST; break; case OC_FIRST: pipe->openstate = OC_BOTH; break; case OC_WAITSECOND: pipe->openstate = OC_LAST; pipe->state &= ~ST_CPEND; returnpkt(pipe->pkt, myproc, DOS_TRUE, pipe->pkt->dp_Res2); break; case OC_BOTH: case OC_LAST: ret = DOS_FALSE; /* more than 2 opens */ goto opfail; } fh->fh_Arg1 = (long)pipe; fh->fh_Port = (struct MsgPort *)DOS_TRUE; opfail: returnpkt(mypkt, myproc, ret, mypkt->dp_Res2); break; case ACTION_END: /* If pending read, return pend bytes read * If pending write, return pend bytes written * return pending message, if any */ switch(pipe->openstate) { case OC_FIRST: pipe->openstate = OC_WAITSECOND; break; case OC_BOTH: pipe->openstate = OC_LAST; break; case OC_LAST: pipe->openstate = 0; break; } pipe->state |= ST_EOF; if (pipe->openstate == OC_WAITSECOND) { pipe->pkt = mypkt; pipe->state |= ST_CPEND; break; } if (pipe->state & (ST_RPEND|ST_WPEND)) { returnpktplain(pipe->pkt, myproc); pipe->state &= ~(ST_RPEND|ST_WPEND); } if (pipe->openstate == 0) { FreeMem(pipe->name, strlen(pipe->name)+1); *pipe->prev = pipe->next; if (pipe->next) pipe->next->prev = pipe->prev; FreeMem(pipe, sizeof(*pipe)); --totalcnt; if (totalcnt == 0) run = 0; } returnpkt(mypkt, myproc, ret, mypkt->dp_Res2); break; case ACTION_READ: /* Take chars from buffer. If buffer empty * and read not satisfied, check for pending * write and take bytes from it's buffer. * When done, must check to see if buffer will * hold ALL of pending write. * When done, if read still is not satisfied, * make it pending. */ mypkt->dp_Res1 = 0; /* * Load from buffer until empty or read fulfilled */ while (pipe->s != pipe->e && mypkt->dp_Res1 != mypkt->dp_Arg3) { int avail = (pipe->s < pipe->e) ? pipe->e - pipe->s : pipe->l - pipe->s; int bytes = mypkt->dp_Arg3 - mypkt->dp_Res1; if (bytes < avail) avail = bytes; bmov(pipe->buf+pipe->s, mypkt->dp_Arg2+mypkt->dp_Res1,avail); pipe->s += avail; mypkt->dp_Res1 += avail; if (pipe->s == pipe->l) pipe->s = 0; } /* * If write packet was pending, the read will either exhaust * the write and possibly become pending, or not exhaust the * write and be returned. */ if (mypkt->dp_Res1 != mypkt->dp_Arg3 && (pipe->state & ST_WPEND)) { int bytes = mypkt->dp_Arg3 - mypkt->dp_Res1; int avail = pipe->pkt->dp_Arg3 - pipe->pkt->dp_Res1; if (bytes > avail) bytes = avail; bmov(pipe->pkt->dp_Arg2 + pipe->pkt->dp_Res1, mypkt->dp_Arg2 + mypkt->dp_Res1, bytes); mypkt->dp_Res1 += bytes; pipe->pkt->dp_Res1 += bytes; if (pipe->pkt->dp_Res1 == pipe->pkt->dp_Arg3) { returnpktplain(pipe->pkt, myproc); pipe->state &= ~ST_WPEND; } } /* If read packet is made pending, buffer is always empty */ if (mypkt->dp_Res1 != mypkt->dp_Arg3 && !(pipe->state&ST_EOF)) { if (pipe->state & (ST_RPEND|ST_WPEND|ST_CPEND)) { returnpkt(pipe->pkt, myproc, DOS_FALSE, ERROR_OBJECT_IN_USE); pipe->state &= ~(ST_RPEND|ST_WPEND|ST_CPEND); } pipe->pkt = mypkt; pipe->state |= ST_RPEND; } else { returnpktplain(mypkt, myproc); } break; case ACTION_WRITE: /* * If pending read then buffer is empty, place * chars directly into pending read. If pending * write, error. * If nothing pending and write buffer not big * enough, make the write pend without filling * the buffer. Otherwise, move write data into * the buffer and return the packet. */ mypkt->dp_Res1 = 0; if (pipe->state & ST_EOF) { returnpkt(mypkt, myproc, DOS_FALSE, ERROR_SEEK_ERROR); break; } if (pipe->state & ST_RPEND) { /* write->read */ int avail = mypkt->dp_Arg3 - mypkt->dp_Res1; int bytes = pipe->pkt->dp_Arg3 - pipe->pkt->dp_Res1; if (avail < bytes) bytes = avail; bmov(mypkt->dp_Arg2+mypkt->dp_Res1, pipe->pkt->dp_Arg2+pipe->pkt->dp_Res1, bytes); mypkt->dp_Res1 += bytes; pipe->pkt->dp_Res1 += bytes; if (pipe->pkt->dp_Res1 == pipe->pkt->dp_Arg3) { returnpktplain(pipe->pkt, myproc); pipe->state &= ~ST_RPEND; } } /* write into buffer */ while (mypkt->dp_Res1 != mypkt->dp_Arg3 && pipe->s != ((pipe->e+1)%pipe->l)) { int avail = mypkt->dp_Arg3 - mypkt->dp_Res1; int bytes; if (pipe->e < pipe->s) bytes = pipe->s - pipe->e - 1; else bytes = pipe->l - pipe->e - (pipe->s == 0); if (avail < bytes) bytes = avail; bmov(mypkt->dp_Arg2 + mypkt->dp_Res1, pipe->buf + pipe->e, bytes); pipe->e += bytes; mypkt->dp_Res1 += bytes; if (pipe->e == pipe->l) pipe->e = 0; } if (mypkt->dp_Res1 != mypkt->dp_Arg3) { if (pipe->state & (ST_RPEND|ST_WPEND|ST_CPEND)) { returnpkt(pipe->pkt, myproc, DOS_FALSE, ERROR_OBJECT_IN_USE); pipe->state &= ~(ST_RPEND|ST_WPEND|ST_CPEND); } pipe->pkt = mypkt; pipe->state |= ST_WPEND; } else { returnpktplain(mypkt, myproc); } break; default: returnpkt(mypkt, myproc, DOS_FALSE, ERROR_ACTION_NOT_KNOWN); break; } } mynode->dn_Task = FALSE; /* we are a process "so we fall off the end of the world" */ /* MUST fall through */ }