 
/* DisDF.c V1.0    Disables DF0-DF3                                       */
/* Patrick F. Misteli 22.7.91                                             */
/* see DisDF.doc and 'usage' below                                        */

#define ND 4                                         /* max floppy drives */
#define NN 100                                   /* max tasknodes to scan */
#define DISABLEDP -123                       /* priority of disabled task */
#define BUSYPRI   -122
#define mTS_WAIT    4                        /* manx 3.4b has 4L for this */
#define mTS_REMOVED 6

#include <exec/types.h>
#include <exec/exec.h>
#include <exec/execbase.h>
#include <exec/memory.h>
#include <exec/tasks.h>
#include <stdio.h>

#include <devices/trackdisk.h>
#include <exec/devices.h>
#include <exec/io.h>

#include <dos.h>                           /* LATTICE is defined in dos.h */
      /* manx 3.4b doesn't have dos.h so add one with AZTEC defined in it */

#ifdef LATTICE
#include <proto/exec.h>

#else
#ifdef AZTEC
struct IORequest *CreateExtIO();
struct MsgPort *CreatePort();
#include <functions.h>

#else
#define OTHER_COMPILER 1
struct IORequest *CreateExtIO();
struct MsgPort *CreatePort();
struct Task *DeviceProc();
struct Task *FindTask();
long SetTaskPri();
long Signal();
char *AllocMem();
struct Task *CreateTask();
#endif
#endif

extern struct ExecBase *SysBase;

char extioerrmsg[]  = "DisDF CreateExtIO() error";
char creatportmsg[] = "DisDF CreatePort() error";
char nofloppymsg[]  = "DisDF no floppy drive found";
char notmountmsg[]  = "DisDF DFi not mounted";
char missingdmsg[]  = "DisDF DFi device process missing";
char missingtmsg[]  = "DisDF DFi trackdisk.device task missing";
char missingfmsg[]  = "DisDF DFi File Process task missing";
char memerrormsg[]  = "DisDF not enough memory";
char statusokmsg[]  = "DisDF DFi trackdisk oxx File System oxx";
char statusermsg[]  = "DisDF DFi error";
char busytaskmsg[]  = "DisDF BusyLoop-122 running";

char *usagemsg[] =
{
 "\nDisDF V1.0 toggles floppy drives' trackdisk.device tasks off or on\n",
 "usage : DisDF [0] [1] [2] [3] [-d|e] [-f] [-h]",
 "         #  drive number if not all DF0: to DF3:",
 "        -c  change priorities and start busyloop task",
 "        -d  disable instead of toggle",
 "        -e  enable  instead of toggle",
 "        -f  disable floppy drives' File System tasks also",
 "        -h  help message to stdout",
 "        -s  status message to stdout only, tasks not changed",
 "        -v  verbose",
 NULL
};

char trackdisk[]  = TD_NAME;                        /* "trackdisk.device" */
char filesystem[] = "File System";
char dfstr[] = "DF0:";
char busyname[] = "BusyLoop-122";

struct Task *me;
struct Node *fnode[ ND ], *tnode[ ND ];
struct Task *busytask;
int drive[ ND ], statusonly, chicken;
long previouspri;

