//////////////////////////////////////////////////////////////////
//
// App:			Global device list
// Author:		Milan Korinek
// Version:		1.0
// Notice:		Share memory list, internal device info list
//				USB Hot Plug monitor 
//////////////////////////////////////////////////////////////////

#include "stdafx.h"

#include "GlobalDevicesLists.h"
#include "support.h"

/* ============================================ */
/*					Constants					*/
/* ============================================ */
#define _DIO_DEVICE_INFO_DEFAULT	 {	DIO_USB_DEVICE_VID,		\
										DIO_USB_DEVICE_PID,		\
										DIO_USB_DEVICE_NAME,	\
										"",						\
										0,						\
										"",						\
										DIO_MAX_PORTS,			\
										DIO_MAX_CHANNELS }		

#define _DEVICE_OWNERSHIP_DEFAULT		{ FALSE, 0, "", "" }
//! 
const DIO_DeviceInfo_t DIO_DEVICE_INFO_DEFAULT = _DIO_DEVICE_INFO_DEFAULT;
//! 
const DeviceOwnership_t DEVICE_OWNERSHIP_DEFAULT = _DEVICE_OWNERSHIP_DEFAULT;
//! 
const DeviceInfoInternal_t DEVICE_INFO_INTERNAL_DEFAULT = {	NULL,
															E_PROCESS_OWNERSHIP_FREE, 
															"", 
															_DIO_DEVICE_INFO_DEFAULT, 
															E_DEVICE_CONNECTION_STATE_DISCONNECTED,
															E_DEVICE_STATE_CLOSE,
															NULL,
															NULL};

/* ============================================ */
/*				Share device list				*/
/* ============================================ */

//! Handle of Share Device List
static HANDLE hndlShareDeviceList = NULL;
//! Pointer of Share Device List
static DeviceOwnership_t * pShareDeviceList = NULL;
//! Handle of Share Device List Sync - Mutex
static HANDLE hndlShareDeviceListSync = NULL;

/* ============================================ */
/*		Device info internal list				*/
/* ============================================ */

//! List with devices info and status
static DeviceInfoInternal_t DeviceInfoIntList[DIO_MAX_DEVICES_SUPPORT];
//! Handle of Device Info Internal List Sync - Mutex (protection during adding new device to list)
static HANDLE hndlDeviceInfoIntListSync = NULL;
//! Handle of USB device list provided by usblibK
static KLST_HANDLE hndlUSBDeviceList = NULL;

/* ============================================ */
/*		USB Hot Plug Monitor					*/
/* ============================================ */

//! Handle of USB Hot Plug Monitor provided by usblibK
static KHOT_HANDLE hndlUSBHotPlugMonitor = NULL;


/* ============================================ */
/*				General functions				*/
/* ============================================ */

DIO_Errors_t InitiateLists(void)
{
	DIO_Errors_t shareMemStatus;
	DIO_Errors_t retErrValue;

	debug_func_begin(DIO_SEVERITY_IMPORTANT, "");

	// Create Share Device list between process
	shareMemStatus = CreateShareDeviceList();
	if (shareMemStatus == DIO_ERROR_SUCCESS) {				// New share device list was created
		if (!SetDefaultShareDeviceList()){
			debug_error(DIO_SEVERITY_CRITICAL, "Could not set default values to Share Device List.");
			retErrValue = DIO_ERROR_FAIL_SHARE_MEM;
			goto CleanUp;
		}
	} else if (shareMemStatus == DIO_ERROR_ALREADY_EXIST) {	// Share Device List already exists
		PrintShareDeviceList(FALSE);						// Print only in debug mode
	} else  {												// Fatal error
		debug_error(DIO_SEVERITY_CRITICAL, "Could not create Share Device List.");
		retErrValue = DIO_ERROR_FAIL_SHARE_MEM;
		goto CleanUp;
	}

	// Initialize Device Info Internal List and start USB Hot Plug monitor
	if (!CreateInternalDeviceList()) {
		debug_error(DIO_SEVERITY_CRITICAL, "Could not create Device Info Internal List.");
		retErrValue = DIO_ERROR_FAIL_INIT;
		goto CleanUp;
	}	

	// Start USB Hot Plug Monitor
	if (!USBHotPlugMonitor_Start()) {
		debug_error(DIO_SEVERITY_CRITICAL, "Could not start USB Hot Plug Monitor.");
		retErrValue = DIO_ERROR_FAIL_LIBUSBK;
		goto CleanUp;
	}
	
	debug_func_end(DIO_SEVERITY_CRITICAL, "error status = %d", DIO_ERROR_SUCCESS);
	return DIO_ERROR_SUCCESS;

CleanUp:
	FreeLists();
	debug_func_end(DIO_SEVERITY_IMPORTANT, "error status = %d", retErrValue);
	return retErrValue;
}

DIO_Errors_t FreeLists(void)
{
	DIO_Errors_t retValue = DIO_ERROR_SUCCESS;
	
	debug_func_begin(DIO_SEVERITY_IMPORTANT, "");

	// Save and return only first error.

	retValue = CloseDevices();

	// Stop USB Hot Plug Monitor
	if (!USBHotPlugMonitor_Stop()) 
		if (retValue == DIO_ERROR_SUCCESS) retValue = DIO_ERROR_FAIL_LIBUSBK;

	// Free Device Info Internal List
	if (!FreeInternalDeviceList()) 
		if (retValue == DIO_ERROR_SUCCESS) retValue = DIO_ERROR_FAIL_FREE;

	// Free Share memory of used devices
	if (!FreeShareDeviceList()) 
		if (retValue == DIO_ERROR_SUCCESS) retValue = DIO_ERROR_FAIL_FREE;

	debug_func_end(DIO_SEVERITY_IMPORTANT, "error status = %d", retValue);
	return retValue;
}

