//////////////////////////////////////////////////////////////////
//
// App:			Suppor functions
// Author:		Milan Korinek
// Version:		1.0
// Notice:		Logging and several auxiliary functions	
//////////////////////////////////////////////////////////////////


#include "stdafx.h"
#include "support.h"


/* ============================================ */
/*			   	Auxiliary functions			    */
/* ============================================ */

char * GetUSBDeviceUniqueID(_In_ KLST_DEVINFO_HANDLE USBDeviceInfo)
{
	return USBDeviceInfo->DeviceInterfaceGUID;
}

BOOL IsDigitalIOUSBCard(_In_ KLST_DEVINFO_HANDLE USBDeviceInfo)
{
	// VID, PID and product name must correspond to Digital I/O USB cards settings.
	return (USBDeviceInfo->Common.Vid == DIO_USB_DEVICE_VID &&
		        USBDeviceInfo->Common.Pid == DIO_USB_DEVICE_PID &&
					strcmp(USBDeviceInfo->DeviceDesc, DIO_USB_DEVICE_NAME) == 0);
}

BOOL LockObject(_In_ HANDLE hndlSyncObject, _In_ BOOL lock)
{
	UINT32 retStatus;
	BOOL retValue = FALSE;

	if (hndlSyncObject == NULL) {
		debug_func_end(DIO_SEVERITY_HIGH, "Handle is NULL.");
		return FALSE;
	}

	if (lock){		// Lock Shared Device List
		retStatus = WaitForSingleObject(hndlSyncObject, MAX_SYNC_TIMEOUT);
	
		switch (retStatus) {
		case WAIT_ABANDONED:
			debug_warning(DIO_SEVERITY_CRITICAL, "WaitForSingleObject(): Abandoned.");
			break;
		case WAIT_OBJECT_0:
			retValue = TRUE;
			break;
		case WAIT_TIMEOUT:
			debug_error(DIO_SEVERITY_CRITICAL, "WaitForSingleObject(): Timeout.");
			break;
		case WAIT_FAILED: 
			debug_error(DIO_SEVERITY_CRITICAL, "WaitForSingleObject(): Failed (win32error=%d).", GetLastError());
			break;
		default:
			debug_error(DIO_SEVERITY_CRITICAL, "WaitForSingleObject(): Unknown state of WaitForSingleObject() - returned: %d.", retStatus);
			break;
		}
	} else {		// Unlock Shared Device List
		retValue = ReleaseMutex(hndlSyncObject);	// Function fails if the calling thread does not own the mutex object.
		if (!retValue) debug_warning(DIO_SEVERITY_CRITICAL, "ReleaseMutex(): Failed (win32error=%d).", GetLastError());
	}

	debug_info(DIO_SEVERITY_NORMAL, "%s - %s",  lock ? "Lock" : "Unlock", retValue ? "Success" : "Fail");

	return retValue;
}

BOOL ConfigureUSBDeviceInterface(_In_ BOOL enable, _In_ DeviceInfoInternal_t * deviceInfo)
{
	if (enable) {	// Enable USB device interface
		// Not neccessary
		if (!deviceInfo->USB_Driver.SetConfiguration(deviceInfo->hndlUSB, 1))
		{
			debug_error(DIO_SEVERITY_CRITICAL, "Could not enable USB device interface. UsbK_SelectInterface failed (win32error=%d).",  GetLastError());
			return FALSE;
		}

		// Blocking USB devices before access from different app.
		if (!deviceInfo->USB_Driver.ClaimInterface(deviceInfo->hndlUSB, 0, FALSE))
		{
			debug_error(DIO_SEVERITY_CRITICAL, "Could not enable USB device interface. UsbK_ClaimInterface failed (win32error=%d).",  GetLastError());
			return FALSE;
		}

		// Calling this function, will be enabled USB interface in Digital I/O USB Card (UDI_VENDOR_ENABLE_EXT()).
		if (!deviceInfo->USB_Driver.SetAltInterface(deviceInfo->hndlUSB, 0, FALSE, 1))
		{
			debug_error(DIO_SEVERITY_CRITICAL, "Could not enable USB device interface. UsbK_SetCurrentAlternateSetting failed (win32error=%d).",  GetLastError());
			return FALSE;
		}
	} else {	// Disable USB device interface
		// Calling this function, will be disabled USB interface in Digital I/O USB Card (UDI_VENDOR_DISABLE_EXT()).
		if (!deviceInfo->USB_Driver.SetAltInterface(deviceInfo->hndlUSB, 0, FALSE, 0))
		{
			debug_error(DIO_SEVERITY_CRITICAL, "Could not disable USB device interface. UsbK_SetCurrentAlternateSetting failed (win32error=%d).",  GetLastError());
			return FALSE;
		}

		// Unblock USB devices for access from different app.
		if (!deviceInfo->USB_Driver.ReleaseInterface(deviceInfo->hndlUSB, 0, FALSE))
		{
			debug_error(DIO_SEVERITY_CRITICAL, "Could not disable USB device interface. UsbK_ReleaseInterface failed (win32error=%d).",  GetLastError());
			return FALSE;
		}
	}
	return TRUE;
}

