//------------------------------------------------------------------------------
// Project: HID test software.
// File:    hidLIB.cpp
// Purpose: HID library.
// History: FB, 2001feb07, created.
//------------------------------------------------------------------------------

#include <windows.h>
#include <stdio.h>

extern "C" {
	#include "hidsdi.h"     // from Windows DDK- should be <...>!
	#include <setupapi.h>
}

#include "hidLIB.hpp"

HANDLE hidOpen(unsigned short idVendor, unsigned short idProduct)
{	HANDLE DeviceHandle= 0;

	/*
	API function: HidD_GetHidGuid
	Get the GUID for all system HIDs.
	Returns: the GUID in HidGuid.
	*/
	GUID HidGuid;
	::HidD_GetHidGuid(&HidGuid);	

	/*
	API function: SetupDiGetClassDevs
	Returns: a handle to a device information set for all installed devices.
	Requires: the GUID returned by GetHidGuid.
	*/
	HDEVINFO hDevInfo= ::SetupDiGetClassDevs(
						&HidGuid,				// ClassGuid
						NULL,					// Enumerator
						NULL,					// hwndParent
						DIGCF_PRESENT
					|	DIGCF_INTERFACEDEVICE);	// Flags

	DWORD MemberIndex = 0;
	do
	{
		/*
		API function: SetupDiEnumDeviceInterfaces
		On return, MyDeviceInterfaceData contains the handle to a
		SP_DEVICE_INTERFACE_DATA structure for a detected device.
		Requires:
		The DeviceInfoSet returned in SetupDiGetClassDevs.
		The HidGuid returned in GetHidGuid.
		An index to specify a device.
		*/

		SP_DEVICE_INTERFACE_DATA devInfoData;
		devInfoData.cbSize = sizeof(devInfoData);

		BOOL result1= ::SetupDiEnumDeviceInterfaces(
							hDevInfo,			// DeviceInfoSet
							0,					// DeviceInfoData
							&HidGuid,			// InterfaceClassGuid
							MemberIndex,		// MemberIndex
							&devInfoData);		// DeviceInterfaceData

		if (result1)
		{	/*
			API function: SetupDiGetDeviceInterfaceDetail
			Returns: an SP_DEVICE_INTERFACE_DETAIL_DATA structure
			containing information about a device.
			To retrieve the information, call this function twice.
			The first time returns the size of the structure in Length.
			The second time returns a pointer to the data in DeviceInfoSet.
			Requires:
			A DeviceInfoSet returned by SetupDiGetClassDevs
			The SP_DEVICE_INTERFACE_DATA structure returned by SetupDiEnumDeviceInterfaces.
			
			The final parameter is an optional pointer to an SP_DEV_INFO_DATA structure.
			This application doesn't retrieve or use the structure.			
			If retrieving the structure, set 
			MyDeviceInfoData.cbSize = length of MyDeviceInfoData.
			and pass the structure's address.
			*/
			
			//Get the Length value.
			//The call will return with a "buffer too small" error which can be ignored.
			DWORD Length;
			BOOL result2= ::SetupDiGetDeviceInterfaceDetail(
								hDevInfo,		// DeviceInfoSet
								&devInfoData,	// DeviceInterfaceData
								NULL,			// DeviceInterfaceDetailData
								0,				// DeviceInterfaceDetailDataSize
								&Length,		// RequiredSize
								NULL);			// DeviceInfoData

			PSP_DEVICE_INTERFACE_DETAIL_DATA detailData= (PSP_DEVICE_INTERFACE_DETAIL_DATA)malloc(Length);
			detailData -> cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);

			//Call the function again, this time passing it the returned buffer size.
			DWORD Required;
			BOOL result3 = ::SetupDiGetDeviceInterfaceDetail(
								hDevInfo,		// DeviceInfoSet
								&devInfoData,	// DeviceInterfaceData
								detailData,		// DeviceInterfaceDetailData
								Length,			// DeviceInterfaceDetailDataSize
								&Required,		// RequiredSize
								NULL);			// DeviceInfoData

			/*
			API function: CreateFile
			Returns: a handle that enables reading and writing to the device.
			Requires:
			The DevicePath in the detailData structure
			returned by SetupDiGetDeviceInterfaceDetail.
			*/

			DeviceHandle= ::CreateFile(
								detailData->DevicePath,				// lpFileName
								GENERIC_READ|GENERIC_WRITE,			// dwDesiredAccess
								FILE_SHARE_READ|FILE_SHARE_WRITE,	// dwShareMode
								NULL,								// lpSecurityAttributes
								OPEN_EXISTING,						// dwCreationDisposition
								0,									// dwFlagsAndAttributes
								NULL);								// hTemplateFile

			/*
			API function: HidD_GetAttributes
			Requests information from the device.
			Requires: the handle returned by CreateFile.
			Returns: a HIDD_ATTRIBUTES structure containing
			the Vendor ID, Product ID, and Product Version Number.
			Use this information to decide if the detected device is
			the one we're looking for.
			*/

			HIDD_ATTRIBUTES Attributes;
			Attributes.Size= sizeof(Attributes);
			BOOLEAN result4= ::HidD_GetAttributes(
								DeviceHandle,	// HidDeviceObject
								&Attributes);	// Attributes
			
			if (Attributes.VendorID  == idVendor
			&&	Attributes.ProductID == idProduct)
			{	//Get the device's capablities.
				//GetDeviceCapabilities();
				//PrepareForOverlappedTransfer();
			}
			else
			{	::CloseHandle(DeviceHandle);
				DeviceHandle= 0;
			}

			free(detailData);
		}

		else
		{	// no more devices to check.
			break;
		}

		MemberIndex++;

	} while (DeviceHandle== 0);

	//Free the memory reserved for hDevInfo by SetupDiClassDevs.
	::SetupDiDestroyDeviceInfoList(hDevInfo);

	return DeviceHandle;
}

