unit PIOExports;

interface

uses
	WinAPI.Windows,
	Core;

function PIO_In8(iBoard: TBoardIndex; Offset: TUInt32): TUInt8; cdecl;
function PIO_In16(iBoard: TBoardIndex; Offset: TUInt32): TUInt16; cdecl;
function PIO_In32(iBoard: TBoardIndex; Offset: TUInt32): TUInt32; cdecl;
function PIO_Out8(iBoard: TBoardIndex; Offset: TUInt32; Data: TUInt8): TWinErrCode; cdecl;
function PIO_Out16(iBoard: TBoardIndex; Offset: TUInt32; Data: TUInt16): TWinErrCode; cdecl;
function PIO_Out32(iBoard: TBoardIndex; Offset: TUInt32; Data: TUInt32): TWinErrCode; cdecl;

function PIO_INS8(iBoard: TBoardIndex; Offset: TUInt32; DataCt: TUInt32; pData: TPUInt8): TWinErrCode; cdecl;
function PIO_INS16(iBoard: TBoardIndex; Offset: TUInt32; DataCt: TUInt32; pData: TPUInt16): TWinErrCode; cdecl;
function PIO_INS32(iBoard: TBoardIndex; Offset: TUInt32; DataCt: TUInt32; pData: TPUInt32): TWinErrCode; cdecl;
function PIO_OUTS8(iBoard: TBoardIndex; Offset: TUInt32; DataCt: TUInt32; pData: TPUInt8): TWinErrCode; cdecl;
function PIO_OUTS16(iBoard: TBoardIndex; Offset: TUInt32; DataCt: TUInt32; pData: TPUInt16): TWinErrCode; cdecl;
function PIO_OUTS32(iBoard: TBoardIndex; Offset: TUInt32; DataCt: TUInt32; pData: TPUInt32): TWinErrCode; cdecl;

function PIO_WaitForIRQ(iBoard: TBoardIndex; IRQIndex: TIRQIndex = iiOnly): TWinErrCode; cdecl;
function PIO_PollForIRQ(iBoard: TBoardIndex; IRQIndex: TIRQIndex = iiOnly): TWinErrCode; cdecl;
function PIO_AbortIRQWait(iBoard: TBoardIndex; IRQIndex: TIRQIndex = iiOnly): TWinErrCode; cdecl;

function PIO_DMAPiongControl(iBoard: TBoardIndex; pControlReply: TPUInt8; ControlSize: TUInt32; ReplySize: TPUInt32 = nil): TWinErrCode; cdecl;
function PIO_DMAPiongIn(iBoard: TBoardIndex; pData: TPUInt8; DataSize: TUInt32; pOverlap: POverlapped): TWinErrCode; cdecl;
function PIO_DMAChPiongIn(iBoard: TBoardIndex; DMAChannel: TUInt32; pData: TPUInt8; DataSize: TUInt32; pOverlap: POverlapped): TWinErrCode; cdecl;

function PIO_GetDeviceHandle(iBoard: TBoardIndex; var hDevice: THandle): TWinErrCode; cdecl;
function PIO_QueryBARBase(iBoard: TBoardIndex; iBAR: LongWord; pBase: PLongWord): TWinErrCode; cdecl;

function DoDMAPiongControl(iBoard: TBoardIndex; Control: AnsiString): TWinErrCode;



implementation

function _InN(N: TUInt8; iBoard: TBoardIndex; Offset: TUInt32): TUInt32;
var
	DataBuffer: TPortOp;
	Status: TUInt32;

begin
	Status := ValidateBoardIndex(iBoard);
	if Status <> ERROR_SUCCESS then begin
		Result := $AA55AA55;
		SetLastError(Status);
		Exit;
	end;

	try
		with DataBuffer do begin
			Addr := Offset;
			Flags := pofRelative;
			Size := N;
			Data32 := 0;
		end;
		SetLastError(ERROR_SUCCESS);
		Status := DoDeviceIoControl(Dev[iBoard].hDevice, IOCTL_PORT_IN, @DataBuffer, SizeOf(DataBuffer), @DataBuffer, SizeOf(DataBuffer));
		if Status = ERROR_SUCCESS then begin
			Result := DataBuffer.Data32 and ($FFFFFFFF shr (4*(4-N)));
		end
		else begin
			Result := $AA55AA55;
		end;
	except
		Result := ERROR_INTERNAL_ERROR;
	end;
end;

function _OutN(N: TUInt8; iBoard: TBoardIndex; Offset: TUInt32; Data: TUInt32): TWinErrCode;
var
	DataBuffer: TPortOp;

begin
	Result := ValidateBoardIndex(iBoard);
	if Result <> ERROR_SUCCESS then Exit;

	try
		with DataBuffer do begin
			Addr := Offset;
			Flags := pofRelative;
			Size := N;
			Data32 := Data;
		end;
		Result := DoDeviceIoControl(Dev[iBoard].hDevice, IOCTL_PORT_OUT, @DataBuffer, SizeOf(DataBuffer), @DataBuffer, SizeOf(DataBuffer));
	except
		Result := ERROR_INTERNAL_ERROR;
	end;