main( argc, argv )
int argc;
char *argv[];
{
 register int i;
 register struct Node *np;
 register int j, k;
 struct Node *anode[ NN ];
 int all, en, ds, fs, verbose;
 long failval;
 struct MsgPort *diskport;
 struct IOExtTD *ioexttd;

 failval = 0L;
 all = TRUE;
 en = ds = fs = verbose = statusonly = chicken = FALSE;
 for ( i = 0; i < ND; i++ )
 {
  drive[i] = FALSE;
  tnode[i] = NULL;
  fnode[i] = NULL;
 }

 if ( argc > 1 )
 {
  for ( i = 1; i < argc; i++ )
  {
   switch ( *argv[i] )
   {
    case '-':
     if ( argv[ i ][ 2 ] )
           usageexit();
     switch( argv[ i ][ 1 ])
     {
      case 'c':
       chicken = TRUE;
      break;
      case 'd':
       if ( en )
        usageexit();
       ds = TRUE;
      break;
      case 'e':
       if ( ds )
        usageexit();
       en = TRUE;
      break;
      case 'f':
       fs = TRUE;
      break;
      case 's':
       statusonly = TRUE;
      break;
      case 'v':
       verbose = TRUE;
      break;
      default :
       usageexit();
      break;
     }
    break;
    case '0' :
    case '1' :
    case '2' :
    case '3' :
     if ( argv[i][1] != 0 )
      usageexit();
     drive[ *argv[i] - 48 ] = TRUE;
     all = FALSE;
    break;
    default:
     usageexit();
    break;
   } /* switch   */
  } /* for i    */
 } /* argc > 1 */

 if ( all )
  drive[0] = drive[1] = drive[2] = drive[3] = TRUE;

 me = FindTask( 0L );
 previouspri = SetTaskPri( me, -1L ); /* allow most other tasks to finish */
 Forbid();                        /* before grabbing the CPU until exit() */

              /* find out which drives are mounted by trying to open them */
 diskport = CreatePort( 0, 0 );
 if( diskport == NULL )
 {
  puts( creatportmsg );
  permexit( 11 );
 }
 ioexttd = (struct IOExtTD *)CreateExtIO( diskport,
                                            (long)sizeof(struct IOExtTD ) );
 if( ioexttd == 0 )
 {
  puts( extioerrmsg );
  DeletePort( diskport );
  permexit( 12L );
 }
 for ( i = 0; i < ND; i++ )
 {
  if ( drive[i] )
   if ( !OpenDevice( trackdisk, (long)i, (struct IORequest *)ioexttd, 0L ) )
   {                          /* get pointer to its trackdisk.device task */
    tnode[i] =
           (struct Node *)ioexttd->iotd_Req.io_Unit->unit_MsgPort.mp_SigTask;
    CloseDevice( (struct IORequest *)ioexttd );
   }
 }
 DeleteExtIO( (struct IORequest *)ioexttd, sizeof( struct IOExtTD ) );
 DeletePort( diskport );

 if ( all )                    /* check that at least one drive was found */
 {
  for ( i = 0; i < ND; i++ )
   if ( tnode[i] )
    break;
  if ( i >= ND )
  {
   puts( nofloppymsg );
   permexit( 13L );
  }
 }
 else
 {
  for ( i = 0; i < ND; i++ )     /* check for drives specified in command */
   if ( !tnode[i] )
    if ( drive[i] )
    {
     notmountmsg[8] = 48 + i;
     puts( notmountmsg );   
    }
 }

 for ( i = 0; i < ND; i++ ) /* find each mounted drive's File System task */
 {
  if ( tnode[i] )
  {
   dfstr[2] = 48 + i;
   fnode[i] = (struct Node *)DeviceProc( dfstr );
   if ( fnode[i] )
    fnode[i] = (struct Node *)( (long)fnode[i] - sizeof(struct Task) );
   else
   {
    tnode[i] = NULL;
    missingdmsg[8] = 48 + i;
    puts( missingdmsg );
    failval += 1L << (i + 4);
   }
  }
 }
            /* ensure that the tasks found above are in exec's task lists */
 for ( i = 0; i < NN; i++ )
  anode[i] = NULL;
 i = 0;
 Disable();
 for ( np = (struct Node *)SysBase->TaskReady.lh_Head; i < NN, np->ln_Succ;
                                                          np = np->ln_Succ )
  anode[i++] = np;
 for ( np = (struct Node *)SysBase->TaskWait.lh_Head;  i < NN, np->ln_Succ;
                                                          np = np->ln_Succ )
  anode[i++] = np;
 Enable();
 for ( i = 0; i < ND; i++ )
 {
  if ( tnode[i] )
  {
   for ( j = 0; j < NN; j++ )
    if ( tnode[i] == anode[j] )
     break;
   if ( j >= NN )          /* if the task is not there, ignore this drive */
   {
    tnode[i] = NULL;
    fnode[i] = NULL;
    missingtmsg[8] = 48 + i;
    puts( missingtmsg );
    failval += 1L << (i + 8);
   }
  }
  if ( fnode[i] )
  {
   for ( j = 0; j < NN; j++ )
    if ( fnode[i] == anode[j] )
     break;
   if ( j >= NN )
   {
    tnode[i] = NULL;
    fnode[i] = NULL;
    missingfmsg[8] = 48 + i;
    puts( missingfmsg );
    failval += 1L << (i + 12);
   }
  }
 }

 if ( statusonly )
 {
  typestatus();
  permexit();
 }

 if ( !chicken )
 {
  Disable();           /* remove tasks found above from exec's task lists */
  for ( i = 0; i < ND; i++ )
   if( tnode[i] )
   {
    Remove( tnode[i] );
    Remove( fnode[i] );
   }
  Enable();
 }

 for ( i = j = 0; i < ND; i++ )                        /* modify priority */
 {
  if ( tnode[i] )
  {
   if ( en || ( ( tnode[i]->ln_Pri == DISABLEDP ) && !ds )  )
   {
    modtask( tnode[i], 5 );
    modtask( fnode[i], 10 );
   }
   else
   {
    modtask( tnode[i], DISABLEDP );
    if ( fs )
     modtask( fnode[i], DISABLEDP );
    else
     modtask( fnode[i], 10 );
   }
  }
 }

 if ( !chicken )
 {
  Disable();                   /* put modified tasks back in exec's lists */
  for ( i = 0; i < ND; i++ )
   if( tnode[i] )
   {
    if ( tnode[i]->ln_Pri == DISABLEDP )
    {
     AddTail( &SysBase->TaskWait, tnode[i] );
     AddTail( &SysBase->TaskWait, fnode[i] );
    }
    else
    {
     AddTail( &SysBase->TaskReady, tnode[i] );
     AddTail( &SysBase->TaskReady, fnode[i] );
    }
   }
  Enable();
 }

 if ( busytask = FindTask( busyname ) )          /* maybe remove busytask */
 {
  for ( i = j = k = 0; i < ND; i++ )
   if ( tnode[i] )
    if ( tnode[i]->ln_Pri == DISABLEDP )
    {
     j++;
     if ( ((struct Task*)tnode[i])->tc_State != mTS_REMOVED )
      k++;
    }
    else         /* previously removed tasks in the wait list need a nudge */
    {
     Signal( (struct Task *)tnode[i],
                                    ((struct Task *)tnode[i])->tc_SigAlloc );
     Signal( (struct Task *)fnode[i],
                                    ((struct Task *)fnode[i])->tc_SigAlloc );
    }
  if ( !j || ( !chicken && k ) )
   rembusytask();
 }
 else                                               /* maybe add busytask */
 {
  if ( chicken )
   for ( i = 0; i < ND; i++ )
    if ( tnode[i] )
     if ( tnode[i]->ln_Pri == DISABLEDP )
      break;;
  if ( i <  ND )
   addbusytask();
 }
 if ( verbose )
  typestatus();
 permexit( failval );
} /* main */