DIO_Errors_t OpenDevice(_In_ DeviceInfoInternal_t * device)
{
	DIO_Errors_t retValue = DIO_ERROR_SUCCESS;
	int i, emptySlot = -1;
	UINT32 Module;

	debug_func_begin(DIO_SEVERITY_HIGH, "");

	Module = device->DeviceInfo.Module;

	// LOCK - Device
	if (!LockObject(device->hndlLock, TRUE)) {
		debug_error(DIO_SEVERITY_IMPORTANT, "END, Could not get access to device: Module = %u, serial number = %s.", 
			Module, device->DeviceInfo.SerialNumber);
		return DIO_ERROR_FAIL_INIT;
	}

	// LOCK - Share memory list of used devices by other proccesses
	if (!LockObject(hndlShareDeviceListSync, TRUE)) {
		debug_error(DIO_SEVERITY_IMPORTANT, "END, Could not get access to device share memory: Module = %u, serial number = %s.", 
			Module, device->DeviceInfo.SerialNumber);

		// UNLOCK - Device
		LockObject(device->hndlLock, FALSE);
		return DIO_ERROR_FAIL_INIT;
	}

	// Is device connected?
	if (device->DeviceConnectionState <= E_DEVICE_CONNECTION_STATE_WAS_CONNECTED) {
		debug_warning(DIO_SEVERITY_IMPORTANT, "Device is not connected Module = %u, serial number = %s, connection state = %d.", 
			Module, device->DeviceInfo.SerialNumber, device->DeviceConnectionState);
		retValue = DIO_ERROR_FAIL_DISCONNECTED;
		goto CleanUp;
	}

	// Is device closed?
	if (device->DeviceState != E_DEVICE_STATE_CLOSE) {
		debug_warning(DIO_SEVERITY_IMPORTANT, "Device is already opened. Module = %u, serial number = %s, device state = %d.", 
			Module, device->DeviceInfo.SerialNumber, device->DeviceState);
		retValue = DIO_ERROR_FAIL_USED;
		goto CleanUp;
	}

	// Is device avaliable (must not be used by other proccess)?
	// This proccess certainly do not use this device (tests above) -> 
	// we not have to check the name and ID of the proccess that owns this device.
	for (i = 0; i < DIO_MAX_DEVICES_SUPPORT; i++) { 
		// Get empty position
		if (!pShareDeviceList[i].IsUsed && (emptySlot == -1)) emptySlot = i;
		// Is device used by other proccess?
		if (pShareDeviceList[i].IsUsed && 
			(strcmp(pShareDeviceList[i].DeviceID, device->DeviceID) == 0)) {
			// \todo { OK, is proccess running? Or is crash? We must chech this and cleanup share memory if crash } 
			debug_warning(DIO_SEVERITY_IMPORTANT, "Device (Module = %u, serial number = %s) is used by other proccess (ID = %lu, name = %s).", 
				Module, device->DeviceInfo.SerialNumber, pShareDeviceList[i].ProcessID, pShareDeviceList[i].ProcessName);
			device->ProcessOwnership = E_PROCESS_OWNERSHIP_USED;	// update state of ownership
			retValue = DIO_ERROR_FAIL_USED;
			goto CleanUp;
		}
	}

	if (emptySlot == -1) {	// No free position in Device Share Memory
		debug_warning(DIO_SEVERITY_IMPORTANT, "No empty slots in Device Share Memory for Module = %u, serial number = %s.", 
			Module, device->DeviceInfo.SerialNumber);
		retValue = DIO_ERROR_FAIL_FULL;
		goto CleanUp;
	}

	// ---- Device is now avaliable -----

	// Enable USB device interface
	if (!ConfigureUSBDeviceInterface(TRUE, device)) {
		debug_error(DIO_SEVERITY_CRITICAL, "Could not set USB device interface for Module = %u, serial number = %s (win32error=%lu).", 
			 Module, device->DeviceInfo.SerialNumber, GetLastError());
		retValue = DIO_ERROR_FAIL_LIBUSBK;
		goto CleanUp;
	}

	// Update device statatus in Device Share Memory
	pShareDeviceList[emptySlot].IsUsed = TRUE;
	strcpy(pShareDeviceList[emptySlot].DeviceID, device->DeviceID);
	pShareDeviceList[emptySlot].ProcessID = GetCurrentProcessId();
	strcpy(pShareDeviceList[emptySlot].ProcessName, "");
	// \todo { set name of current proccess, GetModuleFileName }
	device->ProcessOwnership = E_PROCESS_OWNERSHIP_OWNER;
	device->DeviceState = E_DEVICE_STATE_UNCONFIGURED;

CleanUp:
	// UNLOCK - Share memory list of used devices by other proccesses
	LockObject(hndlShareDeviceListSync, FALSE);
	// UNLOCK - Device
	LockObject(device->hndlLock, FALSE);

	if (retValue == DIO_ERROR_SUCCESS) {
		debug_func_end(DIO_SEVERITY_IMPORTANT, "Device (Module = %u, serial number = %s) was opened successfully by proccess (ID = %lu, name = %s) on position = %d in share device memory.",
			Module, device->DeviceInfo.SerialNumber, pShareDeviceList[emptySlot].ProcessID, pShareDeviceList[emptySlot].ProcessName, emptySlot);
	} else {
		debug_func_end(DIO_SEVERITY_IMPORTANT, "error status = %d.", retValue);
	}

	return retValue;
}

BOOL RecoveryDevice(_In_ DeviceInfoInternal_t * device)
{
	BOOL retValue = TRUE;

	debug_func_begin(DIO_SEVERITY_CRITICAL, "Module = %u, serial number = %s, device state = %d.", 
		device->DeviceInfo.Module, device->DeviceInfo.SerialNumber, device->DeviceState);

	// LOCK - Device
	if (!LockObject(device->hndlLock, TRUE)) {
		debug_error(DIO_SEVERITY_CRITICAL, "END, Could not get access to device: Module = %u, serial number = %s.", 
			device->DeviceInfo.Module, device->DeviceInfo.SerialNumber);
		return FALSE;
	}
	
	// First we must close invalid handle of USB (otherwise we get error from libusbK 170 - The requested resource is in use.)
	if (device->hndlUSB != NULL) { 
		if (!device->USB_Driver.Free(device->hndlUSB)) {
			debug_warning(DIO_SEVERITY_CRITICAL, "Could not close handle of 'device.hndlUSB' with serial number '%s'  (win32error=%lu).", 
				device->DeviceInfo.SerialNumber, GetLastError());
		}
		device->hndlUSB = NULL;
	}
	
	// Try to initiate the usb device and get new handle
	if (!device->USB_Driver.Init(&device->hndlUSB, device->USBDeviceInfo)) {
		debug_error(DIO_SEVERITY_CRITICAL, "Could not initiate USB device (USB_Driver->Init()) for Module = %u, serial number = %s (win32error=%lu).", 
			device->DeviceInfo.Module, device->DeviceInfo.SerialNumber, GetLastError());
		device->hndlUSB = NULL;
		retValue = FALSE;
		goto CleanUp;
	}

	// Check device state only if device was used then we can proceed
	if (device->DeviceState == E_DEVICE_STATE_CLOSE) {
		// success, nonething todo
		goto CleanUp;
	}

	// We must enable USB interface because is disabled (macro UDI_VENDOR_DISABLE_EXT() in firmware)
	// after recconection (even with external power)
	if (!ConfigureUSBDeviceInterface(TRUE, device)) {
		debug_error(DIO_SEVERITY_CRITICAL, "Could not set USB device interface for Module = %u, serial number = %s (win32error=%lu).", 
			 device->DeviceInfo.Module, device->DeviceInfo.SerialNumber, GetLastError());
		retValue = FALSE;
		goto CleanUp;
	}

	// We must now read state of Digital I/O USB card, if card lost power then we must close it
	// otherwise recovery is successfull



CleanUp:

	if (!retValue) { // Could not recovery device, so close it.
		if (CloseDevice(device) != DIO_ERROR_SUCCESS) {
			debug_error(DIO_SEVERITY_CRITICAL, "Could not close device, Module = %u, serial number = %s (win32error=%lu).", 
			 device->DeviceInfo.Module, device->DeviceInfo.SerialNumber, GetLastError());
		}
	}

	// UNLOCK - Device Info Internal List
	LockObject(device->hndlLock, FALSE);

	debug_func_end(DIO_SEVERITY_CRITICAL, "%s", retValue ? "SUCCESS" : "FAIL");

	return retValue;
}

