/*
 * Copyright (C) 1990 Commodore-Amiga, Inc.
 * All rights reserved
 */

/*
 * The following program demonstrates the use of the HD_SCSICmd to send a
 * MODE SENSE to a unit on the requested device (default scsi.device).  This
 * code can be easily modified to send other commands to the drive.
 *
 * compile under Lattice using 'lc -cfist -L modesense'
 */

#include <stdio.h>
#include <stdlib.h>

#include <exec/types.h>
#include <exec/memory.h>
#include <exec/io.h>
#include <devices/timer.h>
#include <devices/scsidisk.h>
#include <libraries/dosextens.h>

#include <proto/exec.h>

#define BUFSIZE 256

UBYTE *buffer;			/* a data buffer used for mode sense data */
struct IOStdReq SCSIReq;	/* a standard IORequest structure */
struct SCSICmd Cmd;		/* where the actual SCSI command goes */
UBYTE  Sense[20];		/* buffer for request sense data */
struct MsgPort Port;		/* our ReplyPort */

void ShowSenseData(void);

static UBYTE TestReady[] = { 0,0,0,0,0,0 };	/* not used but here for  */
static UBYTE StartUnit[] = { 0x1b,0,0,0,1,0 };	/* illustration of other  */
static UBYTE StopUnit[] =  { 0x1b,0,0,0,0,0 };	/* commands.		  */

static UBYTE ModeSense[]={ 0x1a,0,0xff,0,254,0 }; /* the command being sent */

