#include <windows.h>
#include <winioctl.h>
#include <math.h>
#include <FCNTL.h>
#include <io.h>

#include "bulkioct.h"
#include "rtusbapi.h"
#include "usb2185.h"

DWORD GetSystemVersionMy(void);

//------------------------------------------------------------------------
// конструктор
//------------------------------------------------------------------------
CRTUSB2185::CRTUSB2185(HINSTANCE hInst)
{
	// идентификатор модуля DLL
	hInstance = hInst;
	// идентификатор устройства
	hDevice = INVALID_HANDLE_VALUE;
	// вообще запись в ППЗУ модуля запрещена
	EnableFlashWrite = FALSE;
	// запись в служебную область ППЗУ модуля запрещена
	IsServiceFlashWrite = FALSE;
	// адрес начала сегмента кода драйвера в памяти программ DSP
	ProgramBaseAddress = 0x1000;
	// адрес начала сегмента данных драйвера в памяти программ DSP
	VarsBaseAddress = RTUSB2185::VarsBaseAddress;
	// оживим критичекую секцию для работы
	InitializeCriticalSection(&cs);
	// флажок памяти данных DSP
	DSP_DM = 0x4000;
	// флажок памяти программ DSP
	DSP_PM = 0x0000;
	// максимальный кол-во байтов для передачи по ContolPipe
	MAX_USB_BLOCK = 4096;
	// базовый индекс ошибок модуля
	BASE_ERROR_ID = 100;
	// номер последней ошибки
	LastErrorNumber = 0;
}

//------------------------------------------------------------------------
// деструктор
//------------------------------------------------------------------------
CRTUSB2185::~CRTUSB2185() { }




//========================================================================
// Функции общего назначения для работы с модулем RTUSB2185
//========================================================================

//------------------------------------------------------------------------
// откроем виртуальный слот для доступа к USB модулю
//------------------------------------------------------------------------
BOOL WINAPI CRTUSB2185::OpenDevice(WORD VirtualSlot)
{
	// название модуля
	char ModuleName[10];
	// название драйвера
	char szDrvName[18], slot[4];

	// виртуальный слот доступен?
	if(hDevice != INVALID_HANDLE_VALUE) { CloseHandle(hDevice); hDevice = INVALID_HANDLE_VALUE; }

	// определим версию Windows
	DWORD SystemVersion = GetSystemVersionMy();
	if((SystemVersion == UNKNOWN_WINDOWS_VERSION) ||
   	(SystemVersion == WINDOWS_95) || (SystemVersion == WINDOWS_98_OR_LATER) ||
		(SystemVersion == WINDOWS_NT)) { LastErrorNumber = 1; return FALSE; }

	// формируем название драйвера USB
	wsprintf(slot, "%d", VirtualSlot);
	strcpy(szDrvName, "\\\\.\\RtecUsb");
	strncat(szDrvName, slot, strlen(slot));

	// откроем идентификатор для модуля
	hDevice = CreateFile(szDrvName, GENERIC_READ | GENERIC_WRITE, 0, NULL,
						                      OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);

	// проверим хэндл устройства
	if(hDevice == INVALID_HANDLE_VALUE)  { LastErrorNumber = 2; return FALSE; }
	// прочтём название модуля
	else if(!GetModuleName(ModuleName)) { LastErrorNumber = 33; CloseDevice(); return FALSE; }
	// проверим что это USB2185
	else if(strcmp(ModuleName, "USB2185")) { LastErrorNumber = 34; CloseDevice(); return FALSE; }
	else return TRUE;
}

//------------------------------------------------------------------------------
// освободим текущий виртуальный слот
//------------------------------------------------------------------------------
BOOL WINAPI CRTUSB2185::CloseDevice(void)
{
	BOOL status = TRUE;

	if(hDevice != INVALID_HANDLE_VALUE)
	{
		if(!CloseHandle(hDevice)) { LastErrorNumber = 3; status = FALSE; }
		hDevice = INVALID_HANDLE_VALUE;
	}

	return status;
}

//------------------------------------------------------------------------
// возвращается дескриптор устройства
//------------------------------------------------------------------------
HANDLE WINAPI CRTUSB2185::GetModuleHandle(void) { return hDevice; }

//------------------------------------------------------------------------
// возвращается текущая скорость работы шины USB
//------------------------------------------------------------------------
BOOL WINAPI CRTUSB2185::GetUsbSpeed(BYTE * const UsbSpeed)
{
	BOOL status;
	WORD InBuf[4];
	DWORD cbRet=0;

	// виртуальный слот доступен?
	if(hDevice == INVALID_HANDLE_VALUE) { LastErrorNumber = 4; return FALSE; }

	// захватим критическую секцию
	EnterCriticalSection(&cs);

	// формируем вендор получения названия модуля
	InBuf[0] = 1; InBuf[1] = RTUSB2185::V_GET_USB_SPEED; InBuf[2] = 0x0; InBuf[3] = 0x0;
	// засылаем сформированный вендор
	status = DeviceIoControl( hDevice, DIOC_SEND_COMMAND,
				            		&InBuf, sizeof(InBuf),
										UsbSpeed, 1/*bytes*/,
                       			&cbRet, NULL);

	// освободим критическую секцию
	LeaveCriticalSection(&cs);

	// все хорошо :)))))
	if(status) return TRUE;
	// ошибка выполнения DeviceIoControl()
	else { LastErrorNumber = 5; return FALSE; }
}

//------------------------------------------------------------------------------
// название модуля
//------------------------------------------------------------------------------
BOOL WINAPI CRTUSB2185::GetModuleName(PCHAR const ModuleName)
{
	BOOL status;
	char String[15];
	WORD InBuf[4];
	DWORD cbRet=0;

	// виртуальный слот доступен?
	if(hDevice == INVALID_HANDLE_VALUE) { LastErrorNumber = 4; return FALSE; }

	// захватим критическую секцию
	EnterCriticalSection(&cs);

	// формируем вендор получения названия модуля
	InBuf[0] = 1; InBuf[1] = RTUSB2185::V_GET_MODULE_NAME; InBuf[2] = 0x0; InBuf[3] = 0x0;
	// засылаем сформированный вендор
	status = DeviceIoControl( hDevice, DIOC_SEND_COMMAND,
				            		&InBuf, sizeof(InBuf),
										String, 15/*bytes*/,
                       			&cbRet, NULL);


	// освободим критическую секцию
	LeaveCriticalSection(&cs);

	// копируем строчку
	strncpy(ModuleName, String, 10);
	// последний символ должен быть 0 (на всякий случай)
	ModuleName[9] = '\0';

	// все хорошо :)))))
	if(status) return TRUE;
	// ошибка выполнения DeviceIoControl()
	else { LastErrorNumber = 5; return FALSE; }
}