DIO_Errors_t CloseDevices(void)
{
	int i;
	DIO_Errors_t retValue = DIO_ERROR_SUCCESS;
	DIO_Errors_t retErr;

	// LOCK - Device Info Internal List
	if (!LockObject(hndlDeviceInfoIntListSync, TRUE)) {
		debug_error(DIO_SEVERITY_IMPORTANT, "END, Could not get access to Device Info Internal List.");
		return DIO_ERROR_FAIL_FREE;
	}

	// LOCK - Share memory list of used devices by other proccesses
	if (!LockObject(hndlShareDeviceListSync, TRUE)) {
		debug_error(DIO_SEVERITY_IMPORTANT, "END, Could not get access to Device Share Memory.");

		// UNLOCK - Device Info Internal List
		LockObject(hndlDeviceInfoIntListSync, FALSE);
		return DIO_ERROR_FAIL_FREE;
	}

	for (i = 0; i < DIO_MAX_DEVICES_SUPPORT; i++) {
		if (DeviceInfoIntList[i].DeviceState == E_DEVICE_STATE_CLOSE) continue;	// Already closed
		retErr = CloseDevice(&DeviceInfoIntList[i]);							// Close device
		if (retValue == DIO_ERROR_SUCCESS) retValue = retErr;					// Save and return first error
	}

	// UNLOCK - Share memory list of used devices by other proccesses
	LockObject(hndlShareDeviceListSync, FALSE);
	// UNLOCK - Device Info Internal List
	LockObject(hndlDeviceInfoIntListSync, FALSE);

	return retValue;
}


DIO_Errors_t CloseDevice(_In_ DeviceInfoInternal_t * device) 
{
	DIO_Errors_t retValue = DIO_ERROR_SUCCESS;
	int i, deviceSlot = -1;
	UINT32 Module;

	debug_func_begin(DIO_SEVERITY_HIGH, "");

	Module = device->DeviceInfo.Module;

	// LOCK - Device
	if (!LockObject(device->hndlLock, TRUE)) {
		debug_error(DIO_SEVERITY_IMPORTANT, "END, Could not get access to device: Module = %d, serial number = %s.", 
			Module, device->DeviceInfo.SerialNumber);
		return DIO_ERROR_FAIL_FREE;
	}

	// LOCK - Share memory list of used devices by other proccesses
	if (!LockObject(hndlShareDeviceListSync, TRUE)) {
		debug_error(DIO_SEVERITY_IMPORTANT, "END, Could not get access to device share memory: Module = %d, serial number = %s.", 
			Module, device->DeviceInfo.SerialNumber);

		// UNLOCK - Device
		LockObject(device->hndlLock, FALSE);
		return DIO_ERROR_FAIL_FREE;
	}

	// Is device Opened?
	if (device->DeviceState == E_DEVICE_STATE_CLOSE) {
		debug_warning(DIO_SEVERITY_IMPORTANT, "Device is already closed. Module = %d, serial number = %s.", 
			Module, device->DeviceInfo.SerialNumber);
		retValue = DIO_ERROR_ALREADY_FREE;
		goto CleanUp;
	}

	// Is device owned by this proccess?
	// \todo { proccess name }
	if (device->ProcessOwnership != E_PROCESS_OWNERSHIP_OWNER) {
		debug_warning(DIO_SEVERITY_IMPORTANT, "Current proccess (ID = %d, name = %s) is not owner of device module = %d, serial number = %s.", 
			GetCurrentProcessId(), "", Module, device->DeviceInfo.SerialNumber);
		retValue = DIO_ERROR_ALREADY_FREE;
		goto CleanUp;
	}

	// Try to find device in Share Memory List
	for (i = 0; i < DIO_MAX_DEVICES_SUPPORT; i++) { 
		// Is our device in Share Memory List?
		if (pShareDeviceList[i].IsUsed && 
			(strcmp(pShareDeviceList[i].DeviceID, device->DeviceID) == 0)) { 

			// \todo { compare names }
			// Proccess ID must be same otherwise we have serious problem - Which proccess is the owner of device?
			if (pShareDeviceList[i].ProcessID != GetCurrentProcessId()) {
				debug_error(DIO_SEVERITY_CRITICAL, "Unknown ownership of device (Module = %d, serial number = %s) in pShareDeviceList[%d]. Current proccess (ID = %d, name = %s).",
					Module, device->DeviceInfo.SerialNumber, i, GetCurrentProcessId(), "");
				PrintShareDeviceList(TRUE);
				retValue = DIO_ERROR_FAIL_SHARE_MEM;
				goto CleanUp;
			}
			deviceSlot = i;		// Device position in Share Memory
			break;
		}
	}

	if (deviceSlot == -1) {		// Again, we have serious problem - Where is our device? Other proccess rewrite this?
		debug_error(DIO_SEVERITY_CRITICAL, "Device (Module = %d, serial number = %s) is not in pShareDeviceList[]! Current proccess (ID = %d, name = %s).",
			Module, device->DeviceInfo.SerialNumber, GetCurrentProcessId(), "");
		PrintShareDeviceList(TRUE);
		retValue = DIO_ERROR_FAIL_SHARE_MEM;
		goto CleanUp;
	}

	// ---- Close our device ----
	pShareDeviceList[deviceSlot] = DEVICE_OWNERSHIP_DEFAULT;	// set defaults values
	device->ProcessOwnership = E_PROCESS_OWNERSHIP_FREE;
	device->DeviceState = E_DEVICE_STATE_CLOSE;
	
	// Disable USB device interface
	if (!ConfigureUSBDeviceInterface(FALSE, device)) {
		debug_error(DIO_SEVERITY_CRITICAL, "Could not disable USB device interface for Module = %d, serial number = %s (win32error=%d).", 
			 Module, device->DeviceInfo.SerialNumber, GetLastError());
		retValue = DIO_ERROR_FAIL_LIBUSBK;
	}

CleanUp:
	// UNLOCK - Share memory list of used devices by other proccesses
	LockObject(hndlShareDeviceListSync, FALSE);
	// UNLOCK - Device
	LockObject(device->hndlLock, FALSE);

	debug_func_end(DIO_SEVERITY_HIGH, "error status = %d", retValue);

	return retValue;
}

