The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"

#include "ppport.h"


#ifdef WIN32

// windows includes
#include "ftd2xx.h"
#include <windows.h>

#else

//linux includes
#include <ftd2xx.h>
#include "WinTypes.h"
#include <strings.h>

#endif

#include <stdlib.h>


// #define DEBUG 1

// written on linux (debian) for ftdi d2xx version 0.4.16
//


MODULE = FTDI::D2XX		PACKAGE = FTDI::D2XX		

PROTOTYPES: ENABLE

FT_STATUS 
FT_Open( deviceNumber, pHandle )
	int deviceNumber
	FT_HANDLE *pHandle = NO_INIT
	CODE:
		pHandle = malloc(sizeof(FT_HANDLE));
		RETVAL = FT_Open( deviceNumber, pHandle);
		
	OUTPUT:
		pHandle
		RETVAL

FT_STATUS 
FT_OpenEx( Arg1, Flags, pHandle )
    	SV * Arg1
    	DWORD Flags
    	FT_HANDLE *pHandle = NO_INIT
	INIT:
		DWORD pArg1D;
		PVOID pArg1P;
	CODE:
		pHandle = malloc(sizeof(FT_HANDLE));
		if((Flags != FT_OPEN_BY_SERIAL_NUMBER) 
			&& (Flags != FT_OPEN_BY_DESCRIPTION )) {
			// pArg1 equals ID number
			pArg1D = (DWORD)SvUV(Arg1);
			RETVAL = FT_OpenEx( &pArg1D, Flags, pHandle );
		} else {
			// pArg1 equals pointer to string
			pArg1P = SvPV_nolen(Arg1); // Convert to string
			RETVAL = FT_OpenEx( pArg1P, Flags, pHandle );

		}
	OUTPUT:
		pHandle
		RETVAL
  
FT_STATUS 
FT_Close( ftHandle )
    FT_HANDLE *ftHandle
	CODE:
		RETVAL=FT_Close( *ftHandle );
		free(ftHandle);
	OUTPUT:
		RETVAL

  
# So far not implemented
# FT_STATUS 
# FT_ListDevices( pArg1, pArg2, Flags ) 

FT_STATUS FT_CreateDeviceInfoList( NumDevs )
	DWORD NumDevs = NO_INIT
	CODE:
		RETVAL = FT_CreateDeviceInfoList( &NumDevs );
	OUTPUT:
		RETVAL
		NumDevs

# FT_STATUS FT_GetDeviceInfoList( DevInfo, NumDevs)
# Not implemented, use GetDeviceInfoDetail

FT_STATUS FT_GetDeviceInfoDetail( dwIndex, dwFlags, dwType, dwID, dwLocId,SerialNumber,Description, pftHandle)
	DWORD dwIndex
	DWORD dwFlags = NO_INIT
	DWORD dwType = NO_INIT
	DWORD dwID = NO_INIT
	DWORD dwLocId = NO_INIT
	SV * SerialNumber = NO_INIT
	SV * Description = NO_INIT
	FT_HANDLE *pftHandle = NO_INIT
	INIT:
		char * lpSerialNumber;
		char * lpDescription;
	CODE:
		pftHandle = malloc(sizeof(FT_HANDLE));
		lpSerialNumber = malloc(16);
		lpDescription = malloc(64);
		RETVAL = FT_GetDeviceInfoDetail( dwIndex, &dwFlags, &dwType, &dwID, &dwLocId, lpSerialNumber, lpDescription, pftHandle);
		SerialNumber = sv_2mortal(newSVpv(lpSerialNumber,16));
		free(lpSerialNumber);
		Description = sv_2mortal(newSVpv(lpDescription,64));
		free(lpDescription);
	OUTPUT:
		dwFlags
		dwType
		dwID
		dwLocId
		SerialNumber
		Description
		pftHandle
		RETVAL
	