//------------------------------------------------------------------------------
void hidCaps(HANDLE DeviceHandle, int* inreport, int* outreport)
{	/*
	API function: HidD_GetPreparsedData
	Returns: a pointer to a buffer containing the information about the device's capabilities.
	Requires: A handle returned by CreateFile.
	There's no need to access the buffer directly,
	but HidP_GetCaps and other API functions require a pointer to the buffer.
	*/

	PHIDP_PREPARSED_DATA	PreparsedData;
	::HidD_GetPreparsedData(
				DeviceHandle, 
				&PreparsedData);

	/*
	API function: HidP_GetCaps
	Learn the device's capabilities.
	For standard devices such as joysticks, you can find out the specific
	capabilities of the device.
	For a custom device, the software will probably know what the device is capable of,
	and the call only verifies the information.
	Requires: the pointer to the buffer returned by HidD_GetPreparsedData.
	Returns: a Capabilities structure containing the information.
	*/
	
	HIDP_CAPS Capabilities;
	::HidP_GetCaps(
				PreparsedData, 
				&Capabilities);

	*inreport= Capabilities.InputReportByteLength;
	*outreport= Capabilities.OutputReportByteLength;

	//Display the capabilities
	fprintf(stderr, "%s%X\n", "Usage Page:                      ", Capabilities.UsagePage);
	fprintf(stderr, "%s%d\n", "Input Report Byte Length:        ", Capabilities.InputReportByteLength);
	fprintf(stderr, "%s%d\n", "Output Report Byte Length:       ", Capabilities.OutputReportByteLength);
	fprintf(stderr, "%s%d\n", "Feature Report Byte Length:      ", Capabilities.FeatureReportByteLength);
	fprintf(stderr, "%s%d\n", "Number of Link Collection Nodes: ", Capabilities.NumberLinkCollectionNodes);
	fprintf(stderr, "%s%d\n", "Number of Input Button Caps:     ", Capabilities.NumberInputButtonCaps);
	fprintf(stderr, "%s%d\n", "Number of InputValue Caps:       ", Capabilities.NumberInputValueCaps);
	fprintf(stderr, "%s%d\n", "Number of InputData Indices:     ", Capabilities.NumberInputDataIndices);
	fprintf(stderr, "%s%d\n", "Number of Output Button Caps:    ", Capabilities.NumberOutputButtonCaps);
	fprintf(stderr, "%s%d\n", "Number of Output Value Caps:     ", Capabilities.NumberOutputValueCaps);
	fprintf(stderr, "%s%d\n", "Number of Output Data Indices:   ", Capabilities.NumberOutputDataIndices);
	fprintf(stderr, "%s%d\n", "Number of Feature Button Caps:   ", Capabilities.NumberFeatureButtonCaps);
	fprintf(stderr, "%s%d\n", "Number of Feature Value Caps:    ", Capabilities.NumberFeatureValueCaps);
	fprintf(stderr, "%s%d\n", "Number of Feature Data Indices:  ", Capabilities.NumberFeatureDataIndices);

	//No need for PreparsedData any more, so free the memory it's using.
	::HidD_FreePreparsedData(PreparsedData);
}

//------------------------------------------------------------------------------
void hidClose(HANDLE DeviceHandle)
{	::CloseHandle(DeviceHandle);
}

//------------------------------------------------------------------------------
int hidRead(HANDLE handle, unsigned char* buf, int len)
{	DWORD read;
	BOOL result= ::ReadFile(
					handle,						// hFile
					buf,						// lpBuffer
					len,						// nNumberOfBytesToRead
					&read,						// lpNumberOfBytesRead,
					NULL);						// lpOverlapped

	return result? read: 0;
}

//------------------------------------------------------------------------------
int hidWrite(HANDLE handle, unsigned char* buf, int len)
{	DWORD written;
	BOOL result= ::WriteFile(
					handle,						// hFile
					buf,						// lpBuffer
					len,						// nNumberOfBytesToWrite
					&written,					// lpNumberOfBytesWritten,
					NULL);						// lpOverlapped
	return result? written: 0;
}

//------------------------------------------------------------------------------
// end of file
