//////////////////////////////////////////////////////////////////
//
// App:			Digital I/O USB Card Library
// Author:		Milan Korinek
// Version:		1.0
// Notice:		API interface of Digital I/O USB Card Library
//////////////////////////////////////////////////////////////////


#ifndef __DIGITAL_IO_USB_CARD_DRIVER_H__
#define __DIGITAL_IO_USB_CARD_DRIVER_H__

#include <sal.h>

/* ============================================ */
/*			     	   Macros			        */
/* ============================================ */

#ifdef __cplusplus
#	define NOMANGLE extern "C"
#else
#	define NOMANGLE
#endif

#ifdef WIN32
#	ifdef COMPILING_DLL
#		define EXPORT NOMANGLE __declspec(dllexport)
#	else
#		define EXPORT NOMANGLE __declspec(dllimport)
#	endif
#else
#	define EXPORT NOMANGLE
#endif

#ifdef __cplusplus
#	define to_enum(value, type) (static_cast<type>(value))
#else
#	define to_enum(value, type)((int)value)
#endif

/* ============================================ */
/*				Constants and enums				*/
/* ============================================ */

//! Product name of Digital I/O USB card
#define DIO_USB_DEVICE_NAME				"Digital I/O USB card"

//! Serial number template: 'DIO' - constant, 'L' - Model, 'C' - number of channels, 'Y' - year, 'M' - month, 'x' - ID 
#define DIO_USB_SERIAL_NUMBER_TEMPLATE	"DIOLLLCCCCYYMMxxxxxxxx"

//! Vendor ID of Atmel
#define DIO_USB_DEVICE_VID				0x03EB

//! Product ID of Atmel ASF Vendor Class
#define DIO_USB_DEVICE_PID				0x2423

//! Endpoint Interrupt In address
#define DIO_EP_INTERRUPT_IN				0x83

//! Endpoint Interrupt Out address
#define DIO_EP_INTERRUPT_OUT			0x06

//! Endpoint Bulk In address
#define DIO_EP_BULK_IN					0x81

//! Endpoint Bulk Out address
#define DIO_EP_BULK_OUT					0x02

//!  Allocated length for all strings used in this API
#define DIO_STRING_MAX_LEN				256

//!  Allocated length for label
#define DIO_LABEL_MAX_LEN				10

//! The maximum number of supported devices
#define DIO_MAX_DEVICES_SUPPORT			32

//! The maximum number of device ports (one port == eight channels)
#define DIO_MAX_PORTS					4

//! Each port has constant number of channels
#define DIO_CHANNELS_ON_PORT			8

//! The maximum number of device channels
#define DIO_MAX_CHANNELS				32


//! Digital I/O USB card return errors

//! Error bases
#define DIO_ERRORS_BASE_SUCCESS			0
#define DIO_ERRORS_BASE_SUCCESS_LAST	99

#define DIO_ERRORS_BASE_FAIL			100
#define DIO_ERRORS_BASE_FAIL_LAST		199

//! Errors definition
typedef enum _DIO_Errors {
	//! Operation is successfully.
	DIO_ERROR_SUCCESS					= DIO_ERRORS_BASE_SUCCESS,
	//! Object is already initiated.
	DIO_ERROR_ALREADY_INIT,
	//! Object is already used.
	DIO_ERROR_ALREADY_USED,
	//! Object already exists.
	DIO_ERROR_ALREADY_EXIST,
	//! Object is already freed.
	DIO_ERROR_ALREADY_FREE,
	
	//! Memory is full, can not create new object.
	DIO_ERROR_FAIL_FULL					= DIO_ERRORS_BASE_FAIL,
	//! Memory is empty/no object available
	DIO_ERROR_FAIL_EMPTY,
	//! Object initialization failed.
	DIO_ERROR_FAIL_INIT,
	//! Can not free object.
	DIO_ERROR_FAIL_FREE,
	//! Can not create new object.
	DIO_ERROR_FAIL_CREATE,
	//! Reuested object not exists.
	DIO_ERROR_FAIL_NOT_EXISTS,
	//! Object is not initialized. You must initialize object first.
	DIO_ERROR_FAIL_NOT_INIT,
	//! Device is disconnected. Can not perform requested operation.
	DIO_ERROR_FAIL_DISCONNECTED,
	//! Object is corrupted. Unknown internal state. You can try free object.
	DIO_ERROR_FAIL_CORRUPTED,
	//! Internal share memory is corrupted.
	DIO_ERROR_FAIL_SHARE_MEM,
	//! Some function failed from libusbK API.
	DIO_ERROR_FAIL_LIBUSBK,
	//! Can not manipulate with object because object is used by other process
	DIO_ERROR_FAIL_USED,
	//! Unknown request - bad parameter.
	DIO_ERROR_FAIL_UNKNOWN_REQUEST,
	//! Could not send data to Digital I/O USB card
	DIO_ERROR_FAIL_SEND,
	//! Could not send data to Digital I/O USB card
	DIO_ERROR_FAIL_RECEIVE,
	//! Unknown error
	DIO_ERROR_FAIL_UNKNOWN,

} DIO_Errors_t;