char * GetUSBDriverName(_In_ KLST_DEVINFO_HANDLE deviceInfo)
{
	// Display some information on the driver api type.
	switch(deviceInfo->DriverID)
	{
	case KUSB_DRVID_LIBUSBK:
		return "libusbK";
		break;
	case KUSB_DRVID_LIBUSB0:
		return "libusb0";
		break;
	case KUSB_DRVID_WINUSB:
		return "WinUSB";
		break;
	case KUSB_DRVID_LIBUSB0_FILTER:
		return "libusb0";
		break;
	}
	return "";
}


/* ============================================ */
/*		USB Hot Plug Monitor functions			*/
/* ============================================ */

static VOID KUSB_API  USBHotPlugMonitor(
	KHOT_HANDLE Handle, 
	KLST_DEVINFO_HANDLE USBDeviceInfo, 
	KLST_SYNC_FLAG NotificationType)
{
	DeviceInfoInternal_t * deviceInfoInternal;
	int index;
	BOOL connected;
	BOOL recover_device = FALSE;
	
	debug_func_begin(DIO_SEVERITY_MEDIUM, "");

	if (!IsDigitalIOUSBCard(USBDeviceInfo)) {
		debug_func_end(DIO_SEVERITY_MEDIUM, "");
		return;
	} 

	connected = NotificationType == KLST_SYNC_FLAG_ADDED;
	index = FindDeviceInfoByUSBDevice(USBDeviceInfo, &deviceInfoInternal);	// Device Info Internal list will be locked (and unlocked)
	if (index == -1) {		// User plug on new Digital I/O USB card, but it is not added in list yet.

		// Try to add new device to list
		index = AddUSBDevicesToList(USBDeviceInfo);
		if (index >= 0) {
			deviceInfoInternal = &(DeviceInfoIntList[index]);
			debug_info(DIO_SEVERITY_CRITICAL, "New Digital I/O USB cards was found (Total %d):\nDeviceID: \"%s\"\n, Serial number \"%s\"", 
					(index + 1), DeviceInfoIntList[index].DeviceID, DeviceInfoIntList[index].DeviceInfo.SerialNumber);
		} else if (index == -1) {
			debug_warning(DIO_SEVERITY_CRITICAL, "Digital I/O USB cards is already added in list:\nDeviceID: \"%s\"\n, Serial number \"%s\"", 
					GetUSBDeviceUniqueID(USBDeviceInfo), USBDeviceInfo->SerialNumber);
			return;
		} else {
			debug_error(DIO_SEVERITY_CRITICAL, "Could not parse Digital I/O USB card device:\nDeviceID: \"%s\"\n, Serial number \"%s\"", 
					GetUSBDeviceUniqueID(USBDeviceInfo), USBDeviceInfo->SerialNumber);
			return;
		}	
	} else if (index < -1) {  // Fatal error, Lock timeout in FindDeviceInfoByUSBDevice
		debug_error(DIO_SEVERITY_CRITICAL, " for :\nDeviceID: \"%s\"\n, Serial number \"%s\"", 
					GetUSBDeviceUniqueID(USBDeviceInfo), USBDeviceInfo->SerialNumber);
		return;
	}

	// LOCK - Device Info Structure
	if (!LockObject(deviceInfoInternal->hndlLock, TRUE)) {
		debug_error(DIO_SEVERITY_IMPORTANT, "END, Could not get access to device: Module = %d, serial number = %s", 
			deviceInfoInternal->DeviceInfo.Module, deviceInfoInternal->DeviceInfo.SerialNumber);
		return;
	}

	// Check a change state of the device
	if (deviceInfoInternal->DeviceConnectionState >= E_DEVICE_CONNECTION_STATE_WAS_DISCONNECTED) {		// Device was connected 
		if (!connected) {
			deviceInfoInternal->DeviceConnectionState = E_DEVICE_CONNECTION_STATE_WAS_CONNECTED;
			debug_event(DIO_SEVERITY_IMPORTANT, "Device is disconnected, was connected: Module = %d, serial number = %s", 
				deviceInfoInternal->DeviceInfo.Module, deviceInfoInternal->DeviceInfo.SerialNumber);
		} else {
			debug_warning(DIO_SEVERITY_IMPORTANT, "Received - Device is connected, but device is already connected! Some event lost? For : Module = %d, serial number = %s", 
				deviceInfoInternal->DeviceInfo.Module, deviceInfoInternal->DeviceInfo.SerialNumber);
		}
	} else {																							// Device was disconnected
		if (connected) {
			deviceInfoInternal->DeviceConnectionState = E_DEVICE_CONNECTION_STATE_WAS_DISCONNECTED;
			recover_device = TRUE;
			debug_event(DIO_SEVERITY_IMPORTANT, "Device is connected, was disconnected: Module = %d, serial number = %s", 
				deviceInfoInternal->DeviceInfo.Module, deviceInfoInternal->DeviceInfo.SerialNumber);
		} else {
			debug_warning(DIO_SEVERITY_IMPORTANT, "Received - Device is disconnected, but device is already disconnected! Some event lost? For : Module = %d, serial number = %s", 
				deviceInfoInternal->DeviceInfo.Module, deviceInfoInternal->DeviceInfo.SerialNumber);
		}
	}

	// If device was disconnected a now is connected then:
	//	- reopen USB
	//  - get state of Digital I/O USB card:
	//		- Device rebember last state (has external power) - all is OK, but we must inform user about this
	//		- Device don't rebember last state (powered by USB) - close all, user must device open again
	if (recover_device) {
		RecoveryDevice(deviceInfoInternal);
	}


	// UNLOCK
	LockObject(deviceInfoInternal->hndlLock, FALSE);

	// !!!!! Callback event

	debug_func_end(DIO_SEVERITY_MEDIUM, "");
	UNREFERENCED_PARAMETER(Handle);
}