end;

function _DirSN(DirIOCTL: TUInt32; DataN: TUInt8; iBoard: TBoardIndex; Offset: TUInt32; DataCt: TUInt32; pBuf: Pointer): TWinErrCode;
var
	DataBuffer: TPortOp;

begin
	Result := ValidateBoardIndex(iBoard);
	if Result <> ERROR_SUCCESS then Exit;

	try
		with DataBuffer do begin
			Addr := Offset;
			Flags := pofRelative;
			Size := DataN;
			Data32 := 0;
		end;
		Result := DoDeviceIoControl(Dev[iBoard].hDevice, DirIOCTL, @DataBuffer, sizeof(DataBuffer), pBuf, DataN*DataCt);
	except
		Result := ERROR_INTERNAL_ERROR;
	end;
end;



function PIO_In8(iBoard: TBoardIndex; Offset: TUInt32): TUInt8;
begin
	Result := _InN(1, iBoard, Offset);
end;

function PIO_In16(iBoard: TBoardIndex; Offset: TUInt32): TUInt16;
begin
	Result := _InN(2, iBoard, Offset);
end;

function PIO_In32(iBoard: TBoardIndex; Offset: TUInt32): TUInt32;
begin
	Result := _InN(4, iBoard, Offset);
end;

function PIO_Out8(iBoard: TBoardIndex; Offset: TUInt32; Data: TUInt8): TWinErrCode;
begin
	Result := _OutN(1, iBoard, Offset, Data);
end;

function PIO_Out16(iBoard: TBoardIndex; Offset: TUInt32; Data: TUInt16): TWinErrCode;
begin
	Result := _OutN(2, iBoard, Offset, Data);
end;

function PIO_Out32(iBoard: TBoardIndex; Offset: TUInt32; Data: TUInt32): TWinErrCode;
begin
	Result := _OutN(4, iBoard, Offset, Data);
end;



function PIO_INS8(iBoard: TBoardIndex; Offset: TUInt32; DataCt: TUInt32; pData: TPUInt8): TWinErrCode;
begin
	if DataCt = 1 then begin
		pData^ := _InN(1, iBoard, Offset);
		Result := GetLastError;
	end
	else begin
		Result := _DirSN(IOCTL_PIO_INSTR, 1, iBoard, Offset, DataCt, pData);
	end;
end;

function PIO_INS16(iBoard: TBoardIndex; Offset: TUInt32; DataCt: TUInt32; pData: TPUInt16): TWinErrCode;
begin
	if DataCt = 1 then begin
		pData^ := _InN(2, iBoard, Offset);
		Result := GetLastError;
	end
	else begin
		Result := _DirSN(IOCTL_PIO_INSTR, 2, iBoard, Offset, DataCt, pData);
	end;
end;

function PIO_INS32(iBoard: TBoardIndex; Offset: TUInt32; DataCt: TUInt32; pData: TPUInt32): TWinErrCode;
begin
	if DataCt = 1 then begin
		pData^ := _InN(4, iBoard, Offset);
		Result := GetLastError;
	end
	else begin
		Result := _DirSN(IOCTL_PIO_INSTR, 4, iBoard, Offset, DataCt, pData);
	end;
end;

function PIO_OUTS8(iBoard: TBoardIndex; Offset: TUInt32; DataCt: TUInt32; pData: TPUInt8): TWinErrCode;
begin
	if DataCt = 1 then begin
		Result := _OutN(1, iBoard, Offset, pData^);
	end
	else begin
		Result := _DirSN(IOCTL_PIO_OUTSTR, 1, iBoard, Offset, DataCt, pData);
	end;
end;

function PIO_OUTS16(iBoard: TBoardIndex; Offset: TUInt32; DataCt: TUInt32; pData: TPUInt16): TWinErrCode;
begin
	if DataCt = 1 then begin
		Result := _OutN(2, iBoard, Offset, pData^);
	end
	else begin
		Result := _DirSN(IOCTL_PIO_OUTSTR, 2, iBoard, Offset, DataCt, pData);
	end;
end;

function PIO_OUTS32(iBoard: TBoardIndex; Offset: TUInt32; DataCt: TUInt32; pData: TPUInt32): TWinErrCode;
begin
	if DataCt = 1 then begin
		Result := _OutN(4, iBoard, Offset, pData^);
	end
	else begin
		Result := _DirSN(IOCTL_PIO_OUTSTR, 4, iBoard, Offset, DataCt, pData);
	end;
end;



function PIO_WaitForIRQ(iBoard: TBoardIndex; IRQIndex: TIRQIndex = iiOnly): TWinErrCode;
var
	O: TOverlapped;
	hEvent: THandle;
	cbRet: LongWord;