//! Mode of Digital I/O USB card
typedef enum _DIO_E_Mode {

	//! Same behavior as USB-DIO-32 - ACCES I/O - Question-answer mode
	DIO_E_MODE_ACCES_DIO		= 0,

	//! Digital I/O USB card will be process program from card memory
	DIO_E_MODE_SCRIPTER			= 1,

} DIO_E_Mode_t;


//! Port direction
typedef enum _DIO_E_PortDir {

	//! Set port as input
	DIO_E_PORTDIR_INPUT			= 0,

	//! Set port as output
	DIO_E_PORTDIR_OUTPUT		= 1,

	//! Set port to high impedance. It is not supported by Digital I/O USB card V1.1. Port will be set as input
	DIO_E_PORTDIR_HIGH_Z		= 2,

} DIO_E_PortDir_t;

//! Port pull resistors
typedef enum _DIO_E_PullRes {

	//! Set pull-down resistors
	DIO_E_PULLRES_PULLDOWN		= 0,

	//! Set pull-up resistors
	DIO_E_PULLRES_PULLUP		= 1,

	//! Disconnect pull resistors
	DIO_E_PULLRES_HIGH_Z		= 2,

} DIO_E_PullRes_t;

//! Testing script commands
typedef enum _DIO_E_Command {
	
	//! No operation, can be use for alignment (instead of comments)
	DIO_E_COMMAND_NOP			= 0,

	//! Cycle-Loop command, start point. Arg1 = Cycle label, Arg2 = Number of repetitions.
	DIO_E_COMMAND_CYCLE			= 1,

	//! GoTo command, start point. Arg1 = Goto label.
	DIO_E_COMMAND_GOTO			= 2,

	//! GoTo command, target point. Arg1 = Goto label.
	DIO_E_COMMAND_LABEL			= 3,

} DIO_E_Command_t;

//! Result status of script line or whole script
typedef enum _DIO_E_ScriptResult {

	//! Unknown result
	DIO_E_RESULT_UNKNOWN		= 0,

	//! Operation is in progress
	DIO_E_RESULT_IN_PROGRESS	= 1,

	//! Operation completed successfully
	DIO_E_RESULT_SUCCESS		= 2,

	//! Error, timout
	DIO_E_RESULT_ERR_TIMEOUT	= 3,

	//! Error, desired pattern does not correspond
	DIO_E_RESULT_ERR_NOT_MATCH	= 4,
} DIO_E_ScriptResult_t;

//! Packet types
typedef enum _Packet_Types{

	//! Control endpoint: port direction, pull-up/pull-down resistors settings
	E_PACKET_TYPE_BASIC_CONFIGURE = 0,

	//! Interrupt endpoint: Relevant for mode Access DIO. Data will be 
	// immediate write/read to/from ports (from interrupt).
	E_PACKET_TYPE_IMMEDIATE_READ,

	//! Interrupt endpoint: Relevant for mode Access DIO. Data will be 
	// immediate write/read to/from ports (from interrupt).
	E_PACKET_TYPE_IMMEDIATE_WRITE,

} E_Packet_Types_t;

//! Connection state of Digital I/O USB card
typedef enum _E_DeviceConnectionState {

	//! Device is now disconnected.
	E_DEVICE_CONNECTION_STATE_DISCONNECTED = 0,

	//! Device is now disconnected, but was connected (only for configured devices).
	E_DEVICE_CONNECTION_STATE_WAS_CONNECTED = 1,

	//! Device is now connected, but was disconnected (only for configured devices).
	E_DEVICE_CONNECTION_STATE_WAS_DISCONNECTED = 2,

	//! Device is now connected.
	E_DEVICE_CONNECTION_STATE_CONNECTED = 3,

} E_DeviceConnectionState_t;