FT_STATUS FT_GetDriverVersion( ftHandle, dwVersion)
    	FT_HANDLE ftHandle
	DWORD	dwVersion = NO_INIT
	CODE:
		RETVAL = FT_GetDriverVersion( ftHandle, &dwVersion);
	OUTPUT:
		RETVAL
		dwVersion
	
	
FT_STATUS FT_GetLibraryVersion( dwVersion )
	DWORD dwVersion = NO_INIT
	CODE:
		RETVAL = FT_GetLibraryVersion( &dwVersion );
	OUTPUT:
		RETVAL
		dwVersion


## These functions are not defined in the Windows header file from driver Version
## 2.06.00

#ifndef WIN32

FT_STATUS
FT_SetVIDPID( dwVID, dwPID)
	DWORD dwVID 
	DWORD dwPID
	

FT_STATUS
FT_GetVIDPID( dwVID, dwPID)
	DWORD dwVID = NO_INIT; 
	DWORD dwPID = NO_INIT;
	CODE:
		RETVAL = FT_GetVIDPID(&dwVID, &dwPID);
	OUTPUT:
		dwVID
		dwPID
		RETVAL

#endif


FT_STATUS
FT_Read( pHandle, Buffer, nBufferSize, lpBytesReturned)
    FT_HANDLE * pHandle
    SV * Buffer = NO_INIT
    DWORD nBufferSize
    DWORD lpBytesReturned = NO_INIT
	PREINIT:
		char* lpBuffer;
		AV* array;
		DWORD i;
	CODE:
		// get mem
		lpBuffer = malloc(nBufferSize);
		RETVAL = FT_Read(*pHandle, lpBuffer, nBufferSize, &lpBytesReturned);
		
		// convert output to array
		// new array
		array = (AV *)sv_2mortal((SV *)newAV());
		// extend it ( not required but faster) 
		av_extend(array,lpBytesReturned);
		// copy to array
		for( i = 0; i< lpBytesReturned; i++) {
			av_push(array,newSVuv(lpBuffer[i]));
		}
		// give back mem
		free(lpBuffer);
		// return reference of the array
		Buffer = newRV((SV *) array);
	OUTPUT: 
		Buffer
		lpBytesReturned
		RETVAL

FT_STATUS
FT_Write( ftHandle, Buffer, nBufferSize, BytesWritten)
    	FT_HANDLE  ftHandle
    	SV * Buffer
    	DWORD nBufferSize
    	DWORD BytesWritten = NO_INIT
    	PREINIT:
		AV * arrayBuffer;
		char * lpBuffer;
		DWORD i;
	CODE:	
		if( (!SvROK(Buffer)) 
			|| (SvTYPE(SvRV(Buffer)) != SVt_PVAV) 
			|| !((DWORD)av_len((AV *)SvRV(Buffer)) < nBufferSize)) 
		{
			printf("Data type error\n");
			printf("!SvROK(Buffer): %d\n",!SvROK(Buffer));
			printf("(SvTYPE(SvRV(Buffer)) != SVt_PVAV) %di\n", (SvTYPE(SvRV(Buffer)) != SVt_PVAV));
			printf("av_len((AV *)SvRV(Buffer)): %d\n", av_len((AV *)SvRV(Buffer)));
			
			XSRETURN_UNDEF;
		}
	
		// copy from array (reference) to buffer
		lpBuffer = malloc(nBufferSize);
		arrayBuffer = (AV *)SvRV(Buffer);
		for(i=0; i<nBufferSize;i++) {
			lpBuffer[i] = (char)SvUV(*av_fetch(arrayBuffer,i,0));
//			#ifdef DEBUG
//			printf("FT_Write buffer [%i] = %X\n", i, lpBuffer[i]);
//			#endif
		}
		RETVAL = FT_Write( ftHandle, lpBuffer, nBufferSize, &BytesWritten);
		free(lpBuffer);
	OUTPUT:
		RETVAL
		BytesWritten