//------------------------------------------------------------------------------
// получим серийный номер модуля
//------------------------------------------------------------------------------
BOOL WINAPI CRTUSB2185::GetModuleSerialNumber(PCHAR const SerialNumber)
{
	BOOL status;
	WORD InBuf[4];
	DWORD cbRet=0;

	// виртуальный слот доступен?
	if(hDevice == INVALID_HANDLE_VALUE) { LastErrorNumber = 4; return false; }

	// захватим критическую секцию
	EnterCriticalSection(&cs);

	// формируем вендор получения серийного номера модуля
	InBuf[0] = 1; InBuf[1] = RTUSB2185::V_GET_MODULE_SERIAL_NUMBER; InBuf[2] = 0x0; InBuf[3] = 0x0;
	// засылаем сформированный вендор
	status = DeviceIoControl( hDevice, DIOC_SEND_COMMAND,
	                       		&InBuf, sizeof(InBuf),
	                           SerialNumber, 9/*bytes*/,
	                       		&cbRet, NULL);

	// освободим критическую секцию
	LeaveCriticalSection(&cs);

	// последний символ должен быть 0 (на всякий случай)
	SerialNumber[8] = '\0';

	// все хорошо :)))))
	if(status) return TRUE;
	// ошибка выполнения DeviceIoControl()
	else { LastErrorNumber = 5; return FALSE; }
}

//------------------------------------------------------------------------
// получение номера версии прошивки интерфейсной части
// модуля RTUSB2185 (микроконтроллер AVR)
//------------------------------------------------------------------------
BOOL WINAPI CRTUSB2185::GetAvrVersion(PCHAR const AvrVersion)
{
	BOOL status;
	char String[15];
	WORD InBuf[4];
	DWORD cbRet=0;

	// виртуальный слот доступен?
	if(hDevice == INVALID_HANDLE_VALUE) { LastErrorNumber = 4; return FALSE; }

	// захватим критическую секцию
	EnterCriticalSection(&cs);

	// формируем вендор получения названия модуля
	InBuf[0] = 1; InBuf[1] = RTUSB2185::V_GET_MODULE_NAME; InBuf[2] = 0x0; InBuf[3] = 0x0;
	// засылаем сформированный вендор
	status = DeviceIoControl( hDevice, DIOC_SEND_COMMAND,
				            		&InBuf, sizeof(InBuf),
										String, 15/*bytes*/,
                       			&cbRet, NULL);

	// освободим критическую секцию
	LeaveCriticalSection(&cs);

	// копируем строчку
	strncpy(AvrVersion, String+10, 5);
	// последний символ должен быть 0 (на всякий случай)
	AvrVersion[4] = '\0';

	// все хорошо :)))))
	if(status) return TRUE;
	// ошибка выполнения DeviceIoControl()
	else { LastErrorNumber = 5; return FALSE; }
}

//------------------------------------------------------------------------------
// освободим указатель на устройство
//------------------------------------------------------------------------------
BOOL WINAPI CRTUSB2185::ReleaseInstance(void)
{
	BOOL status = TRUE;

	// освободим ресурс критической секции
	DeleteCriticalSection(&cs);
	// освободим идентификатор виртуального слота
	if(!CloseDevice()) status = FALSE;
	// освободим указатель на устройство
	delete this;

	return status;
}


//------------------------------------------------------------------------
// сброс сигнального процессора DSP
//------------------------------------------------------------------------
BOOL WINAPI CRTUSB2185::RESET_DSP(void)
{
	BOOL status;
	WORD InBuf[4];
	DWORD cbRet=0;

	// виртуальный слот доступен?
	if(hDevice == INVALID_HANDLE_VALUE) { LastErrorNumber = 4; return FALSE; }

	// захватим критическую секцию
	EnterCriticalSection(&cs);

	// формируем вендор сброса DSP модуля
	InBuf[0] = 0; InBuf[1] = RTUSB2185::V_RESET_DSP; InBuf[2] = 0x0; InBuf[3] = 0x0;
	// засылаем сформированный вендор
	status = DeviceIoControl( hDevice, DIOC_SEND_COMMAND,
	                       		&InBuf, sizeof(InBuf),
                       			NULL, 0/*bytes*/,
                       			&cbRet, NULL);

	// освободим критическую секцию
	LeaveCriticalSection(&cs);

	// все хорошо :)))))
	if(status) return TRUE;
	// ошибка выполнения DeviceIoControl()
	else { LastErrorNumber = 5; return FALSE; }
}