static BOOL USBHotPlugMonitor_Start()
{
	KHOT_PARAMS hotParams;

	// All resources must be free
	if (hndlUSBHotPlugMonitor != NULL) {
		debug_error(DIO_SEVERITY_CRITICAL, "Attempt to rewrite unreleased resources of USB Hot Plug Monitor.");
		return FALSE;
	}

	// Start USB Hot Plug Monitor
	memset(&hotParams, 0, sizeof(hotParams));
	hotParams.OnHotPlug = USBHotPlugMonitor;
	hotParams.Flags = (KHOT_FLAG)(hotParams.Flags | KHOT_FLAG_PASS_DUPE_INSTANCE);

	strcpy(hotParams.PatternMatch.DeviceInterfaceGUID, "*");

	if (!HotK_Init(&hndlUSBHotPlugMonitor, &hotParams))
	{
		debug_error(DIO_SEVERITY_CRITICAL, "END, Could not initiate USB Hot Plug Monitor (win32error=%d).",  GetLastError());
		return FALSE;
	}
	return TRUE;
}

static BOOL USBHotPlugMonitor_Stop()
{
	BOOL retValue = TRUE;

	// Close handle of USB Hot Plug Monitor
	if (hndlUSBHotPlugMonitor != NULL) {
		if (!HotK_Free(hndlUSBHotPlugMonitor)) {
			debug_warning(DIO_SEVERITY_IMPORTANT, "Could not close handle of 'hndlUSBHotPlugMonitor' (win32error=%d).", GetLastError());
			retValue = FALSE;
		}
		hndlUSBHotPlugMonitor = NULL;
	}

	return retValue;
}


/* ============================================ */
/*		Share device list functions				*/
/* ============================================ */

static DIO_Errors_t CreateShareDeviceList(void)
{
	DIO_Errors_t retValue = DIO_ERROR_SUCCESS;
	TCHAR szName[] = SHARE_DEVICE_LIST_MEM_NAME;

	debug_func_begin(DIO_SEVERITY_HIGH, "");

	// Default initialization
	if ((hndlShareDeviceListSync != NULL) ||
			(hndlShareDeviceList != NULL) ||
				(pShareDeviceList != NULL)) {
		debug_error(DIO_SEVERITY_MEDIUM, "END, Attempt to rewrite unreleased resources of Share Device List.");
		return DIO_ERROR_ALREADY_INIT;
	}

	// Create mutex for synchronization
	hndlShareDeviceListSync = CreateMutex(
				NULL,							// default security
				FALSE,							// initial owner
				SHARE_DEVICE_LIST_SYNC_NAME);	// mutex name

	if (hndlShareDeviceListSync == NULL) {
		debug_error(DIO_SEVERITY_CRITICAL, "END, Could not create Share Device List Sync Mutex (win32error=%d).", GetLastError());
		retValue = DIO_ERROR_FAIL_CREATE;
		goto CleanUp;
	}

	if (GetLastError() == ERROR_ALREADY_EXISTS) {
		debug_warning(DIO_SEVERITY_MEDIUM, "END, Share Device List Sync Mutex already exists.");
	}

	// Create new share memory if not exists
	hndlShareDeviceList =  CreateFileMapping(
                INVALID_HANDLE_VALUE,			// use paging file
                NULL,							// default security
                PAGE_READWRITE,					// read/write access
                0,								// maximum object size (high-order DWORD)
				SHARE_DEVICE_LIST_MEM_SIZE,		// maximum object size (low-order DWORD)
				szName);						// name of mapping object

	if (hndlShareDeviceList == NULL) {
		debug_error(DIO_SEVERITY_CRITICAL, "END, Could not create Share Device List (win32error=%d).", GetLastError());
		retValue = DIO_ERROR_FAIL_CREATE;
		goto CleanUp;
	}

	if (GetLastError() == ERROR_ALREADY_EXISTS)	{
		debug_warning(DIO_SEVERITY_MEDIUM, "Share Device List already exists.");
		retValue = DIO_ERROR_ALREADY_EXIST;
	}

	// Get access to share memory
	pShareDeviceList = (DeviceOwnership_t *) MapViewOfFile(
				hndlShareDeviceList,			// handle to map object
				FILE_MAP_ALL_ACCESS,			// read/write permission
				0,
				0,
				SHARE_DEVICE_LIST_MEM_SIZE);	// memory map size

	if (pShareDeviceList == NULL) {
		debug_error(DIO_SEVERITY_CRITICAL, "END, Could not map view of Share Device List (win32error=%d).", GetLastError());
		retValue = DIO_ERROR_FAIL_CREATE;
		goto CleanUp;
	}

	debug_func_end(DIO_SEVERITY_HIGH, "error status = %d", retValue);
	return retValue;	// Success

CleanUp:
	FreeShareDeviceList();
	debug_func_end(DIO_SEVERITY_HIGH, "error status = %d", retValue);
	return retValue;	// Error
}

static BOOL FreeShareDeviceList(void)
{
	BOOL retValue = TRUE;

	debug_func_begin(DIO_SEVERITY_HIGH, "");

	// Unmap of Share Device List
	if (pShareDeviceList != NULL) {
		if (!UnmapViewOfFile(pShareDeviceList)) {
			debug_warning(DIO_SEVERITY_IMPORTANT, "Could not unmap 'pShareDeviceList' (win32error=%d).", GetLastError());
			retValue = FALSE;
		}
		pShareDeviceList = NULL;
	}

	// Close handle of Share Device List
	if (hndlShareDeviceList != NULL) {
		if (!CloseHandle(hndlShareDeviceList)) {
			debug_warning(DIO_SEVERITY_IMPORTANT, "Could not close handle of 'hndlShareDeviceList' (win32error=%d).", GetLastError());
			retValue = FALSE;
		}
		hndlShareDeviceList = NULL;
	}

	// Close handle of Share Device List Sync Mutex
	if (hndlShareDeviceListSync != NULL) {
		if (!CloseHandle(hndlShareDeviceListSync)) {
			debug_warning(DIO_SEVERITY_IMPORTANT, "Could not close handle of 'hndlShareDeviceListSync' (win32error=%d).", GetLastError());
			retValue = FALSE;
		}
		hndlShareDeviceListSync = NULL;
	}

	debug_func_end(DIO_SEVERITY_HIGH, "success = 1");
	return TRUE;
}

