/* vb2091 0.3 (c) in 1994 by Volker Barthelmann */

#include <string.h>

#include <exec/execbase.h>
#include <exec/memory.h>
#include <exec/io.h>
#include <exec/semaphores.h>
#include <devices/trackdisk.h>
#include <devices/timer.h>
#include <dos/dosextens.h>
#include <dos/filehandler.h>
#include <dos/dostags.h>

#include <clib/exec_protos.h>
#include <clib/alib_protos.h>
#include <clib/dos_protos.h>

#define MAXUNIT 8

char zahl[]="0123456789";
struct IOStdReq *IO[MAXUNIT];
struct SignalSemaphore *ss;

#define DEV_BEGINIO (-30)

#define min(a,b)    ((a) < (b) ? (a) : (b))

#define STAY   1
#define BROKEN 2
#define SINGLEF 4
#define NOCACHE 8
#define NOWRITE 16

void (*oldbeginio)(__reg("a1") struct IOStdReq *, __reg("a6") struct Device *);
void (*oldarray[8])();

void    *Unit[MAXUNIT];

int SIZE=262144,msize,flags,UnitNr,unitmask;
int MAXN=16,MIN1BUF=131072,MIN2BUF=65536;
char *buffer;

struct MsgPort *iosink[MAXUNIT];


void mybeginio(__reg("a1") struct IOStdReq *req,__reg("a6") struct Device *dev)
{
    struct MsgPort *port;
    struct IOStdReq *mreq;
    unsigned int actual,data,offset,length,lactual,ldata,flag,msize;
    int un=-1;

    geta4();

    mreq=req;
    ObtainSemaphore(ss);

    for(flag=0;flag<MAXUNIT;flag++)
        if(req->io_Unit==Unit[flag])
            un=flag;
    if(un==-1) goto old;

/*    oldbeginio=oldarray[un];*/

    if((req->io_Command & 0xFF) == CMD_READ){
        if((flags & BROKEN)==0 && (unsigned long) req->io_Data<16777216)
            goto old;
        port=(struct MsgPort *)CreatePort(NULL,0);
        IO[un]->io_Message.mn_ReplyPort=port;
        actual=0;
        length=mreq->io_Length;
        if(length>MIN2BUF*MAXN){
            msize=min((length+MAXN-1)/MAXN,SIZE/2);
        }else{
            if(length<MIN1BUF) msize=min(length,SIZE/2);
                          else msize=min(MIN2BUF,SIZE/2);
        }
        if(msize>=512) msize&=0xfffffe00;
        offset=mreq->io_Offset;
        data=(unsigned int) mreq->io_Data;
        lactual=flag=0;ldata=data;
        if(flags&NOCACHE) CacheControl(0,CACRF_EnableD);
        do{
            IO[un]->io_Offset=offset;
            if(flags&SINGLEF){
                IO[un]->io_Length=min(SIZE,length);
                IO[un]->io_Data=buffer;
            }else{
                IO[un]->io_Length=min(msize,length);
                if(flag==0) IO[un]->io_Data=buffer; else
                  IO[un]->io_Data=buffer+SIZE/2;
            }
            IO[un]->io_Command=CMD_READ;
            oldbeginio(req=IO[un],IO[un]->io_Device);
            if((flags&SINGLEF)==0){
                if(flag!=0) CopyMem(buffer,(APTR) ldata,lactual);
                else CopyMem(buffer+SIZE/2,(APTR) ldata,lactual);
            }
            WaitIO((struct IORequest *) IO[un]);
            actual+=IO[un]->io_Actual;lactual=IO[un]->io_Actual;
            ldata=data;data+=IO[un]->io_Actual;
            offset+=IO[un]->io_Actual;
            length-=IO[un]->io_Actual;
            flag=1-flag;
            if(flags&SINGLEF) CopyMem(buffer,(APTR) ldata,lactual);

        }while(length>0 && IO[un]->io_Actual>=IO[un]->io_Length);
        if((flags&SINGLEF)==0){
            if(flag!=0) CopyMem(buffer,(APTR) ldata,lactual);
            else CopyMem(buffer+SIZE/2,(APTR) ldata,lactual);
        }
        if(flags&NOCACHE) CacheControl(0xffffffff,CACRF_EnableD);
        mreq->io_Actual=actual;
        mreq->io_Error=IO[un]->io_Error;
        DeletePort(port);
        if(!(mreq->io_Flags & IOF_QUICK))
            ReplyMsg(&mreq->io_Message);
        ReleaseSemaphore(ss);
        return;
    }
    if((req->io_Command & 0xFF) == CMD_WRITE){
        if(flags&NOWRITE) goto old;
        if((flags & BROKEN)==0 && (unsigned long) req->io_Data<16777216)
            goto old;
        port=(struct MsgPort *)CreatePort(NULL,0);
        IO[un]->io_Message.mn_ReplyPort=port;
        actual=0;
        length=mreq->io_Length;
        offset=mreq->io_Offset;
        data=(unsigned int) mreq->io_Data;
        if(flags&NOCACHE) CacheControl(0,CACRF_EnableD);
        do{
            if(SIZE>=length) IO[un]->io_Length=length; else
              IO[un]->io_Length=SIZE;
            CopyMem((APTR) data,buffer,IO[un]->io_Length);
            IO[un]->io_Offset=offset;
            IO[un]->io_Data=buffer;
            IO[un]->io_Command=CMD_WRITE;
            oldbeginio(req=IO[un],IO[un]->io_Device);
            WaitIO((struct IORequest *) IO[un]);
            actual+=IO[un]->io_Actual;
            data+=IO[un]->io_Actual;
            offset+=IO[un]->io_Actual;
            length-=IO[un]->io_Actual;
        }while(length>0 && IO[un]->io_Actual>=IO[un]->io_Length);
        if(flags&NOCACHE) CacheControl(0xffffffff,CACRF_EnableD);
        mreq->io_Actual=actual;
        mreq->io_Error=IO[un]->io_Error;
        DeletePort(port);
        if(!(mreq->io_Flags & IOF_QUICK))
            ReplyMsg(&mreq->io_Message);
        ReleaseSemaphore(ss);
        return;
    }
old:
    oldbeginio(req,dev);
    ReleaseSemaphore(ss);
}


