//
// This is the main MSA interface code. It sits on top of the USB
// driver (currently ezusb.sys) which uses windows USB drivers to
// communicate with the MSA.
// The interface is defined in msaio.h - this is the place to start...


// plagiarised from the TAPR VNA design.
// as a consequence, the rights of Thomas C. McDermott, N5EG are acknowledged.
// Here is the licence text from his code. The only part used in this way
// is the basic structure of the VNA device and helper.
// All else is different 
// All the other code is Copyright (C) Dave Roberts G8KBB 2004
//
// ----------------- Extract from USB_EZ_interface.cpp -----------------
//    Copyright 2004, Thomas C. McDermott, N5EG
//    This file is part of VNAR - the Vector Network Analyzer program.
//
//    VNAR is free software; you can redistribute it and/or modify
//    it under the terms of the GNU General Public License as published by
//    the Free Software Foundation; either version 2 of the License, or
//    (at your option) any later version.
//
//    VNAR is distributed in the hope that it will be useful,
//    but WITHOUT ANY WARRANTY; without even the implied warranty of
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//    GNU General Public License for more details.
//
//    You should have received a copy of the GNU General Public License
//    along with VNAR, if not, write to the Free Software
//    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
// ------------------------End Extract ----------------------------------

//#define LOG_COMMANDS
//#define CACHELOG
//#define TRACESIZE 1000000

#include "stdio.h"

#include "stdafx.h"
#include "objbase.h"
//#include "iostream.h"
#include "Setupapi.h"

#include ".\cyapi\inc\cyapi.h"
extern "C"
{
    // Declare USB constants and structures
	#include "winioctl.h"				// IOCTL definitions
//	#include "c:\ntddk\inc\usb100.h"	// general USB
//	#include "c:\ntddk\inc\usbdi.h"		// USB Device Interface
//	#include "c:\ntddk\inc\devioctl.h"	// Device IOCTL definitions
}

#include "msadll.h"


class Helper		// Holds the USB device state while hiding it from other code
					// Helper is not on the managed heap, and requies explicit lifetime control.
					// Neither it nor it's contents get moved in memory by the garbage collector
{
public:
	BULK_TRANSFER_CONTROL * pInPipe;
	BULK_TRANSFER_CONTROL * pOutPipe;
	PUSB_DEVICE_DESCRIPTOR pDevDescr;
	PEZUSB_DRIVER_VERSION pDrvVerEzusb;
	ULONG * pDrvVers;
	GET_STRING_DESCRIPTOR_IN * pStrDescr;
	PUSB_INTERFACE_DESCRIPTOR pInterfaceInfo;
	PUSB_CONFIGURATION_DESCRIPTOR pConfigDescr;
	char * pDevString;					// USB Device Identifier, string version
	HANDLE DevDrvHandle;
	LPDWORD pBytesReturned;
	int instance;
	CCyUSBDevice *USBDevice;
	bool bUsingCyUSB;
#ifdef CACHELOG
	char *traceblock, *traceptr;
#endif
	Helper()
	{
//		cout << "Helper Constructor called\n";
		pInPipe = new BULK_TRANSFER_CONTROL;
		pOutPipe = new BULK_TRANSFER_CONTROL;
		pDevDescr = new USB_DEVICE_DESCRIPTOR;
		pDrvVers = new ULONG;
		pDrvVerEzusb = new EZUSB_DRIVER_VERSION ;
		pStrDescr = new GET_STRING_DESCRIPTOR_IN;
		pInterfaceInfo = new USB_INTERFACE_DESCRIPTOR;
		pDevString = new USB_STRING;
		pConfigDescr = new USB_CONFIGURATION_DESCRIPTOR;
		pBytesReturned = new DWORD;
		instance = 0xff;
	}
	~Helper()
	{
//		cout << "Helper Destructor called\n";
		delete pInPipe;
		delete pOutPipe;
		delete pDevDescr;
		delete pDrvVers;
		delete pDrvVerEzusb;
		delete pStrDescr;
		delete pInterfaceInfo;
		delete pDevString;
		delete pConfigDescr;
		delete pBytesReturned;
	}
};

// The functions below relate to the declaration in MSAio.h
// and define its public interface

// on initialisation of the MSA device, instance is set to FF so
// it then searches for the first device it can find. The instance
// (0..9) can then be read with GetInstance(). A specific instance may
// be set by setting the instance.

// {AE18AA60-7F6A-11d4-97DD-00010229B959}
//static GUID CYUSBDRV_GUID = {0xae18aa60, 0x7f6a, 0x11d4, 0x97, 0xdd, 0x0, 0x1, 0x2, 0x29, 0xb9, 0x59};