//------------------------------------------------------------------------------
// функция загрузки программы DSP в модуль RTUSB2185
//------------------------------------------------------------------------------
BOOL WINAPI CRTUSB2185::LOAD_DSP(const PCHAR FileName)
{
	int	FileHandle;
	DWORD	NBytes;
	char  *CodeByte;
	WORD	*CodeWord;
	WORD 	PmBase, PmWords, DmBase, DmWords;
	char 	PatternHeadStr[]="crrtd";
	char	HeadStr[6];

	// виртуальный слот доступен?
	if(hDevice == INVALID_HANDLE_VALUE) { LastErrorNumber = 4; return FALSE; }
 	// осуществим сброс DSP модуля
	else if(!RESET_DSP()) { LastErrorNumber = 6; return FALSE; }

	if(FileName == NULL)
	{
		// найдем ресурс с кодом драйвера DSP
		HRSRC RsRes = FindResource(hInstance, "USB2185DSP", RT_RCDATA);
		if(RsRes == NULL) { LastErrorNumber = 7; return FALSE; }
		// загрузим его
		HGLOBAL RcResHandle = LoadResource(hInstance, RsRes);
		if(RcResHandle == NULL) { LastErrorNumber = 8; return FALSE; }
		// зафиксируем
		char  *pRcData = (char *)LockResource(RcResHandle);
		if(pRcData == NULL) { LastErrorNumber = 9; return FALSE; }
		// определим размер ресурса
		NBytes = SizeofResource(hInstance, RsRes);
		if(NBytes == NULL) { LastErrorNumber = 10; return FALSE; }

		// выделим массив в памяти компьютера под драйвер DSP
		CodeByte = new char[NBytes+2];
		if(CodeByte == NULL)  { LastErrorNumber = 11; return FALSE; }

		// скопируем данные ресурса
		for(DWORD i=0; i < NBytes; i++) CodeByte[i] = pRcData[i];
	}
	else
	{
		// откроем файл с кодом драйвера DSP
		FileHandle=open(FileName, O_BINARY | O_RDONLY);
		if(FileHandle == -1) { LastErrorNumber = 12; return FALSE; }

		// определим кол-во байт в файле с кодом драйвера DSP
		NBytes = filelength(FileHandle);
		if((LONG)NBytes == -1) { LastErrorNumber = 13; return FALSE; }

		// выделим память под драйвер DSP
		CodeByte = new char[NBytes+2];
		if(CodeByte == NULL)  { LastErrorNumber = 11; return FALSE; }

		// теперь спокойно прочитаем файл
		if(read(FileHandle, CodeByte, NBytes) == -1) { LastErrorNumber = 14; return FALSE; }

		// закроем файл
		close(FileHandle);
	}

	memcpy(HeadStr, CodeByte, 5); HeadStr[5] = '\0';
	if(strcmp(PatternHeadStr, HeadStr)) { LastErrorNumber = 32; return FALSE; }

	// указатель к WORD
	CodeWord = (WORD *)CodeByte;

	// базовый адрес памяти программ
	PmBase = CodeWord[6];
	// кол-во слов для памяти программ
	PmWords = CodeWord[7];
	// базовый адрес памяти данных
	DmBase = CodeWord[8];
	// кол-во слов для памяти данных
	DmWords = CodeWord[9];

	// загрузим память данных DSP
	if(!PUT_DM_ARRAY((WORD)DmBase, DmWords, (SHORT *)(CodeWord + 2*PmWords + 10))) { delete[] CodeByte;  LastErrorNumber = 15; return FALSE; }

	// загрузим память программ DSP, начиная с адреса 0х1
	if(!PUT_PM_ARRAY((WORD)(PmBase+1), MAKE_PM_BUFFER_RTUSBAPI(PmWords-1, (CodeWord + 12)))) { delete[] CodeByte;  LastErrorNumber = 16; return FALSE; }

	// загрузим нулевую ячейку памяти программ DSP, т.е. стартанем драйвер DSP
	if(!PUT_PM_ARRAY((WORD)PmBase, MAKE_PM_BUFFER_RTUSBAPI(1, CodeWord + 10))) { delete[] CodeByte;  LastErrorNumber = 16; return FALSE; }

	// освободим память
	delete[] CodeByte;

	// теперь найдем базовый адрес блока переменных в памяти программ DSP
	LONG PBA; if(!GET_PM_WORD(RTUSB2185::D_PROGRAM_BASE_ADDRESS, &PBA)) { LastErrorNumber = 17; return FALSE; }
	ProgramBaseAddress = (WORD)(PBA >> 8);

	return TRUE;
}

//------------------------------------------------------------------------
// проверка работоспособности загруженного модуля
//------------------------------------------------------------------------
BOOL WINAPI CRTUSB2185::MODULE_TEST(void)
{
	// виртуальный слот доступен?
	if(hDevice == INVALID_HANDLE_VALUE) { LastErrorNumber = 4; return FALSE; }

	// для начала проверим содержимое тестовых переменных драйвера DSP
	WORD TMode1; if(!GET_VAR_WORD(RTUSB2185::D_TEST_VAR1, (SHORT *)&TMode1)) { LastErrorNumber = 18; return FALSE; }
	WORD TMode2; if(!GET_VAR_WORD(RTUSB2185::D_TEST_VAR2, (SHORT *)&TMode2)) { LastErrorNumber = 18; return FALSE; }
	if((TMode1 != 0x5555) || (TMode2 != 0xAAAA)) { LastErrorNumber = 19;  return FALSE; }
	else
	{
		WORD Ready;
		WORD TimeOut = 40;

		// проинициируем переменную D_TEST_INTR в драйвере DSP
		if(!PUT_VAR_WORD(RTUSB2185::D_TEST_INTR_VAR, 0x77BB)) { LastErrorNumber = 20; return FALSE; }

		// ждём пока загруженный драйвер DSP не установит переменную D_READY в '1'
		do
		{
			Sleep(25);
			if(!GET_VAR_WORD(RTUSB2185::D_MODULE_READY, (SHORT *)&Ready)) { LastErrorNumber = 18; return FALSE; }
		} while((!Ready) && (TimeOut--));
		// выход по таймауту
		if(TimeOut == 0xFFFF)  { LastErrorNumber = 21; return FALSE; }

		// посылаем в DSP модуля тестовую команду
		if(!SEND_COMMAND(RTUSB2185::C_TEST)) { LastErrorNumber = 22; return FALSE; }

		Sleep(100);

		// если всё хорошо, то теперь из D_TEST_INTR должно прочитаться 0xAA55
		WORD TestLoadVar; if(!GET_VAR_WORD(RTUSB2185::D_TEST_INTR_VAR, (SHORT *)&TestLoadVar)) { LastErrorNumber = 18; return FALSE; }
		if(TestLoadVar != 0xAA55) { LastErrorNumber = 23; return FALSE; }
	}

	return TRUE;
}