int
main(int argc, char *argv[])
{
    long            tmp;
    BPTR            op;
    int             devopen,i;
    char *Device="2nd.scsi.device",*p;

    tmp = 0;
    devopen = 0;
    op=Output();
    for(i=1; i<argc; i++){
        /*printf("Arg: %d  %s: ",i,argv[i]);*/
        if(!strcmp("BUFSIZE",argv[i])){
            SIZE=atoi(argv[++i])*1024;continue;}
        if(!strcmp("MIN1BUF",argv[i])){
            MIN1BUF=atoi(argv[++i])*1024;continue;}
        if(!strcmp("MIN2BUF",argv[i])){
            MIN2BUF=atoi(argv[++i])*1024;continue;}
        if(!strcmp("MAXN",argv[i])){
            MAXN=atoi(argv[++i]);continue;}
        if(!strcmp("DEVICE",argv[i])){
            Device=argv[++i];continue;}
        if(!strcmp("UNIT",argv[i])){
            p=argv[++i];
            while(*p!=0){
                if(*p<'0'||*p>'7') {Write(op,"You can only use unit 0..7!!\n",29L);exit(0);}
                unitmask|=1<<(*p-'0');
                p++;
            }
            continue;}
        if(!strcmp("BROKEN",argv[i])){
            flags|=BROKEN;continue;}
        if(!strcmp("SINGLE",argv[i])){
            flags|=SINGLEF;continue;}
        if(!strcmp("NOCACHE",argv[i])){
            flags|=NOCACHE;continue;}
        if(!strcmp("NOWRITE",argv[i])){
            flags|=NOWRITE;continue;}
        Write(op,"Wrong argument: ",16L);
        Write(op,argv[i],strlen(argv[i]));
        Write(op,"\nPLEASE READ THE MANUAL !!\n",27L);
        exit(0);
    }

    Write(op,"vb2091 V0.3 (c) in 1994,1996 by Volker Barthelmann\n",52L);
    /*printf("Flags: %d  SIZE: %d  Device: %s\n",flags,SIZE,Device);*/
    /*printf("unitmask: %d\n",unitmask);*/
    for(i=0;i<MAXUNIT;i++){
        if((unitmask&(1<<i))==0) continue;
        iosink[i] = (struct MsgPort *)CreatePort(NULL, 0);
        IO[i] = (struct IOStdReq *) CreateExtIO(iosink[i], sizeof (*IO[0]));
        tmp = OpenDevice(Device, i, (struct IORequest *) IO[i], 0);

        if (tmp) {
            Write(op,"Unable to open ",15L);
            Write(op,Device,strlen(Device));
            Write(op," Unit ",6L);Write(op,&(zahl[i]),1L);
            Write(op,"\n",1L);
/*            if(IO[i])DeleteExtIO((struct IORequest *) IO[i]);
            if(iosink[i]) DeletePort(iosink[i]);  */
            IO[i]=0;
            continue;
        } else{
            Write(op,"Opened ",7L);
            Write(op,Device,strlen(Device));
            Write(op," Unit ",6L);Write(op,&(zahl[i]),1L);
            Write(op,"\n",1L);

            devopen = 1;
            /*printf("Opened %s unit %d\n",Device,i);*/

            SumLibrary((struct Library *) IO[i]->io_Device);
/*            oldarray[i] = (void (* )()) SetFunction((struct Library *) IO[i]->io_Device, DEV_BEGINIO, (APTR) &mybeginio);*/
            if(!oldbeginio) oldbeginio = (void (* )()) SetFunction((struct Library *) IO[i]->io_Device, DEV_BEGINIO, (APTR) &mybeginio);
            Unit[i]=IO[i]->io_Unit;
        }
    }
    ss = (struct SignalSemaphore *)AllocMem(sizeof (struct SignalSemaphore), MEMF_PUBLIC);
    if (!ss)
        goto quit;
    InitSemaphore(ss);

    if(flags&BROKEN)
        buffer=(char *)AllocMem(SIZE,MEMF_24BITDMA|MEMF_FAST|MEMF_PUBLIC);
    else
        buffer=(char *)AllocMem(SIZE,MEMF_24BITDMA|MEMF_PUBLIC);

    if(buffer==0) {Write(op,"Cannot allocate buffer!!\n",25L);goto quit;}


    if(devopen) Wait(SIGBREAKF_CTRL_C);

quit:

    ObtainSemaphore(ss);
    for(i=MAXUNIT-1;i>=0;i--){
/*        if(IO[i]) printf("Restoring Unit %d\n",i);*/
/*        if(IO[i]) SetFunction((struct Library *) IO[i]->io_Device, DEV_BEGINIO, (APTR) oldarray[i]);*/
        if(IO[i]) SetFunction((struct Library *) IO[i]->io_Device, DEV_BEGINIO, (APTR) oldbeginio);
    }
    ReleaseSemaphore(ss);


    if(buffer)
        FreeMem(buffer, SIZE);
    if (ss)
        FreeMem(ss, sizeof (struct SignalSemaphore));
    for(i=0;i<MAXUNIT;i++){
        if (devopen) {
            if(IO[i]) CloseDevice((struct IORequest *) IO[i]);
        }
        if (IO[i])
            DeleteExtIO((struct IORequest *) IO[i]);
        if (iosink[i])
            DeletePort(iosink[i]);
    }

    return 0;
}