//! Device states
typedef enum _E_DeviceState {

	//! Device is closed
	E_DEVICE_STATE_CLOSE = 0,

	//! Device is unconfigured
	E_DEVICE_STATE_UNCONFIGURED,

	//! Device is configured
	E_DEVICE_STATE_CONFIGURED,

	//! Unknown state, device will be ignored
	E_DEVICE_STATE_IGNORE = 255,

} E_DeviceState_t;

/* ============================================ */
/*			     	Structures				    */
/* ============================================ */

//! Basic device information
typedef struct _DIO_DeviceInfo {

	//! USB VID (Vendor ID)
	UINT32 VID;

	//! USB PID (Product ID)
	UINT32 PID;

	//! Device name
	char Name[DIO_STRING_MAX_LEN];

	//! Serial number of device
	char SerialNumber[DIO_STRING_MAX_LEN];

	//! Address of device
	UINT32 Module;

	//! Device version
	char Version[DIO_STRING_MAX_LEN];

	//! Count of ports
	UINT32 Ports;

	//! Count of channels
	UINT32 Channels;

} DIO_DeviceInfo_t;

//! Behavior of Digital I/O USB card
typedef struct _DIO_Behavior {

	//! Mode of Digital I/O USB card
	DIO_E_Mode_t Mode;

} DIO_Behavior_t;

//! Port configuration
typedef struct _DIO_PortConfig {

	//! Default settings of outputs
	UINT8				OutputDefault;

	//! Port direction settings
	DIO_E_PortDir_t		PortDirection;

	//! Port pull resistors settings
	DIO_E_PullRes_t		PullResistors;

} DIO_PortConfig_t;

//! One script line
typedef struct _DIO_ScriptLine {

	//! Array of outputs setting (ports configured as inputs will be ignored)
	UINT8 Outputs[DIO_MAX_PORTS];

	//! Array of mask 0, inputs must stay Low (ports configured as outputs will be ignored)
	UINT8 Mask_Zero[DIO_MAX_PORTS];

	//! Array of mask 1, inputs must stay High
	UINT8 Mask_Ones[DIO_MAX_PORTS];

	//! Array of /, inputs must be changed from Low to High
	UINT8 Mask_Rise[DIO_MAX_PORTS];

	//! Array of \, inputs must be changed from High to Low
	UINT8 Mask_Fall[DIO_MAX_PORTS];

	//! Inputs must not be changed during this time interval
	UINT32 WaitTime;

	//! inputs defined in Mask_Rise and Mask_Fall masks must be changed during this time interval
	UINT32 Timeout;

} DIO_ScriptLine_t;

//! One script command
typedef struct _DIO_ScriptCommand{

	//! Command type
	DIO_E_Command_t Command;

	//! Label, will be converted to number
	char CommandArguments1[DIO_LABEL_MAX_LEN];

	//! Argument for loop, count of repeating
	UINT32 CommandArguments2;

	//! Reserved
	UINT32 CommandArguments3;
} DIO_ScriptCommand_t;

//! Result of one script line
typedef struct _DIO_ScriptLineResult{

	//! Result
	DIO_E_ScriptResult_t CommandLineResult;

	//! Pass time
	UINT32 PassTime;

	//! State of all ports
	UINT8 * PortsState;
} DIO_ScriptLineResult_t;

/* ============================================ */
/*				General functions				*/
/* ============================================ */
	
/**
 * \brief Initialize the driver.
 *
 * \note This is must be first called function 
 *
 * \retval DIO_ERROR_SUCCESS				Success.
 * \retval DIO_ERROR_ALREADY_INIT			DLL is already initiate. Call DIO_Clear() function first.
 * \retval DIO_ERROR_FAIL_SHARE_MEM			Can not create share memory.
 */
EXPORT DIO_Errors_t DIO_Initialize(void);

/**
 * \brief Clear the driver.
 *
 * \note This is must be last called function 
 *
 * \retval DIO_ERROR_SUCCESS	Success
 */
EXPORT DIO_Errors_t DIO_Clear(void);

/**
 * \brief Get count of all connected Digital I/O USB cards
 *
 * \note Include currently used devices by other proccess
 *
 * \retval  0 No devices found (which may mean the driver is not installed properly)
 * \retval >0 Count of all connected devices
 */
