/*\
 *
 * DeviceType «Freeware» © Steven Norburn <sndev@bigfoot.com>.
 *
 *	This tests a device to if it supports NSD, TD64 or SCSI Direct.
 *
 * History,
 *
 * 0.10ß (22.09.01)
 *	First release.
 *
 *
\*/

#define __USE_SYSBASE

#include <proto/exec.h>
#include <proto/dos.h>

#include <exec/errors.h>
#include <devices/newstyle.h>
#include <devices/scsidisk.h>
#include <devices/trackdisk.h>

#define PROG "DeviceType"
#define VERSION "«Freeware» 0.10ß"
#define DATE "24.09.01"
#define COPYRIGHT "© Steven Norburn <sndev@bigfoot.com>"

static char version_string[] =
"$VER: "PROG" "VERSION" ("DATE") " COPYRIGHT;

static char template[] =
"D=Device/A,U=Unit/A/N,Geo=GetGeometry/S";

enum
{
	ARG_DEVICE,
	ARG_BU,
	ARG_GEO,
	NUM_ARGS
};

LONG arg_array[NUM_ARGS];

struct ExecBase *SysBase;
struct DosLibrary *DOSBase;


 /*--------------*\
<*> Startup Code <*>
 \*--------------*/

int Main(void);
struct Library *OpenLibraryMsg( CONST_STRPTR libName, ULONG version );

int __saveds main(void) /* __saveds Restore Globel Data Pointer ! */
{
int	ret = RETURN_FAIL;
struct	RDArgs *rdargs;

	SysBase = (*(struct ExecBase **) 4);

	if((DOSBase = (struct DosLibrary *)OpenLibrary(DOSNAME, 37L)))
	{
		if(!(rdargs = ReadArgs(template, arg_array, NULL))){
			Printf("\n%s\n\nUsage: "PROG" %s\n", &version_string[6], template);
			ret = RETURN_WARN;
		}
		else{
			ret = Main();
			FreeArgs(rdargs);
		}
		CloseLibrary((struct Library *)DOSBase);
	}
	return(ret);
}

/*
struct Library *OpenLibraryMsg(CONST_STRPTR libName, UWORD version )
{
struct Library *lib
	if((lib = OpenLibrary(libName, 0L))) {

		if(lib->lib_Version < version) {

			Printf( "Failed to open version %ul of %s !\n", libName, version);
			CloseLibrary(lib);
			lib = NULL;
		}

	} else if(DOSBase != NULL) Printf( "Failed to open %s !\n", libName);

	return(lib);
}
*/


 /*------------------------*\
<*> New Style Device Check <*>
 \*------------------------*/

#define	TD_READ64	24
#define	TD_WRITE64	25
#define	TD_SEEK64	26
#define	TD_FORMAT64	27
#define	io_HighOffset io_Actual

#define	NSD	1<<0
#define	TD32	1<<1
#define	NSDSD	1<<2
#define	NSD64	1<<3
#define	ETD64	1<<4

#define	TD64	1<<5

#define	SCSIDirect	1<<6


int TestforNSD(struct IOStdReq *io)
{
struct	NSDeviceQueryResult nsdqr;
int	ret = 0;
UWORD	*cmdcheck;

	nsdqr.SizeAvailable = 0;
	nsdqr.DevQueryFormat = 0;

	io->io_Command = NSCMD_DEVICEQUERY;
	io->io_Length = sizeof(nsdqr);
	io->io_Data	= (APTR)&nsdqr;

	if ((SetSignal(0,0) & SIGBREAKF_CTRL_C)) return(0);

	if(!(DoIO((struct IORequest *)io)) &&
	   (io->io_Actual >= 16) &&
	   (io->io_Actual <= sizeof(nsdqr)) &&
	   (nsdqr.SizeAvailable == io->io_Actual))
	{
		/* Ok, this must be a new style device */
		ret = NSD;

		if(nsdqr.DeviceType == NSDEVTYPE_TRACKDISK)
		{
			ret = NSD | TD32;

			/* Device specific code follows */
			/* Is it safe to use 64 bits with this driver? We can reject
			 * bad mounts pretty easily via this check!
			 */
			for(cmdcheck = nsdqr.SupportedCommands; *cmdcheck; cmdcheck++)
			{
				if ((SetSignal(0,0) & SIGBREAKF_CTRL_C)) return(0);

				if(*cmdcheck == TD_READ64)
					ret = ret | TD64;

				if(*cmdcheck == HD_SCSICMD)
					ret = ret | NSDSD;

				if(*cmdcheck == NSCMD_TD_READ64)
					ret = ret | NSD64;

				if(*cmdcheck == NSCMD_ETD_READ64)
					/* According to specs, this implies NSD64 == TRUE! */
					ret = ret | ETD64;
			}
		}
	}
	return(ret);
}


 /*---------------------*\
<*> Track Disk 64 Check <*>
 \*---------------------*/

/*
it may optionally check whether the underlying driver actually supports
them by issuing a TD_READ64 request with io_Offset, io_HighOffset, and
io_Length all set to zero; if the underlying driver returns IOERR_NOCMD,
the filesystem-level application should report an error (e.g.  fail the
start-up packet in case of a filesystem).  For reasons of efficiency, this
check should be performed only _once_ upon start-up.
*/