static BOOL SetDefaultShareDeviceList(void)
{
	int i;

	debug_func_begin(DIO_SEVERITY_HIGH, "");

	// LOCK
	if (!LockObject(hndlShareDeviceListSync, TRUE)) {
		debug_error(DIO_SEVERITY_CRITICAL, "END, Can not get access to Share Device List.");
		return FALSE;
	}

	for (i = 0; i < DIO_MAX_DEVICES_SUPPORT; i++) {
		pShareDeviceList[i] = DEVICE_OWNERSHIP_DEFAULT;
		pShareDeviceList[i].ProcessID = i;
	}

	// UNLOCK
	LockObject(hndlShareDeviceListSync, FALSE);
	debug_func_end(DIO_SEVERITY_HIGH, "success = 1");
	return TRUE;
}

void PrintShareDeviceList(_In_ BOOL all)
{
#ifndef DIO_NDEBUG
	int i;
	BOOL locked = TRUE;

	if (pShareDeviceList == NULL) return;

	debug_func_begin(DIO_SEVERITY_CRITICAL, "Device Share Memory List dump:");

	// LOCK
	if (!LockObject(hndlShareDeviceListSync, TRUE)) {
		debug_warning(DIO_SEVERITY_CRITICAL, "Can not get exclusive access to Share Device List.");
		locked = FALSE;	// We need this dump, can not return now
	}
	for (i = 0; i < DIO_MAX_DEVICES_SUPPORT; i++) {
		if (!all && (pShareDeviceList[i].ProcessID == 0)) continue;		// Skip empty devices
		debug_printf(DIO_SEVERITY_CRITICAL, "ShareDevicelist[%i]: Process ID - %d, Process name - %s, Device ID - %d\n", 
			i, pShareDeviceList[i].ProcessID, pShareDeviceList[i].ProcessName, pShareDeviceList[i].DeviceID);
	}

	// UNLOCK 
	if (locked) LockObject(hndlShareDeviceListSync, FALSE);
#endif
}


/* ============================================ */
/*		Device info list functions				*/
/* ============================================ */

static BOOL CreateInternalDeviceList(void)
{
	unsigned int deviceCount = 0;

	debug_func_begin(DIO_SEVERITY_HIGH, "");

	// All resources must be free
	if ((hndlDeviceInfoIntListSync != NULL) ||
			(hndlUSBDeviceList != NULL)) {
		debug_error(DIO_SEVERITY_CRITICAL, "Attempt to rewrite unreleased resources of Internal Device List.");
		return FALSE;
	}

	// Get new list of connected USB devices
	if (!LstK_Init(&hndlUSBDeviceList, KLST_FLAG_NONE)) // KLST_FLAG_INCLUDE_DISCONNECT
	{
		debug_error(DIO_SEVERITY_CRITICAL, "Could not get list of USB devices (win32error=%d).", GetLastError());
		goto CleanUp;
	}
	
	// Get count of USB devices
	if (!LstK_Count(hndlUSBDeviceList, &deviceCount)) {
		debug_error(DIO_SEVERITY_CRITICAL, "Could not get count of USB devices (win32error=%d).", GetLastError());
		goto CleanUp;
	}
	
	if (deviceCount == 0) {
		debug_warning(DIO_SEVERITY_CRITICAL, "You must install Digital I/O USB cards first.");
		goto CleanUp;
	}

	// Set dafult values to list
	SetDefaultDeviceInfoList();

	// Create Device Info List lock - Mutex
	hndlDeviceInfoIntListSync = CreateMutex(
				NULL,			// default security
				FALSE,			// initial owner
				NULL);			// local mutex without name

	if (hndlDeviceInfoIntListSync == NULL) {
		debug_error(DIO_SEVERITY_CRITICAL, "Could not create Device Info Internal list Sync Mutex (win32error=%d).", GetLastError());
		goto CleanUp;
	}

	// Create Device Info Internal List
	if (!GenInternalListFromUSBDevices()) {
		debug_error(DIO_SEVERITY_CRITICAL, "No USB devices found. You must install Digital I/O USB cards first.");
		goto CleanUp;
	}

	debug_func_end(DIO_SEVERITY_HIGH, "success = 1");
	return TRUE;

CleanUp:
	FreeShareDeviceList();
	debug_func_end(DIO_SEVERITY_HIGH, "success = 0");
	return FALSE;
}

static BOOL FreeInternalDeviceList(void)
{
	BOOL retValue = TRUE;
	debug_func_begin(DIO_SEVERITY_HIGH, "");

	// Close handle of Device Info Internal List Sync Mutex
	if (hndlDeviceInfoIntListSync != NULL) {
		if (!CloseHandle(hndlDeviceInfoIntListSync)) {
			debug_warning(DIO_SEVERITY_IMPORTANT, "Could not close handle of 'hndlDeviceInfoIntListSync' (win32error=%d).", GetLastError());
			retValue = FALSE;
		}
		hndlDeviceInfoIntListSync = NULL;
	}

	// Close all handles of items in device info internal list
	if (!FreeAllInternalDeviceItems()) {
		debug_warning(DIO_SEVERITY_IMPORTANT, "Could not close some handle in Device Info Interval List.");
			retValue = FALSE;
	}
	
	// Close handle of USB Device list
	if (hndlUSBDeviceList != NULL) {
		if(!LstK_Free(hndlUSBDeviceList)) {
			debug_warning(DIO_SEVERITY_IMPORTANT, "Could not close handle of 'hndlUSBDeviceList' (win32error=%d).", GetLastError());
			retValue = FALSE;
		}
		hndlUSBDeviceList = NULL;
	}

	SetDefaultDeviceInfoList();

	debug_func_end(DIO_SEVERITY_HIGH, "success = %d", retValue);
	return retValue;
}