//------------------------------------------------------------------------
// текущая версия загруженной драйвера DSP
//------------------------------------------------------------------------
BOOL WINAPI CRTUSB2185::GET_DSP_INFO(RTUSB2185::DSP_INFO * const DspInfo)
{
	WORD i;
	WORD dv;

	// считываем название устройства, для которого предназначен данный
	// драйвер DSP (9 символов + '\0')
	// для модуля USB-2185 должна быть строчка "USB2185"
	for(i = 0x0; i < sizeof(DspInfo->Target)/2; i++)
		if(!GET_VAR_WORD((WORD)(RTUSB2185::D_TARGET + i), (SHORT *)&DspInfo->Target[2*i])) { LastErrorNumber = 18; return FALSE; }

	// считываем метку разработчика драйвера DSP (5 символов + '\0')
	// штатный драйвер DSP имеет метку "rtech"
	for(i = 0x0; i < sizeof(DspInfo->Label)/2; i++)
		if(!GET_VAR_WORD((WORD)(RTUSB2185::D_LABEL + i), (SHORT *)&DspInfo->Label[2*i])) { LastErrorNumber = 18; return FALSE; }

	// считаем версию драйвера DSP
	if(!GET_VAR_WORD(RTUSB2185::D_VERSION, (SHORT *)&dv)) { LastErrorNumber = 18; return FALSE; }
	DspInfo->DspMajor = (BYTE)(dv >> 8);
	DspInfo->DspMinor = (BYTE)(dv & 0xFF);

	return TRUE;
}

//------------------------------------------------------------------------
// посылка в драйвер DSP модуля номера команды
//------------------------------------------------------------------------
BOOL WINAPI CRTUSB2185::SEND_COMMAND(const WORD Command)
{
	WORD InBuf[4];
	DWORD cbRet=0;
	WORD TimeOut = 500;

	// виртуальный слот доступен?
	if(hDevice == INVALID_HANDLE_VALUE) { LastErrorNumber = 4; return FALSE; }
	// зашлем номер команды
	else if(Command >= RTUSB2185::C_LAST_COMMAND) { LastErrorNumber = 24; return FALSE; }

	// вендор посылки командного прерывания в DSP
	InBuf[0] = 0; InBuf[1] = RTUSB2185::V_COMMAND_IRQ; InBuf[2] = 0x0; InBuf[3] = 0x0;
	// передадим в драйвер DSP номер требуемой команды
	if(!PUT_VAR_WORD(RTUSB2185::D_COMMAND, Command)) { LastErrorNumber = 20; return FALSE; }

	// захватим критическую секцию
	EnterCriticalSection(&cs);

	// теперь сгенерим командное прерывание в DSP модуля
	if(!DeviceIoControl(hDevice, DIOC_SEND_COMMAND, &InBuf, sizeof(InBuf), NULL, 0, &cbRet, NULL)) { LastErrorNumber = 5; LeaveCriticalSection(&cs); return FALSE; }

	// освободим критическую секцию
	LeaveCriticalSection(&cs);

	// проверим, что данная команда опознана драйвером DSP,
	// т.е в переменной D_COMMAND должен вернуться ноль
	do
	{
	   if(!GET_VAR_WORD(RTUSB2185::D_COMMAND, (SHORT *)&Command)) { LastErrorNumber = 18; return FALSE; }
	} while(Command && (TimeOut--));

	// проверим был ли таймаут выпролнения команды
	if(TimeOut == 0xFFFF) { LastErrorNumber = 25; return FALSE; }
	else return TRUE;
}

//------------------------------------------------------------------------------
// функция выдачи строки с последней ошибкой выполнения интерфейсной функции
//------------------------------------------------------------------------------
int WINAPI CRTUSB2185::GetLastErrorString(LPTSTR const lpBuffer, DWORD nSize)
{
	return LoadString(hInstance, BASE_ERROR_ID + LastErrorNumber, lpBuffer, nSize);
}



//========================================================================
// функции для ввода инфрмации из модуля в РС
//========================================================================

//------------------------------------------------------------------------
// старт ввода данных из модуля в РС
//------------------------------------------------------------------------
BOOL WINAPI CRTUSB2185::START_READ(void)
{
	WORD InBuf[4];
	DWORD cbRet=0;

	// формируем вендор старта ввода данных из модуля (для AVR)
	InBuf[0] = 0; InBuf[1] = RTUSB2185::V_START_READ; InBuf[2] = (WORD)(0x0 | DSP_DM); InBuf[3] = (WORD)(0x1800);
	// виртуальный слот доступен?
	if(hDevice == INVALID_HANDLE_VALUE) { LastErrorNumber = 4; return FALSE; }

	// захватим критическую секцию
	EnterCriticalSection(&cs);

	// шлём вендор старта ввода данных (для AVR)
	if(!DeviceIoControl(hDevice, DIOC_SEND_COMMAND, &InBuf, sizeof(InBuf), NULL, 0, &cbRet, NULL)) { LastErrorNumber = 5; LeaveCriticalSection(&cs); return FALSE; }

	// освободим критическую секцию
	LeaveCriticalSection(&cs);

	// теперь зашлём команду старта в драйвер DSP модуля
	if(!SEND_COMMAND(RTUSB2185::C_START_READ)) { LastErrorNumber = 22; return FALSE; }
	else return TRUE;
}

//------------------------------------------------------------------------
// останов ввода данных из модуля
//------------------------------------------------------------------------
BOOL WINAPI CRTUSB2185::STOP_READ(void)
{
	DWORD cbRet=0;

	// виртуальный слот доступен?
	if(hDevice == INVALID_HANDLE_VALUE) { LastErrorNumber = 4; return FALSE; }
	// команда в драйвер DSP для останова ввода данных
	else if(!SEND_COMMAND(RTUSB2185::C_STOP_READ)) { LastErrorNumber = 22; return FALSE; }

	// захватим критическую секцию
	EnterCriticalSection(&cs);

	// вендор очистки канала чтения bulk'а
	if(!DeviceIoControl(hDevice, DIOC_RESET_PIPE3/*reset Read Pipe*/, NULL, NULL, NULL, NULL, &cbRet, NULL)) { LastErrorNumber = 5; LeaveCriticalSection(&cs); return FALSE; }

	// освободим критическую секцию
	LeaveCriticalSection(&cs);

	return TRUE;
}