// {A8C2BDB4-97EA-4711-BD4C-0453585BB290}
static const GUID MSA_USB_GUID = 
{ 0xa8c2bdb4, 0x97ea, 0x4711, { 0xbd, 0x4c, 0x4, 0x53, 0x58, 0x5b, 0xb2, 0x90 } };

#define MY_GUID MSA_USB_GUID

void MSADevice::GetHandle(void)
{
	HANDLE hDevice = INVALID_HANDLE_VALUE;
	int i;


	if (d->USBDevice->DeviceCount() && !d->USBDevice->Open(0)) 
	{
		d->USBDevice->Reset();
		d->USBDevice->Open(0);
	}
	if (d->USBDevice->IsOpen())
	{
		hDevice = d->USBDevice->DeviceHandle();
		d->bUsingCyUSB = true;
	}
	else
	{
		d->bUsingCyUSB = false;
		char usbdevname[32];
		ZeroMemory( usbdevname, sizeof( usbdevname ) );
		strcpy_s( usbdevname, sizeof( usbdevname ), "\\\\.\\ezusb-0" );
//		char  * usbdevname = "\\\\.\\ezusb-0";		// device 0

		if( d->instance != 0xff )
		{
			usbdevname[10] = d->instance +'0';
			hDevice = CreateFile((LPCTSTR)&usbdevname[0],		// try device 0
				GENERIC_WRITE,
				FILE_SHARE_WRITE,
				NULL,
				OPEN_EXISTING,
				0,
				NULL);
		}
		if( hDevice==INVALID_HANDLE_VALUE )
		{
			for( i=0; i<10; i++)
			{
				usbdevname[10] = i + '0';
				hDevice = CreateFile((LPCTSTR)&usbdevname[0],	// try device i
					GENERIC_WRITE,
					FILE_SHARE_WRITE,
					NULL,
					OPEN_EXISTING,
					0,
					NULL);
				if (hDevice!=INVALID_HANDLE_VALUE)
				{
					d->instance = i;
					break;
				}
			}
		}
	}
	if (hDevice==INVALID_HANDLE_VALUE)
		state = -1;						// open failed
	else
		state = 1;						// open succeded
	d->DevDrvHandle = hDevice;
};

void MSADevice::ReleaseHandle(void)
{
	if( !d->bUsingCyUSB )
	{
		if (d->DevDrvHandle != INVALID_HANDLE_VALUE )
			CloseHandle(d->DevDrvHandle);
	}
	d->DevDrvHandle = INVALID_HANDLE_VALUE;
};

bool MSADevice::ToggleReset(bool hold)
{
	if( d->bUsingCyUSB )
	{
		d->USBDevice->Reset();
		return( 1 );
	}
	else
	{
		 //use the vendor request type to set/release the reset register in the 8051

		VENDOR_REQUEST_IN  * pRequest = new VENDOR_REQUEST_IN;

		pRequest->bRequest = 0xA0;			// Anchorchips Vendor Request Type
		pRequest->wValue = CPUCS_REG_EZUSB;		// 8051 Control / Status Register
		pRequest->wIndex = 0x00;
		pRequest->wLength = 0x01;
		pRequest->bData = (hold) ? 1 : 0;	// 1 holds 8051 in reset, 0 starts 8051 (at 0x0000)
		pRequest->direction = 0x00;

		GetHandle();

		Result = DeviceIoControl((HANDLE)d->DevDrvHandle,
			IOCTL_Ezusb_VENDOR_REQUEST,
			pRequest,
			sizeof(VENDOR_REQUEST_IN),
			NULL,
			0,
			d->pBytesReturned,
			NULL) ? true : false;;

		ReleaseHandle();
		return(Result);
	}
};



__declspec(dllexport) _stdcall MSADevice::MSADevice()							// Construct the MSADevice
{
//		cout << "MSADevice Constructor called\n";
	d = new Helper;					// Allocate a Helper to the MSADevice
	d->USBDevice = NULL;
	d->USBDevice = new CCyUSBDevice(NULL, MY_GUID);
//	MessageBufferIndex = 0;
	GetHandle();
	ReleaseHandle();
	pAllArray = NULL;
	nSteps = 0;
	nArrayCols = 0;
#ifdef CACHELOG
	int i;
	d->traceblock = (char *)malloc( TRACESIZE );
	if( d->traceblock != NULL )
	{
		i = sprintf(d->traceblock, "Cache created\n");
		d->traceptr = d->traceblock + i;
	}
#endif
};