begin
	Result := ValidateBoardIndex(iBoard);
	if Result <> ERROR_SUCCESS then Exit;

	hEvent := CreateEvent(nil, true, false, '');
	try
		O.Internal := 0;
		O.InternalHigh := 0;
		O.Offset := 0;
		O.OffsetHigh := 0;
		O.hEvent := hEvent;

		SetLastError(ERROR_SUCCESS);
		if not DeviceIOControl(Dev[iBoard].hDevice, IOCTL_IRQ_WAIT_BEGIN, nil, 0, nil, 0, cbRet, @O) then begin
			Result := GetLastError;
			if Result = ERROR_SUCCESS then Result := ERROR_GEN_FAILURE;
			if Result <> ERROR_IO_PENDING then Exit; //Some error occurred in the driver so return false.

			GetOverlappedResult(Dev[iBoard].hDevice, O, cbRet, True);
			Result := GetLastError;
			if Result = ERROR_NO_MORE_FILES then Result := ERROR_SUCCESS;
			//Result should be:
			//ERROR_SUCCESS(if got an IRQ)
			//ERROR_OPERATION_ABORTED(if got an abort)
			//other error code in case of error
		end;
	finally
		CloseAndInvalidateHandle(hEvent);
	end;
end;

function PIO_PollForIRQ(iBoard: TBoardIndex; IRQIndex: TIRQIndex = iiOnly): TWinErrCode; cdecl;
begin
	Result := ValidateBoardIndex(iBoard);
	if Result <> ERROR_SUCCESS then Exit;

	Result := DoDeviceIoControl(Dev[iBoard].hDevice, IOCTL_IRQ_POLL, nil, 0, nil, 0);
end;

function PIO_AbortIRQWait(iBoard: TBoardIndex; IRQIndex: TIRQIndex = iiOnly): TWinErrCode; cdecl;
begin
	Result := ValidateBoardIndex(iBoard);
	if Result <> ERROR_SUCCESS then Exit;

	Result := DoDeviceIoControl(Dev[iBoard].hDevice, IOCTL_IRQ_WAIT_ABORT, nil, 0, nil, 0);
end;



function PIO_DMAPiongControl(iBoard: TBoardIndex; pControlReply: TPUInt8; ControlSize: TUInt32; ReplySize: TPUInt32 = nil): TWinErrCode; cdecl;
var
	DummyReplySize: TUInt32;

begin
	Result := ValidateBoardIndex(iBoard);
	if Result <> ERROR_SUCCESS then Exit;

	if ReplySize = nil then begin
		DummyReplySize := 0;
		ReplySize := @DummyReplySize;
	end;

	Result := DoDeviceIoControl(Dev[iBoard].hDevice, IOCTL_DMA_PIONG_CONTROL, pControlReply, ControlSize, pControlReply, ReplySize^, @ReplySize^);
end;

function PIO_DMAPiongIn(iBoard: TBoardIndex; pData: TPUInt8; DataSize: TUInt32; pOverlap: POverlapped): TWinErrCode; cdecl;
begin
	Result := ValidateBoardIndex(iBoard);
	if Result <> ERROR_SUCCESS then Exit;

	Result := DoDeviceIoControl(Dev[iBoard].hDevice, IOCTL_DMA_PIONG_READ, nil, 0, pData, DataSize, nil, pOverlap);
end;

function PIO_DMAChPiongIn(iBoard: TBoardIndex; DMAChannel: TUInt32; pData: TPUInt8; DataSize: TUInt32; pOverlap: POverlapped): TWinErrCode; cdecl;
begin
	Result := ValidateBoardIndex(iBoard);
	if Result <> ERROR_SUCCESS then Exit;

	Result := DoDeviceIoControl(Dev[iBoard].hDevice, IOCTL_DMA_PIONG_READ, @DMAChannel, SizeOf(DMAChannel), pData, DataSize, nil, pOverlap);
end;



function PIO_GetDeviceHandle(iBoard: TBoardIndex; var hDevice: THandle): TWinErrCode; cdecl;
begin
	try
		hDevice := 0;
	except
		Result := ERROR_NOACCESS;
		Exit;
	end;

	Result := ValidateBoardIndex(iBoard);
	if Result <> ERROR_SUCCESS then Exit;

	hDevice := Dev[iBoard].hDevice;
end;

function PIO_QueryBARBase(iBoard: TBoardIndex; iBAR: LongWord; pBase: PLongWord): TWinErrCode; cdecl;
var
	Data: LongWord;

begin
	Result := ValidateBoardIndex(iBoard);
	if Result <> ERROR_SUCCESS then Exit;

	if pBase = nil then begin
		Result := ERROR_INVALID_ACCESS;
		Exit;
	end;
	try
		pBase^ := 0;
	except
		Result := ERROR_INVALID_ACCESS;
		Exit;
	end;
	if iBAR > 5 then begin
		Result := ERROR_INVALID_PARAMETER;
		Exit;
	end;

	Data := iBAR;
	Result := DoDeviceIoControl(Dev[iBoard].hDevice, IOCTL_QUERY_BAR_BASE, @Data, SizeOf(Data), @Data, SizeOf(Data));
	if Result = ERROR_SUCCESS then pBase^ := Data;
end;



function DoDMAPiongControl(iBoard: TBoardIndex; Control: AnsiString): TWinErrCode;
begin
	Result := PIO_DMAPiongControl(iBoard, @Control[1], Length(Control), nil);
end;

end.
