/*
 * CiderPress
 * Copyright (C) 2007 by faddenSoft, LLC.  All Rights Reserved.
 * See the file LICENSE for distribution terms.
 */
/*
 * ASPI (Advanced SCSI Programming Interface) definitions.
 *
 * This may be included directly by an application.  It must not be necessary
 * to include the lower-level headers, e.g. wnaspi32.h.
 */
#ifndef __ASPI__
#define __ASPI__

#ifndef _WIN32
/*
 * Placeholder definition to keep Linux build happy.
 */
namespace DiskImgLib {
    class DISKIMG_API ASPI {
    public:
        ASPI(void) {}
        virtual ~ASPI(void) {}
    };
};

#else



#ifndef __WNASPI32_H__
struct SRB_ExecSCSICmd;		// fwd
#endif

namespace DiskImgLib {

/*
 * Descriptor for one SCSI device.
 */
class DISKIMG_API ASPIDevice {
public:
	ASPIDevice(void) : fVendorID(nil), fProductID(nil),
		fAdapter(0xff), fTarget(0xff), fLun(0xff), fDeviceReady(false)
		{}
	virtual ~ASPIDevice(void) {
		delete[] fVendorID;
		delete[] fProductID;
	}

	void Init(unsigned char adapter, unsigned char target, unsigned char lun,
		const unsigned char* vendor, unsigned const char* product,
		int deviceType, bool ready)
	{
		fAdapter = adapter;
		fTarget = target;
		fLun = lun;
		assert(fVendorID == nil);
		fVendorID = new char[strlen((const char*)vendor)+1];
		strcpy(fVendorID, (const char*)vendor);
		assert(fProductID == nil);
		fProductID = new char[strlen((const char*)product)+1];
		strcpy(fProductID, (const char*)product);
		fDeviceReady = ready;
		fDeviceType = deviceType;
	}

	enum {
		kTypeDASD = 0,		// kScsiDevTypeDASD
		kTypeCDROM = 5,		// kScsiDevTypeCDROM
	};

	unsigned char GetAdapter(void) const { return fAdapter; }
	unsigned char GetTarget(void) const { return fTarget; }
	unsigned char GetLun(void) const { return fLun; }
	const char* GetVendorID(void) const { return fVendorID; }
	const char* GetProductID(void) const { return fProductID; }
	bool GetDeviceReady(void) const { return fDeviceReady; }
	int GetDeviceType(void) const { return fDeviceType; }

private:
	// strings from SCSI inquiry, padded with spaces at end
	char*		fVendorID;
	char*		fProductID;

	unsigned char	fAdapter;	// physical or logical host adapter (0-15)
	unsigned char	fTarget;	// SCSI ID on adapter (0-15)
	unsigned char	fLun;		// logical unit (0-7)

	int			fDeviceType;	// e.g. kScsiDevTypeCDROM
	bool		fDeviceReady;
};

/*
 * There should be only one instance of this in the library (part of the
 * DiskImgLib Globals).  It wouldn't actually break anything to have more than
 * one, but there's no need for it.
 */
class DISKIMG_API ASPI {
public:
	ASPI(void) :
		fhASPI(nil),
		GetASPI32SupportInfo(nil),
		SendASPI32Command(nil),
		GetASPI32DLLVersion(nil),
		fASPIVersion(0),
		fHostAdapterCount(-1)
		{}
	virtual ~ASPI(void);

	// load ASPI DLL if it exists
	DIError Init(void);

	// return the version returned by the loaded DLL
	DWORD GetVersion(void) const {
		assert(fhASPI != nil);
		return fASPIVersion;
	}

	// Return an *array* of ASPIDevice structures for drives in system;
	//  the caller is expected to delete[] it when done.
	enum { kDevMaskCDROM = 0x01, kDevMaskHardDrive = 0x02 };
	DIError GetAccessibleDevices(int deviceMask, ASPIDevice** ppDeviceArray,
		int* pNumDevices);

	// Get the type of the device (DTYPE_*, 0x00-0x1f) using ASPI query
	DIError GetDeviceType(unsigned char adapter, unsigned char target,
		unsigned char lun, unsigned char* pType);
	// convert DTYPE_* to a string
	const char* DeviceTypeToString(unsigned char deviceType);

	// Get the capacity, expressed as the highest-available LBA and the device
	//  block size.
	DIError GetDeviceCapacity(unsigned char adapter, unsigned char target,
		unsigned char lun, unsigned long* pLastBlock, unsigned long* pBlockSize);

	// Read blocks from the device.
	DIError ReadBlocks(unsigned char adapter, unsigned char target,
		unsigned char lun, long startBlock, short numBlocks, long blockSize,
		void* buf);

	// Write blocks to the device.
	DIError WriteBlocks(unsigned char adapter, unsigned char target,
		unsigned char lun, long startBlock, short numBlocks, long blockSize,
		const void* buf);

private:
	/*
	 * The interesting bits that come out of an SC_HA_INQUIRY request.
	 */
	typedef struct AdapterInfo {
		unsigned char	adapterScsiID;		// SCSI ID of the adapter itself
		unsigned char	managerID[16+1];	// string describing manager
		unsigned char	identifier[16+1];	// string describing host adapter
		unsigned char	maxTargets;			// max #of targets on this adapter
		unsigned short	bufferAlignment;	// buffer alignment requirement
	} AdapterInfo;
	/*
	 * The interesting bits from a SCSI device INQUIRY command.
	 */
	typedef struct Inquiry {
		unsigned char	vendorID[8+1];		// vendor ID string
		unsigned char	productID[16+1];	// product ID string
		unsigned char	productRevision[4];	// product revision bytes
	} Inquiry;

	// Issue an ASPI adapter inquiry request.
	DIError HostAdapterInquiry(unsigned char adapter,
		AdapterInfo* pAdapterInfo);
	// Issue a SCSI device inquiry request.
	DIError DeviceInquiry(unsigned char adapter, unsigned char target,
		unsigned char lun, Inquiry* pInquiry);
	// Issue a SCSI test unit ready request.
	DIError TestUnitReady(unsigned char adapter, unsigned char target,
		unsigned char lun, bool* pReady);
	// execute a SCSI command
	DIError ExecSCSICommand(SRB_ExecSCSICmd* pSRB);


	enum {
		kMaxAdapters = 16,
		kMaxTargets = 16,
		kMaxLuns = 8,
		kTimeout = 30,			// timeout, in seconds
		kMaxAccessibleDrives = 16,
	};

	HMODULE	fhASPI;
	DWORD	(*GetASPI32SupportInfo)(void);
	DWORD	(*SendASPI32Command)(void* lpsrb);
	DWORD	(*GetASPI32DLLVersion)(void);
	DWORD	fASPIVersion;
	int		fHostAdapterCount;
};

}; // namespace DiskImgLib

#endif /*__ASPI__*/

#endif /*_WIN32*/