__declspec(dllexport) _stdcall MSADevice::~MSADevice()
{
//		cout << "MSAdevice Destructor called\n";
	ReleaseHandle();
#ifdef CACHELOG
	int i;
	if( d->traceblock != NULL )
	{
		i = sprintf(d->traceptr, "Cache closed\n");
		d->traceptr += i;
		FILE *fp = fopen( "c:\\vnalog.txt", "a");
		if( fp != NULL )
		{
			fwrite( d->traceblock, 1, d->traceptr - d->traceblock, fp );
			fclose(fp);
		}
		free( d->traceblock );
		d->traceptr = d->traceblock = NULL;
	}
#endif
	ReleaseHandle();
	if( d->bUsingCyUSB )
		d->USBDevice->Close();
	delete d->USBDevice;
	delete d;						// since d is on the unmanaged heap

}

// empty code - other than checking the MSA is responding OK and checking
// the pipe descriptions there is not a lot to do here. Will fill
// these in later.

__declspec(dllexport) bool _stdcall MSADevice::Init(void)						// Build Device Descriptors and Pipes
{
	GetHandle();
	if( d->bUsingCyUSB )
	{
		d->USBDevice->GetDeviceDescriptor( d->pDevDescr );
		*d->pDrvVers = d->USBDevice->DriverVersion;
		d->USBDevice->GetConfigDescriptor( d->pConfigDescr );
	}
	else
	{
		Result = DeviceIoControl((HANDLE)d->DevDrvHandle,
			IOCTL_Ezusb_GET_DEVICE_DESCRIPTOR,
			NULL,
			0,
			d->pDevDescr,
			sizeof(USB_DEVICE_DESCRIPTOR),
			d->pBytesReturned,
			NULL) ? true : false;
		if( Result != true ) return Result;

		Result = DeviceIoControl((HANDLE)d->DevDrvHandle,
			IOCTL_EZUSB_GET_DRIVER_VERSION,
			NULL,
			0,
			d->pDrvVerEzusb,
			sizeof(EZUSB_DRIVER_VERSION),
			d->pBytesReturned,
			NULL) ? true : false;
		if( Result != true ) return Result;

		Result = DeviceIoControl((HANDLE)d->DevDrvHandle,
			IOCTL_Ezusb_GET_CONFIGURATION_DESCRIPTOR,
			NULL,
			0,
			d->pConfigDescr,
			sizeof(USB_CONFIGURATION_DESCRIPTOR),
			d->pBytesReturned,
			NULL) ? true : false;
		if( Result != true ) return Result;

		//	Result = DeviceIoControl((HANDLE)d->DevDrvHandle,
		//		IOCTL_Ezusb_GET_PIPE_INFO,
		//		NULL,
		//		0,
		//		d->pInterfaceInfo,
		//		sizeof(EZUSB_PIPE),
		//		d->pBytesReturned,
		//		NULL);
		//	if( Result != true ) return Result;
	}
	ReleaseHandle();
	return(true); // guarantee success
};

__declspec(dllexport) int _stdcall MSADevice::get_State() {return state;};

__declspec(dllexport) int _stdcall MSADevice::get_BytesReturned() { return *(d->pBytesReturned); };

__declspec(dllexport) bool _stdcall MSADevice::Start() { return(ToggleReset(0)); };		// Release reset on the 8051 processor

__declspec(dllexport) bool _stdcall MSADevice::Stop()	 { return(ToggleReset(1)); };		// Halt the 8051 processor

__declspec(dllexport) int _stdcall MSADevice::get_Instance() {return d->instance;};

__declspec(dllexport) int _stdcall MSADevice::GetVersions() 
{ 
	if( d->bUsingCyUSB )
		return (d->USBDevice->BcdDevice & 0xff) + (DLL_VERSION<<8); 
	else
		return (d->pDevDescr->bcdDevice & 0xff) + (DLL_VERSION<<8);
};

__declspec(dllexport) int _stdcall MSADevice::GetDeviceId() 
{ 
	if( d->bUsingCyUSB )
		return (d->USBDevice->BcdDevice ); 
	else
		return (d->pDevDescr->bcdDevice ); 
}

__declspec(dllexport) bool _stdcall MSADevice::set_Instance(int instance)
{
	if( unsigned(instance) > 9 )
		return false;
	
	d->instance = instance;
	return true;
};


