//////////////////////////////////////////////////////////////////
//
// App:			Communication
// Author:		Milan Korinek
// Version:		1.0
// Notice:		Function for communication with Digital I/O USB card
//////////////////////////////////////////////////////////////////

#include "stdafx.h"

#include "Communication.h"
#include "support.h"

//==========================================================================

DIO_Errors_t SetDeviceConfiguration(_In_ DeviceInfoInternal_t * deviceInfo, _In_ DIO_Behavior_t * DeviceBehavior, _In_ DIO_PortConfig_t PortsConfiguration[DIO_MAX_PORTS])
{
	DIO_Errors_t retValue = DIO_ERROR_SUCCESS;
	UINT8 payload[EP_CONTROL_BUFFER_SIZE];
	UINT payload_length, transferred = 0;

	debug_func_begin(DIO_SEVERITY_HIGH, "Device module = %u, serial number = %s.", deviceInfo->DeviceInfo.Module, deviceInfo->DeviceInfo.SerialNumber);

	// Create configuration packet
	if (!CreateDeviceConfigurationPacket(E_DEVICE_STATE_IGNORE, DeviceBehavior, PortsConfiguration, payload, &payload_length)) {
		debug_error(DIO_SEVERITY_CRITICAL, "Can not create Device Configuration packet.");
		return DIO_ERROR_FAIL_CORRUPTED;
	}

	// Send configuration packet
	retValue = USBControlEndpoint(TRUE, deviceInfo, (USHORT)E_PACKET_TYPE_BASIC_CONFIGURE, 0, payload_length, payload, &transferred);
	if (retValue != DIO_ERROR_SUCCESS) {
		debug_error(DIO_SEVERITY_CRITICAL, "Could not set configure device module = %u, serial number = %s.",
			deviceInfo->DeviceInfo.Module, deviceInfo->DeviceInfo.SerialNumber);
		PrintRawPacket(payload, payload_length);
	} else if (payload_length != transferred) {
		debug_error(DIO_SEVERITY_CRITICAL, "Transferred lenght (%u) is different from packet length (%u) for module = %u, serial number = %s.",
			transferred, payload_length, deviceInfo->DeviceInfo.Module, deviceInfo->DeviceInfo.SerialNumber);
		retValue = DIO_ERROR_FAIL_SEND;
	}

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

	return retValue;
}

DIO_Errors_t GetDeviceConfiguration(_In_ DeviceInfoInternal_t * deviceInfo, _Out_ E_DeviceState_t * DeviceState, _Out_ DIO_Behavior_t * DeviceBehavior, _Out_ DIO_PortConfig_t PortsConfiguration[DIO_MAX_PORTS])
{
	DIO_Errors_t retValue = DIO_ERROR_SUCCESS;
	UINT8 payload[EP_CONTROL_BUFFER_SIZE];
	UINT payload_length = EP_CONTROL_BUFFER_SIZE; // only size of buffer
	UINT transferred = 0;

	// Receive configuration packet
	retValue = USBControlEndpoint(FALSE, deviceInfo, (USHORT)E_PACKET_TYPE_BASIC_CONFIGURE, 0, payload_length, payload, &transferred);

	// Check return value
	if (retValue == DIO_ERROR_SUCCESS) {																	// Receive packet success
		// Try to parse packet
		if (ParseDeviceConfigurationPacket(payload, payload_length, DeviceState, DeviceBehavior, PortsConfiguration)) {	// Parsing success
			// Receive length of packet must be same as parsed data
			if (transferred != payload_length) {															// Length not match
				debug_error(DIO_SEVERITY_CRITICAL, "Receive length of packet (%u) is not same as parsed data (%u) for device module = %u, serial number = %s.",
					transferred, payload_length, deviceInfo->DeviceInfo.Module, deviceInfo->DeviceInfo.SerialNumber);
				PrintRawPacket(payload, payload_length);
				retValue = DIO_ERROR_FAIL_CORRUPTED;
			}
		} else {																							// Parsing fail
			debug_error(DIO_SEVERITY_CRITICAL, "Could not parse configuration from device module = %u, serial number = %s.",
				deviceInfo->DeviceInfo.Module, deviceInfo->DeviceInfo.SerialNumber);
			PrintRawPacket(payload, payload_length);
			retValue = DIO_ERROR_FAIL_CORRUPTED;
		}
	} else {																								// Receive packet fail
		debug_error(DIO_SEVERITY_CRITICAL, "Could not get configuration from device module = %u, serial number = %s.",
			deviceInfo->DeviceInfo.Module, deviceInfo->DeviceInfo.SerialNumber);
		retValue = DIO_ERROR_FAIL_SEND;
	}

	return retValue;
}