void PrintDeviceInfoInternalList(_In_ BOOL all)
{
#ifndef DIO_NDEBUG
	int i;
	BOOL locked = TRUE;

	if (pShareDeviceList == NULL) return;

	debug_func_begin(DIO_SEVERITY_CRITICAL, "Device Info Internal List dump:");

	// LOCK
	if (!LockObject(hndlDeviceInfoIntListSync, TRUE)) {
		debug_warning(DIO_SEVERITY_CRITICAL, "Can not get exclusive access to Device Info Internal List.");
		locked = FALSE;	// We need this dump, can not return now
	}
	for (i = 0; i < DIO_MAX_DEVICES_SUPPORT; i++) {
		if (!all && (strlen(DeviceInfoIntList[i].DeviceID) == 0)) continue;		// Skip empty devices
		debug_printf(DIO_SEVERITY_CRITICAL, "DeviceInfoIntList[%i]: Device ID - %d, Module = %d, serial number = %s, proccess ownership = %d, connection state = %d, device state = %d.\n", 
			i, DeviceInfoIntList[i].DeviceID, DeviceInfoIntList[i].DeviceInfo.Module, DeviceInfoIntList[i].DeviceInfo.SerialNumber, 
			DeviceInfoIntList[i].ProcessOwnership, DeviceInfoIntList[i].DeviceConnectionState, DeviceInfoIntList[i].DeviceState);
	}

	// UNLOCK 
	if (locked) LockObject(hndlDeviceInfoIntListSync, FALSE);
#endif
}

static BOOL FreeAllInternalDeviceItems()
{
	BOOL retValue = TRUE;
	int i;

	for (i = 0; i < DIO_MAX_DEVICES_SUPPORT; i++) {
		if (!FreeInternalDeviceItem(i)) retValue = FALSE;
	}

	return retValue;
}

static BOOL FreeInternalDeviceItem(unsigned int index)
{
	BOOL retValue = TRUE;

	if (index >= DIO_MAX_DEVICES_SUPPORT) {
		debug_warning(DIO_SEVERITY_CRITICAL, "Index out of range. Index = %d, devices = %d.", index, DIO_MAX_DEVICES_SUPPORT);
		return FALSE;
	}

	// Close handle of Device Lock - Mutex
	if (DeviceInfoIntList[index].hndlLock != NULL) {
		if (!CloseHandle(DeviceInfoIntList[index].hndlLock)) {
			debug_warning(DIO_SEVERITY_IMPORTANT, "Could not close handle of 'DeviceInfoIntList[%d].hndlLock ' with serial number '%s' (win32error=%d).", 
				index, DeviceInfoIntList[index].DeviceInfo.SerialNumber, GetLastError());
			retValue = FALSE;
		}
		DeviceInfoIntList[index].hndlLock = NULL;
	}

	// Close handle of USB
	if (DeviceInfoIntList[index].hndlUSB != NULL) { 
		if (!DeviceInfoIntList[index].USB_Driver.Free(DeviceInfoIntList[index].hndlUSB)) {
			debug_warning(DIO_SEVERITY_IMPORTANT, "Could not close handle of 'DeviceInfoIntList[%d].hndlUSB' with serial number '%s'  (win32error=%d).", 
				index, DeviceInfoIntList[index].DeviceInfo.SerialNumber, GetLastError());
			retValue = FALSE;
		}
		DeviceInfoIntList[index].hndlUSB = NULL;
	}
	DeviceInfoIntList[index].USBDeviceInfo = NULL;

	// Set default values
	DeviceInfoIntList[index] = DEVICE_INFO_INTERNAL_DEFAULT;

	return retValue;
}


static BOOL GenInternalListFromUSBDevices(void)
{
	KLST_DEVINFO_HANDLE deviceInfo = NULL;
	unsigned int deviceCount = 0;
	int index;
	BOOL devicesAdded = FALSE;

	debug_func_begin(DIO_SEVERITY_HIGH, "");

	// LOCK
	if (!LockObject(hndlDeviceInfoIntListSync, TRUE)) {
		debug_error(DIO_SEVERITY_CRITICAL, "END, Can not get access to Device Info Internal List.");
		return FALSE;
	}

	LstK_MoveReset(hndlUSBDeviceList);		// Reset the device list position.

	// Find all Digital I/O USB cards
	while(LstK_MoveNext(hndlUSBDeviceList, &deviceInfo))	// Call LstK_MoveNext after a LstK_MoveReset to advance to the first element.
	{
		if (IsDigitalIOUSBCard(deviceInfo))
		{
			index = AddUSBDevicesToList(deviceInfo);
			if (index >= 0) {
				devicesAdded = TRUE;
				debug_info(DIO_SEVERITY_CRITICAL, "New Digital I/O USB cards was found (Total %d):\nDeviceID: \"%s\"\n, Serial number \"%s\"", 
						(index + 1), DeviceInfoIntList[index].DeviceID, DeviceInfoIntList[index].DeviceInfo.SerialNumber);
			} else if (index == -1) {
				debug_warning(DIO_SEVERITY_CRITICAL, "Digital I/O USB cards is already added in list:\nDeviceID: \"%s\"\n, Serial number \"%s\"", 
						GetUSBDeviceUniqueID(deviceInfo), deviceInfo->SerialNumber);
			} else {
				debug_error(DIO_SEVERITY_CRITICAL, "Could not parse Digital I/O USB card device:\nDeviceID: \"%s\"\n, Serial number \"%s\"", 
						GetUSBDeviceUniqueID(deviceInfo), deviceInfo->SerialNumber);
			}
		}
	}

	// UNLOCK
	LockObject(hndlDeviceInfoIntListSync, FALSE);

	debug_func_end(DIO_SEVERITY_HIGH, "device added = %d", devicesAdded);
	return devicesAdded;
}