__declspec(dllexport) bool _stdcall MSADevice::Read(MSA_RXBUFFER * readbuf)				// Read data from BULK endpoint
{
	void  * rb = readbuf;			// pin the readbuf in memory
	LONG len = 255;
	Result = 0;
	
	if( d->DevDrvHandle == INVALID_HANDLE_VALUE )
		GetHandle();
	if( d->bUsingCyUSB )
	{
		if( d->USBDevice->BulkInEndPt )
			Result = d->USBDevice->BulkInEndPt->XferData( (PUCHAR)rb, len );
		*(d->pBytesReturned) = len;
	}
	else
	{
		d->pInPipe->pipeNum = 1;			// most likely

		Result = DeviceIoControl((HANDLE)d->DevDrvHandle,
			IOCTL_EZUSB_BULK_READ,
			d->pInPipe,
			sizeof(BULK_TRANSFER_CONTROL),
			rb,								// readbuf
			255,
			d->pBytesReturned,
			NULL) ? true : false;
	}

#ifdef LOG_COMMANDS
	int i;
	FILE *fp = fopen( "c:\\vnalog.txt", "a");
	fprintf(fp,"Read command, count = %d Data =", *d->pBytesReturned );
	if( *d->pBytesReturned > 0 && *d->pBytesReturned < 255 )
	{
		for( i=0; i<*d->pBytesReturned; i++)
			fprintf(fp, "%02x ",*((unsigned char *)rb+i) &0xff);
	}
	fprintf(fp,"\n");
	fclose(fp);
#endif
#ifdef CACHELOG
	int i,j;
	if( d->traceblock != NULL )
	{
		if( d->traceptr - d->traceblock > TRACESIZE - 1024 )
		{
			FILE *fp = fopen( "c:\\vnalog.txt", "a");
			if( fp != NULL )
			{
				fwrite( d->traceblock, 1, d->traceptr - d->traceblock, fp );
				fclose(fp);
			}
			d->traceptr = d->traceblock;
		}

		j = 0;
		j += sprintf(d->traceptr+j, "Read command, count = %d Data =", *d->pBytesReturned);
		if( *d->pBytesReturned > 0 && *d->pBytesReturned < 255 )
		{
			for( i=0; i<*d->pBytesReturned; i++)
				j += sprintf(d->traceptr+j, "%02x ", *((unsigned char *)rb+i) & 0xff);
		}
		j += sprintf(d->traceptr+j,"\n");

		d->traceptr += j;
	}
#endif

//	ReleaseHandle();
	return(Result);
};

__declspec(dllexport) bool _stdcall MSADevice::Write(MSA_TXBUFFER * writebuf, int message_size)					// Write data to BULK endpoint
{
	void  * wb = writebuf;				// pin the writebuf in memory
	LONG len = message_size;
	Result = 0;

	if( d->DevDrvHandle == INVALID_HANDLE_VALUE )
		GetHandle();

	if( d->bUsingCyUSB )
	{
		if( d->USBDevice->BulkOutEndPt )
			Result = d->USBDevice->BulkOutEndPt->XferData( (PUCHAR)wb, len );
	}
	else
	{
		d->pOutPipe->pipeNum = 0;			// most likely

#ifdef LOG_COMMANDS
	FILE *fp = fopen( "c:\\vnalog.txt", "a");
	if( fp != NULL )
	{
		int i;

		fprintf(fp, "Write Length %d: Message = ", message_size);
		for( i=0; i<message_size; i++)
			fprintf(fp, "%02x ", *((unsigned char *)wb+i));
		fprintf(fp,"\n");
		fclose(fp);
	}
#endif
#ifdef CACHELOG
	int i,j;
	if( d->traceblock != NULL )
	{
		if( d->traceptr - d->traceblock > TRACESIZE - 1024 )
		{
			FILE *fp = fopen( "c:\\vnalog.txt", "a");
			if( fp != NULL )
			{
				fwrite( d->traceblock, 1, d->traceptr - d->traceblock, fp );
				fclose(fp);
			}
			d->traceptr = d->traceblock;
		}

		j = 0;
		j += sprintf(d->traceptr+j, "Write Length %d: Message = ", message_size);
		for( i=0; i<message_size; i++)
			j += sprintf(d->traceptr+j, "%02x ", *((unsigned char *)wb+i));
		j += sprintf(d->traceptr+j,"\n");

		d->traceptr += j;
	}
#endif

		Result = DeviceIoControl((HANDLE)d->DevDrvHandle,
			IOCTL_EZUSB_BULK_WRITE,
			d->pOutPipe,
			sizeof(BULK_TRANSFER_CONTROL),
			wb,									// writebuf
			message_size,
			d->pBytesReturned,
			NULL) ? true : false;
	}


//	ReleaseHandle();
	return(Result);
};


// Wrappers for the rather inflexible basic interface !

extern "C" __declspec(dllexport) void *UsbMSAInitialise(  )
{
	MSADevice* MSA = NULL;
	MSA = new MSADevice;
	if( MSA != NULL )
		MSA->Init();
	return MSA;
}

