home *** CD-ROM | disk | FTP | other *** search
- /*
- * tapedev.c - by Markus Wandel - 1990
- * Placed in the public domain 7 Oct 1990
- * Please have the courtesy to give credit
- * if you use this code in any program.
- *
- */
-
- #include <exec/ports.h>
- #include <exec/io.h>
- #include <devices/scsidisk.h>
- #include <libraries/dos.h>
- #include <libraries/dosextens.h>
- #include <libraries/filehandler.h>
-
- #define CLOSED 0
- #define JUST_OPENED 1
- #define OPEN_FOR_READ 2
- #define OPEN_FOR_WRITE 3
-
- #define SCSI_OK 0
- #define SCSI_ERROR 1
- #define SCSI_UNIT_ATTENTION 2
- #define SCSI_FILEMARK 3
-
- #define UNKNOWN 0
- #define REWOUND 1
- #define IN_FILE 2
- #define AT_FILEMARK 3
- #define AT_END 4
-
- #define ACT_FINDINPUT 1005
- #define ACT_FINDOUTPUT 1006
- #define ACT_END 1007
-
- extern void *FindTask();
- extern struct DosPacket *taskwait();
- extern struct MsgPort *CreatePort();
- extern struct IOStdReq *CreateStdIO();
- extern long OpenDevice();
- extern void *OpenLibrary();
- extern int AutoAutoRequest();
- extern void *malloc();
-
- unsigned long buffer_size;
- unsigned long buffer_cutoff;
- long unitnum;
- long flags;
- char *devname;
- int devlength;
-
- struct MsgPort *mp;
- struct IOStdReq *iob;
- struct Process *myproc;
- int tapestate;
- int errnum;
- ULONG buffer_highmark;
- ULONG buffer_current;
- int read_eof;
- struct SCSICmd cmd;
- ULONG actual;
- void *IntuitionBase;
- int notified;
-
- UBYTE sense_cmd[] = { 3,0,0,0,20,0 };
- UBYTE rewind_cmd[] = { 1,0,0,0,0,0 };
- UBYTE test_ready_cmd[] = { 0,0,0,0,0,0 };
- UBYTE skip_filemark_cmd[] = { 0x11,1,0,0,1,0 };
- UBYTE skip_end_cmd[] = {0x11,3,0,0,1,0 };
- UBYTE write_filemark_cmd[] = {0x10,0,0,0,1,0};
- UBYTE mode_sense_cmd[] = {0x1a,0,0,0,3,0};
-
- UBYTE *buffer;
- UBYTE sensebuf[20];
-
- _main()
- {
- register struct DosPacket *pkt;
- struct DeviceNode *devnode;
- register int openstate;
- UBYTE *ptr;
- int i;
- long error;
-
- myproc = (struct Process *)FindTask(0L);
- pkt = taskwait(myproc);
- devnode = (struct DeviceNode *) ((pkt->dp_Arg3)<<2);
-
- if(parsestartup((pkt->dp_Arg2)<<2) || buffer_size < 10) {
- returnpkt(pkt,myproc,DOSFALSE,ERROR_NO_FREE_STORE);
- return 0;
- }
-
- buffer_cutoff = buffer_size << 8;
- buffer_size <<= 9;
-
- buffer = (UBYTE *)malloc(buffer_size);
- if(!buffer) {
- returnpkt(pkt,myproc,DOSFALSE,ERROR_NO_FREE_STORE);
- return 0;
- }
-
- IntuitionBase = OpenLibrary("intuition.library",0L);
-
- if(!IntuitionBase) {
- returnpkt(pkt,myproc,DOSFALSE,ERROR_NO_FREE_STORE);
- free(buffer);
- return 0;
- }
-
- mp = CreatePort(0L,0L);
- if(!mp) {
- returnpkt(pkt,myproc,DOSFALSE,ERROR_NO_FREE_STORE);
- CloseLibrary(IntuitionBase);
- free(buffer);
- return 0;
- }
- iob = CreateStdIO(mp);
- if(!iob) {
- CloseLibrary(IntuitionBase);
- free(buffer);
- DeletePort(mp);
- returnpkt(pkt,myproc,DOSFALSE,ERROR_NO_FREE_STORE);
- return 0;
- }
-
- devname[devlength] = 0;
- error = OpenDevice(devname,unitnum,iob,flags);
- devname[devlength] = '/';
-
- if(error) {
- CloseLibrary(IntuitionBase);
- free(buffer);
- DeleteStdIO(iob);
- DeletePort(mp);
- returnpkt(pkt,myproc,DOSFALSE,ERROR_OBJECT_NOT_FOUND);
- return 0;
- }
-
- devnode->dn_Task = &myproc->pr_MsgPort;
- returnpkt(pkt,myproc,DOSTRUE,0L);
-
- openstate = CLOSED;
- tapestate = UNKNOWN;
-
- for(;;) {
- pkt = taskwait(myproc);
- switch(pkt->dp_Type) {
- case ACT_FINDINPUT:
- case ACT_FINDOUTPUT:
- if(openstate != CLOSED) returnpkt(pkt,myproc,DOSFALSE,
- ERROR_OBJECT_IN_USE);
- else if(not_ready()) {
- returnpkt(pkt,myproc,DOSFALSE,ERROR_OBJECT_NOT_FOUND);
- } else {
- errnum = 0;
- notified = 0;
- openstate = JUST_OPENED;
- ptr = (UBYTE *) ((pkt->dp_Arg3)<<2);
- for(i=*ptr++;i>1;i--) {
- if(*ptr++ == ':') {
- if(*ptr == 'R' || *ptr == 'r')
- rewind();
- else if(*ptr == 'A' || *ptr == 'a')
- append();
- i=0;
- }
- }
- if(tapestate == UNKNOWN) rewind();
- returnpkt(pkt,myproc,DOSTRUE,0L);
- }
- break;
- case ACTION_READ:
- switch(openstate) {
- case JUST_OPENED:
- init_read();
- openstate = OPEN_FOR_READ;
- case OPEN_FOR_READ:
- tape_read(pkt);
- break;
- default:
- returnpkt(pkt,myproc,-1L,ERROR_READ_PROTECTED);
- }
- break;
- case ACTION_WRITE:
- switch(openstate) {
- case JUST_OPENED:
- init_write();
- openstate = OPEN_FOR_WRITE;
- case OPEN_FOR_WRITE:
- tape_write(pkt);
- break;
- default:
- returnpkt(pkt,myproc,-1L,ERROR_OBJECT_NOT_FOUND);
- }
- break;
- case ACT_END:
- if(openstate == OPEN_FOR_WRITE) finish_write();
- openstate = CLOSED;
- returnpkt(pkt,myproc,DOSTRUE,0L);
- break;
- default:
- returnpkt(pkt,myproc,DOSFALSE,(long)ERROR_ACTION_NOT_KNOWN);
- }
- }
- }
-
- /*
- * This function is called for every open and tests if a tape is ready.
- *
- * tapestate becomes UNKNOWN if no tape is ready.
- *
- */
- not_ready()
- {
- do {
- switch(doscsi(0L,test_ready_cmd,0L,6,1)) {
- case SCSI_OK:
- return 0;
- case SCSI_UNIT_ATTENTION:
- if(doscsi(0L,test_ready_cmd,0L,6,1)==SCSI_OK) return 0;
- default:
- tapestate = UNKNOWN;
- }
- } while(AutoAutoRequest("Tape unit not ready.",0L,0L,"Retry","Cancel"));
- return -1;
- }
-
- rewind()
- {
- if(doscsi(0L,rewind_cmd,0L,6,1))
- errnum = ERROR_OBJECT_NOT_FOUND;
- else tapestate = REWOUND;
- }
-
- append()
- {
- if(doscsi(0L,skip_end_cmd,0L,6,1))
- errnum = ERROR_OBJECT_NOT_FOUND;
- else tapestate = AT_END;
- }
-
- init_read()
- {
- int i;
- buffer_current = 0;
- buffer_highmark = 0;
- read_eof = 0;
- if(!errnum) switch(tapestate) {
- case IN_FILE:
- if(doscsi(0L,skip_filemark_cmd,0L,6,1)) {
- tapestate = UNKNOWN;
- errnum = ERROR_OBJECT_NOT_FOUND;
- }
- tapestate = AT_FILEMARK;
- case AT_FILEMARK:
- case REWOUND:
- break;
- default:
- errnum = ERROR_OBJECT_NOT_FOUND;
- }
- }
-
- init_write()
- {
- UBYTE msbuf[3];
- buffer_current = 0;
- if(!errnum) switch(tapestate) {
- case AT_FILEMARK:
- case IN_FILE:
- if(doscsi(0L,skip_end_cmd,0L,6,1)) {
- errnum = ERROR_OBJECT_NOT_FOUND;
- tapestate = UNKNOWN;
- }
- case AT_END:
- case REWOUND:
- if(doscsi(msbuf,mode_sense_cmd,3L,6,1)) {
- errnum = ERROR_OBJECT_NOT_FOUND;
- return;
- }
- if(msbuf[2] & 0x80) {
- errnum = ERROR_OBJECT_NOT_FOUND;
- AutoAutoRequest("Tape is write protected.",0L,0L,0L,"Abort");
- notified = 1;
- return;
- }
- break;
- default:
- errnum = ERROR_OBJECT_NOT_FOUND;
- }
- }
-
- char readerr1[] = "An error has occurred while";
- char readerr2[] = "trying to read from the tape.";
-
- tape_read(pkt)
- register struct DosPacket *pkt;
- {
- register UBYTE *ptr = (UBYTE *)pkt->dp_Arg2;
- register ULONG length = pkt->dp_Arg3;
- register ULONG fragment;
- UBYTE cmdbuf[6];
-
- if(errnum) {
- if(!notified) {
- AutoAutoRequest(readerr1,readerr2,0L,0L," OK ");
- notified = 1;
- }
- returnpkt(pkt,myproc,-1L,(long)errnum);
- return;
- }
- /*
- * First, transfer whatever is left in the buffer.
- */
- if(buffer_current < buffer_highmark ) {
- fragment = buffer_highmark - buffer_current;
- if(length<fragment) fragment = length;
- CopyMem(buffer+buffer_current,ptr,fragment);
- length -= fragment;
- buffer_current += fragment;
- ptr += fragment;
- }
- if(!read_eof) {
- if(length>buffer_cutoff) {
- fragment = length & ~511;
- cmdbuf[0] = 8;
- cmdbuf[1] = 1;
- *((ULONG *)&cmdbuf[2]) = fragment>>1;
- switch(doscsi(ptr,cmdbuf,fragment,6,1)) {
- case SCSI_FILEMARK:
- fragment = actual;
- read_eof = 1;
- break;
- case SCSI_OK:
- tapestate = IN_FILE;
- break;
- default:
- read_eof = 1;
- fragment = 0;
- AutoAutoRequest(readerr1,readerr2,0L,0L," OK ");
- notified = 1;
- break;
- }
- ptr += fragment;
- length -= fragment;
- }
- if(length && !read_eof) {
- cmdbuf[0] = 8;
- cmdbuf[1] = 1;
- *((ULONG *)&cmdbuf[2]) = buffer_size>>1;
- switch(doscsi(buffer,cmdbuf,(long)buffer_size,6,1)) {
- case SCSI_FILEMARK:
- read_eof = 1;
- buffer_highmark = actual;
- break;
- case SCSI_OK:
- buffer_highmark = actual;
- tapestate = IN_FILE;
- break;
- default:
- read_eof = 1;
- buffer_highmark = 0;
- AutoAutoRequest(readerr1,readerr2,0L,0L," OK ");
- notified = 1;
- errnum = ERROR_SEEK_ERROR;
- }
- buffer_current = length;
- if(buffer_highmark < buffer_current) buffer_current = buffer_highmark;
- CopyMem(buffer,ptr,buffer_current);
- length -= buffer_current;
- }
- }
- if(errnum) if(pkt->dp_Arg3==length) {
- returnpkt(pkt,myproc,-1L,ERROR_SEEK_ERROR);
- return;
- }
- returnpkt(pkt,myproc,pkt->dp_Arg3-length,0L);
- }
-
- char writerr1[] = "An error has occurred while";
- char writerr2[] = "trying to write to the tape.";
- char writerr3[] = "Tape data is probably invalid.";
-
- tape_write(pkt)
- register struct DosPacket *pkt;
- {
- register UBYTE *ptr = (UBYTE *)pkt->dp_Arg2;
- register ULONG length = pkt->dp_Arg3;
- register ULONG fragment;
- UBYTE cmdbuf[6];
-
- if(errnum) {
- if(!notified) {
- AutoAutoRequest(writerr1,writerr2,writerr3,0L," OK ");
- notified = 1;
- }
- returnpkt(pkt,myproc,-1L,(long)errnum);
- return;
- }
- if(buffer_current || (length <= buffer_cutoff)) {
- fragment = buffer_size - buffer_current;
- if(length < fragment) fragment = length;
- CopyMem(ptr,buffer+buffer_current,fragment);
- ptr += fragment;
- buffer_current += fragment;
- length -= fragment;
- /*
- * If the buffer is full, flush it.
- *
- */
- if(buffer_current == buffer_size) {
- cmdbuf[0] = 10;
- cmdbuf[1] = 1;
- *((ULONG *)&cmdbuf[2]) = buffer_size>>1;
- if(doscsi(buffer,cmdbuf,(long)buffer_size,6,0)) {
- AutoAutoRequest(writerr1,writerr2,writerr3,0L," OK ");
- notified = 1;
- errnum = ERROR_SEEK_ERROR;
- }
- buffer_current = 0;
- }
- }
- if(!errnum) {
- if(length>buffer_cutoff) {
- fragment = length & ~511;
- cmdbuf[0] = 10;
- cmdbuf[1] = 1;
- *((ULONG *)&cmdbuf[2]) = fragment>>1;
- if(doscsi(ptr,cmdbuf,fragment,6,0)) {
- AutoAutoRequest(writerr1,writerr2,writerr3,0L," OK ");
- notified = 1;
- errnum = ERROR_SEEK_ERROR;
- }
- length -= fragment;
- ptr += fragment;
- }
- if(length && !errnum) {
- CopyMem(ptr,buffer,length);
- buffer_current = length;
- length = 0;
- }
- }
- if(errnum)
- returnpkt(pkt,myproc,-1L,ERROR_SEEK_ERROR);
- else
- returnpkt(pkt,myproc,pkt->dp_Arg3,0L);
- }
-
- finish_write()
- {
- UBYTE cmdbuf[6];
- ULONG fragment;
-
- if(buffer_current && !errnum) {
- fragment = (buffer_current + 511) & ~511;
- while(buffer_current < fragment) buffer[buffer_current++] = 0;
- cmdbuf[0] = 10;
- cmdbuf[1] = 1;
- *((ULONG *)&cmdbuf[2]) = fragment>>1;
- if(doscsi(buffer,cmdbuf,fragment,6,0))
- AutoAutoRequest(writerr1,writerr2,writerr3,0L," OK ");
- notified = 1;
- }
- if(doscsi(0L,write_filemark_cmd,0L,6,1)) {
- if(!notified) AutoAutoRequest(writerr1,writerr2,writerr3,0L," OK ");
- } else
- tapestate = AT_END;
- }
-
-
- int doscsi(buf,cmdbuf,length,cmdlength,flags)
- UWORD *buf;
- UBYTE *cmdbuf;
- ULONG length;
- int cmdlength,flags;
- {
- cmd.scsi_Data = buf;
- cmd.scsi_Length = length;
- cmd.scsi_CmdLength = cmdlength;
- cmd.scsi_Flags = flags;
- cmd.scsi_Command = cmdbuf;
- iob->io_Command = 28;
- iob->io_Data = (APTR) &cmd;
- iob->io_Length = sizeof(cmd);
- DoIO(iob);
- actual = cmd.scsi_Actual;
- switch(iob->io_Error) {
- case 0:
- if(length == actual) return SCSI_OK;
- else return SCSI_ERROR;
- case HFERR_BadStatus:
- cmd.scsi_Command = sense_cmd;
- cmd.scsi_Flags = 1;
- cmd.scsi_CmdLength = 6;
- cmd.scsi_Length = 20;
- cmd.scsi_Data = (UWORD *) sensebuf;
- DoIO(iob);
- if(iob->io_Error) return SCSI_ERROR;
- if(cmd.scsi_Actual > 4) {
- if(sensebuf[2] == 1 && actual == length) {
- return SCSI_OK;
- }
- if(sensebuf[2] == 6) {
- tapestate = UNKNOWN;
- return SCSI_UNIT_ATTENTION;
- }
- if(sensebuf[2] == 8) {
- tapestate = AT_END;
- return SCSI_ERROR;
- }
- if(sensebuf[2] == 0x80) {
- tapestate = AT_FILEMARK;
- return SCSI_FILEMARK;
- }
- }
- default:
- tapestate = UNKNOWN;
- return SCSI_ERROR;
- }
- }
-
- int parsestartup(msg)
- register char *msg;
- {
- register int i,state = 0;
-
- buffer_size = 0;
- unitnum = 0;
- flags = 0 ;
-
- if(!msg) return 1;
-
- for(i=1;i<=msg[0];i++) {
- if(msg[i] == '/') {
- switch(++state) {
- case 1: devname = &msg[i+1];
- break;
- case 2: devlength = (&msg[i]) - devname;
- break;
- case 4:
- return 1;
- }
- } else if(state != 1) {
- if(msg[i] < '0' || msg[i] > '9') return 1;
- switch(state) {
- case 0: buffer_size = 10 * buffer_size + msg[i] - '0';
- break;
- case 2: unitnum = 10 * unitnum + msg[i] - '0';
- break;
- case 3: flags = 10 * flags + msg[i] - '0';
- }
- }
- }
- return (state<3);
- }
-