DIO_Errors_t SetDeviceOutputs(_In_ DeviceInfoInternal_t * deviceInfo, _In_ UINT32 ports, _In_ UINT32 offset, _In_ UINT8 * Data)
{
	DIO_Errors_t retValue = DIO_ERROR_SUCCESS;
	UINT8 payload[EP_INTERRUPT_IMMEDIATE_RW_BUFFER_SIZE];
	UINT transferred = 0;
	BOOL ret;

	if ((ports != 0) && ((ports + offset) > DIO_MAX_PORTS)) {		// Unknown request 
		return DIO_ERROR_FAIL_UNKNOWN_REQUEST;
	} else if ((ports == 0) && (offset >= DIO_MAX_CHANNELS)) {
		return DIO_ERROR_FAIL_UNKNOWN_REQUEST;
	}
	// Set buffer to zero
	memset(payload, 0, sizeof(payload));

	// Create packet
	payload[0] = (UINT8) E_PACKET_TYPE_IMMEDIATE_WRITE;
	payload[1] = (UINT8) ports;								// count of ports
	payload[2] = (UINT8) offset;							// for ports == 0 -> channel index
	memcpy(&payload[3], Data, (ports != 0) ? ports : 1);	// for ports == 0 -> in Data is channel settings True/False

	// Send packet
	ret = USBInterruptEndpoint(TRUE, deviceInfo, EP_INTERRUPT_IMMEDIATE_RW_BUFFER_SIZE, payload, &transferred);

	// Check results
	if (!ret) {
		debug_error(DIO_SEVERITY_CRITICAL, "Could not send data to device module = %u, serial number = %s.",
			deviceInfo->DeviceInfo.Module, deviceInfo->DeviceInfo.SerialNumber);
		PrintRawPacket(payload, EP_INTERRUPT_IMMEDIATE_RW_BUFFER_SIZE);
		retValue = DIO_ERROR_FAIL_SEND;
	} else if (EP_INTERRUPT_IMMEDIATE_RW_BUFFER_SIZE != transferred) {
		debug_error(DIO_SEVERITY_CRITICAL, "Transferred lenght (%u) is different from packet length (%u) for module = %u, serial number = %s.",
			transferred, EP_INTERRUPT_IMMEDIATE_RW_BUFFER_SIZE, deviceInfo->DeviceInfo.Module, deviceInfo->DeviceInfo.SerialNumber);
		retValue = DIO_ERROR_FAIL_SEND;
	}

	return retValue;
}