//------------------------------------------------------------------------
// потоковое чтение данных из модуля по каналу Bulk
//------------------------------------------------------------------------
BOOL WINAPI CRTUSB2185::ReadData(SHORT * const lpBuffer, DWORD * const nNumberOfWordsToRead, LPDWORD lpNumberOfBytesRead, LPOVERLAPPED lpOverlapped)
{
	// виртуальный слот доступен?
	if(hDevice == INVALID_HANDLE_VALUE) { LastErrorNumber = 4; return FALSE; }
	// число отсчетов должно находиться в диапазоне от 0x200(512)
	else if(*nNumberOfWordsToRead < 0x200) *nNumberOfWordsToRead = 0x200;
	// и вплоть до 0x100000(1024*1024)
	else if(*nNumberOfWordsToRead > (1024*1024)) *nNumberOfWordsToRead = 1024*1024;
	// также число отсчетов должно быть кратно 0x100(512)
	*nNumberOfWordsToRead -= (*nNumberOfWordsToRead)%512;
	// делаем запрос на асинхронное чтение  заданного кол-ва данных
	return ReadFile(hDevice, (LPVOID)lpBuffer, 2*(*nNumberOfWordsToRead), lpNumberOfBytesRead, lpOverlapped);
}




//========================================================================
// функции вывода данных из РС в модуль
//========================================================================

//------------------------------------------------------------------------
// запуск процедуры вывода данных из РС в модуль
//------------------------------------------------------------------------
BOOL WINAPI CRTUSB2185::START_WRITE(void)
{
	WORD InBuf[4];
	DWORD cbRet=0;

	// виртуальный слот доступен?
	if(hDevice == INVALID_HANDLE_VALUE) { LastErrorNumber = 4; return FALSE; }

	// захватим критическую секцию
	EnterCriticalSection(&cs);

	// вендор старта передачи данных в модуль (для AVR)
	InBuf[0] = 0; InBuf[1] = RTUSB2185::V_START_WRITE; InBuf[2] = (WORD)(0x3000 | DSP_DM); InBuf[3] = (WORD)(0x7C0);
	if(!DeviceIoControl(hDevice, DIOC_SEND_COMMAND, &InBuf, sizeof(InBuf), NULL, 0, &cbRet, NULL)) { LastErrorNumber = 5; LeaveCriticalSection(&cs); return FALSE; }

	// освободим критическую секцию
	LeaveCriticalSection(&cs);

	// теперь зашлём команду старта в драйвер DSP модуля
	if(!SEND_COMMAND(RTUSB2185::C_START_WRITE)) { LastErrorNumber = 22; return FALSE; }
	else return TRUE;
}

//------------------------------------------------------------------------
// останов процедуры вывода данных из РС в модуль
//------------------------------------------------------------------------
BOOL WINAPI CRTUSB2185::STOP_WRITE(void)
{
	DWORD cbRet=0;

	// виртуальный слот доступен?
	if(hDevice == INVALID_HANDLE_VALUE) { LastErrorNumber = 4; return FALSE; }
	// команда в драйвер DSP для останова передачи данных
	else if(!SEND_COMMAND(RTUSB2185::C_STOP_WRITE)) { LastErrorNumber = 22; return FALSE; }

	// захватим критическую секцию
	EnterCriticalSection(&cs);

	// вендор очистки канала записи bulk'а
	if(!DeviceIoControl(hDevice, DIOC_RESET_PIPE1/*reset Write Pipe*/, NULL, NULL, NULL, NULL, &cbRet, NULL)) { LastErrorNumber = 5; LeaveCriticalSection(&cs); return FALSE; }

	// освободим критическую секцию
	LeaveCriticalSection(&cs);

	return TRUE;
}

//------------------------------------------------------------------------
/// потоковое запись данных в модуль по каналу Bulk
//------------------------------------------------------------------------
BOOL WINAPI CRTUSB2185::WriteData(SHORT * const lpBuffer, DWORD * const nNumberOfWordsToWrite, LPDWORD lpNumberOfBytesWritten, LPOVERLAPPED lpOverlapped)
{
	// виртуальный слот доступен?
	if(hDevice == INVALID_HANDLE_VALUE) { LastErrorNumber = 4; return FALSE; }
	// число отсчетов должно находиться в диапазоне от 0x20(32)
	else if(*nNumberOfWordsToWrite < 0x20) *nNumberOfWordsToWrite = 0x20;
	// и вплоть до 0x100000(1024*1024)
	else if(*nNumberOfWordsToWrite > (1024*1024)) *nNumberOfWordsToWrite = 1024*1024;
	// также число отсчетов должно быть кратно 0x20(32)
	*nNumberOfWordsToWrite -= (*nNumberOfWordsToWrite)%32;

	return WriteFile(hDevice, (LPVOID)lpBuffer, 2*(*nNumberOfWordsToWrite), lpNumberOfBytesWritten, lpOverlapped);
}



//========================================================================
// функции для работы с пользовательским ППЗУ модуля
//========================================================================

//------------------------------------------------------------------------
// чтение прользовательского ППЗУ модуля
//------------------------------------------------------------------------
BOOL WINAPI CRTUSB2185::GET_FLASH(RTUSB2185::FLASH * const fi)
{
	WORD InBuf[4];
	DWORD cbRet=0;

	// виртуальный слот доступен?
	if(hDevice == INVALID_HANDLE_VALUE) { LastErrorNumber = 4; return FALSE; }

	// захватим критическую секцию
	EnterCriticalSection(&cs);

	// формируем вендор записи пользовательского ППЗУ
	InBuf[0] = 1; InBuf[1] = RTUSB2185::V_GET_FLASH; InBuf[2] = (WORD)(0x1E00); InBuf[3] = 0x0;
	// засылаем вендор записи пользовательского ППЗУ
  	if(!DeviceIoControl(hDevice,DIOC_SEND_COMMAND, &InBuf, sizeof(InBuf), fi, 256/*bytes*/, &cbRet, NULL)) { LastErrorNumber = 5; LeaveCriticalSection(&cs); return FALSE; }

	// освободим критическую секцию
	LeaveCriticalSection(&cs);

	return TRUE;
}

