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

#include "bulkioct.h"
#include "rtusbapi.h"
#include "usbdetect.h"

DWORD GetSystemVersionMy(void);

//------------------------------------------------------------------------
// коструктор
//------------------------------------------------------------------------
CRTUSBDETECT::CRTUSBDETECT(HINSTANCE hInst)
{
	// оживим критичекую секцию для работы
	InitializeCriticalSection(&cs);
	// идентификатор модуля DLL
	hInstance = hInst;
	// идентификатор устройства
	hDevice = INVALID_HANDLE_VALUE;
	// базовый индекс ошибок модуля
	BASE_ERROR_ID = 300;
	// номер последней ошибки
	LastErrorNumber = 0;
}

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



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

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

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

	// определим версию Windows
	DWORD SystemVersion = GetSystemVersionMy();
	if((SystemVersion != 0x00010003/*Win2000 или WinXP*/) && (SystemVersion != 0x00020001/*Win98*/))  { 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 return TRUE;
}

//------------------------------------------------------------------------------
// освободим текущий виртуальный слот
//------------------------------------------------------------------------------
BOOL WINAPI CRTUSBDETECT::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 CRTUSBDETECT::GetModuleHandle(void) { return hDevice; }

//------------------------------------------------------------------------
// возвращается текущая скорость работы шины USB
//------------------------------------------------------------------------
BOOL WINAPI CRTUSBDETECT::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] = 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 CRTUSBDETECT::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] = 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 CRTUSBDETECT::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] = 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; }
}

//------------------------------------------------------------------------
// получение номера версии прошивки интерфейсной части
// модуля RTUSB3000 (микроконтроллер AVR)
//------------------------------------------------------------------------
BOOL WINAPI CRTUSBDETECT::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] = 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 CRTUSBDETECT::ReleaseInstance(void)
{
	BOOL status = TRUE;

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

	return status;
}

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