EXPORT UINT32 DIO_GetDevices(void);

/**
 * \brief Get information about device such as nam, serial number etc.
 *
 * \param Module Number of the device you want to query.
 * \param DeviceInfo Device information structure to fill. The calling function is responsible for allocate (and free) this structure. 
 *
 * \retval DIO_ERROR_SUCCESS Success.
 */
EXPORT DIO_Errors_t DIO_GetDeviceInfo(_In_ UINT32 Module, _Out_ DIO_DeviceInfo_t * DeviceInfo);


/* ============================================ */
/*		Configuration functions	 			    */
/* ============================================ */

/**
 * \brief The device will be open and blocked for calling proccess. 
 *
 * \note Function must be called before using other functions working with selected device
 *
 * \retval DIO_ERROR_SUCCESS Success.
 */
EXPORT DIO_Errors_t DIO_OpenDevice(_In_ UINT32 Module);

/**
 * \brief Close the device. The device will be available to other process
 *
 * \retval DIO_ERROR_SUCCESS Success.
 */
EXPORT DIO_Errors_t DIO_CloseDevice(_In_ UINT32 Module);

/**
 * \brief Close the all devices. The devices will be available to other process
 *
 * \note Function will be called implicitly in DIO_Clear()
 *
 * \retval DIO_ERROR_SUCCESS Success.
 */
EXPORT DIO_Errors_t DIO_CloseDevices(void);

/**
 * \brief Set Digital I/O USB card configuration. 
 *
 * \param Module Number of the device you want to query.
 * \param DeviceBehavior Device behavior structure.
 * \param PortsConfiguration Pointer to first element of an array of ports configuration structure.
 *
 * \retval DIO_ERROR_SUCCESS Success.
 */
EXPORT DIO_Errors_t DIO_SetDeviceConfiguration(_In_ UINT32 Module, _In_ DIO_Behavior_t DeviceBehavior, _In_ DIO_PortConfig_t PortsConfiguration[DIO_MAX_PORTS]);

/**
 * \brief Get Digital I/O USB card configuration. 
 *
 * \param Module Number of the device you want to query.
 * \param DeviceBehavior Pointer to device behavior structure. 
		  The calling function is responsible for allocate (and free) this structure. 
 * \param PortsConfiguration Pointer to first element of an array of ports configuration structure. 
 *		  The calling function is responsible for allocate (and free) this array.
 *
 * \retval DIO_ERROR_SUCCESS Success.
 */
EXPORT DIO_Errors_t DIO_GetDeviceConfiguration(_In_ UINT32 Module, _Out_ DIO_Behavior_t * DeviceBehavior, _Out_ DIO_PortConfig_t PortsConfiguration[DIO_MAX_PORTS]);


/* ============================================ */
/*			Device access functions				*/
/* ============================================ */

/* ------ DIO_E_Mode_t.DIO_MODE_ACCES_DIO ------- */

/**
 * \brief Set all outputs to specific level.
 *
 * \param Module Number of the device.
 * \param Each byte is copied to the corresponding output byte. 
 *		  Bytes written to ports configured as inputs are ignored
 *
 * \retval DIO_ERROR_SUCCESS Success.
 */
EXPORT DIO_Errors_t DIO_WriteAll(_In_ UINT32 Module, _In_ UINT8 Data[DIO_MAX_PORTS]);

/**
 * \brief Set outputs of one port (eight channels) to specific level.
 *
 * \param Module Number of the device.
 * \param PortIndex Index of the port you wish to write.
 * \param Data The byte will be copied to the port outputs. Each set bit will cause the
 *		  same port bit to be set to "1"
 *
 * \retval DIO_ERROR_SUCCESS Success.
 */
EXPORT DIO_Errors_t DIO_Write8(_In_ UINT32 Module, _In_ UINT32 PortIndex, _In_ UINT8 Data);

/**
 * \brief Set output of one channel to specific level.
 *
 * \param Module Number of the device.
 * \param Channel Number of the channel you wish to change. Writes to channels configured as inputs are ignored
 * \param Data TRUE will set the bit to "1", FALSE will clear the bit to "0"
 *
 * \retval DIO_ERROR_SUCCESS Success.
 */
EXPORT DIO_Errors_t DIO_Write1(_In_ UINT32 Module, _In_ UINT32 Channel, _In_ BOOL Data);