//------------------------------------------------------------------------
// запись пользовательского ППЗУ модуля
//------------------------------------------------------------------------
BOOL WINAPI CRTUSB2185::PUT_FLASH(RTUSB2185::FLASH * const fi)
{
	WORD InBuf[4];
	DWORD cbRet=0;

	// виртуальный слот доступен?
	if(hDevice == INVALID_HANDLE_VALUE) { LastErrorNumber = 4; return FALSE; }

	// захватим критическую секцию
	EnterCriticalSection(&cs);

	// формируем вендор записи пользовательского ППЗУ
	InBuf[0] = 0; InBuf[1] = RTUSB2185::V_PUT_FLASH; InBuf[2] = (WORD)(0x1E00); InBuf[3] = 0x0;
	// засылаем вендор записи пользовательского ППЗУ
  	if(!DeviceIoControl(hDevice,DIOC_SEND_COMMAND, &InBuf, sizeof(InBuf), fi, 256/*bytes*/, &cbRet, NULL)) { LastErrorNumber = 5; LeaveCriticalSection(&cs); return FALSE; }

	// освободим критическую секцию
	LeaveCriticalSection(&cs);

	return TRUE;
}




//========================================================================
// функции для прямой работы с памятью DSP модуля
//========================================================================

//------------------------------------------------------------------------
// запись слова в область переменных в памяти программ драйвера DSP
//------------------------------------------------------------------------
BOOL WINAPI CRTUSB2185::PUT_VAR_WORD(WORD Address, SHORT Data)
{
	// виртуальный слот доступен?
	if(hDevice == INVALID_HANDLE_VALUE) { LastErrorNumber = 4; return FALSE; }
	else if((Address < VarsBaseAddress) || (Address > ProgramBaseAddress)) { LastErrorNumber = 29; return FALSE; }
	else if(!PUT_PM_WORD(Address, (LONG)Data<<8)) { LastErrorNumber = 30; return FALSE; }
	return TRUE;
}

//------------------------------------------------------------------------
// чтение слова из область переменных в памяти программ драйвера DSP
//------------------------------------------------------------------------
BOOL WINAPI CRTUSB2185::GET_VAR_WORD(WORD Address, SHORT * const Data)
{
	LONG PmData;

	// виртуальный слот доступен?
	if(hDevice == INVALID_HANDLE_VALUE) { LastErrorNumber = 4; return FALSE; }
	else if((Address < VarsBaseAddress) || (Address > ProgramBaseAddress)) { LastErrorNumber = 29; return FALSE; }
	else if(!GET_PM_WORD(Address, &PmData)) { LastErrorNumber = 31; return FALSE; }
	*Data = (SHORT)(PmData >> 8);

	return TRUE;
}

//------------------------------------------------------------------------
// запись слова в ячейку памяти данных драйвера DSP
//------------------------------------------------------------------------
BOOL WINAPI CRTUSB2185::PUT_DM_WORD(const WORD Address, SHORT Data)
{
	BOOL status;
	WORD InBuf[4];
	DWORD cbRet=0;

	// виртуальный слот доступен?
	if(hDevice == INVALID_HANDLE_VALUE) { LastErrorNumber = 4; return FALSE; }

	// захватим критическую секцию
	EnterCriticalSection(&cs);

	// формируем вендор записи слова в память данных DSP
	InBuf[0] = 0; InBuf[1] = RTUSB2185::V_PUT_ARRAY; InBuf[2] = (WORD)(Address | DSP_DM); InBuf[3] = 0x0;
	// засылаем сформированный вендор
	status = DeviceIoControl( hDevice,DIOC_SEND_COMMAND,
                             &InBuf,sizeof(InBuf),
                             &Data, 2/*bytes*/,
                             &cbRet,NULL);

	// освободим критическую секцию
	LeaveCriticalSection(&cs);

	// все хорошо :)))))
	if(status) return TRUE;
	// ошибка выполнения DeviceIoControl()
	else { LastErrorNumber = 5; return FALSE; }
}

//------------------------------------------------------------------------
// чтение слова из ячейки памяти данных драйвера DSP
//------------------------------------------------------------------------
BOOL WINAPI CRTUSB2185::GET_DM_WORD(WORD Address, SHORT * const Data)
{
	BOOL status;
	WORD InBuf[4];
	DWORD cbRet=0;

	// виртуальный слот доступен?
	if(hDevice == INVALID_HANDLE_VALUE) { LastErrorNumber = 4; return FALSE; }

	// захватим критическую секцию
	EnterCriticalSection(&cs);

	// формируем вендор чтения слова из памяти данных DSP
	InBuf[0] = 1; InBuf[1] = RTUSB2185::V_GET_ARRAY; InBuf[2] = (WORD)(Address | DSP_DM); InBuf[3] = 0x0;
	// засылаем сформированный вендор
	status = DeviceIoControl( hDevice,DIOC_SEND_COMMAND,
									&InBuf,sizeof(InBuf),
									 Data, 2/*bytes*/,
									&cbRet,NULL);

	// освободим критическую секцию
	LeaveCriticalSection(&cs);

	// все хорошо :)))))
	if(status) return TRUE;
	// ошибка выполнения DeviceIoControl()
	else { LastErrorNumber = 5; return FALSE; }
}

//------------------------------------------------------------------------
// запись слова в ячейку памяти программ драйвера DSP
//------------------------------------------------------------------------
BOOL WINAPI CRTUSB2185::PUT_PM_WORD(const WORD Address, LONG Data)
{
	BOOL status;
	WORD InBuf[4];
	DWORD cbRet=0;

	// виртуальный слот доступен?
	if(hDevice == INVALID_HANDLE_VALUE) { LastErrorNumber = 4; return FALSE; }

	// захватим критическую секцию
	EnterCriticalSection(&cs);

	// формируем вендор записи слова в память программ DSP
	InBuf[0] = 0; InBuf[1] = RTUSB2185::V_PUT_ARRAY; InBuf[2] = (WORD)(Address | DSP_PM); InBuf[3] = 0x0;
	// засылаем сформированный вендор
	status = DeviceIoControl( hDevice,DIOC_SEND_COMMAND,
                             &InBuf, sizeof(InBuf),
                             &Data, 4/*bytes*/,
                             &cbRet,NULL);

	// освободим критическую секцию
	LeaveCriticalSection(&cs);

	// все хорошо :)))))
	if(status) return TRUE;
	// ошибка выполнения DeviceIoControl()
	else { LastErrorNumber = 5; return FALSE; }
}