DIO_Errors_t USBControlEndpoint(_In_ BOOL write, _In_ DeviceInfoInternal_t * deviceInfo, _In_ USHORT value, _In_ USHORT index, _In_ UINT payloadLength, _Inout_ UCHAR * payload, _Out_ UINT * transferred)
{
	DIO_Errors_t retValue = DIO_ERROR_SUCCESS;
	WINUSB_SETUP_PACKET winPkt;
	KUSB_SETUP_PACKET* defPkt = (KUSB_SETUP_PACKET*)&winPkt;
	UINT transferredInternal = 0;

	debug_func_begin(DIO_SEVERITY_CRITICAL, "%s device, value = %u, index = %u, buffer length = %u.",
		write ? "Write data to" : "Read data from", value, index, payloadLength);

	// Set control packet
	memset(&winPkt, 0, sizeof(winPkt));
	defPkt->BmRequest.Dir		= write ? BMREQUEST_DIR_HOST_TO_DEVICE : BMREQUEST_DIR_DEVICE_TO_HOST;
	defPkt->BmRequest.Type		= BMREQUEST_TYPE_VENDOR;
	defPkt->BmRequest.Recipient = BMREQUEST_RECIPIENT_ENDPOINT;
	defPkt->Request				= 0;	// Must be 0 for user defined packet
	defPkt->Value				= value;
	defPkt->Index				= index;
	defPkt->Length				= payloadLength;

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

	// Check device status
	if ((deviceInfo->ProcessOwnership != E_PROCESS_OWNERSHIP_OWNER) ||								// Current proccess must be owner of device
			(deviceInfo->DeviceConnectionState < E_DEVICE_CONNECTION_STATE_WAS_DISCONNECTED ) ||	// Device must be connected
				(deviceInfo->DeviceState == E_DEVICE_STATE_CLOSE)) { 								// Device must opened

		debug_warning(DIO_SEVERITY_CRITICAL, "Device is not ready. Module = %d, serial number = %s, proccess ownership = %d, device connection state = %d, device state = %d.", 
			deviceInfo->DeviceInfo.Module, deviceInfo->DeviceInfo.SerialNumber, deviceInfo->ProcessOwnership, deviceInfo->DeviceConnectionState, deviceInfo->DeviceState);
		retValue = DIO_ERROR_FAIL_NOT_INIT;
		goto CleanUp;
	}

	if (deviceInfo->USB_Driver.ControlTransfer(deviceInfo->hndlUSB, winPkt, payload, payloadLength, &transferredInternal, NULL)) {
		debug_func_end(DIO_SEVERITY_CRITICAL, "Transfer %s, transferred bytes %u.", retValue ? "SUCCESS" : "FAILED", transferredInternal);
	} else {
		debug_func_end(DIO_SEVERITY_CRITICAL, "Transfer FAILED , Transferred bytes %u (win32error=%lu).", transferredInternal, GetLastError());
		retValue = DIO_ERROR_FAIL_LIBUSBK;
	}

CleanUp:

	// UNLOCK - Device Info Structure
	LockObject(deviceInfo->hndlLock, FALSE);

	*transferred = transferredInternal;
	
	return retValue;
}

BOOL USBInterruptEndpoint(_In_ BOOL write, _In_ DeviceInfoInternal_t * deviceInfo,  _In_ UINT payloadLength, _Inout_ UCHAR * payload, _Out_ UINT * transferred)
{
	BOOL retValue;

	if (write) {
		retValue = deviceInfo->USB_Driver.WritePipe(deviceInfo->hndlUSB, DIO_EP_INTERRUPT_OUT, payload, payloadLength, transferred, NULL);
	} else {
		retValue = deviceInfo->USB_Driver.ReadPipe(deviceInfo->hndlUSB, DIO_EP_INTERRUPT_IN, payload, payloadLength, transferred, NULL);
	}
	
	if (!retValue) {
		debug_error(DIO_SEVERITY_CRITICAL, "Could not %s interrupt endpoint (win32error=%lu).", write ? "write to" : "read from", GetLastError());
	}

	return retValue;
}