extern "C" __declspec(dllexport) void  UsbMSARelease( void *pMSAData )
{
	MSADevice* MSA = (MSADevice* )pMSAData;
	delete MSA;
}

extern "C" __declspec(dllexport) int  UsbMSAGetVersions( void *pMSAData )
{
	MSADevice* MSA = (MSADevice* )pMSAData;
	return MSA->GetVersions();
}

extern "C" __declspec(dllexport) int  UsbMSAInit( void *pMSAData )
{
	MSADevice* MSA = (MSADevice* )pMSAData;
	return MSA->Init();
}

extern "C" __declspec(dllexport) int  UsbMSAGetState( void *pMSAData )
{
	MSADevice* MSA = (MSADevice* )pMSAData;
	return MSA->get_State();
}

extern "C" __declspec(dllexport) int  UsbMSAStart( void *pMSAData )
{
	MSADevice* MSA = (MSADevice* )pMSAData;
	return MSA->Start();
}

extern "C" __declspec(dllexport) int  UsbMSAStop( void *pMSAData )
{
	MSADevice* MSA = (MSADevice* )pMSAData;
	return MSA->Stop();
}

extern "C" __declspec(dllexport) int  UsbMSAGetInstance( void *pMSAData )
{
	MSADevice* MSA = (MSADevice* )pMSAData;
	return MSA->get_Instance();
}

extern "C" __declspec(dllexport) int  UsbMSASetInstance( void *pMSAData, int instance )
{
	MSADevice* MSA = (MSADevice* )pMSAData;
	return MSA->set_Instance(instance);
}

extern "C" __declspec(dllexport) int  UsbMSAGetBytesReturned( void *pMSAData )
{
	MSADevice* MSA = (MSADevice* )pMSAData;
	return MSA->get_BytesReturned();
}

extern "C" __declspec(dllexport) int  UsbMSADeviceRead( void *pMSAData, MSA_RXBUFFER * readbuf )
{
	MSADevice* MSA = (MSADevice* )pMSAData;
	return MSA->Read(readbuf);
}

extern "C" __declspec(dllexport) int  UsbMSADeviceWrite( void *pMSAData, MSA_TXBUFFER * writebuf, int message_size )
{
	MSADevice* MSA = (MSADevice* )pMSAData;
	return MSA->Write(writebuf, message_size);
}

#define HEX(x) ( ((x)&0x0f) < 10 ? ((x)&0x0f)+'0' : ((x)&0x0f)+'A'-10)

extern "C" __declspec(dllexport) int  UsbMSADeviceReadString( void *pMSAData, char *data, int message_size )
{
	MSA_RXBUFFER readbuf;
	MSADevice* MSA = (MSADevice* )pMSAData;
	if( !MSA->Read( &readbuf ) )
		return false;
	data[0] = HEX( readbuf.last_command >> 4);
	data[1] = HEX( readbuf.last_command  );
	data[2] = HEX( readbuf.return_status >> 4);
	data[3] = HEX( readbuf.return_status  );
	data[4] = HEX( readbuf.ioa >> 4);
	data[5] = HEX( readbuf.ioa  );
	data[6] = HEX( readbuf.iob >> 4);
	data[7] = HEX( readbuf.iob  );
	data[8] = HEX( readbuf.ioc >> 4);
	data[9] = HEX( readbuf.ioc  );
	data[10] = HEX( readbuf.iod >> 4);
	data[11] = HEX( readbuf.iod  );
	data[12] = HEX( readbuf.ioe >> 4);
	data[13] = HEX( readbuf.ioe  );
	data[14] = HEX( readbuf.ADC_reads_done >> 4);
	data[15] = HEX( readbuf.ADC_reads_done  );
	for( int i=0; i<readbuf.ADC_reads_done; i++)
	{
		data[16+i*8] = HEX( readbuf.data[i*4+3] >> 4);
		data[17+i*8] = HEX( readbuf.data[i*4+3]  );
		data[18+i*8] = HEX( readbuf.data[i*4+2] >> 4);
		data[19+i*8] = HEX( readbuf.data[i*4+2]  );
		data[20+i*8] = HEX( readbuf.data[i*4+1] >> 4);
		data[21+i*8] = HEX( readbuf.data[i*4+1]  );
		data[22+i*8] = HEX( readbuf.data[i*4] >> 4);
		data[23+i*8] = HEX( readbuf.data[i*4]  );
	}
	return true;
}