static int AddUSBDevicesToList(_In_ KLST_DEVINFO_HANDLE deviceInfo)
{
	int i, len, retError = 0;
	int emptySlot = -1;
	BOOL devAdded = FALSE;
	char strIndex[3] = "00";
	UINT32 devModule = -1;
	KUSB_DRIVER_API USB_Driver;
	KUSB_HANDLE hndlUSB = NULL;


	debug_func_begin(DIO_SEVERITY_NORMAL, "");

	// LOCK
	if (!LockObject(hndlDeviceInfoIntListSync, TRUE)) {
		debug_error(DIO_SEVERITY_CRITICAL, "END, Can not get access to Device Info Internal List.");
		return -2;
	}

	for(i = 0; i < DIO_MAX_DEVICES_SUPPORT; i++) {
		if (strlen(DeviceInfoIntList[i].DeviceID) == 0) {			// Empty position, add device to list
			emptySlot = i;
			break;
		} else {													// Position is full in the list
			if (IsSameUSBDevice(deviceInfo, DeviceInfoIntList[i])) {// Device is already in the list
				retError = -1;
				goto CleanUp;											
			}
		}
	}

	if (emptySlot == -1) goto CleanUp;		// Not found empty position					

	// Check Serial number length
	len = strlen(deviceInfo->SerialNumber);
	if (len == strlen(DIO_USB_SERIAL_NUMBER_TEMPLATE)) {	
		strIndex[0] = deviceInfo->SerialNumber[len - 2];
		strIndex[1] = deviceInfo->SerialNumber[len - 1];
		if (sscanf(strIndex, "%lu", &devModule) != 1) {		// could not create device ID from serial number
			retError = -2;
			goto CleanUp;
		}
	}

	// Create device lock - Mutex
	DeviceInfoIntList[i].hndlLock = CreateMutex(
				NULL,			// default security
				FALSE,			// initial owner
				NULL);			// local mutex without name

	if (DeviceInfoIntList[i].hndlLock == NULL) {
		debug_error(DIO_SEVERITY_CRITICAL, "END, Could not create Device Info Sync Mutex (win32error=%d).", GetLastError());
		retError = -2;
		goto CleanUp;
	}


	// Load a dynamic driver api for this device.  The dynamic driver api
	// is more versatile because it adds support for winusb.sys devices.
	if (!LibK_LoadDriverAPI(&USB_Driver,deviceInfo->DriverID))
	{
		debug_error(DIO_SEVERITY_CRITICAL, "Could not load USB Driver '%s' API for Module = %d, serial number = %s (win32error=%d).", 
			GetUSBDriverName(deviceInfo), devModule, deviceInfo->SerialNumber, GetLastError());
		retError = -2;
		goto CleanUp;
	}

	// Try to initiate the usb device
	if (!USB_Driver.Init(&hndlUSB, deviceInfo)) {
		debug_error(DIO_SEVERITY_CRITICAL, "Could not initiate USB device (USB_Driver->Init()) for Module = %d, serial number = %s (win32error=%d).", 
				devModule, deviceInfo->SerialNumber, GetLastError());
		retError = -2;
		goto CleanUp;
	}

	// Copy USB device info to our list
	DeviceInfoIntList[i].hndlUSB = hndlUSB;
	DeviceInfoIntList[i].USB_Driver = USB_Driver;
	DeviceInfoIntList[i].USBDeviceInfo = deviceInfo;
	strcpy(DeviceInfoIntList[i].DeviceID, GetUSBDeviceUniqueID(deviceInfo));			
	DeviceInfoIntList[i].DeviceConnectionState = E_DEVICE_CONNECTION_STATE_CONNECTED;
	strcpy(DeviceInfoIntList[i].DeviceInfo.SerialNumber, deviceInfo->SerialNumber);
	DeviceInfoIntList[i].DeviceInfo.Module = devModule;	

CleanUp:

	// UNLOCK
	LockObject(hndlDeviceInfoIntListSync, FALSE);

	debug_func_end(DIO_SEVERITY_NORMAL, "device list index = %d, error = %d", i, retError);

	return (retError == 0) ? emptySlot : retError;
}

static void SetDefaultDeviceInfoList(void)
{
	int i;
	for (i = 0; i < DIO_MAX_DEVICES_SUPPORT; i++) {
		 DeviceInfoIntList[i] = DEVICE_INFO_INTERNAL_DEFAULT;
	}
}

int FindDeviceInfoByModule(_In_ UINT32 module, _Out_ DeviceInfoInternal_t ** pDest)
{
	BOOL found = FALSE;
	int i;

	debug_func_begin(DIO_SEVERITY_LOW, "module = %d", module);

	// LOCK
	if (!LockObject(hndlDeviceInfoIntListSync, TRUE)) {
		debug_error(DIO_SEVERITY_CRITICAL, "END, Can not get access to Device Info Internal List.");
		return -2;
	}

	for(i = 0; i < DIO_MAX_DEVICES_SUPPORT; i++) {
		if (strlen(DeviceInfoIntList[i].DeviceID) == 0) break;							// end of list
		if (DeviceInfoIntList[i].DeviceInfo.Module == module) {
			if (pDest != NULL) * pDest = &(DeviceInfoIntList[i]);
			found = TRUE;
			break;
		} 
	}

	// Unlock
	LockObject(hndlDeviceInfoIntListSync, FALSE);

	debug_func_end(DIO_SEVERITY_LOW, "found = %d, device list index = %d", found, i);
	return found ? i : -1;
}

static int FindDeviceInfoByUSBDevice(_In_ KLST_DEVINFO_HANDLE deviceInfo, _Out_ DeviceInfoInternal_t ** pDest)
{
	BOOL found = FALSE;
	int i;

	debug_func_begin(DIO_SEVERITY_LOW, "Device serial number = %s", deviceInfo->SerialNumber);

	// LOCK
	if (!LockObject(hndlDeviceInfoIntListSync, TRUE)) {
		debug_error(DIO_SEVERITY_CRITICAL, "END, Can not get access to Device Info Internal List.");
		return -2;
	}

	for(i = 0; i < DIO_MAX_DEVICES_SUPPORT; i++) {
		if (strlen(DeviceInfoIntList[i].DeviceID) == 0) break;							// end of list
		if (IsSameUSBDevice(deviceInfo, DeviceInfoIntList[i])) {
			if (pDest != NULL) * pDest = &(DeviceInfoIntList[i]);
			found = TRUE;
			break;
		} 
	}

	// Unlock
	LockObject(hndlDeviceInfoIntListSync, FALSE);

	debug_func_end(DIO_SEVERITY_LOW, "found = %d, device list index = %d", found, i);
	return found ? i : -1;
}

UINT32 GetDeviceInfoListCount(void)
{
	int i;
	UINT32 devCount = 0;

	// LOCK
	if (!LockObject(hndlDeviceInfoIntListSync, TRUE)) {
		debug_error(DIO_SEVERITY_CRITICAL, "END, Can not get access to Device Info Internal List.");
		return 0;
	}

	for(i = 0; i < DIO_MAX_DEVICES_SUPPORT; i++) {
		if (strlen(DeviceInfoIntList[i].DeviceID) == 0) break;	// end of list
		devCount++;
	}

	// Unlock
	LockObject(hndlDeviceInfoIntListSync, FALSE);

	return devCount;
}

static BOOL IsSameUSBDevice(_In_ KLST_DEVINFO_HANDLE deviceInfo, _In_ DeviceInfoInternal_t deviceInfoInternal)
{
	return strcmp(deviceInfoInternal.DeviceID, GetUSBDeviceUniqueID(deviceInfo)) == 0;
}