BOOL USBBulkEndpoint(_In_ BOOL write, _In_ DeviceInfoInternal_t * deviceInfo,  _In_ UINT payloadLength, _Inout_ UCHAR * payload, _Out_ UINT * transferred)
{
	BOOL retValue;

	if (write) {
		retValue = deviceInfo->USB_Driver.WritePipe(deviceInfo->hndlUSB, DIO_EP_BULK_OUT, payload, payloadLength, transferred, NULL);
	} else {
		retValue = deviceInfo->USB_Driver.ReadPipe(deviceInfo->hndlUSB, DIO_EP_BULK_IN, payload, payloadLength, transferred, NULL);
	}
	
	if (!retValue) {
		debug_error(DIO_SEVERITY_CRITICAL, "Could not %s bulk endpoint (win32error=%lu).", write ? "write to" : "read from", GetLastError());
	}

	return retValue;
}

/* ============================================ */
/*	Shared Functions with Digital I/O USB card  */
/* ============================================ */

/* Serialize and Parsing function of configuration */

void Parse_Copy(_In_ UINT8 * source, _Inout_ UINT * sourcePos, _Out_ void * dest, _In_ size_t size)
{
	memcpy(dest, &source[*sourcePos], size);
	*sourcePos += size;
}

void Serialize_Copy(_In_ void * source, _In_ size_t size, _Out_ UINT8 * dest, _Inout_ UINT * destPos)
{
	memcpy(&dest[*destPos], source, size);
	*destPos += size;
}


void Parse_DIO_Behavior_t(_In_ UINT8 * buffer, _Inout_ UINT * buffPos, _Out_ DIO_Behavior_t * structure)
{
	structure->Mode = to_enum(buffer[(*buffPos)++], DIO_E_Mode_t);
}

void Serialize_DIO_Behavior_t(_In_ DIO_Behavior_t * structure, _Out_ UINT8 * buffer, _Inout_ UINT * buffPos)
{
	buffer[(*buffPos)++] = (UINT8)structure->Mode;
}

void Parse_DIO_PortConfig_t(_In_ UINT8 * buffer, _Inout_ UINT * buffPos, _Out_ DIO_PortConfig_t * structure)
{
	Parse_Copy(buffer, buffPos, &structure->OutputDefault, sizeof(structure->OutputDefault));
	structure->PortDirection = to_enum(buffer[(*buffPos)++], DIO_E_PortDir_t);
	structure->PullResistors = to_enum(buffer[(*buffPos)++], DIO_E_PullRes_t);
}

void Serialize_DIO_PortConfig_t(_In_ DIO_PortConfig_t * structure, _Out_ UINT8 * buffer, _Inout_ UINT * buffPos)
{
	Serialize_Copy(&structure->OutputDefault, sizeof(structure->OutputDefault), buffer, buffPos);
	buffer[(*buffPos)++] = (UINT8)structure->PortDirection;
	buffer[(*buffPos)++] = (UINT8)structure->PullResistors;
}


BOOL ParseDeviceConfigurationPacket(_In_ UINT8 * payload,
_In_ UINT payload_length,
_Out_ E_DeviceState_t * DeviceState,
_Out_ DIO_Behavior_t * DeviceBehavior,
_Out_ DIO_PortConfig_t PortsConfiguration[DIO_MAX_PORTS])
{
	UINT buffPos = 0;
	int i;

	*DeviceState = to_enum(payload[buffPos++], E_DeviceState_t); // Only host read
	Parse_DIO_Behavior_t(payload, &buffPos, DeviceBehavior);
	
	for (i = 0; i < DIO_MAX_PORTS; i++) {
		Parse_DIO_PortConfig_t(payload, &buffPos, &PortsConfiguration[i]);
	}

	return TRUE;
}

BOOL CreateDeviceConfigurationPacket(		_In_ E_DeviceState_t DeviceState,
_In_ DIO_Behavior_t * DeviceBehavior,
_In_ DIO_PortConfig_t PortsConfiguration[DIO_MAX_PORTS],
_Out_ UINT8 * payload,
_Out_ UINT * payload_length)
{
	int i;
	*payload_length = 0;
	payload[(*payload_length)++] = (UINT8)DeviceState;		// From device to host only
	Serialize_DIO_Behavior_t(DeviceBehavior, payload, payload_length);

	for (i = 0; i < DIO_MAX_PORTS; i++) {
		Serialize_DIO_PortConfig_t(&PortsConfiguration[i], payload, payload_length);
	}

	return TRUE;
}