int TestforTD64(struct IOStdReq *io)
{
int	ret = 0;

	io->io_Command = TD_READ64;
	io->io_Offset = 0;
	io->io_Data  = 0;
	io->io_HighOffset = 0;
	io->io_Length  = 0;

	if ((SetSignal(0,0) & SIGBREAKF_CTRL_C)) return(0);

	if(DoIO((struct IORequest *)io) != IOERR_NOCMD) ret = TD64;

	return(ret);
}


 /*-------------------*\
<*> SCSI Direct Check <*>
 \*-------------------*/

int TestforSCSIDirect(struct IOStdReq *io)
{
int	ret = 0;

struct	SCSICmd Cmd;		/* where the actual SCSI command goes */

static	UBYTE TestReady[] = { 0,0,0,0,0,0 };

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

	Cmd.scsi_Data = NULL; 	/* where we put returned data */
	Cmd.scsi_Length = 0;		/* how much we will accept	*/
	Cmd.scsi_Command=(UBYTE *)TestReady; /* issuing a TEST_READY dummy command */
	Cmd.scsi_CmdLength = 6;	/* length of the command	*/
	Cmd.scsi_Actual = 1;		/* Dummy Value, TestReady should change to 0 */
	Cmd.scsi_Flags = SCSIF_NOSENSE|SCSIF_READ;

	if ((SetSignal(0,0) & SIGBREAKF_CTRL_C)) return(0);

	if((DoIO((struct IORequest *)io) != IOERR_NOCMD) && (Cmd.scsi_Actual == 0))
		ret = SCSIDirect;

	return(ret);
}


 /*--------------------*\
<*> Get Geometry Check <*>
 \*--------------------*/

int ShowGeo(struct IOStdReq *io)
{
int	ret = 0;
struct DriveGeometry geo;

	io->io_Command = TD_GETGEOMETRY;
	io->io_Flags  = 0;
	io->io_Data  = &geo;
	io->io_Length  = sizeof(struct DriveGeometry);

	if ((SetSignal(0,0) & SIGBREAKF_CTRL_C)) return(0);

	if(DoIO((struct IORequest *)io) != 0)
		Printf("Get Geometry has Failed !\n");
	else {
		Printf("SectorSize\t= %ld\n", geo.dg_SectorSize);
		Printf("TotalSectors\t= %ld\n", geo.dg_TotalSectors);
		Printf("Cylinders\t= %ld\n", geo.dg_Cylinders);
		Printf("CylSectors\t= %ld\n", geo.dg_CylSectors);
		Printf("Heads\t\t= %ld\n", geo.dg_Heads);
		Printf("TrackSectors\t= %ld\n", geo.dg_TrackSectors);
		Printf("BufMemType\t= %ld\n", geo.dg_BufMemType);
		Printf("DeviceType\t= %ld\n", (LONG)geo.dg_DeviceType);
		if(geo.dg_Flags & DGF_REMOVABLE)
			Printf("Removable Media\n");

	}
	return(ret);
}


 /*------*\
<*> MAIN <*>
 \*------*/

int Main(void)
{
int	ret = RETURN_OK;
char	*devicename;
ULONG	baseunit;
struct	IOStdReq *io;

	devicename = (char *) arg_array [ ARG_DEVICE];
	baseunit = *(ULONG*) arg_array [ ARG_BU ];

	if (!(SetSignal(0,0) & SIGBREAKF_CTRL_C))
	{
		if(!(io = CreateIORequest(CreateMsgPort(), sizeof(struct IOStdReq) + 128))) {
			Printf("Couldn't Create IORequest Structure.\n");
			return (RETURN_FAIL);
		}
		if(OpenDevice(devicename, baseunit, (struct IORequest *)io, 0)) {
			Printf("Couldn't open unit %ld on %s\n",baseunit,devicename);
			ret = RETURN_FAIL;
		} else {
		int	nsd = TestforNSD(io);
		char	*div = "";
			Printf("It's a ");

			if(nsd != 0)
			{
				Printf("New Style ");
				div = "& ";
			}

			if((nsd & TD32) != 0)
			{
				Printf("(TD");
				if(nsd & NSDSD)
					Printf(", SD");
				if(nsd & TD64)
					Printf(", TD64?");
				if(nsd & NSD64)
					Printf(", NSD64");
				if(nsd & ETD64)
					Printf(", ETD64");
				Printf(") ");
				nsd = 0;
			}

			if(nsd == 0)
			{
				if(TestforTD64(io))
				{
					Printf("%sTrack Disk 64 ? ",div);
					div = "& ";
				}
				if(TestforSCSIDirect(io))
					Printf("%sSCSI Direct ",div);
			}
			Printf("device !\n");

			if(arg_array[ARG_GEO]) ShowGeo(io);

			CloseDevice((struct IORequest *)io);
		}
		DeleteMsgPort(io->io_Message.mn_ReplyPort);
		DeleteIORequest(io);
	}
	if(SetSignal(0,SIGBREAKF_CTRL_C) & SIGBREAKF_CTRL_C)
	{
		Printf("***Break\n");
		ret = RETURN_FAIL;
	}
	return(ret);
}