void main(int argc, char **argv) {
    int unit,tval,i;
    char *dname = "scsi.device";
    UBYTE *tbuf;
    
    if( (argc < 2) || (argc > 3) ) {
	printf("Usage: %s unit [xxxx.device]\n",argv[0]);
	exit(100);
    }

    unit = atoi( argv[1] );
    if ( argc == 3 ) dname = argv[2];

    buffer = (UBYTE *) AllocMem( BUFSIZE, MEMF_PUBLIC|MEMF_CLEAR );
    if (!buffer) { printf("Couldn't get memory\n"); exit(100); }

    Port.mp_Node.ln_Pri = 0;			/* setup the ReplyPort */
    Port.mp_SigBit      = AllocSignal(-1);
    Port.mp_SigTask     = (struct Task *)FindTask(0);
    NewList( &(Port.mp_MsgList) );

    SCSIReq.io_Message.mn_ReplyPort = &Port;

    if (OpenDevice( dname, unit, &SCSIReq, 0))  {
	printf("Couldn't open unit %ld on %s\n",unit,dname);
	FreeMem( buffer,BUFSIZE );
	exit(100);
    }

    SCSIReq.io_Length  = sizeof(struct SCSICmd);
    SCSIReq.io_Data    = (APTR)&Cmd;	
    SCSIReq.io_Command = HD_SCSICMD;	/* the command we are sending	*/	

    Cmd.scsi_Data = (UWORD *)buffer;	/* where we put mode sense data	*/
    Cmd.scsi_Length = 254;		/* how much we will accept	*/
    Cmd.scsi_CmdLength = 6;		/* length of the command	*/
    Cmd.scsi_Flags = SCSIF_AUTOSENSE|SCSIF_READ;
					/* do automatic REQUEST_SENSE	*/
					/* set expected data direction 	*/
    Cmd.scsi_SenseData =(UBYTE *)Sense;	/* where sense data will go	*/
    Cmd.scsi_SenseLength = 18;		/* how much we will accept	*/
    Cmd.scsi_SenseActual = 0;		/* how much has been received	*/

    Cmd.scsi_Command=(UBYTE *)ModeSense;/* issuing a MODE_SENSE command	*/
    DoIO( &SCSIReq );			/* send it to the device driver	*/
    if(Cmd.scsi_Status) ShowSenseData(); /* if bad status then show it	*/

    else {
	printf("\nBlock descriptor header\n");
	printf("=======================\n");
	printf("Mode Sense data length  = %d\n",(short)buffer[0]);
	printf("Block descriptor length = %d\n",(short)buffer[3]);
	tbuf = &buffer[4];
	printf("Density code            = %d\n",(short)tbuf[0]);
	tval = (tbuf[1]<<16) + (tbuf[2]<<8) + tbuf[3];
	printf("Number of blocks        = %ld\n",tval);
	tval = (tbuf[5]<<16) + (tbuf[6]<<8) + tbuf[7];
	printf("Block size              = %ld\n",tval);

	tbuf += buffer[3];		/* move to page descriptors */

	while( (tbuf - buffer) < buffer[0] ) {
	    switch ( tbuf[0] & 0x7f ) {
	    case 1:
		printf("\nError Recovery Parameters\n");
		printf("=========================\n");
		printf("Page length             = %d\n",(short)tbuf[1]);
		printf("DISABLE CORRECTION      = %d\n",(short)tbuf[2]&1);
		printf("DISABLE XFER ON ERROR   = %d\n",(short)(tbuf[2]>>1)&1);
		printf("POST ERROR              = %d\n",(short)(tbuf[2]>>2)&1);
		printf("ENABLE EARLY CORRECTION = %d\n",(short)(tbuf[2]>>3)&1);
		printf("READ CONTINUOUS         = %d\n",(short)(tbuf[2]>>4)&1);
		printf("TRANSFER BLOCK          = %d\n",(short)(tbuf[2]>>5)&1);
		printf("AUTO READ REALLOCATION  = %d\n",(short)(tbuf[2]>>6)&1);
		printf("AUTO WRITE REALLOCATION = %d\n",(short)(tbuf[2]>>7)&1);
		printf("Retry count             = %d\n",(short)tbuf[3]);
		printf("Correction span         = %d\n",(short)tbuf[4]);
		printf("Head offset count       = %d\n",(short)tbuf[5]);
		printf("Data strobe offset count= %d\n",(short)tbuf[6]);
		printf("Recovery time limit     = %d\n",(short)tbuf[7]);
		tbuf += tbuf[1]+2;
		break;

	    case 2 :
		printf("\nDisconnect/Reconnect Control\n");
		printf("============================\n");
		printf("Page length             = %d\n",(short)tbuf[1]);
		printf("Buffer full ratio       = %d\n",(short)tbuf[2]);
		printf("Buffer empty ratio      = %d\n",(short)tbuf[3]);
		tval = (tbuf[4]<<8)+tbuf[5];
		printf("Bus inactivity limit    = %d\n",tval);
		tval = (tbuf[6]<<8)+tbuf[7];
		printf("Disconnect time limit   = %d\n",tval);
		tval = (tbuf[8]<<8)+tbuf[9];
		printf("Connect time limit      = %d\n",tval);
		tval = (tbuf[10]<<8)+tbuf[11];
		printf("Maximum burst size      = %d\n",tval);
		printf("Disable disconnection   = %d\n",(short)tbuf[12]&1);
		tbuf += tbuf[1]+2;
		break;

	    case 3:
		printf("\nDevice Format Parameters\n");
		printf("========================\n");
		printf("Page length             = %d\n",(short)tbuf[1]);
		tval = (tbuf[2]<<8)+tbuf[3];
		printf("Tracks per zone         = %d\n",tval);
		tval = (tbuf[4]<<8)+tbuf[5];
		printf("Alternate sectors/zone  = %d\n",tval);
		tval = (tbuf[6]<<8)+tbuf[7];
		printf("Alternate tracks/zone   = %d\n",tval);
		tval = (tbuf[8]<<8)+tbuf[9];
		printf("Alternate tracks/volume = %d\n",tval);
		tval = (tbuf[10]<<8)+tbuf[11];
		printf("Sectors per track       = %d\n",tval);
		tval = (tbuf[12]<<8)+tbuf[13];
		printf("Bytes per sector        = %d\n",tval);
		tval = (tbuf[14]<<8)+tbuf[15];
		printf("Interleave              = %d\n",tval);
		tval = (tbuf[16]<<8)+tbuf[17];
		printf("Track skew factor       = %d\n",tval);
		tval = (tbuf[18]<<8)+tbuf[19];
		printf("Cylinder skew factor    = %d\n",tval);

		tbuf += tbuf[1]+2;
		break;

	    case 4:
		printf("\nDrive Geometry Parameters\n");
		printf("=========================\n");
		printf("Page length             = %d\n",(short)tbuf[1]);
		tval = (tbuf[2]<<16)+(tbuf[3]<<8)+tbuf[4];
		printf("Number of cylinders     = %ld\n",tval);
		printf("Number of heads         = %d\n",(short)tbuf[5]);
		tval = (tbuf[6]<<16)+(tbuf[6]<<8)+tbuf[8];
		printf("Start write precomp     = %ld\n",tval);
		tval = (tbuf[9]<<16)+(tbuf[10]<<8)+tbuf[11];
		printf("Start reduced write     = %ld\n",tval);
		tval = (tbuf[12]<<8)+tbuf[13];
		printf("Drive step rate         = %d\n",tval);
		tval = (tbuf[14]<<16)+(tbuf[15]<<8)+tbuf[16];
		printf("Landing zone cylinder   = %ld\n",tval);

		tbuf += tbuf[1]+2;
		break;

	    default:
		printf("\nVendor Unique Page Code %2x\n",(short)tbuf[0]);
		printf("==========================\n");
		for( i=0; i<=tbuf[1]+1; i++ )
		    printf("%x ",(short)tbuf[i]);
		printf("\n");
		tbuf += tbuf[1]+2;
	    }
	}
    }
    CloseDevice( &SCSIReq );
    FreeMem( buffer, BUFSIZE );
    FreeSignal(Port.mp_SigBit);
}

void ShowSenseData(void)
{
int i;

    for(i=0; i<18; i++)
        printf("%x ",(int)Sense[i]);
    printf("\n");
}

