//---------------------------------------------------------------------------
#pragma hdrstop

extern "C" {
#include "hidsdi.h"
#include <setupapi.h>
}
#pragma comment(lib,"HID-BCB.LIB")

#include "usb_hid.h"
#include "Unit1.h"
//---------------------------------------------------------------------------
/*
 *  HID Find Devices
 *    Parameters:      None
 *    Return Value:    Number of Devices found
 */
int HID_FindDevices()
{	GUID								HidGuid;								// идентификатор класса HID-устройств
	HANDLE								DevHandle;								// указатель на открытый девайс
	HDEVINFO							DevInfo;								// указатель на полученный набор (список) HID-устройств
	SP_DEVICE_INTERFACE_DATA			DevData;								// структура с данными об интерфейсе очередного устройства
	PSP_DEVICE_INTERFACE_DETAIL_DATA	DevDetail;								// структура с детальными данными об интерфейсе очередного устройства (содержит путь к устройству)
	PSP_DEVICE_INTERFACE_DETAIL_DATA	DevDetailSelected;						// структура с детальными данными об интерфейсе выбранного устройства (содержит путь к устройству)
	PHIDP_PREPARSED_DATA				PreparsedData;							// блок данных для обработки репортов
	HIDD_ATTRIBUTES						Attributes;								// структура для хранения атрибутов устройства
	HIDP_CAPS							Capabilities;							// возможности из дескриптора репорта
	ULONG								Length;									// размер буфера для получения детальных данных об интерфейсе очередного устройства
	int									Index;									// номер очередного рассматриваемого интерфейса
	int									Sz;										// размер пути
	BOOL								ok;										// признак успешности вызова некоторых функций

	// получаем GUID класса устройств USB HID
	HidD_GetHidGuid(&HidGuid);
	Form1->RichEdit1->Lines->Add("HID GUID: " + Sysutils::GUIDToString(HidGuid)); // пишем полученный GUID в Log

	// получаем список всех HID-устройств (то есть устройств с указанным GUID), зарегистрированных в системе
	DevInfo = SetupDiGetClassDevs(	&HidGuid,
									NULL,
									NULL,
									(DIGCF_PRESENT | DIGCF_DEVICEINTERFACE)
								 );
	Form1->RichEdit1->Lines->Add("Ok! Список зарегистрированных в системе HID-устройств получен");	// сообщаем, что список получен
	DevData.cbSize = sizeof(DevData);											// размер структуры, содержащей данные об интерфейсе

	DevDetail = NULL;															// сбрасываем указатель на структуру с детальными данными об интерфейсе
	if (DevSelected != -1)
	{	/* Копируем детальные данные (путь) текущего выбранного девайса во временную переменную (структуру),
			чтобы его можно было найти во вновь сформированном списке */
		DevDetailSelected = (PSP_DEVICE_INTERFACE_DETAIL_DATA) malloc(DevDetailDataSz[DevSelected]);
		memcpy(DevDetailSelected, DevDetailData[DevSelected], DevDetailDataSz[DevSelected]);
	}
	else
	{	DevDetailSelected = NULL;		  										// если текущего выбранного девайса нет - сбрасываем указатель
	}

	// сбрасываем все указатели на детальные данные в списке
	for (Index = 0; Index < DEV_NUM; Index++)
	{	if (DevDetailData[Index])
		{	free(DevDetailData[Index]);											// очищаем структуру (освобождаем память)
			DevDetailData[Index] = NULL;										// сбрасываем указатель
		}
	}

	/* Scan all Devices */
	Index = -1;																	// инициализируем счётчик для перебора всех найденных hid-устройств
	DevCount = 0;																// инициализируем счётчик добавленных в список устройств
	DeviceHandle = INVALID_HANDLE_VALUE;										// сбрасываем указатель на открытый девайс
	do
	{	Index++;																// увеличиваем счётчик для перебоора всех найденных hid-устройств
		if(DeviceHandle != INVALID_HANDLE_VALUE)	CloseHandle(DeviceHandle); 	// закрываем открытый ранее девайс, если почему-то не закрыли до этого

		ok = SetupDiEnumDeviceInterfaces(DevInfo,0,&HidGuid,Index,&DevData);	// получаем информацию об очередном устройстве
		if(!ok)	break;															// видимо больше нет устройств, - завершаем цикл do

		// сначала узнаём какого размера нужен буфер для получения пути к устройству
		ok = SetupDiGetDeviceInterfaceDetail(DevInfo,&DevData,NULL,0,&Length,NULL);

		/* Выделяем память под буфер с детальными данными (путь) */
		if(DevDetail)	free(DevDetail);										// если у нас уже есть буфер, содержащий путь - удаляем его
		DevDetail = (PSP_DEVICE_INTERFACE_DETAIL_DATA) malloc(Length);          // создаём новый буфер нужного размера в виде структуры PSP_DEVICE_INTERFACE_DETAIL_DATA
		Sz = Length;															// запоминаем размер созданной структуры
		DevDetail->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);			// устанавливаем размер созданной структуры

		// получаем детальную информацию (путь к устройству)
		ok = SetupDiGetDeviceInterfaceDetail(DevInfo,&DevData,DevDetail,Length,NULL,NULL);
		if(!ok)	continue;														// завершаем текущую итерацию и переходим к следующему устройству
		// выводим полученный путь к устройству в log
		Form1->RichEdit1->Lines->Add("Device_" + IntToStr(Index) + ":");
		Form1->RichEdit1->Lines->Add("   |-> path: " + UnicodeString(DeviceInterfaceDataDetail->DevicePath));

		/* проверяем, доступно ли устройство для чтения/записи, пытаясь открыть его в режиме общего доступа */
		DevHandle = CreateFile(	DevDetail->DevicePath,
								0,
								FILE_SHARE_READ | FILE_SHARE_WRITE,
								(LPSECURITY_ATTRIBUTES)NULL,
								OPEN_EXISTING,
								0,
								NULL
							  );
		if(DevHandle == INVALID_HANDLE_VALUE)	continue;						// если доступ получить не удалось - завершаем итерацию, переходим к следующему устройству
		// сообщаем, что девайс доступен для чтения/записи
		Form1->RichEdit1->Lines->Add("   |-> Устройство доступно для чтения/записи");

		Attributes.Size = sizeof(Attributes);	 								// определяем размер структуры для хранения атрибутов устройства
		ok = HidD_GetAttributes(DeviceHandle,&Attributes);						// получаем атрибуты устройства
		if(!ok) continue; 														// завершаем текущую итерацию и переходим к следующему устройству
		// выводим в лог атрибуты девайса
		Form1->RichEdit1->Lines->Add("   |-> VID = " + IntToHex(Attributes.VendorID,4) +
									" & PID = " + IntToHex(Attributes.ProductID,4));

		ok = HidD_GetManufacturerString(DeviceHandle, Manufacturer, 128);	 	// получаем производителя
		if(!ok) continue;													 	// завершаем текущую итерацию и переходим к следующему устройству
		// выводим в лог производителя
		Form1->RichEdit1->Lines->Add("   |-> Manufacturer: " + UnicodeString(Manufacturer));

		ok = HidD_GetProductString(DeviceHandle, Product, 128);	  				// получаем имя продукта
		if(!ok) continue;										   				// завершаем текущую итерацию и переходим к следующему устройству
		// выводим в лог имя продукта
		Form1->RichEdit1->Lines->Add("   |-> Product: " + UnicodeString(Product));

		ok = HidD_GetSerialNumberString(DeviceHandle, Serial, 128);				// получаем серийный номер продукта
		if(!ok) continue;										   				// завершаем текущую итерацию и переходим к следующему устройству
		// сообщаем серийный номер продукта
		Form1->RichEdit1->Lines->Add("   |-> Serial: " + UnicodeString(Serial));

		ok = HidD_GetPreparsedData(DevHandle, &PreparsedData);					// запрашиваем блок данных для обработки репортов
	if (!ok) continue;

	/* Get Device's Capabilities */
	HidP_GetCaps(PreparsedData, &Capabilities);

	/* Free the PreparsedData */
	HidD_FreePreparsedData(PreparsedData);

	/* Remember Device Interface Detail Data for acceptable Devices */
    if ((Capabilities.UsagePage == 0xFF00) && (Capabilities.Usage == 0x0001)) {
      DevDetailData[DevCount] = DevDetail;
      DevDetailDataSz[DevCount] = Sz;
      DevInputReportSz[DevCount] = Capabilities.InputReportByteLength;
      DevOutputReportSz[DevCount] = Capabilities.OutputReportByteLength;
      DevFeatureReportSz[DevCount] = Capabilities.FeatureReportByteLength;
      if (DevDetailSelected)
        if (!strcmp(DevDetailSelected->DevicePath, DevDetailData[DevCount]->DevicePath))
          DevSelected = DevCount;
      DevCount++;
      DevDetail = NULL;
    }

    CloseHandle (DevHandle);

  } while (DevCount < DEV_NUM);

  if (DevDetail) free(DevDetail);
  if (DevDetailSelected) free(DevDetailSelected);

  SetupDiDestroyDeviceInfoList (DevInfo);

  return (DevCount);
}
//---------------------------------------------------------------------------
int ConnectToDevice(int SelectedIndex)
{	if(SelectedIndex <0)	return -1; 											// если ничего не выбрано, то и открывать нечего
	else																		// если что-то выбрано - будем пытаться это открыть
	{   DeviceHandle = CreateFile(	DeviceInterfaceDataDetailArray[SelectedIndex]->DevicePath,
									GENERIC_READ | GENERIC_WRITE,
									FILE_SHARE_READ|FILE_SHARE_WRITE,
									(LPSECURITY_ATTRIBUTES)NULL,
									OPEN_EXISTING,
									FILE_FLAG_OVERLAPPED,
									NULL);										// открываем девайс для асинхронного чтения / записи
		if(DeviceHandle == INVALID_HANDLE_VALUE)	return -1;					// ничего не вышло
		else										return SelectedIndex;		// отправляем индекс открытого девайса
	}
}
//---------------------------------------------------------------------------
void DisconnectFromDevice()
{	CancelIo(DeviceHandle);														// завершаем все отложенные операции ввода/вывода
	if (DeviceHandle != INVALID_HANDLE_VALUE)									// если есть открытый девайс - закываем
	{	CloseHandle(DeviceHandle);
		DeviceHandle = INVALID_HANDLE_VALUE;
	}
}
//---------------------------------------------------------------------------
#pragma package(smart_init)