extern "C" __declspec(dllexport) int  UsbMSADeviceWriteString( void *pMSAData, char *data, int message_size )
{
	MSA_TXBUFFER writebuf;
//	_strupr_s( data, message_size*2 );
	for( int i=0; i< message_size && i < 254; i++ )
	{
		if( !isxdigit( *data ) )
			return false;
		unsigned char c = toupper(*data);
		data++;
		c -= '0';
		if( c > 9 )
			c = c -'A' + '0' + 10;
		if( !isxdigit( *data ) )
			return false;
		unsigned char c1 =  toupper(*data);
		data++;
		c1 -= '0';
		if( c1 > 9 )
			c1 = c1 -'A' + '0' + 10;
		c = (c<<4)+c1;
		if( i == 0 )
			writebuf.set.command_code = c;
		else if( i == 1 )
			writebuf.set.length = c;
		else
			writebuf.set.data[i-2] = c;
	}
	MSADevice* MSA = (MSADevice* )pMSAData;
	return MSA->Write(&writebuf, message_size);
}

// generic interface to read ADCs
// The command string in *data tells the USB interface what to read
// the program then waits until either it has read too many times (should be max of 2) or it gets a result
//
extern "C" __declspec(dllexport) int  UsbMSADeviceReadAdcs( void *pMSAData, char *data, int message_size, unsigned long *pResults )
{
	MSADevice* MSA = (MSADevice* )pMSAData;
	MSA_RXBUFFER readbuf;

	if( !UsbMSADeviceWriteString( pMSAData, data, message_size ) )
		return 0;
	for( int i=0; i < 5; i++ )
	{
		if( !MSA->Read( &readbuf ) )
			return false;
		if( readbuf.ADC_reads_done != 0 )
		{
			pResults[0] = readbuf.ADC_reads_done;
			pResults[1] = *((unsigned long *)&readbuf.data[0]);
			if( readbuf.ADC_reads_done > 1 )
				pResults[2] = *((unsigned long *)&readbuf.data[4]);
			return 1;
		}
	}
	return 0;
}

// as above but uses a structre from basic for the parameters instead of a string
//
extern "C" __declspec(dllexport) int  UsbMSADeviceReadAdcsStruct( void *pMSAData, unsigned short *pData, unsigned long *pResults )
{
	MSADevice* MSA = (MSADevice* )pMSAData;
	MSA_RXBUFFER readbuf;
	MSA_TXBUFFER writebuf;

	if( pData[0] < 1 || pData[0] > 3 )
		return 0;
	writebuf.set.command_code = 0xB0+pData[0]-1;
	writebuf.set.length = (unsigned char)pData[1];
	writebuf.set.data[0] = (unsigned char)pData[2];
	writebuf.set.data[1] = (unsigned char)pData[3];
	writebuf.set.data[2] = (unsigned char)pData[4];
	if( !MSA->Write(&writebuf, 0x5))
		return 0;
	for( int i=0; i < 5; i++ )
	{
		if( !MSA->Read( &readbuf ) )
			return false;
		if( readbuf.ADC_reads_done != 0 )
		{
			pResults[0] = readbuf.ADC_reads_done;
			pResults[1] = *((unsigned long *)&readbuf.data[0]);
			if( readbuf.ADC_reads_done > 1 )
				pResults[2] = *((unsigned long *)&readbuf.data[4]);
			return 1;
		}
	}
	return 0;
}

// This function should not be necessary but Liberty basic is SOOOO limiting
// Given a set of 8 pointers to 64 bit integers, form an array of data to be sent
// to the SLIMs.
// The 8 pointers refer to each bit in the bytes ( 0 to 7 ). ANy unused one should be a NULL pointer
// The location for the results must have already been set  with UsbMSADeviceSetAllArrayPtr()
//
extern "C" __declspec(dllexport) int  UsbMSADevicePopulateAllArray( void *pMSAData, unsigned short Steps, unsigned short bits,
																   __int64 *pBit0Array, 
																   __int64 *pBit1Array, 
																   __int64 *pBit2Array, 
																   __int64 *pBit3Array, 
																   __int64 *pBit4Array, 
																   __int64 *pBit5Array, 
																   __int64 *pBit6Array, 
																   __int64 *pBit7Array, 
																   unsigned long *pResults )
{
	MSADevice* MSA = (MSADevice* )pMSAData;
	unsigned char val[64];
	int i,j;

	if( MSA->pAllArray == NULL )
		return 0;
	if( MSA->nSteps < Steps )
		return 0;
	if( MSA->nArrayCols < bits )
		return 0;
	for( i=0; i<= Steps; i++)
	{
		for( j=0; j<bits; j++)
			val[j] = 0;
		if( pBit0Array != 0 )
			ProcessBitArray( val, bits, pBit0Array[i], 1 );
		if( pBit1Array != 0 )
			ProcessBitArray( val, bits, pBit1Array[i], 2 );
		if( pBit2Array != 0 )
			ProcessBitArray( val, bits, pBit2Array[i], 4 );
		if( pBit3Array != 0 )
			ProcessBitArray( val, bits, pBit3Array[i], 8 );
		if( pBit4Array != 0 )
			ProcessBitArray( val, bits, pBit4Array[i], 0x10 );
		if( pBit5Array != 0 )
			ProcessBitArray( val, bits, pBit5Array[i], 0x20 );
		if( pBit6Array != 0 )
			ProcessBitArray( val, bits, pBit6Array[i], 0x40 );
		if( pBit7Array != 0 )
			ProcessBitArray( val, bits, pBit7Array[i], 0x80 );
		for( j=0; j<bits; j++)
			*(MSA->pAllArray+bits*i+j) = val[j];
	}
	return 1;
}