#FTD2XX_API 
#FT_STATUS WINAPI FT_IoCtl(		// Linux, OS X: Not supported
#    FT_HANDLE ftHandle,
#    DWORD dwIoControlCode,
#    LPVOID lpInBuf,
#    DWORD nInBufSize,
#    LPVOID lpOutBuf,
#    DWORD nOutBufSize,
#    LPDWORD lpBytesReturned,
#    LPOVERLAPPED lpOverlapped
#    );


FT_STATUS FT_SetBaudRate( FT_HANDLE ftHandle, ULONG BaudRate );


FT_STATUS FT_SetDivisor( FT_HANDLE ftHandle, USHORT Divisor );


FT_STATUS FT_SetDataCharacteristics( FT_HANDLE ftHandle, UCHAR WordLength, UCHAR StopBits, UCHAR Parity	);


FT_STATUS FT_SetFlowControl( FT_HANDLE ftHandle,USHORT FlowControl,UCHAR XonChar,UCHAR XoffChar	);


FT_STATUS FT_ResetDevice( FT_HANDLE ftHandle);


FT_STATUS FT_SetDtr( FT_HANDLE ftHandle	);


FT_STATUS FT_ClrDtr( FT_HANDLE ftHandle	);


FT_STATUS FT_SetRts( FT_HANDLE ftHandle	);


FT_STATUS FT_ClrRts( FT_HANDLE ftHandle	);
		
FT_STATUS FT_GetModemStatus( ftHandle,	ModemStatus )
	FT_HANDLE ftHandle
	ULONG ModemStatus = NO_INIT
	CODE:
		RETVAL = FT_GetModemStatus( ftHandle,&ModemStatus );
	OUTPUT:
		RETVAL
		ModemStatus

FT_STATUS FT_SetChars( FT_HANDLE ftHandle,UCHAR EventChar,UCHAR EventCharEnabled,UCHAR ErrorChar,UCHAR ErrorCharEnabled);


FT_STATUS FT_Purge( FT_HANDLE ftHandle,	ULONG Mask);


FT_STATUS FT_SetTimeouts(FT_HANDLE ftHandle,ULONG ReadTimeout,ULONG WriteTimeout);


FT_STATUS FT_GetQueueStatus( ftHandle, RxBytes) 
	FT_HANDLE ftHandle
	DWORD RxBytes = NO_INIT
	CODE:
		RETVAL = FT_GetQueueStatus( ftHandle, &RxBytes);
	OUTPUT:
		RETVAL
		RxBytes

## unfinished
# FT_STATUS FT_SetEventNotification(FT_HANDLE ftHandle,DWORD Mask,PVOID Param);

FT_STATUS 
FT_GetStatus( ftHandle, RxBytes, TxBytes, EventDWord)
    	FT_HANDLE ftHandle
    	DWORD RxBytes = NO_INIT
    	DWORD TxBytes = NO_INIT
    	DWORD EventDWord = NO_INIT
	CODE:
		RETVAL = FT_GetStatus(ftHandle, &RxBytes, &TxBytes, &EventDWord);
	OUTPUT:
		RxBytes
		TxBytes
		EventDWord
		RETVAL

FT_STATUS FT_SetBreakOn(FT_HANDLE ftHandle);

FT_STATUS FT_SetBreakOff(FT_HANDLE ftHandle );

#// Linux, OS X: Not supported
FT_STATUS FT_SetWaitMask(ftHandle, Mask)	
    FT_HANDLE ftHandle
    DWORD Mask
    
#// Linux, OS X: Not supported
FT_STATUS FT_WaitOnMask(ftHandle, Mask)		
    	FT_HANDLE ftHandle
    	DWORD Mask = NO_INIT
    	CODE:
		RETVAL = FT_WaitOnMask(ftHandle, &Mask);
	OUTPUT:
		RETVAL
		Mask

FT_STATUS FT_GetEventStatus( ftHandle, EventDWord)
    FT_HANDLE ftHandle
    DWORD EventDWord = NO_INIT
    CODE:
	RETVAL = FT_GetEventStatus( ftHandle, &EventDWord);
    OUTPUT:
	EventDWord
	RETVAL