//------------------------------------------------------------------------
// чтение слова из ячейки памяти программ драйвера DSP
//------------------------------------------------------------------------
BOOL WINAPI CRTUSB2185::GET_PM_WORD(WORD Address, LONG * const Data)
{
	BOOL status;
	WORD InBuf[4];
	DWORD cbRet=0;

	// виртуальный слот доступен?
	if(hDevice == INVALID_HANDLE_VALUE) { LastErrorNumber = 4; return FALSE; }

	// захватим критическую секцию
	EnterCriticalSection(&cs);

	// формируем вендор чтения слова из памяти программ DSP
	InBuf[0] = 1; InBuf[1] = RTUSB2185::V_GET_ARRAY; InBuf[2] = (WORD)(Address | DSP_PM); InBuf[3] = 0x0;
	// засылаем сформированный вендор
	status = DeviceIoControl( hDevice,DIOC_SEND_COMMAND,
                             &InBuf,sizeof(InBuf),
                             Data, 4/*bytes*/,
                             &cbRet,NULL);

	// освободим критическую секцию
	LeaveCriticalSection(&cs);

	// все хорошо :)))))
	if(status) return TRUE;
	// ошибка выполнения DeviceIoControl()
	else { LastErrorNumber = 5; return FALSE; }
}

//------------------------------------------------------------------------
// запись массива слов в область памяти данных драйвера DSP
//------------------------------------------------------------------------
BOOL WINAPI CRTUSB2185::PUT_DM_ARRAY(WORD BaseAddress, WORD NPoints, SHORT * const Data)
{
	BOOL error = FALSE;
	WORD InBuf[4];
	WORD BlockQuantity, RestDataPoints;
	DWORD cbRet = 0x0;

	// виртуальный слот доступен?
	if(hDevice == INVALID_HANDLE_VALUE) { LastErrorNumber = 4; return FALSE; }

	BlockQuantity = (WORD)((sizeof(SHORT)*NPoints)/MAX_USB_BLOCK);
	RestDataPoints = (WORD)((sizeof(SHORT)*NPoints)%MAX_USB_BLOCK); // в байтах

	InBuf[0] = 0; InBuf[1] = RTUSB2185::V_PUT_ARRAY; InBuf[3] = 0x0;
	for(WORD i=0; i < BlockQuantity; i++)
	{
		InBuf[2] = (WORD)((BaseAddress + i*MAX_USB_BLOCK/sizeof(SHORT)) | DSP_DM);
		EnterCriticalSection(&cs);
		if(!DeviceIoControl(hDevice,DIOC_SEND_COMMAND, &InBuf, sizeof(InBuf), Data + i*MAX_USB_BLOCK/sizeof(SHORT), MAX_USB_BLOCK/*bytes*/, &cbRet, NULL)) error = TRUE;
		LeaveCriticalSection(&cs);
		if(error) break;
	}

	if(!error && RestDataPoints)
	{
		InBuf[2] = (WORD)((BaseAddress + BlockQuantity*MAX_USB_BLOCK/sizeof(SHORT)) | DSP_DM);
		EnterCriticalSection(&cs);
		if(!DeviceIoControl(hDevice,DIOC_SEND_COMMAND, &InBuf, sizeof(InBuf), Data + BlockQuantity*MAX_USB_BLOCK/sizeof(SHORT), RestDataPoints/*bytes*/, &cbRet, NULL)) error = TRUE;
		LeaveCriticalSection(&cs);
	}

	if(error) { LastErrorNumber = 5; return FALSE; }
	return TRUE;
}

//------------------------------------------------------------------------
// чтение массива слов из области памяти данных драйвера DSP
//------------------------------------------------------------------------
BOOL WINAPI CRTUSB2185::GET_DM_ARRAY(WORD BaseAddress, WORD NPoints, SHORT * const Data)
{
	BOOL error = FALSE;
	WORD InBuf[4];
	WORD BlockQuantity, RestDataPoints;
	DWORD cbRet = 0x0;

	// виртуальный слот доступен?
	if(hDevice == INVALID_HANDLE_VALUE) { LastErrorNumber = 4; return FALSE; }

	BlockQuantity = (WORD)((sizeof(SHORT)*NPoints)/MAX_USB_BLOCK);
	RestDataPoints = (WORD)((sizeof(SHORT)*NPoints)%MAX_USB_BLOCK); // в байтах

	InBuf[0] = 1; InBuf[1] = RTUSB2185::V_GET_ARRAY; InBuf[3] = 0x0;
	for(WORD i=0; i < BlockQuantity; i++)
	{
		InBuf[2] = (WORD)((BaseAddress + i*MAX_USB_BLOCK/sizeof(SHORT)) | DSP_DM);
		EnterCriticalSection(&cs);
		if(!DeviceIoControl( hDevice, DIOC_SEND_COMMAND, &InBuf,sizeof(InBuf), Data + i*MAX_USB_BLOCK/sizeof(SHORT), MAX_USB_BLOCK/*bytes*/, &cbRet, NULL)) error = TRUE;
		LeaveCriticalSection(&cs);
		if(error) break;
	}

	if(!error && RestDataPoints)
	{
		InBuf[2] = (WORD)((BaseAddress + BlockQuantity*MAX_USB_BLOCK/sizeof(SHORT)) | DSP_DM);
		EnterCriticalSection(&cs);
		if(!DeviceIoControl(hDevice, DIOC_SEND_COMMAND, &InBuf, sizeof(InBuf), Data + BlockQuantity*MAX_USB_BLOCK/sizeof(SHORT), RestDataPoints/*bytes*/, &cbRet, NULL)) error = TRUE;
		LeaveCriticalSection(&cs);
	}

	if(error) { LastErrorNumber = 5; return FALSE; }
	return TRUE;
}