// helper function to the above. 

inline void ProcessBitArray( unsigned char *pVal, int bits, __int64 x, int bit )
{
	__int64 mask = 1;
	for( int j=0; j<bits; j++, mask <<=1)
		if( x & mask )
			pVal[j] |=  bit;
}

// Given a pointer to a memory block allocated and managed by the caller, and an offset, store an unsigned long long
// this is only needed because of the limitations of Liberty Basic.

extern "C" __declspec(dllexport) int  UsbMSADevicePopulateDDSArray( void *pMSAData, __int64 *pArray, unsigned long *pData, unsigned short step, unsigned long *pResults )
{
	__int64 llData = (((__int64)(pData[0])) <<32)+pData[1];
	pArray[step] = llData;
	return 1;
}

// Given a pointer to a memory block allocated and managed by the caller, and an offset, store an unsigned long long
// this is only needed because of the limitations of Liberty Basic.
// this version does a bit revrse of the value

extern "C" __declspec(dllexport) int  UsbMSADevicePopulateDDSArrayBitReverse( void *pMSAData, __int64 *pArray, unsigned long *pData, unsigned short step, unsigned short bits, unsigned long *pResults )
{
	__int64 y = 0;
	__int64 x = (((__int64)(pData[0])) <<32)+pData[1];
	for (int i = 0; i < bits; ++i)
	{
		y <<= 1;
		y |= (x & 1);
		x >>= 1;
	}

	pArray[step] = y;
	return 1;
}



// This is the main function used in scanning.
// Given access to the prebuilt AllArray data, populated by the basic program with step data
// and a step and filtbank, clock data into the slims
// If you want to understand this, see [CommandAllSlims] in the basic program

extern "C" __declspec(dllexport) int  UsbMSADeviceAllSlims( void *pMSAData, unsigned short thisstep, unsigned short filtbank, unsigned long *pResults )
{
	MSADevice* MSA = (MSADevice* )pMSAData;
	char *pData = MSA->pAllArray+(thisstep*MSA->nArrayCols);

	MSA_TXBUFFER writebuf;

	if( MSA->pAllArray == NULL )
		return 0;
	if( thisstep > MSA->nSteps )
		return 0;
	writebuf.set.command_code = 0xA1;
	writebuf.set.length = 0x28;
	writebuf.set.data[0] = 1;
	for( int i=0; i<40; i++)
		writebuf.set.data[i+1] = (*pData++) +filtbank;
	if( !MSA->Write(&writebuf, 0x2B))
		return 0;
	writebuf.set.command_code = 0xA1;
	writebuf.set.length = 0x1;
	writebuf.set.data[0] = 0;
	writebuf.set.data[1] = (unsigned char)filtbank;
	if( !MSA->Write(&writebuf, 0x4))
		return 0;
	return 1;
}

// This is another main function used in scanning.
// Given access to the prebuilt AllArray data, populated by the basic program with step data
// and a step and filtbank, clock data into the slims
// If you want to understand this, see [CommandAllSlims] in the basic program
// this one combines the above with latching the clocked data in one go

extern "C" __declspec(dllexport) int  UsbMSADeviceAllSlimsAndLoad( void *pMSAData, unsigned short thisstep, unsigned short filtbank, unsigned short latch, unsigned short pdmcmd, unsigned short pdmlatch, unsigned long *pResults )
{
	MSA_TXBUFFER writebuf;
	MSADevice* MSA = (MSADevice* )pMSAData;
	if( !UsbMSADeviceAllSlims( pMSAData, thisstep, filtbank, pResults ) )
		return 0;
	writebuf.set.command_code = 0xA3;
	writebuf.set.length = 0x3;
	writebuf.set.data[0] = 0;
	writebuf.set.data[1] = (unsigned char)(latch+pdmcmd);
	writebuf.set.data[2] = (unsigned char)(pdmcmd+pdmlatch);
	writebuf.set.data[3] = (unsigned char)(pdmcmd);
	if( !MSA->Write(&writebuf, 0x6))
		return 0;
	return 1;
}