FT_STATUS FT_ReadEE( ftHandle, dwWordOffset, Value)
    	FT_HANDLE ftHandle
	DWORD dwWordOffset
    	WORD Value = NO_INIT
	CODE:
		RETVAL = FT_ReadEE( ftHandle, dwWordOffset, &Value);
	OUTPUT:
		RETVAL
		Value



FT_STATUS FT_WriteEE(FT_HANDLE ftHandle,DWORD dwWordOffset,WORD wValue);


FT_STATUS FT_EraseEE(FT_HANDLE ftHandle	);

# Implements FT_EE_Program but requires an Array Ref as input. The data conversion from a Hash structure
# to the array will be implemented in Perl
FT_STATUS FT_EE_ProgramByArray( ftHandle, Data) 
	FT_HANDLE ftHandle
	SV * Data
	PREINIT:
		// Data is a reference of a array
		AV * arrayBuffer;
		char * pData;
		DWORD i;
	CODE:	
		if( (!SvROK(Data)) || 
			(SvTYPE(SvRV(Data)) != SVt_PVAV) || 
			(av_len((AV *)SvRV(Data)) < 0)) 
		{
			XSRETURN_UNDEF;
		}
	
		// copy from array (reference) to buffer
		pData = malloc(sizeof(FT_PROGRAM_DATA));
		arrayBuffer = (AV *)SvRV(Data);
		for(i=0; i<sizeof(FT_PROGRAM_DATA);i++) {
			pData[i] = (char)SvUV(*av_fetch(arrayBuffer,i,0));
		}
		RETVAL = FT_EE_Program(ftHandle, (PFT_PROGRAM_DATA)pData);
		free(pData);
	OUTPUT:
		RETVAL
	

# The functionallity of FT_EE_ProgramEx is generally not needed in Perl and can be simply implemented by
# Perl and FT_EE_ProgramByArray if required.

# Implements FT_EE_Read but returns an RefArray as output. The data conversion from a Array
# to the Hash structure will be implemented in Perl
FT_STATUS FT_EE_ReadToArray( ftHandle, Data)
    	FT_HANDLE ftHandle
	SV * Data = NO_INIT
	PREINIT:
		char *lpBuffer;
		AV * array;
		DWORD i;
	CODE:
		// get mem
		lpBuffer = malloc(sizeof(FT_PROGRAM_DATA));
		RETVAL = FT_EE_Read(ftHandle, (PFT_PROGRAM_DATA)lpBuffer);
		
		// convert output to array
		// new array
		array = (AV *)sv_2mortal((SV *)newAV());
		// extend it ( not required but faster) 
		av_extend(array,sizeof(FT_PROGRAM_DATA));
		// copy to array
		for( i = 0; i< sizeof(FT_PROGRAM_DATA); i++) {
			av_push(array,newSVuv(lpBuffer[i]));
		}
		// give back mem
		free(lpBuffer);
		// return reference of the array
		Data = newRV((SV *) array);
	OUTPUT:
		Data
		RETVAL
	
FT_STATUS  FT_EE_UASize( ftHandle, dwSize)
    	FT_HANDLE ftHandle
	DWORD dwSize = NO_INIT
	CODE:
		RETVAL = FT_EE_UASize( ftHandle, &dwSize);
	OUTPUT:
		dwSize
		RETVAL


FT_STATUS  FT_EE_UAWrite( ftHandle, Data, dwDataLen)
    	FT_HANDLE ftHandle
	SV * Data
	WORD dwDataLen
	PREINIT:
		// Data is a reference of a array
		AV * arrayBuffer;
		PUCHAR pData;
		DWORD i;
	CODE:	
		if( (!SvROK(Data)) || 
			(SvTYPE(SvRV(Data)) != SVt_PVAV) || 
			(av_len((AV *)SvRV(Data)) < 0)) 
		{
			XSRETURN_UNDEF;
		}

		// copy from array (reference) to buffer
		pData = malloc(dwDataLen);
		arrayBuffer = (AV *)SvRV(Data);
		for(i=0; i<dwDataLen;i++) {
			pData[i] = (char)SvUV(*av_fetch(arrayBuffer,i,0));
		}
		RETVAL = FT_EE_UAWrite(ftHandle, pData, dwDataLen);
		free(pData);
	OUTPUT:
		RETVAL