usageexit()
{
 register int i;
 for ( i = 0; usagemsg[i][0]; i++ )
  puts( usagemsg[i] );
 exit( 10 );
}

modtask( task, pri )                  /* modifies task state and priority */
struct Task *task;
int pri;
{
 task->tc_State = mTS_WAIT;
 if ( ( ((struct Node *)task)->ln_Pri = pri ) == DISABLEDP )
  if ( !chicken )
   task->tc_State = mTS_REMOVED;
}

typestatus()
{
 register int i;
 for ( i = 0; i < ND; i++ )
 {
  if ( !drive[i] )
   continue;
  if ( tnode[i] )
  {
   statusokmsg[8]  = i + 48;
   if ( tnode[i]->ln_Pri == DISABLEDP )
   {
    statusokmsg[21] = 'f';
    statusokmsg[22] = 'f';
   }
   else
   {
    statusokmsg[21] = 'n';
    statusokmsg[22] = ' ';
   }
   if ( fnode[i]->ln_Pri == DISABLEDP )
   {
    statusokmsg[37] = 'f';
    statusokmsg[38] = 'f';
   }
   else
   {
    statusokmsg[37] = 'n';
    statusokmsg[38] = ' ';
   }
   puts( statusokmsg );
  }
  else
   if ( !statusonly )
   {
    statusermsg[8] = i + 48;
    puts( statusermsg );
   }
 } /* for i */
 if ( FindTask( busyname ) )
  puts( busytaskmsg );
} /* typestatus */

permexit( exitval )                 /* all exits after Forbid() come here */
long exitval;
{
 SetTaskPri( me, previouspri );    /* otherwise the CLI or WB stays at -1 */
 Permit();               /* SetTaskPri() reschedules Forbidden tasks ?... */
 exit( exitval );
}

addbusytask()                  /* starts a busyloop task at priority -122 */
{
 char *busycode;
 if ( !( busycode = AllocMem( 32L, MEMF_CLEAR ) ) )
 {
  puts( memerrormsg );
  exit( 14 );
 }
 busycode[0] = 0x60; busycode[1] = 0xfe;                   /* kk:  BRA kk */
 CopyMemQuick( busyname, busycode + 2, 12L );
 if ( !( busytask =
               CreateTask( busycode + 2, (long)BUSYPRI, busycode, 128L ) ) )
 {
  FreeMem( busycode, 32L );
  puts( memerrormsg );
  exit( 14 );
 }
 busytask->tc_UserData = (APTR)busycode;
}

rembusytask()
{
 char *busycode;
 busycode = (char *)busytask->tc_UserData;
 DeleteTask( busytask );
 FreeMem( busycode, 32L );
}

/* fin DisDF.c */