//------------------------------------------------------------------------
// запись массива слов в область память данных драйвера DSP
//------------------------------------------------------------------------
BOOL WINAPI CRTUSB2185::PUT_PM_ARRAY(WORD BaseAddress, WORD NPoints, LONG * const Data)
{
	BOOL error = FALSE;
	WORD InBuf[4];
	WORD BlockQuantity, RestDataPoints;
	DWORD cbRet=0;

	// виртуальный слот доступен?
	if(hDevice == INVALID_HANDLE_VALUE) { LastErrorNumber = 4; return FALSE; }

	BlockQuantity = (WORD)((sizeof(LONG)*NPoints)/MAX_USB_BLOCK);
	RestDataPoints = (WORD)((sizeof(LONG)*NPoints)%MAX_USB_BLOCK); // в байтах

	InBuf[0] = 0; InBuf[1] = RTUSB2185::V_PUT_ARRAY; InBuf[3] = 0x0;
	for(WORD i=0; i < BlockQuantity; i++)
	{
		InBuf[2] = (WORD)((BaseAddress + i*MAX_USB_BLOCK/sizeof(LONG)) | DSP_PM);
		EnterCriticalSection(&cs);
		if(!DeviceIoControl(hDevice, DIOC_SEND_COMMAND, &InBuf, sizeof(InBuf), Data + i*MAX_USB_BLOCK/sizeof(LONG), MAX_USB_BLOCK/*bytes*/, &cbRet, NULL)) error = TRUE;
		LeaveCriticalSection(&cs);
		if(error) break;
	}

	if(!error && RestDataPoints)
	{
		InBuf[2] = (WORD)((BaseAddress + BlockQuantity*MAX_USB_BLOCK/sizeof(LONG)) | DSP_PM);
		EnterCriticalSection(&cs);
		if(!DeviceIoControl(hDevice, DIOC_SEND_COMMAND, &InBuf, sizeof(InBuf), Data + BlockQuantity*MAX_USB_BLOCK/sizeof(LONG), RestDataPoints/*bytes*/, &cbRet, NULL)) error = TRUE;
		LeaveCriticalSection(&cs);
	}

	if(error) { LastErrorNumber = 5; return FALSE; }
	return TRUE;
}

//------------------------------------------------------------------------
// чтение массива слов из области памяти данных драйвера DSP
//------------------------------------------------------------------------
BOOL WINAPI CRTUSB2185::GET_PM_ARRAY(WORD BaseAddress, WORD NPoints, LONG * const Data)
{
	BOOL error = FALSE;
	WORD InBuf[4];
	WORD BlockQuantity, RestDataPoints;
	DWORD cbRet=0;

	// виртуальный слот доступен?
	if(hDevice == INVALID_HANDLE_VALUE) { LastErrorNumber = 4; return FALSE; }

	BlockQuantity = (WORD)((sizeof(LONG)*NPoints)/MAX_USB_BLOCK);
	RestDataPoints = (WORD)((sizeof(LONG)*NPoints)%MAX_USB_BLOCK); // в байтах

	InBuf[0] = 1; InBuf[1] = RTUSB2185::V_GET_ARRAY; InBuf[3] = 0x0;
	for(WORD i=0; i < BlockQuantity; i++)
	{
		InBuf[2] = (WORD)((BaseAddress + i*MAX_USB_BLOCK/sizeof(LONG)) | DSP_PM);
		EnterCriticalSection(&cs);
		if(!DeviceIoControl(hDevice, DIOC_SEND_COMMAND, &InBuf, sizeof(InBuf), Data + i*MAX_USB_BLOCK/sizeof(LONG), MAX_USB_BLOCK/*bytes*/, &cbRet, NULL)) error = TRUE;
		LeaveCriticalSection(&cs);
		if(error) break;
	}

	if(!error && RestDataPoints)
	{
		InBuf[2] = (WORD)((BaseAddress + BlockQuantity*MAX_USB_BLOCK/sizeof(LONG)) | DSP_PM);
		EnterCriticalSection(&cs);
		if(!DeviceIoControl(hDevice, DIOC_SEND_COMMAND, &InBuf, sizeof(InBuf), Data + BlockQuantity*MAX_USB_BLOCK/sizeof(LONG), RestDataPoints/*bytes*/, &cbRet, NULL)) error = TRUE;
		LeaveCriticalSection(&cs);
	}

	if(error) { LastErrorNumber = 5; return FALSE; }
	return TRUE;
}



//------------------------------------------------------------------------------
// попробуем получить версию используемой Windows
//------------------------------------------------------------------------------
DWORD CRTUSB2185::GetWindowsVersion(void)
{
	OSVERSIONINFO osvi;

	ZeroMemory(&osvi, sizeof(OSVERSIONINFO));
	osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);

	if(!GetVersionEx(&osvi))
	{
		osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
		if(!GetVersionEx((OSVERSIONINFO *)&osvi)) return UNKNOWN_WINDOWS_VERSION;
	}

	switch (osvi.dwPlatformId)
	{
		case VER_PLATFORM_WIN32_NT:
			if(osvi.dwMajorVersion <= 4 ) return WINDOWS_NT; 		// WinNT
			else if((osvi.dwMajorVersion == 5) && (osvi.dwMinorVersion == 0)) return WINDOWS_2000; 			// Win2000
			else if((osvi.dwMajorVersion == 5) && (osvi.dwMinorVersion == 1)) return WINDOWS_XP; 				// WinXP
			else if((osvi.dwMajorVersion == 5) && (osvi.dwMinorVersion == 2)) return WINDOWS_2003_SERVER; 	// Win2003 Server
			else if(osvi.dwMajorVersion == 6) return WINDOWS_VISTA; 	// Vista
			else return UNKNOWN_WINDOWS_VERSION;

		case VER_PLATFORM_WIN32_WINDOWS:
			if((osvi.dwMajorVersion > 0x4) || ((osvi.dwMajorVersion == 0x4)&&(osvi.dwMinorVersion > 0x0))) return WINDOWS_98_OR_LATER; // Win98 or later
			else return WINDOWS_95; 											// Win95

		case VER_PLATFORM_WIN32s: return WINDOWS_32S; 					// Win32s
	}

	return UNKNOWN_WINDOWS_VERSION;
}

//------------------------------------------------------------------------
// вспомогательная функция
//------------------------------------------------------------------------
LONG *CRTUSB2185::Transform_Buffer(WORD PmPoints, LONG * const Buffer)
{
	for(WORD i=0; i < PmPoints; i++) Buffer[i] = ((Buffer[i] & 0x0000FFFF) << 8) | ((Buffer[i] & 0x00FF0000) >> 16);
	return Buffer;
}