FT_STATUS  FT_EE_UARead( ftHandle, Buffer, nBufferSize, lpBytesReturned)
    	FT_HANDLE ftHandle
	SV * Buffer = NO_INIT
	DWORD nBufferSize
	DWORD lpBytesReturned = NO_INIT
	PREINIT:
		PUCHAR lpBuffer;
		AV * array;
		DWORD i;
	CODE:
		// get mem
		lpBuffer = malloc(nBufferSize);
		RETVAL = FT_EE_UARead(ftHandle, lpBuffer, nBufferSize, &lpBytesReturned);
		
		// convert output to array
		// new array
		array = (AV *)sv_2mortal((SV *)newAV());
		// extend it ( not required but faster) 
		av_extend(array,lpBytesReturned);
		// copy to array
		for( i = 0; i< lpBytesReturned; i++) {
			av_push(array,newSVuv(lpBuffer[i]));
		}
		// give back mem
		free(lpBuffer);
		// return reference of the array
		Buffer = newRV((SV *) array);
	OUTPUT: 
		Buffer
		lpBytesReturned
		RETVAL


FT_STATUS  FT_SetLatencyTimer(FT_HANDLE ftHandle,UCHAR ucLatency );


FT_STATUS  FT_GetLatencyTimer( ftHandle, Latency)
    FT_HANDLE ftHandle
    UCHAR Latency = NO_INIT
	CODE:
		RETVAL = FT_GetLatencyTimer( ftHandle, &Latency);
	OUTPUT:
		RETVAL
		Latency




FT_STATUS  FT_SetBitMode(FT_HANDLE ftHandle, UCHAR ucMask,UCHAR ucEnable);


FT_STATUS  FT_GetBitMode( ftHandle, Mode )
    	FT_HANDLE ftHandle
    	UCHAR Mode
    	CODE:
		RETVAL = FT_GetBitMode( ftHandle, &Mode );
	OUTPUT:
		Mode
		RETVAL



FT_STATUS  FT_SetUSBParameters(FT_HANDLE ftHandle,ULONG ulInTransferSize,ULONG ulOutTransferSize);
	

FT_STATUS  FT_SetDeadmanTimeout(FT_HANDLE ftHandle,ULONG ulDeadmanTimeout);
#		// -1 for infinite (2.6 kernels only). High +ve number for 2.4 kernels
	

FT_STATUS  FT_GetDeviceInfo( ftHandle, ftDevice, dwID, SerialNumber, Description, Dummy)
    	FT_HANDLE ftHandle
    	FT_DEVICE ftDevice
	DWORD dwID = NO_INIT
	char * SerialNumber = NO_INIT
	char * Description = NO_INIT
	SV * Dummy
    	CODE:
		SerialNumber = malloc(16); // from Databook
		Description = malloc(64);  // from Databook
		RETVAL = FT_GetDeviceInfo( ftHandle, &ftDevice, &dwID, SerialNumber, Description, Dummy);
	OUTPUT:
		dwID
		ftDevice
		SerialNumber
		Description
		RETVAL



FT_STATUS  FT_StopInTask(FT_HANDLE ftHandle);


FT_STATUS  FT_RestartInTask(FT_HANDLE ftHandle);

# 	// Linux, OS X: Not supported
FT_STATUS  FT_SetResetPipeRetryCount(FT_HANDLE ftHandle,DWORD dwCount);

#	// Linux, OS X: Not supported
FT_STATUS  FT_ResetPort(FT_HANDLE ftHandle);
	
#	// Linux, OS X: Not supported
FT_STATUS  FT_CyclePort(FT_HANDLE ftHandle);