/**
 * \brief Read all ports.
 *
 * \param Module Number of the device.
 * \param Data Pointer to the first element of an array of bytes. Each port will be read, and the reading
 *		  stored in the corresponding byte in the array. Data read from ports configured as output results 
 *		  in a "read-back" of the output.
 *
 * \retval DIO_ERROR_SUCCESS Success.
 */
EXPORT DIO_Errors_t DIO_ReadAll(_In_ UINT32 Module, _Out_ UINT8 Data[DIO_MAX_PORTS]);

/**
 * \brief Read one port.
 *
 * \param Module Number of the device.
 * \param PortIndex Index of the port you wish to read.
 * \param Data Pointer to a byte in which the input byte will be stored. Data read from ports
 *		  configured as output results in a "read-back" of the output
 *
 * \retval DIO_ERROR_SUCCESS Success.
 */
EXPORT DIO_Errors_t DIO_Read8(_In_ UINT32 Module, _In_ UINT32 PortIndex, _Out_ UINT8 * Data);

/**
 * \brief Read one channel.
 *
 * \param Module Number of the device you want to query.
 * \param Channel Number of the channel you wish to change. Writes to channels configured as inputs are ignored
 * \param Data Pointer to a byte which will be set to zero or one based on the input bit. Data
 *		  read from ports configured as output results in a "read-back" of the output.
 *
 * \retval DIO_ERROR_SUCCESS Success.
 */
EXPORT DIO_Errors_t DIO_Read1(_In_ UINT32 Module, _In_ UINT32 Channel, _Out_ BOOL * Data);

/* ------- DIO_E_Mode_t.DIO_MODE_SCRIPTER ------- */
/*				NOT IMPLEMENTED					  */

/**
 * \brief Begining of sending testing script.
 *
 * \param Module Number of the device you want to query.
 *
 * \retval DIO_ERROR_SUCCESS Success.
 */
EXPORT DIO_Errors_t DIO_BeginSendScript(_In_ UINT32 Module);

/**
 * \brief End of sending testing script and start testing.
 *
 * \param Module Number of the device you want to query.
 *
 * \retval DIO_ERROR_SUCCESS Success.
 */
EXPORT DIO_Errors_t DIO_EndSendScript(_In_ UINT32 Module);

/**
 * \brief Stop of sending testing script.
 *
 * \param Module Number of the device you want to query.
 *
 * \retval DIO_ERROR_SUCCESS Success.
 */
EXPORT DIO_Errors_t DIO_StopSendScript(_In_ UINT32 Module);

/**
 * \brief Get script status from device
 *
 * \param Module Number of the device you want to query.
 * \param ScriptStatus Status of script.
 *
 * \retval DIO_ERROR_SUCCESS Success.
 */
EXPORT DIO_Errors_t DIO_GetScriptStatus(_In_ UINT32 Module, _Out_ DIO_E_ScriptResult_t * ScriptStatus);

/**
 * \brief Write one stript command
 *
 * \param Module Number of the device you want to query.
 * \param ScriptCommnad Script command
 *
 * \retval DIO_ERROR_SUCCESS Success.
 */
EXPORT DIO_Errors_t DIO_WriteScriptCommand(_In_ UINT32 Module, _In_ DIO_ScriptCommand_t ScriptCommnad);

/**
 * \brief Write one stript line
 *
 * \param Module Number of the device you want to query.
 * \param CommnadLine Script line
 *
 * \retval DIO_ERROR_SUCCESS Success.
 */
EXPORT DIO_Errors_t DIO_WriteScriptLine(_In_ UINT32 Module, _In_ DIO_ScriptLine_t CommandLine);

/**
 * \brief Read the oldest script line result
 *
 * \param Module Number of the device you want to query.
 * \param CommandLineResult script line result line
 *
 * \retval DIO_ERROR_SUCCESS Success.
 */
EXPORT DIO_Errors_t DIO_ReadScriptLineResult(_In_ UINT32 Module, _Out_ DIO_ScriptLineResult_t * CommandLineResult);

/**
 * \brief Get count of script line results
 *
 * \param Module Number of the device you want to query.
 * \param Count of results
 *
 * \retval DIO_ERROR_SUCCESS Success.
 */
EXPORT DIO_Errors_t DIO_GetScriptLineResultCount(_In_ UINT32 Module, _Out_ UINT32 * ResultsCount);


#endif /* __DIGITAL_IO_USB_CARD_DRIVER_H__ */