// as above but use a structure for the parameters

extern "C" __declspec(dllexport) int  UsbMSADeviceAllSlimsAndLoadStruct( void *pMSAData, unsigned short *pData, unsigned long *pResults )
{
	MSA_TXBUFFER writebuf;
	MSADevice* MSA = (MSADevice* )pMSAData;
	if( !UsbMSADeviceAllSlims( pMSAData, pData[0] /*thisstep*/, pData[1] /*filtbank*/, pResults ) )
		return 0;
	writebuf.set.command_code = 0xA3;
	writebuf.set.length = 0x3;
	writebuf.set.data[0] = 0;
	unsigned char pdmcmd = (unsigned char)(pData[3]*pData[4]);
	writebuf.set.data[1] = (unsigned char)(pData[2]+pdmcmd); // (latch+pdmcmd);
	writebuf.set.data[2] = (unsigned char)(pdmcmd+pData[5]);
	writebuf.set.data[3] = (unsigned char)(pdmcmd);
	if( !MSA->Write(&writebuf, 0x6))
		return 0;
	return 1;
}
   // struct UsbAllAslimsAndLoadData, thisstep as short, filtbank as short, latches as short, pdmcommand as short, pdmcmd1mult as short, pdmcmd2mult as short

// call this before trying to populate the arrays with data It tells the dll
// the location and size of the memory array

extern "C" __declspec(dllexport) int  UsbMSADeviceSetAllArrayPtr( void *pMSAData, void *pAllArray, short nArrayRows, short nArrayCols, unsigned long *pResults )
{
	MSADevice* MSA = (MSADevice* )pMSAData;
	MSA->pAllArray = ( char *)pAllArray;
	MSA->nSteps = nArrayRows;
	MSA->nArrayCols = nArrayCols;
	return 1;
}

// One of two interface functions used to write  data
// THisone ses a USB command from the caller, a fixed data value and a variable data value
// and sends a pattern of (fixed) for each zero bit and (fixed+variable) for each 1 bit in the 64 bit word
// The clock byte may also be specified

extern "C" __declspec(dllexport) int  UsbMSADeviceWriteInt64MsbFirst( void *pMSAData, short nUsbCommandByte, unsigned long *pData, 
															 short nBits, short clock, short fixeddata, short vardata,
															 unsigned long *pResults )
{
	if( nBits > 63 || nBits < 1 )
		return 0;
	MSADevice* MSA = (MSADevice* )pMSAData;
	MSA_TXBUFFER writebuf;
	unsigned char nByte;
	__int64 x = (((__int64)(pData[0])) <<32)+pData[1];
	__int64 nBitMask = 1<<(nBits-1);

	writebuf.set.command_code = nUsbCommandByte & 0xff;
	writebuf.set.length = nBits & 0xff;
	writebuf.set.data[0] = clock & 0xff;
	for( int i=1; i<=nBits; i++, nBitMask >>=1)
	{
		nByte = (fixeddata&0xff)+( (x&nBitMask) ? vardata & 0xff : 0 );
		writebuf.set.data[i] = nByte;
	}
	if( !MSA->Write(&writebuf, nBits+3))
		return 0;
	return 1;
}

// this is like the ablove function but writes data in the opposite direction

extern "C" __declspec(dllexport) int  UsbMSADeviceWriteInt64LsbFirst( void *pMSAData, short nUsbCommandByte, unsigned long *pData, 
															 short nBits, short clock, short fixeddata, short vardata,
															 unsigned long *pResults )
{
	if( nBits > 63 || nBits < 1 )
		return 0;
	MSADevice* MSA = (MSADevice* )pMSAData;
	MSA_TXBUFFER writebuf;
	unsigned char nByte;
	__int64 x = (((__int64)(pData[0])) <<32)+pData[1];
	__int64 nBitMask = 1;

	writebuf.set.command_code = nUsbCommandByte & 0xff;
	writebuf.set.length = nBits & 0xff;
	writebuf.set.data[0] = clock & 0xff;
	for( int i=1; i<=nBits; i++, nBitMask <<=1)
	{
		nByte = (fixeddata&0xff)+( (x&nBitMask) ? vardata & 0xff : 0 );
		writebuf.set.data[i] = nByte;
	}
	if( !MSA->Write(&writebuf, nBits+3))
		return 0;
	return 1;
}