DIO_Errors_t DeviceImmediateRW(BOOL write, _In_ DeviceInfoInternal_t * deviceInfo, _In_ UINT32 ports, _In_ UINT32 offset, _Out_ UINT8 * Data)
{
	DIO_Errors_t retValue = DIO_ERROR_SUCCESS;
	UINT8 payload[EP_INTERRUPT_IMMEDIATE_RW_BUFFER_SIZE];
	UINT transferred = 0;
	BOOL ret;

	if ((ports != 0) && ((ports + offset) > DIO_MAX_PORTS)) {		// Unknown request 
		return DIO_ERROR_FAIL_UNKNOWN_REQUEST;
	} else if ((ports == 0) && (offset >= DIO_MAX_CHANNELS)) {
		return DIO_ERROR_FAIL_UNKNOWN_REQUEST;
	}
	// Set buffer to zero
	memset(payload, 0, sizeof(payload));

	// Create packet
	payload[0] = (UINT8) (write ? E_PACKET_TYPE_IMMEDIATE_WRITE : E_PACKET_TYPE_IMMEDIATE_READ);
	payload[1] = (UINT8) ports;								// count of ports
	payload[2] = (UINT8) offset;							// for ports == 0 -> channel index
	if (write) memcpy(&payload[3], Data, (ports != 0) ? ports : 1);	// for ports == 0 -> in Data is channel settings True/False

	// Send request packet
	ret = USBInterruptEndpoint(TRUE, deviceInfo, EP_INTERRUPT_IMMEDIATE_RW_BUFFER_SIZE, payload, &transferred);

	// Check results
	if (!ret) {
		debug_error(DIO_SEVERITY_CRITICAL, "Could not send request to read device module = %u, serial number = %s.",
			deviceInfo->DeviceInfo.Module, deviceInfo->DeviceInfo.SerialNumber);
		PrintRawPacket(payload, EP_INTERRUPT_IMMEDIATE_RW_BUFFER_SIZE);
		retValue = DIO_ERROR_FAIL_SEND;
	} else if (EP_INTERRUPT_IMMEDIATE_RW_BUFFER_SIZE != transferred) {
		debug_error(DIO_SEVERITY_CRITICAL, "Transferred lenght (%u) is different from packet length (%u) for module = %u, serial number = %s.",
			transferred, EP_INTERRUPT_IMMEDIATE_RW_BUFFER_SIZE, deviceInfo->DeviceInfo.Module, deviceInfo->DeviceInfo.SerialNumber);
		retValue = DIO_ERROR_FAIL_SEND;
	}

	if (write) return retValue;
	// Reading section ----------------

	// Set buffer to zero
	memset(payload, 0, sizeof(payload));

	// Read data
	ret = USBInterruptEndpoint(FALSE, deviceInfo, EP_INTERRUPT_IMMEDIATE_RW_BUFFER_SIZE, payload, &transferred);

	// Check results
	if (!ret) {
		debug_error(DIO_SEVERITY_CRITICAL, "Could not get data from device module = %u, serial number = %s.",
			deviceInfo->DeviceInfo.Module, deviceInfo->DeviceInfo.SerialNumber);
		PrintRawPacket(payload, EP_INTERRUPT_IMMEDIATE_RW_BUFFER_SIZE);
		retValue = DIO_ERROR_FAIL_RECEIVE;
	} else if (EP_INTERRUPT_IMMEDIATE_RW_BUFFER_SIZE != transferred) {
		debug_error(DIO_SEVERITY_CRITICAL, "Transferred lenght (%u) is different from packet length (%u) for module = %u, serial number = %s.",
			transferred, EP_INTERRUPT_IMMEDIATE_RW_BUFFER_SIZE, deviceInfo->DeviceInfo.Module, deviceInfo->DeviceInfo.SerialNumber);
		retValue = DIO_ERROR_FAIL_RECEIVE;
	}

	// Get data
	memcpy(Data, &payload[3], (ports != 0) ? ports : 1);

	return retValue;
}

void PrintRawPacket(_In_ UINT8 * payload, _In_ UINT payload_length)
{
#ifndef DIO_NDEBUG
	UINT i;

	debug_func_begin(DIO_SEVERITY_CRITICAL, "Packet dump (lenght = %d):\n", payload_length);
	for (i = 0; i < payload_length; i++) {
		debug_printf(DIO_SEVERITY_CRITICAL, "0x%02X ", (unsigned int)payload[i]);
	}
	debug_printf(DIO_SEVERITY_CRITICAL, "\n");

#endif
}

