////////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2001-2026 Alexey Kuryakin daqgroup@mail.ru under MIT license //
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
// This file is part of the CRW-DAQ project by DaqGroup - component CRWLIB.   //
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
// Purpose:                                                                   //
// Interface API for LCARD devices.                                           //
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
// History:                                                                   //
// 20240805 - Created by A.K.                                                 //
// 20240807 - 1st working version. See comments (VIP NB:).                    //
////////////////////////////////////////////////////////////////////////////////

unit _crw_lcard; //  Interface API for LCARD devices.

{$I _crw_sysdef.inc}

{$I _crw_sysmode.inc}

interface

uses
 //////////////////////////////////////////////////////
 {$I _crw_uses_first.inc} // NB: MUST BE FIRST USES !!!
 //////////////////////////////////////////////////////
 sysutils, classes, math,
 _crw_alloc, _crw_proc, _crw_base64, _crw_crypt;

////////////////////////////////////////////////////////////////////////////////
// unit cmd791
////////////////////////////////////////////////////////////////////////////////

 { Working with L-1450 }

 // definitions for L791 busmaster board

const // raw L791 registers
 R_ADC_BUFFER_L791                 = $0000;
 R_DAC_BUFFER_L791                 = $0400;
 R_CONTROL_TABLE_L791              = $0600;
 R_CONTROL_TABLE_LENGTH_L791       = $07F4;
 R_CHANNEL_TIME_L791               = $07F8;
 R_INT_FRAME_TIME_L791             = $07FC;
 R_ADC_PAGE_DESC_L791              = $0800;
 R_DAC_PAGE_DESC_L791              = $0A00;
 R_ADC_PCI_COUNT_L791              = $0F80;
 R_DAC_PCI_COUNT_L791              = $0F84;
 R_DAC_TIME_L791                   = $0F88;
 R_ADC_BUFFER_PTR_L791             = $0F90;
 R_DAC_BUFFER_PTR_L791             = $0F94;
 R_DIGITAL_IO_L791                 = $0F98;
 R_ADC_SAMPLE_QNT_L791             = $0F9C;
 R_ADC_MASTER_QNT_L791             = $0FA0;
 R_FLASH_ADDRESS_L791              = $0FA4;
 R_FLASH_DATA_L791                 = $0F8C;
 R_INTERRUPT_ENABLE_L791           = $0FF0;
 R_STATUS_L791                     = $0FF8;
 R_CONTROL_L791                    = $0FFC;

 // dword array access index
 I_ADC_BUFFER_L791                 = ($0000 shr 2);
 I_DAC_BUFFER_L791                 = ($0400 shr 2);
 I_CONTROL_TABLE_L791              = ($0600 shr 2);
 I_CONTROL_TABLE_LENGTH_L791       = ($07F4 shr 2);
 I_CHANNEL_TIME_L791               = ($07F8 shr 2);
 I_INT_FRAME_TIME_L791             = ($07FC shr 2);
 I_ADC_PAGE_DESC_L791              = ($0800 shr 2);
 I_DAC_PAGE_DESC_L791              = ($0A00 shr 2);
 I_ADC_PCI_COUNT_L791              = ($0F80 shr 2);
 I_DAC_PCI_COUNT_L791              = ($0F84 shr 2);
 I_DAC_TIME_L791                   = ($0F88 shr 2);
 I_ADC_BUFFER_PTR_L791             = ($0F90 shr 2);
 I_DAC_BUFFEI_PTR_L791             = ($0F94 shr 2);
 I_DIGITAL_IO_L791                 = ($0F98 shr 2);
 I_ADC_SAMPLE_QNT_L791             = ($0F9C shr 2);
 I_ADC_MASTER_QNT_L791             = ($0FA0 shr 2);
 I_FLASH_ADDRESS_L791              = ($0FA4 shr 2);
 I_FLASH_DATA_L791                 = ($0F8C shr 2);
 I_INTERRUPT_ENABLE_L791           = ($0FF0 shr 2);
 I_STATUS_L791                     = ($0FF8 shr 2);
 I_CONTROL_L791                    = ($0FFC shr 2);

 // bits defines
 // CONTROL_REGISTER
 BIT_ADC_EN                        = 0;
 BIT_ADC_MASTER_EN                 = 1;
 BIT_CLR_ADC_CNT                   = 2;
 BIT_AUTO_STOP_ADC_MST             = 3;
 BIT_AUTO_STOP_ADC                 = 4;
 // 5-7 reserved
 BIT_SYNC_MODE_0                   = 8; //
 BIT_SYNC_MODE_1                   = 9; // 00 - no sync 10 - digital start 11 - digital kadr sync
 BIT_SYNC_SOURCE                   = 10;
 // 11 reserved
 BIT_ADC_BUF_DEPTH_0               = 12;
 BIT_ADC_BUF_DEPTH_1               = 13;
 BIT_ADC_BUF_DEPTH_2               = 14;
 // 15 reserved
 BIT_DAC_EN                        = 16;
 BIT_DAC_MASTER_EN                 = 17;
 BIT_CLR_DAC_CNT                   = 18;
 // 19-23 reserved
 BIT_EEPROM_CMD_0                  = 24;
 BIT_EEPROM_CMD_1                  = 25;
 BIT_EEPROM_START                  = 26;
 BIT_EEPROM_WR_EN                  = 27;
 BIT_OUTPUT_EN                     = 28;
 //29-31 reserved

 // STATUS_REGISTER
 SBIT_ADC_MST_EVENT                = 0;
 SBIT_ADC_OVF_EVENT                = 1;
 // 2 reserved
 SBIT_ADC_BUF_EVENT                = 3;
 // 4-15 reserved
 SBIT_DAC_USR_EVENT                = 16;
 // 17 reserved
 SBIT_DAC_UNF_EVENT                = 18;
 // 19-23 reserved
 SBIT_PWR_OVR_EVENT                = 24;
 SBIT_EEPROM_BUSY                  = 25;
 // 26 -30 reserved
 SBIT_INT                          = 31;

 // IRQ_ENABLE_REGISTER
 //IBIT
 // not yet writed

////////////////////////////////////////////////////////////////////////////////
// unit E140Cmd
////////////////////////////////////////////////////////////////////////////////

 { Working with E140 boards }

const
 // номера доступных пользовательских запросов для USB (vendor request)
 V_RESET_DSP_E140                  = 0;
 V_PUT_ARRAY_E140                  = 1;
 V_GET_ARRAY_E140                  = 2;
 V_START_ADC_E140                  = 3;
 V_STOP_ADC_E140                   = 4;
 V_START_ADC_ONCE_E140             = 5;
 V_START_DAC_E140                  = 6;
 V_STOP_DAC_E140                   = 7;

 V_GET_MODULE_NAME_E140            = 11;

 L_ADC_PARS_BASE_E140              = $0060;
 L_ADC_ONCE_FLAG_E140              = (L_ADC_PARS_BASE_E140 + 136);
 L_FLASH_ENABLED_E140              = (L_ADC_PARS_BASE_E140 + 137);
 L_DAC_PARS_BASE_E140              = $0160;
 L_TTL_OUT_E140                    = $0400;
 L_TTL_IN_E140                     = $0400;
 L_ENABLE_TTL_OUT_E140             = $0402;
 L_ADC_SAMPLE_E140                 = $0410;
 L_ADC_CHANNEL_SELECT_E140         = $0412;
 L_ADC_START_E140                  = $0413;
 L_DAC_SAMPLE_E140                 = $0420;
 L_DAC_SAMPLES_E140                = $0428;
 L_SUSPEND_MODE_E140               = $0430;
 L_DATA_FLASH_BASE_E140            = $0800;
 L_CODE_FLASH_BASE_E140            = $1000;
 L_BIOS_VERSION_E140               = $1080;
 L_DESCRIPTOR_BASE_E140            = $2780;
 L_RAM_E140                        = $8000;

////////////////////////////////////////////////////////////////////////////////
// unit E154Cmd
////////////////////////////////////////////////////////////////////////////////

const
 // номера доступных пользовательских запросов для USB (vendor request)
 V_RESET_DSP_E154                  = 0;
 V_PUT_ARRAY_E154                  = 1;
 V_GET_ARRAY_E154                  = 2;
 V_START_ADC_E154                  = 3;
 V_STOP_ADC_E154                   = 4;
 V_START_ADC_ONCE_E154             = 5;
 //#define V_GO_SLEEP_E440         6
 //#define V_WAKEUP_E440           7
 V_GET_MODULE_NAME_E154            = 11;

 L_ADC_PARS_BASE_E154              = $0060;
 L_ADC_ONCE_FLAG_E154              = (L_ADC_PARS_BASE_E154 + 136);
 L_FLASH_ENABLED_E154              = (L_ADC_PARS_BASE_E154 + 137);
 L_TTL_OUT_E154                    = $0400;
 L_TTL_IN_E154                     = $0400;
 L_ENABLE_TTL_OUT_E154             = $0402;
 L_ADC_SAMPLE_E154                 = $0410;
 L_ADC_CHANNEL_SELECT_E154         = $0412;
 L_ADC_START_E154                  = $0413;
 L_DAC_SAMPLE_E154                 = $0420;
 L_SUSPEND_MODE_E154               = $0430;
 L_DATA_FLASH_BASE_E154            = $0800;
 L_CODE_FLASH_BASE_E154            = $1000;
 L_BIOS_VERSION_E154               = $1080;
 L_DESCRIPTOR_BASE_E154            = $2780;
 L_RAM_E154                        = $8000;

////////////////////////////////////////////////////////////////////////////////
// unit E440Cmd
////////////////////////////////////////////////////////////////////////////////

 { Working with E440 boards }

type
 UCHAR  = BYTE;
 USHORT = WORD;
 ULONG  = LONGWORD;

const
 cmTEST_E440                       = 0;
 cmENABLE_FLASH_WRITE_E440         = 1;
 cmREAD_FLASH_WORD_E440            = 2;
 cmWRITE_FLASH_WORD_E440           = 3;
 cmSTART_ADC_E440                  = 4;
 cmSTOP_ADC_E440                   = 5;
 cmADC_KADR_E440                   = 6;
 cmADC_SAMPLE_E440                 = 7;
 cmSTART_DAC_E440                  = 8;
 cmSTOP_DAC_E440                   = 9;
 cmDAC_SAMPLE_E440                 = 10;
 cmENABLE_TTL_OUT_E440             = 11;
 cmTTL_IN_E440                     = 12;
 cmTTL_OUT_E440                    = 13;
 cmLAST_COMMAND_E440               = 14;

 V_RESET_DSP_E440                  = 0;
 V_PUT_ARRAY_E440                  = 1;
 V_GET_ARRAY_E440                  = 2;
 V_START_ADC_E440                  = 3;
 V_START_DAC_E440                  = 4;
 V_COMMAND_IRQ_E440                = 5;
 V_GO_SLEEP_E440                   = 6;
 V_WAKEUP_E440                     = 7;
 V_GET_MODULE_NAME_E440            = 11;

 // all data in PM so we convert it to USHORT...
 //#define LBIOS_OUTVAR(v) ((ULONG)v<<8)
 //#define LBIOS_INVAR(v) ((USHORT)(v>>8))

 DM_E440                           = $4000;
 PM_E440                           = $0000;

 DataBaseAddress_E440              = $30;

 L_PROGRAM_BASE_ADDRESS_E440       =  (DataBaseAddress_E440 + $0);
 L_READY_E440                      =  (DataBaseAddress_E440 + $1);
 L_TMODE1_E440                     =  (DataBaseAddress_E440 + $2);
 L_TMODE2_E440                     =  (DataBaseAddress_E440 + $3);
 L_TEST_LOAD_E440                  =  (DataBaseAddress_E440 + $4);
 L_COMMAND_E440                    =  (DataBaseAddress_E440 + $5);

 L_DAC_SCLK_DIV_E440               =  (DataBaseAddress_E440 + $7);
 L_DAC_RATE_E440                   =  (DataBaseAddress_E440 + $8);
 L_ADC_RATE_E440                   =  (DataBaseAddress_E440 + $9);
 L_ADC_ENABLED_E440                =  (DataBaseAddress_E440 + $A);
 L_ADC_FIFO_BASE_ADDRESS_E440      =  (DataBaseAddress_E440 + $B);
 L_CUR_ADC_FIFO_LENGTH_E440        =  (DataBaseAddress_E440 + $C);
 L_ADC_FIFO_LENGTH_E440            =  (DataBaseAddress_E440 + $E);
 L_CORRECTION_ENABLED_E440         =  (DataBaseAddress_E440 + $F);
 L_LBIOS_VERSION_E440              =  (DataBaseAddress_E440 + $10);
 L_ADC_SAMPLE_E440                 =  (DataBaseAddress_E440 + $11);
 L_ADC_CHANNEL_E440                =  (DataBaseAddress_E440 + $12);
 L_INPUT_MODE_E440                 =  (DataBaseAddress_E440 + $13);
 L_SYNCHRO_AD_CHANNEL_E440         =  (DataBaseAddress_E440 + $16);
 L_SYNCHRO_AD_POROG_E440           =  (DataBaseAddress_E440 + $17);
 L_SYNCHRO_AD_MODE_E440            =  (DataBaseAddress_E440 + $18);
 L_SYNCHRO_AD_TYPE_E440            =  (DataBaseAddress_E440 + $19);

 L_CONTROL_TABLE_LENGHT_E440       =  (DataBaseAddress_E440 + $1B);
 L_FIRST_SAMPLE_DELAY_E440         =  (DataBaseAddress_E440 + $1C);
 L_INTER_KADR_DELAY_E440           =  (DataBaseAddress_E440 + $1D);

 L_DAC_SAMPLE_E440                 =  (DataBaseAddress_E440 + $20);
 L_DAC_ENABLED_E440                =  (DataBaseAddress_E440 + $21);
 L_DAC_FIFO_BASE_ADDRESS_E440      =  (DataBaseAddress_E440 + $22);
 L_CUR_DAC_FIFO_LENGTH_E440        =  (DataBaseAddress_E440 + $24);
 L_DAC_FIFO_LENGTH_E440            =  (DataBaseAddress_E440 + $25);

 L_FLASH_ENABLED_E440              =  (DataBaseAddress_E440 + $26);
 L_FLASH_ADDRESS_E440              =  (DataBaseAddress_E440 + $27);
 L_FLASH_DATA_E440                 =  (DataBaseAddress_E440 + $28);

 L_ENABLE_TTL_OUT_E440             =  (DataBaseAddress_E440 + $29);
 L_TTL_OUT_E440                    =  (DataBaseAddress_E440 + $2A);
 L_TTL_IN_E440                     =  (DataBaseAddress_E440 + $2B);

 L_SCALE_E440                      = (DataBaseAddress_E440 + $30);
 L_ZERO_E440                       = (DataBaseAddress_E440 + $34);

 L_CONTROL_TABLE_E440              = $80;

function LBIOS_OUTVAR(v:USHORT):ULONG; // ((ULONG)v<<8)
function LBIOS_INVAR(v:ULONG):USHORT;  // ((USHORT)(v>>8))

////////////////////////////////////////////////////////////////////////////////
// unit E2010Cmd
////////////////////////////////////////////////////////////////////////////////

const
 V_RESET_FPGA_E2010                = 0;
 V_PUT_ARRAY_E2010                 = 1;
 V_GET_ARRAY_E2010                 = 2;
 V_INIT_FPGA_E2010                 = 3;
 V_START_ADC_E2010                 = 4;
 V_STOP_ADC_E2010                  = 5;
 V_GET_MODULE_NAME_E2010           = 11;
 V_CALL_APPLICATION_E2010          = 15;
 // 13 - special put_array

 // synchro config
 INT_START_TRANS                   = $01;
 INT_START                         = $81;
 EXT_START_UP                      = $84;
 EXT_START_DOWN                    = $94;
 EXT_STRTA_DOWN_REVB               = $8C; //(0x84+0x08)
 // clock src
 INT_CLK_TRANS                     = $00;
 INT_CLK                           = $40;
 EXT_CLK_UP                        = $42;
 EXT_CLK_DOWN                      = $62;

 //bit macros for channel input range and mode config, use |/+ operator to configure
 V30_0                             = $0000;
 V10_0                             = $0008;
 V03_0                             = $0010;
 GND_0                             = $0000;
 SIG_0                             = $0400;

 V30_1                             = $0000;
 V10_1                             = $0002;
 V03_1                             = $0004;
 GND_1                             = $0000;
 SIG_1                             = $0200;

 V30_2                             = $0000;
 V10_2                             = $8000;
 V03_2                             = $0100;
 GND_2                             = $0000;
 SIG_2                             = $1000;

 V30_3                             = $0000;
 V10_3                             = $2000;
 V03_3                             = $4000;
 GND_3                             = $0000;
 SIG_3                             = $0800;

 // advanced analog sync
 A_SYNC_OFF                        = $0000;
 A_SYNC_UP_EDGE                    = $0080;
 A_SYNC_DOWN_EDGE                  = $0084;
 A_SYNC_HL_LEVEL                   = $0088;
 A_SYNC_LH_LEVEL                   = $008C;

 // channel num macros
 CH_0                              = $00;
 CH_1                              = $01;
 CH_2                              = $02;
 CH_3                              = $03;

 // виртуальные адреса для доступа к различным ресурсам модуля
 SEL_TEST_MODE                     = $81000000;
 SEL_AVR_DM                        = $82000000;
 SEL_AVR_PM                        = $83000000;
 SEL_DIO_PARAM                     = $84000000;
 SEL_DIO_DATA                      = $85000000;
 SEL_ADC_PARAM                     = $86000000;
 //#define SEL_ADC_DATA            (0x87000000L)

 SEL_BULK_REQ_SIZE                 = $88000000;
 SEL_DAC_DATA                      = $89000000;
 SEL_ADC_CALIBR_KOEFS              = $8A000000;
 SEL_FPGA_DATA                     = $8B000000;
 SEL_DEBUG_INFO                    = $8F000000;

 // различные адреса памяти программ микроконтроллера
 FIRMWARE_START_ADDRESS            = (SEL_AVR_PM or $0000);
 USER_FLASH_ADDRESS                = (SEL_AVR_PM or $2D00);
 FIRMWARE_DESCRIPTOR_ADDRESS       = (SEL_AVR_PM or $2F00);
 MODULE_DESCRIPTOR_ADDRESS         = (SEL_AVR_PM or $3000);
 BOOT_LOADER_START_ADDRESS         = (SEL_AVR_PM or $3C00);
 BOOT_LOADER_DESCRIPTOR_ADDRESS    = (SEL_AVR_PM or $3FB0);

// различные адреса переменных в памяти данных микроконтроллера
 DATA_STATE_ADDRESS                = (SEL_AVR_DM or ($0150 + $00));
 EXTRA_SYNCHRO_PARS_ADDRESS        = (SEL_AVR_DM or ($0150 + 13));
 ADC_CORRECTION_ADDRESS            = (SEL_AVR_DM or ($0150 + 13 + 13));
 LUSBAPI_OR_LCOMP_ADDRESS          = (SEL_AVR_DM or ($0150 + 13 + 13 + 1));

 EP1K10_SIZE                       = (22*1024+100);

////////////////////////////////////////////////////////////////////////////////
// unit PciCmd
////////////////////////////////////////////////////////////////////////////////

 { Working with PCI PLX boards }

const
 L_CONTROL_TABLE_PLX               = $8A00;
 L_SCALE_PLX                       = $8D00;
 L_ZERO_PLX                        = $8D04;
 L_CONTROL_TABLE_LENGHT_PLX        = $8D08;
 L_BOARD_REVISION_PLX              = $8D3F;
 L_READY_PLX                       = $8D40;
 L_TMODE1_PLX                      = $8D41;
 L_TMODE2_PLX                      = $8D42;
 L_DAC_IRQ_SOURCE_PLX              = $8D43;
 L_DAC_ENABLE_IRQ_VALUE_PLX        = $8D44;
 L_DAC_IRQ_FIFO_ADDRESS_PLX        = $8D45;
 L_DAC_IRQ_STEP_PLX                = $8D46;
 L_ENABLE_TTL_OUT_PLX              = $8D47;
 L_DSP_TYPE_PLX                    = $8D48;
 L_COMMAND_PLX                     = $8D49;
 L_TTL_OUT_PLX                     = $8D4C;
 L_TTL_IN_PLX                      = $8D4D;
 L_FIFO_PTR_PLX                    = $8D50;
 L_TEST_LOAD_PLX                   = $8D52;
 L_ADC_RATE_PLX                    = $8D53;
 L_INTER_KADR_DELAY_PLX            = $8D54;
 L_DAC_RATE_PLX                    = $8D55;
 L_DAC_VALUE_PLX                   = $8D56;
 L_ENABLE_IRQ_PLX                  = $8D57;
 L_IRQ_STEP_PLX                    = $8D58;
 L_IRQ_FIFO_ADDRESS_PLX            = $8D5A;
 L_ENABLE_IRQ_VALUE_PLX            = $8D5B;
 L_DAC_SCLK_DIV_PLX                = $8D5E;
 L_CORRECTION_ENABLE_PLX           = $8D60;
 L_ADC_ENABLE_PLX                  = $8D62;
 L_ADC_FIFO_BASE_ADDRESS_PLX       = $8D63;
 L_ADC_FIFO_BASE_ADDRESS_INDEX_PLX = $8D64;
 L_ADC_FIFO_LENGTH_PLX             = $8D65;
 L_ADC_NEW_FIFO_LENGTH_PLX         = $8D66;

 L_DAC_ENABLE_STREAM_PLX           = $8D67;
 L_DAC_FIFO_BASE_ADDRESS_PLX       = $8D68;
 L_DAC_FIFO_LENGTH_PLX             = $8D69;
 L_DAC_NEW_FIFO_LENGTH_PLX         = $8D6A;

 L_SYNCHRO_TYPE_PLX                = $8D70;
 L_SYNCHRO_AD_CHANNEL_PLX          = $8D73;
 L_SYNCHRO_AD_POROG_PLX            = $8D74;
 L_SYNCHRO_AD_MODE_PLX             = $8D75;
 L_SYNCHRO_AD_SENSITIVITY_PLX      = $8D76;
 L_DAC                             = $8F00;

 { command defines }

 cmTEST_PLX                        = 0;
 cmLOAD_CONTROL_TABLE_PLX          = 1;
 cmADC_ENABLE_PLX                  = 2;
 cmADC_FIFO_CONFIG_PLX             = 3;
 cmSET_ADC_KADR_PLX                = 4;
 cmENABLE_DAC_STREAM_PLX           = 5;
 cmDAC_FIFO_CONFIG_PLX             = 6;
 cmSET_DAC_RATE_PLX                = 7;
 cmDAC_SAMPLE_PLX                  = 8;
 cmTTL_IN_PLX                      = 9;
 cmTTL_OUT_PLX                     = 10;
 cmSYNCHRO_CONFIG_PLX              = 11;
 cmENABLE_IRQ_PLX                  = 12;
 cmIRQ_TEST_PLX                    = 13;
 cmSET_DSP_TYPE_PLX                = 14;
 cmENABLE_TTL_OUT_PLX              = 15;

////////////////////////////////////////////////////////////////////////////////
// unit ioctl
////////////////////////////////////////////////////////////////////////////////

const
 L_NONE                            = 0; // no board in slot
 L_L1250                           = 1; // L1250 board
 L_N1250                           = 2; // N1250 board (may be not work)
 L_L1251                           = 3; // L1251 board
 L_L1221                           = 4; // L1221 board
 L_PCIA                            = 5; // PCI rev A board
 L_PCIB                            = 6; // PCI rev B board
 L_L264                            = 8; // L264 ISA board
 L_L305                            = 9; // L305 ISA board
 L_L1450C                          = 10;
 L_L1450                           = 11;
 L_L032                            = 12;
 L_HI8                             = 13;
 L_PCIC                            = 14;

 L_L791                            = 19;
 L_E440                            = 30;
 L_E140                            = 31;
 L_E2010                           = 32;
 L_E270                            = 33;
 L_CAN_USB                         = 34;
 L_AK9                             = 35;
 L_LTR010                          = 36;
 L_LTR021                          = 37;
 L_E154                            = 38;
 L_E2010B                          = 39;
 L_LTR031                          = 40;
 L_LTR030                          = 41;
 L_E310                            = 77;
 L_CA01                            = 90; // cardio

 //defines for GetParemeter/SetParameter common for all boards
 L_BOARD_TYPE                      = 10000;
 L_POINT_SIZE                      = 10001;
 L_SYNC_ADDR_LO                    = 10002;
 L_SYNC_ADDR_HI                    = 10003;
 L_DATA_ADDR_LO                    = 10004;
 L_DATA_ADDR_HI                    = 10005;
 L_SYNC1_ADDR_LO                   = 10006;
 L_SYNC1_ADDR_HI                   = 10007;
 L_DATA1_ADDR_LO                   = 10008;
 L_DATA1_ADDR_HI                   = 10009;

 L_USER_BASE                       = 10100; // 128 user prpoperty to store data
 // next prop 10228

 L_SUCCESS                         = 0;
 L_NOTSUPPORTED                    = 1;
 L_ERROR                           = 2;
 L_ERROR_NOBOARD                   = 3;
 L_ERROR_INUSE                     = 4;

 L_ADC_PARAM                       = 1;
 L_DAC_PARAM                       = 2;

 L_ASYNC_ADC_CFG                   = 3;
 L_ASYNC_TTL_CFG                   = 4;
 L_ASYNC_DAC_CFG                   = 5;

 L_ASYNC_ADC_INP                   = 6;
 L_ASYNC_TTL_INP                   = 7;

 L_ASYNC_TTL_OUT                   = 8;
 L_ASYNC_DAC_OUT                   = 9;

 L_STREAM_ADC                      = 1;
 L_STREAM_DAC                      = 2;
 L_STREAM_TTLIN                    = 3;
 L_STREAM_TTLOUT                   = 4;

 L_EVENT_ADC_BUF                   = 1;
 L_EVENT_DAC_BUF                   = 2;

 //
 L_EVENT_ADC_OVF                   = 3;
 L_EVENT_ADC_FIFO                  = 4;
 L_EVENT_DAC_USER                  = 5;
 L_EVENT_DAC_UNF                   = 6;
 L_EVENT_PWR_OVR                   = 7;

{$PUSH}                            // Save compiler options
{$ALIGN OFF} // {$A-}              // Next definitions required byte adjustment

type
 PORT_PAR = object
 public
  port      : ULONG;
  datatype  : ULONG;
 end;
 PPORT_PAR = ^PORT_PAR;

 SLOT_PAR = object                             // Структура описывает параметры виртуального слота.
 public
  Base      : ULONG;                           // базовый адрес первого региона портов;
  BaseL     : ULONG;                           // протяженность первого региона портов в байтах;
  Base1     : ULONG;                           // базовый адрес второго региона портов;
  BaseL1    : ULONG;                           // протяженность второго региона портов в байтах;
  Mem       : ULONG;                           // адрес первого региона памяти;
  MemL      : ULONG;                           // протяженность первого региона памяти в байтах;
  Mem1      : ULONG;                           // адрес второго региона памяти;
  MemL1     : ULONG;                           // протяженность второго региона памяти в байтах;
  Irq       : ULONG;                           // используемое драйвером аппаратное прерывание;
  BoardType : ULONG;                           // тип платы;
  DSPType   : ULONG;                           // тип установленного на плате DSP;
  Dma       : ULONG;                           // используемый для ввода данных канал ПДП: 0 - не использовать,5,6;
  DmaDac    : ULONG;                           // используемый для вывода данных канал ПДП: 0 - не использовать,6;
  DTA_REG   : ULONG;                           //
  IDMA_REG  : ULONG;                           //
  CMD_REG   : ULONG;                           //
  IRQ_RST   : ULONG;                           //
  DTA_ARRAY : ULONG;                           //
  RDY_REG   : ULONG;                           //
  CFG_REG   : ULONG;                           // адреса регистров платы относительного базового адреса;
 end;
 PSLOT_PAR = ^SLOT_PAR;

 DAQ_PAR = object                              // Базовая структура для описания параметров сбора данных.
 public
  s_Type  : ULONG;                             // тип операции ввода/вывода
  FIFO    : ULONG;                             // размер половины аппаратного буфера FIFO на плате
  IrqStep : ULONG;                             // шаг генерации прерываний
  Pages   : ULONG;                             // размер кольцевого буфера в шагах прерываний
 end;
 PDAQ_PAR = ^DAQ_PAR;

 ASYNC_PAR = object(DAQ_PAR)
 public
 dRate : double;
 Rate  : ULONG;
 NCh   : ULONG;
 Chn   : array [0..127] of ULONG;
 Data  : array [0..127] of ULONG;
 Mode  : ULONG;
 end;
 PASYNC_PAR = ^ASYNC_PAR;

 //internal
 DAC_PAR_0 = object(DAQ_PAR)
 public
  AutoInit  : ULONG;
  dRate     : double;
  Rate      : ULONG;
  // FIFO    : USHORT;
  // IrqStep : USHORT;
  // Pages   : USHORT;
 IrqEna     : ULONG;
 DacEna     : ULONG;
 DacNumber  : ULONG;
 end;
 PDAC_PAR_0 = ^DAC_PAR_0;

 DAC_PAR_1 = object(DAQ_PAR)
 public
  AutoInit : ULONG;
  dRate    : double;
  Rate     : ULONG;
  // FIFO    : USHORT;
  // IrqStep : USHORT;
  // Pages   : USHORT;
  IrqEna   : ULONG;
  DacEna   : ULONG;
 end;
 PDAC_PAR_1 = ^DAC_PAR_1;

 DAC_PAR = record
  case Integer of
   0: (t1:DAC_PAR_0);
   1: (t2:DAC_PAR_1);
 end;

 // exported
 ADC_PAR_0 = object(DAQ_PAR)
 public
  AutoInit           : ULONG;
  dRate              : double;
  dKadr              : double;
  dScale             : double;
  Rate               : ULONG;
  Kadr               : ULONG;
  Scale              : ULONG;
  FPDelay            : ULONG;
  SynchroType        : ULONG;
  SynchroSensitivity : ULONG;
  SynchroMode        : ULONG;
  AdChannel          : ULONG;
  AdPorog            : ULONG;
  NCh                : ULONG;
  Chn                : array [0..127] of ULONG;
  //   FIFO    : USHORT;
  //   IrqStep : USHORT;
  //   Pages   : USHORT;
  IrqEna             : ULONG;
  AdcEna             : ULONG;
 end;
 PADC_PAR_0 = ^ADC_PAR_0;

 ADC_PAR_1 = object(DAQ_PAR)
 public
  AutoInit    : ULONG;
  dRate       : double;
  dKadr       : double;
  Reserved1   : USHORT;
  DigRate     : USHORT;
  DM_Ena      : ULONG;
  Rate        : ULONG;
  Kadr        : ULONG;
  StartCnt    : ULONG;
  StopCnt     : ULONG;
  SynchroType : ULONG;
  SynchroMode : ULONG;
  AdPorog     : ULONG;
  SynchroSrc  : ULONG;
  AdcIMask    : ULONG;
  NCh         : ULONG;
  Chn         : array [0..127] of ULONG;
  // FIFO     : USHORT;
  // IrqStep  : USHORT;
  // Pages    : USHORT;
  IrqEna      : ULONG;
  AdcEna      : ULONG;
 end;
 PADC_PAR_1 = ^ADC_PAR_1;

 ADC_PAR = record
  case Integer of
   0: (t1:ADC_PAR_0);
   1: (t2:ADC_PAR_1);
 end;

 //exported
 PLATA_DESCR = object
 public
  SerNum       : array [0..8] of CHAR;
  BrdName      : array [0..4] of CHAR;
  Rev          : CHAR;
  DspType      : array [0..4] of CHAR;
  Quartz       : ULONG;
  IsDacPresent : USHORT;
  Reserv1      : array [0..6] of USHORT;
  KoefADC      : array [0..7] of USHORT;
  KoefDAC      : array [0..3] of USHORT;
  Custom       : array [0..31] of USHORT;
 end;
 PPLATA_DESCR = ^PLATA_DESCR;

 PLATA_DESCR_1450 = object
 public
  SerNum          : array [0..8] of CHAR;
  BrdName         : array [0..6] of CHAR;
  Rev             : CHAR;
  DspType         : array [0..4] of CHAR;
  IsDacPresent    : CHAR;
  IsExtMemPresent : CHAR;
  Quartz          : ULONG;
  Reserv1         : array [0..5] of USHORT;
  KoefADC         : array [0..7] of USHORT;
  KoefDAC         : array [0..3] of USHORT;
  Custom          : array [0..31] of USHORT;
 end;
 PPLATA_DESCR_1450 = ^PLATA_DESCR_1450;

 PLATA_DESCR_L791 = object
 public
  crc          : USHORT;
  SerNum       : array [0..15] of CHAR;
  BrdName      : array [0..15] of CHAR;
  Rev          : CHAR;
  DspType      : array [0..4] of CHAR;
  Quartz       : ULONG;
  IsDacPresent : USHORT;
  KoefADC      : array [0..15] of single;
  KoefDAC      : array [0..3] of single;
  Custom       : USHORT;
 end;
 PPLATA_DESCR_L791 = ^PLATA_DESCR_L791;

 PLATA_DESCR_E440 = object                     // Структура описывает FLASH на USB модуле E14-440.
 public
  SerNum       : array [0..8] of CHAR;         // серийный номер платы
  BrdName      : array [0..6] of CHAR;         // название платы
  Rev          : CHAR;                         // ревизия платы
  DspType      : array [0..4] of CHAR;         // тип DSP
  IsDacPresent : CHAR;                         // наличие ЦАП
  Quartz       : ULONG;                        // частота кварца
  Reserv2      : array [0..12] of USHORT;      // зарезервировано
  KoefADC      : array [0..7] of USHORT;       // калибровочные коэф. АЦП
  KoefDAC      : array [0..3] of USHORT;       // калибровочные коэф. ЦАП
  Custom       : array [0..31] of USHORT;      // пользовательское место
 end;
 PPLATA_DESCR_E440 = ^PLATA_DESCR_E440;

 PLATA_DESCR_E140 = object                     // Структура описывает FLASH на USB модуле E14-140.
 public
  SerNum       : array [0..8] of CHAR;         // серийный номер платы
  BrdName      : array [0..10] of CHAR;        // название платы
  Rev          : CHAR;                         // ревизия платы
  DspType      : array [0..10] of CHAR;        // тип DSP
  IsDacPresent : CHAR;                         // наличие ЦАП
  Quartz       : ULONG;                        // частота кварца
  Reserv2      : array [0..2] of UCHAR;        // зарезервировано
  KoefADC      : array [0..7] of single;       // калибровочные коэф. АЦП
  KoefDAC      : array [0..3] of single;       // калибровочные коэф. ЦАП
  Custom       : array [0..19] of USHORT;      // пользовательское место
 end;
 PPLATA_DESCR_E140 = ^PLATA_DESCR_E140;

 PLATA_DESCR_E154 = object
 public
  SerNum       : array [0..8] of CHAR;
  BrdName      : array [0..10] of CHAR;
  Rev          : CHAR;
  DspType      : array [0..10] of CHAR;
  IsDacPresent : CHAR;
  Quartz       : ULONG;
  Reserv2      : array [0..2] of UCHAR;
  KoefADC      : array [0..7] of single;
  KoefDAC      : array [0..3] of single;
  Custom       : array [0..19] of USHORT;
 end;
 PPLATA_DESCR_E154 = ^PLATA_DESCR_E154;

 PLATA_DESCR_E2010 = object
 public
  BrdName      : array [0..15] of CHAR;
  SerNum       : array [0..15] of CHAR;
  DspType      : array [0..15] of CHAR;
  Quartz       : ULONG;
  Rev          : CHAR;
  IsDacPresent : CHAR;
  KoefADC      : array [0..23] of single;
  KoefDAC      : array [0..3] of single;
  Custom       : array [0..43] of USHORT;
  CRC          : USHORT;
 end;
 PPLATA_DESCR_E2010 = ^PLATA_DESCR_E2010;

 WORD_IMAGE = object
 public
  data : array [0..63] of USHORT;
 end;
 PWORD_IMAGE = ^WORD_IMAGE;

 BYTE_IMAGE = object
 public
  data : array [0..127] of BYTE;
 end;
 PBYTE_IMAGE = ^BYTE_IMAGE;

 PLATA_DESCR_U = record
  case Integer of
   0: (t1:PLATA_DESCR);
   1: (t2:PLATA_DESCR_1450);
   2: (t3:PLATA_DESCR_L791);
   3: (wi:WORD_IMAGE);
   4: (bi:BYTE_IMAGE);
   5: (t4:PLATA_DESCR_E440);
   6: (t5:PLATA_DESCR_E140);
 end;

 WORD_IMAGE_256 = object
 public
  data : array [0..127] of USHORT;
 end;
 PWORD_IMAGE_256 = ^WORD_IMAGE_256;

 BYTE_IMAGE_256 = object
 public
  data : array [0..255] of BYTE;
 end;
 PBYTE_IMAGE_256 = ^BYTE_IMAGE_256;

 PLATA_DESCR_U2 = record
  case Integer of
   0: (t1:PLATA_DESCR);
   1: (t2:PLATA_DESCR_1450);
   2: (t3:PLATA_DESCR_L791);
   3: (wi:WORD_IMAGE_256);
   4: (bi:BYTE_IMAGE_256);
   5: (t4:PLATA_DESCR_E440);
   6: (t5:PLATA_DESCR_E140);
   7: (t6:PLATA_DESCR_E2010);
   8: (t7:PLATA_DESCR_E154);
 end;

{$POP}                             // Restore compiler options

////////////////////////////////////////////////////////////////////////////////
// unit ifc_ldev
////////////////////////////////////////////////////////////////////////////////

const
 IID_IUnknown : TGUID = '{00000000-0000-0000-C000-000000000046}';
 IID_ILDEV    : TGUID = '{32bb8320-b41b-11cf-a6bb-0080c7b2d682}';
 IID_ILDEV2   : TGUID = '{c737c7ef-ecc2-49f2-ba4e-94c889f07399}';

 // DEFINE_GUID(IID_IUnknown, 0x00000000, 0x0000,0x0000, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46);
 // DEFINE_GUID(IID_ILDEV,    0x32bb8320, 0xb41b,0x11cf, 0xa6, 0xbb, 0x00, 0x80, 0xc7, 0xb2, 0xd6, 0x82);

 ///////////////////////////////////////////////////////////////////////////////
 // VIP NB:
 // 1) Linux lcard library (liblcomp.so) should be compiled with special
 //    definition in file include/stubs.h:
 //    #define __stdcall __attribute__((cdecl))
 // 2) Linux interfaces uses cdecl call convention instead of Windows stdcall,
 //    so all interface functions should be marked as:
 //    {$IFDEF WINDOWS}stdcall;{$ELSE}cdecl;{$ENDIF}
 // 3) Interfaces uses to access driver functions in FPC.
 //    IUnknown uses instead of direct definition of LUnknown.
 //    So now we don't need to use AddRef/Release directly,
 //    because FPC add reference counter code automatically.
 ///////////////////////////////////////////////////////////////////////////////

type // NB!
 LUnknown = IUnknown;
 //LUnknown = interface
 // function QueryInterface(const iid:TGUID; out ppv):HRESULT; {$IFDEF WINDOWS}stdcall;{$ELSE}cdecl;{$ENDIF}
 // function AddRef:ULONG; {$IFDEF WINDOWS}stdcall;{$ELSE}cdecl;{$ENDIF}
 // function Release:ULONG; {$IFDEF WINDOWS}stdcall;{$ELSE}cdecl;{$ENDIF}
 //end;

type
 IDaqLDevice = interface(LUnknown)
  function inbyte (offset:ULONG; var data:UCHAR; len:ULONG; key:ULONG ):ULONG; {$IFDEF WINDOWS}stdcall;{$ELSE}cdecl;{$ENDIF}
  function inword (offset:ULONG; var data:USHORT; len:ULONG; key:ULONG):ULONG; {$IFDEF WINDOWS}stdcall;{$ELSE}cdecl;{$ENDIF}
  function indword(offset:ULONG; var data:ULONG; len:ULONG; key:ULONG ):ULONG; {$IFDEF WINDOWS}stdcall;{$ELSE}cdecl;{$ENDIF}

  function outbyte (offset:ULONG; var data:UCHAR; len:ULONG; key:ULONG ):ULONG; {$IFDEF WINDOWS}stdcall;{$ELSE}cdecl;{$ENDIF}
  function outword (offset:ULONG; var data:USHORT; len:ULONG; key:ULONG):ULONG; {$IFDEF WINDOWS}stdcall;{$ELSE}cdecl;{$ENDIF}
  function outdword(offset:ULONG; var data:ULONG; len:ULONG; key:ULONG ):ULONG; {$IFDEF WINDOWS}stdcall;{$ELSE}cdecl;{$ENDIF}

  // Working with MEM ports
  function inmbyte (offset:ULONG; var data:UCHAR; len:ULONG; key:ULONG ):ULONG; {$IFDEF WINDOWS}stdcall;{$ELSE}cdecl;{$ENDIF}
  function inmword (offset:ULONG; var data:USHORT; len:ULONG; key:ULONG):ULONG; {$IFDEF WINDOWS}stdcall;{$ELSE}cdecl;{$ENDIF}
  function inmdword(offset:ULONG; var data:ULONG; len:ULONG; key:ULONG ):ULONG; {$IFDEF WINDOWS}stdcall;{$ELSE}cdecl;{$ENDIF}

  function outmbyte (offset:ULONG; var data:UCHAR; len:ULONG; key:ULONG ):ULONG; {$IFDEF WINDOWS}stdcall;{$ELSE}cdecl;{$ENDIF}
  function outmword (offset:ULONG; var data:USHORT; len:ULONG; key:ULONG):ULONG; {$IFDEF WINDOWS}stdcall;{$ELSE}cdecl;{$ENDIF}
  function outmdword(offset:ULONG; var data:ULONG; len:ULONG; key:ULONG ):ULONG; {$IFDEF WINDOWS}stdcall;{$ELSE}cdecl;{$ENDIF}

  function GetWord_DM(Addr:USHORT; var Data:USHORT):ULONG; {$IFDEF WINDOWS}stdcall;{$ELSE}cdecl;{$ENDIF}
  function PutWord_DM(Addr:USHORT; Data:USHORT):ULONG; {$IFDEF WINDOWS}stdcall;{$ELSE}cdecl;{$ENDIF}
  function PutWord_PM(Addr:USHORT; Data:ULONG):ULONG; {$IFDEF WINDOWS}stdcall;{$ELSE}cdecl;{$ENDIF}
  function GetWord_PM(Addr:USHORT; var Data:ULONG):ULONG; {$IFDEF WINDOWS}stdcall;{$ELSE}cdecl;{$ENDIF}

  function GetArray_DM(Addr:USHORT; Count:ULONG; var Data:USHORT):ULONG; {$IFDEF WINDOWS}stdcall;{$ELSE}cdecl;{$ENDIF}
  function PutArray_DM(Addr:USHORT; Count:ULONG; var Data:USHORT):ULONG; {$IFDEF WINDOWS}stdcall;{$ELSE}cdecl;{$ENDIF}
  function PutArray_PM(Addr:USHORT; Count:ULONG; var Data:ULONG):ULONG; {$IFDEF WINDOWS}stdcall;{$ELSE}cdecl;{$ENDIF}
  function GetArray_PM(Addr:USHORT; Count:ULONG; var Data:ULONG):ULONG; {$IFDEF WINDOWS}stdcall;{$ELSE}cdecl;{$ENDIF}

  function SendCommand(Cmd:USHORT):ULONG; {$IFDEF WINDOWS}stdcall;{$ELSE}cdecl;{$ENDIF}

  function PlataTest:ULONG; {$IFDEF WINDOWS}stdcall;{$ELSE}cdecl;{$ENDIF}

  function GetSlotParam(var slPar:SLOT_PAR):ULONG; {$IFDEF WINDOWS}stdcall;{$ELSE}cdecl;{$ENDIF}

  function OpenLDevice:THandle; {$IFDEF WINDOWS}stdcall;{$ELSE}cdecl;{$ENDIF}
  function CloseLDevice:ULONG; {$IFDEF WINDOWS}stdcall;{$ELSE}cdecl;{$ENDIF}

  ///
  function SetParametersStream(var ap:DAQ_PAR; var UsedSize:ULONG; out Data; out Sync; StreamId:ULONG):ULONG; {$IFDEF WINDOWS}stdcall;{$ELSE}cdecl;{$ENDIF}
  function RequestBufferStream(var Size:ULONG; StreamId:ULONG):ULONG; {$IFDEF WINDOWS}stdcall;{$ELSE}cdecl;{$ENDIF} //in words
  function FillDAQparameters(var ap:DAQ_PAR):ULONG; {$IFDEF WINDOWS}stdcall;{$ELSE}cdecl;{$ENDIF}
  ///

  function InitStartLDevice:ULONG; {$IFDEF WINDOWS}stdcall;{$ELSE}cdecl;{$ENDIF}
  function StartLDevice:ULONG; {$IFDEF WINDOWS}stdcall;{$ELSE}cdecl;{$ENDIF}
  function StopLDevice:ULONG; {$IFDEF WINDOWS}stdcall;{$ELSE}cdecl;{$ENDIF}

  function LoadBios(FileName:PAnsiChar):ULONG; {$IFDEF WINDOWS}stdcall;{$ELSE}cdecl;{$ENDIF}

  (*
  function InputADC(Chan:USHORT; var Data:USHORT):ULONG; {$IFDEF WINDOWS}stdcall;{$ELSE}cdecl;{$ENDIF}

  function InputTTL(var Data:ULONG; Mode:ULONG):ULONG; {$IFDEF WINDOWS}stdcall;{$ELSE}cdecl;{$ENDIF}
  function OutputTTL(Data:ULONG; Mode:ULONG):ULONG; {$IFDEF WINDOWS}stdcall;{$ELSE}cdecl;{$ENDIF}
  function ConfigTTL(Data:ULONG):ULONG; {$IFDEF WINDOWS}stdcall;{$ELSE}cdecl;{$ENDIF}

  function OutputDAC(Data:ShortInt; Mode:ULONG):ULONG; {$IFDEF WINDOWS}stdcall;{$ELSE}cdecl;{$ENDIF}
  function ConfigDAC(Mode:ULONG; Number:ULONG):ULONG; {$IFDEF WINDOWS}stdcall;{$ELSE}cdecl;{$ENDIF}
  *)

  function IoAsync(var sp:DAQ_PAR):ULONG; {$IFDEF WINDOWS}stdcall;{$ELSE}cdecl;{$ENDIF}

  function ReadPlataDescr(var pd):ULONG; {$IFDEF WINDOWS}stdcall;{$ELSE}cdecl;{$ENDIF}
  function WritePlataDescr(var pd; Ena:USHORT):ULONG; {$IFDEF WINDOWS}stdcall;{$ELSE}cdecl;{$ENDIF}
  function ReadFlashWord(FlashAddress:USHORT; var Data:USHORT):ULONG; {$IFDEF WINDOWS}stdcall;{$ELSE}cdecl;{$ENDIF}
  function WriteFlashWord(FlashAddress:USHORT; Data:USHORT):ULONG; {$IFDEF WINDOWS}stdcall;{$ELSE}cdecl;{$ENDIF}
  function EnableFlashWrite(Flag:USHORT):ULONG; {$IFDEF WINDOWS}stdcall;{$ELSE}cdecl;{$ENDIF}

  function EnableCorrection(Ena:USHORT):ULONG; {$IFDEF WINDOWS}stdcall;{$ELSE}cdecl;{$ENDIF}

  function GetParameter(name:ULONG; var param:ULONG):ULONG; {$IFDEF WINDOWS}stdcall;{$ELSE}cdecl;{$ENDIF}
  function SetParameter(name:ULONG; var param:ULONG):ULONG; {$IFDEF WINDOWS}stdcall;{$ELSE}cdecl;{$ENDIF}

  function SetLDeviceEvent(hEvent:THandle; EventId:ULONG):ULONG; {$IFDEF WINDOWS}stdcall;{$ELSE}cdecl;{$ENDIF}
 end;

type
 IDaqLDevice2 = interface(LUnknown)
  function InitStartLDeviceEx(StreamId:ULONG):ULONG; {$IFDEF WINDOWS}stdcall;{$ELSE}cdecl;{$ENDIF}
  function StartLDeviceEx(StreamId:ULONG):ULONG; {$IFDEF WINDOWS}stdcall;{$ELSE}cdecl;{$ENDIF}
  function StopLDeviceEx(StreamId:ULONG):ULONG; {$IFDEF WINDOWS}stdcall;{$ELSE}cdecl;{$ENDIF}
 end;

////////////////////////////////////////////////////////////////////////////////
// unit Create
// VIP NB:
// 1) In Windows lcomp.dll expected to be in PATH or in current directory.
// 2) In Linux liblcomp.so should be in ldconfig search path or in curr.dir.
//    Write path of directory containing liblcomp.so to /etc/ld.so.conf.d/,
//    then call `sudo ldconfig` to update library database. After that the
//    library liblcomp.so will be accessible from anywhere.
////////////////////////////////////////////////////////////////////////////////

const
 {$IFDEF WINDOWS}
 LCOMP_DLL_NAME = 'lcomp.dll';
 {$ELSE}
 LCOMP_DLL_NAME = 'liblcomp.so';
 {$ENDIF}

 // Load lcomp.dll/liblcomp.so and init interface factory.
function LoadLCreateInstance(name:PChar=LCOMP_DLL_NAME):Boolean;

 // Interface factory.
function LCreateInstance(Slot:ULONG):LUnknown;

////////////////////////////////////////////////////////////////////////////////
// LCARD drivers require kernel modules to be loaded.
// Assume crwkit and daqgroup-lcard packages installed.
////////////////////////////////////////////////////////////////////////////////

const
 L_MAX_NUM_SLOTS = 128; // Max avail number of slots (LCARD devices)
 L_MIN_SLOT      = 0;                 // Min slot number
 L_MAX_SLOT      = L_MAX_NUM_SLOTS-1; // Max slot number

const // Command to start LCARD kernel modules.
 {$IFDEF WINDOWS}
 LKernelModulesLoader : LongString = '';
 {$ELSE}
 LKernelModulesLoader : LongString = 'unix sudoit unix ldev-cpl start';
 {$ENDIF}

 // Device path like /dev/ldevice0.
function LDevicePath(Slot:LongInt):LongString;

 // Device udev symlink like /dev/ldev0.
function LDeviceLink(Slot:LongInt):LongString;

 // Check device path or link exists.
function LDeviceExists(const Path:LongString):Boolean;

 // Check if LCARD kernel modules loaded.
 // The device /dev/ldevice0 must exists.
function LKernelModulesLoaded:Boolean;

 // Check if LCARD kernel modules are loaded.
 // Load modules if they still not loaded yet.
function LKernelModulesLoad:Boolean;

////////////////////////////////////////////////////////////////////////////////
// Utility functions for LCARD devices
////////////////////////////////////////////////////////////////////////////////

 // Return Object reference counter.
function LRefCounter(aObject:LUnknown):LongInt;

// Get LCARD board name by BoardType.
function LBoardName(BoardType:ULONG):LongString; overload;
function LBoardName(const sp:SLOT_PAR):LongString; overload;

 // Get LCARD board type:name by BoardType.
function LBoardTypeName(BoardType:ULONG):LongString; overload;
function LBoardTypeName(const sp:SLOT_PAR):LongString; overload;

 // Get LCARD error name by code.
function LErrorName(code:ULONG):LongString;

// Get LCARD error code:name by code.
function LErrorCodeName(code:ULONG):LongString;

 // Get (read from flash) board name by board type.
function LPlataBrdName(BoardType:ULONG; const pd:PLATA_DESCR_U2):LongString; overload;
function LPlataBrdName(const sp:SLOT_PAR; const pd:PLATA_DESCR_U2):LongString; overload;

 // Get (read from flash) board serial number by board type.
function LPlataSerNum(BoardType:ULONG; const pd:PLATA_DESCR_U2):LongString; overload;
function LPlataSerNum(const sp:SLOT_PAR; const pd:PLATA_DESCR_U2):LongString; overload;

// Get (read from flash) board Revision by board type.
function LPlataRev(BoardType:ULONG; const pd:PLATA_DESCR_U2):Char; overload;
function LPlataRev(const sp:SLOT_PAR; const pd:PLATA_DESCR_U2):Char; overload;

// Get (read from flash) board DSP type by board type.
function LPlataDspType(BoardType:ULONG; const pd:PLATA_DESCR_U2):LongString; overload;
function LPlataDspType(const sp:SLOT_PAR; const pd:PLATA_DESCR_U2):LongString; overload;

// Get (read from flash) board IsDaqPresent flag by board type.
function LPlataIsDacPresent(BoardType:ULONG; const pd:PLATA_DESCR_U2):USHORT; overload;
function LPlataIsDacPresent(const sp:SLOT_PAR; const pd:PLATA_DESCR_U2):USHORT; overload;

// Get (read from flash) board Quartz by board type.
function LPlataQuartz(BoardType:ULONG; const pd:PLATA_DESCR_U2):ULONG; overload;
function LPlataQuartz(const sp:SLOT_PAR; const pd:PLATA_DESCR_U2):ULONG; overload;

// Get (read from flash) board CRC by board type.
function LPlataCrc(BoardType:ULONG; const pd:PLATA_DESCR_U2):LongInt; overload;
function LPlataCrc(const sp:SLOT_PAR; const pd:PLATA_DESCR_U2):LongInt; overload;

// Get array as CSV (comma separated list x1, x2, x3, …).
function LArrayToCsv(const x:array of single; const sep:LongString=', '):LongString; overload;
function LArrayToCsv(const x:array of USHORT; const sep:LongString=', '):LongString; overload;
function LArrayToCsv(const x:array of ULONG; const sep:LongString=', '):LongString; overload;

// Get (read from flash) board KoefADC by board type.
function LPlataKoefAdcCsv(BoardType:ULONG; const pd:PLATA_DESCR_U2; const sep:LongString=', '):LongString; overload;
function LPlataKoefAdcCsv(const sp:SLOT_PAR; const pd:PLATA_DESCR_U2; const sep:LongString=', '):LongString; overload;

// Get (read from flash) board KoefDAC by board type.
function LPlataKoefDacCsv(BoardType:ULONG; const pd:PLATA_DESCR_U2; const sep:LongString=', '):LongString; overload;
function LPlataKoefDacCsv(const sp:SLOT_PAR; const pd:PLATA_DESCR_U2; const sep:LongString=', '):LongString; overload;

// Get (read from flash) board Custon by board type.
function LPlataCustomCsv(BoardType:ULONG; const pd:PLATA_DESCR_U2; const sep:LongString=', '):LongString; overload;
function LPlataCustomCsv(const sp:SLOT_PAR; const pd:PLATA_DESCR_U2; const sep:LongString=', '):LongString; overload;
function LPlataCustomHex(BoardType:ULONG; const pd:PLATA_DESCR_U2):LongString; overload;
function LPlataCustomHex(const sp:SLOT_PAR; const pd:PLATA_DESCR_U2):LongString; overload;

 //////////////////////////////////////////////////////////
 // LBoard encaplulate all LCARD device parameters together
 //////////////////////////////////////////////////////////
type
 LBoard = object                                  // LCard board params
  Slot  : LongInt;                                // Slot number or -1
  Errno : LongInt;                                // Last error code
  Sp    : SLOT_PAR;                               // Slot parameters
  Pd    : PLATA_DESCR_U2;                         // Plata description
  Ap    : ASYNC_PAR;                              // For Async I/O
  Adc   : ADC_PAR;                                // ADC parameters
  Dac   : DAC_PAR;                                // DAC parameters
 public
  procedure Reset;                                // Zero all
  function  BoardType:ULONG; inline;              // Board type code
  function  BoardName:LongString;                 // Board name by type
  function  BoardTypeName:LongString;             // Board type:name
  function  ErrorName(code:ULONG):LongString;     // Error name by code
  function  ErrorCodeName(code:ULONG):LongString; // Error code:name
  function  PlataBoardName:LongString;            // Board name from description
  function  PlataSerNum:LongString;               // Board serial number
  function  PlataRev:Char;                        // Board revision code
  function  PlataDspType:LongString;              // Board DSP type
  function  PlataIsDacPresent:USHORT;             // Is DAC present on board?
  function  PlataQuartz:ULONG;                    // Board quartz, Hz
  function  PlataCrc:LongInt;                     // Board CRC or -1
  function  PlataKoefAdcCsv:LongString;           // Koef ADC as CSV list
  function  PlataKoefDacCsv:LongString;           // Koef DAC as CSV list
  function  PlataCustomCsv:LongString;            // Custom data as CSV
  function  PlataCustomHex:LongString;            // Custom data as HEX
 end;

implementation

//////////////////////////////////////
// Utility functions for LCARD devices
//////////////////////////////////////

function LRefCounter(aObject:LUnknown):LongInt;
begin
 Result:=0;
 if Assigned(aObject) then begin
  aObject._AddRef;
  Result:=aObject._Release;
  if (Result>0) then Dec(Result);
 end;
end;

function LBoardName(BoardType:ULONG):LongString;
begin
 case BoardType of
  L_NONE      : Result:='NONE';    // no board in slot
  L_L1250     : Result:='L1250';   // L1250 board
  L_N1250     : Result:='N1250';   // N1250 board (may be not work)
  L_L1251     : Result:='L1251';   // L1251 board
  L_L1221     : Result:='L1221';   // L1221 board
  L_PCIA      : Result:='PCIA';    // PCI rev A board
  L_PCIB      : Result:='PCIB';    // PCI rev B board
  L_L264      : Result:='L264';    // L264 ISA board
  L_L305      : Result:='L305';    // L305 ISA board
  L_L1450C    : Result:='L1450C';  //
  L_L1450     : Result:='L1450';   //
  L_L032      : Result:='L032';    //
  L_HI8       : Result:='HI8';     //
  L_PCIC      : Result:='PCIC';    //
  L_L791      : Result:='L791';    //
  L_E440      : Result:='E440';    //
  L_E140      : Result:='E140';    //
  L_E2010     : Result:='E2010';   //
  L_E270      : Result:='E270';    //
  L_CAN_USB   : Result:='CAN_USB'; //
  L_AK9       : Result:='AK9';     //
  L_LTR010    : Result:='LTR010';  //
  L_LTR021    : Result:='LTR021';  //
  L_E154      : Result:='E154';    //
  L_E2010B    : Result:='E2010B';  //
  L_LTR031    : Result:='LTR031';  //
  L_LTR030    : Result:='LTR030';  //
  L_E310      : Result:='E310';    //
  L_CA01      : Result:='CA01';    // cardio
  else          Result:='UNKNOWN';
 end;
end;
function LBoardName(const sp:SLOT_PAR):LongString;
begin
 Result:=LBoardName(sp.BoardType);
end;

function LBoardTypeName(BoardType:ULONG):LongString;
begin
 Result:=IntToStr(BoardTYpe)+':'+LBoardName(BoardType);
end;
function LBoardTypeName(const sp:SLOT_PAR):LongString;
begin
 Result:=LBoardTypeName(sp.BoardType);
end;

function LErrorName(code:ULONG):LongString;
begin
 case code of
  L_SUCCESS       : Result:='SUCCESS';
  L_NOTSUPPORTED  : Result:='NOTSUPPORTED';
  L_ERROR         : Result:='ERROR';
  L_ERROR_NOBOARD : Result:='ERROR_NOBOARD';
  L_ERROR_INUSE   : Result:='ERROR_INUSE';
  else              Result:='ERROR_UNKNOWN';
 end;
end;

function LErrorCodeName(code:ULONG):LongString;
begin
 Result:=IntToStr(code)+':'+LErrorName(code);
end;

function LBoardTypeTx(BoardType:ULONG):Integer;
begin
 case BoardType of
  L_PCIA,L_PCIB,L_PCIC : Result:=1; // t1
  L_L1450,L_L1450C     : Result:=2; // t2
  L_L791               : Result:=3; // t3
  L_E440               : Result:=4; // t4
  L_E140               : Result:=5; // t5
  L_E2010B,L_E2010     : Result:=6; // t6
  L_E154               : Result:=7; // t7
  else                   Result:=0; // None
 end;
end;

function SafeStrPas(Str:PChar; n:Integer):LongString;
var buf:TParsingBuffer;
begin
 if (n<1) then n:=MaxInt;
 n:=Min(n,SizeOf(buf)-1);
 Result:=StrPas(StrLCopy(buf,Str,n));
end;

function LPlataBrdName(BoardType:ULONG; const pd:PLATA_DESCR_U2):LongString;
begin
 case LBoardTypeTx(BoardType) of
  1:   Result:=SafeStrPas(pd.t1.BrdName,SizeOf(pd.t1.BrdName));
  2:   Result:=SafeStrPas(pd.t2.BrdName,SizeOf(pd.t2.BrdName));
  3:   Result:=SafeStrPas(pd.t3.BrdName,SizeOf(pd.t3.BrdName));
  4:   Result:=SafeStrPas(pd.t4.BrdName,SizeOf(pd.t4.BrdName));
  5:   Result:=SafeStrPas(pd.t5.BrdName,SizeOf(pd.t5.BrdName));
  6:   Result:=SafeStrPas(pd.t6.BrdName,SizeOf(pd.t6.BrdName));
  7:   Result:=SafeStrPas(pd.t7.BrdName,SizeOf(pd.t7.BrdName));
  else Result:='';
 end;
end;
function LPlataBrdName(const sp:SLOT_PAR; const pd:PLATA_DESCR_U2):LongString;
begin
 Result:=LPlataBrdName(sp.BoardType,pd);
end;

function LPlataSerNum(BoardType:ULONG; const pd:PLATA_DESCR_U2):LongString;
begin
 case LBoardTypeTx(BoardType) of
  1:   Result:=SafeStrPas(pd.t1.SerNum,SizeOf(pd.t1.SerNum));
  2:   Result:=SafeStrPas(pd.t2.SerNum,SizeOf(pd.t2.SerNum));
  3:   Result:=SafeStrPas(pd.t3.SerNum,SizeOf(pd.t3.SerNum));
  4:   Result:=SafeStrPas(pd.t4.SerNum,SizeOf(pd.t4.SerNum));
  5:   Result:=SafeStrPas(pd.t5.SerNum,SizeOf(pd.t5.SerNum));
  6:   Result:=SafeStrPas(pd.t6.SerNum,SizeOf(pd.t6.SerNum));
  7:   Result:=SafeStrPas(pd.t7.SerNum,SizeOf(pd.t7.SerNum));
  else Result:='';
 end;
end;
function LPlataSerNum(const sp:SLOT_PAR; const pd:PLATA_DESCR_U2):LongString;
begin
 Result:=LPlataSerNum(sp.BoardType,pd);
end;

function LPlataRev(BoardType:ULONG; const pd:PLATA_DESCR_U2):Char;
begin
 case LBoardTypeTx(BoardType) of
  1:   Result:=pd.t1.Rev;
  2:   Result:=pd.t2.Rev;
  3:   Result:=pd.t3.Rev;
  4:   Result:=pd.t4.Rev;
  5:   Result:=pd.t5.Rev;
  6:   Result:=pd.t6.Rev;
  7:   Result:=pd.t7.Rev;
  else Result:='?';
 end;
end;
function LPlataRev(const sp:SLOT_PAR; const pd:PLATA_DESCR_U2):Char;
begin
 Result:=LPlataRev(sp.BoardType,pd);
end;

function LPlataDspType(BoardType:ULONG; const pd:PLATA_DESCR_U2):LongString;
begin
 case LBoardTypeTx(BoardType) of
  1:   Result:=SafeStrPas(pd.t1.DspType,SizeOf(pd.t1.DspType));
  2:   Result:=SafeStrPas(pd.t2.DspType,SizeOf(pd.t2.DspType));
  3:   Result:=SafeStrPas(pd.t3.DspType,SizeOf(pd.t3.DspType));
  4:   Result:=SafeStrPas(pd.t4.DspType,SizeOf(pd.t4.DspType));
  5:   Result:=SafeStrPas(pd.t5.DspType,SizeOf(pd.t5.DspType));
  6:   Result:=SafeStrPas(pd.t6.DspType,SizeOf(pd.t6.DspType));
  7:   Result:=SafeStrPas(pd.t7.DspType,SizeOf(pd.t7.DspType));
  else Result:='';
 end;
end;
function LPlataDspType(const sp:SLOT_PAR; const pd:PLATA_DESCR_U2):LongString;
begin
 Result:=LPlataDspType(sp.BoardType,pd);
end;

function LPlataIsDacPresent(BoardType:ULONG; const pd:PLATA_DESCR_U2):USHORT;
begin
 case LBoardTypeTx(BoardType) of
  1:   Result:=Ord(pd.t1.IsDacPresent);
  2:   Result:=Ord(pd.t2.IsDacPresent);
  3:   Result:=Ord(pd.t3.IsDacPresent);
  4:   Result:=Ord(pd.t4.IsDacPresent);
  5:   Result:=Ord(pd.t5.IsDacPresent);
  6:   Result:=Ord(pd.t6.IsDacPresent);
  7:   Result:=Ord(pd.t7.IsDacPresent);
  else Result:=0;
 end;
end;
function LPlataIsDacPresent(const sp:SLOT_PAR; const pd:PLATA_DESCR_U2):USHORT;
begin
 Result:=LPlataIsDacPresent(sp.BoardType,pd);
end;

function LPlataQuartz(BoardType:ULONG; const pd:PLATA_DESCR_U2):ULONG;
begin
 case LBoardTypeTx(BoardType) of
  1:   Result:=pd.t1.Quartz;
  2:   Result:=pd.t2.Quartz;
  3:   Result:=pd.t3.Quartz;
  4:   Result:=pd.t4.Quartz;
  5:   Result:=pd.t5.Quartz;
  6:   Result:=pd.t6.Quartz;
  7:   Result:=pd.t7.Quartz;
  else Result:=0;
 end;
end;
function LPlataQuartz(const sp:SLOT_PAR; const pd:PLATA_DESCR_U2):ULONG;
begin
 Result:=LPlataQuartz(sp.BoardType,pd);
end;

function LPlataCrc(BoardType:ULONG; const pd:PLATA_DESCR_U2):LongInt;
begin
 case LBoardTypeTx(BoardType) of
  1:   Result:=-1;
  2:   Result:=-1;
  3:   Result:=pd.t3.crc;
  4:   Result:=-1;
  5:   Result:=-1;
  6:   Result:=pd.t6.CRC;
  7:   Result:=-1;
  else Result:=-1;
 end;
end;
function LPlataCrc(const sp:SLOT_PAR; const pd:PLATA_DESCR_U2):LongInt;
begin
 Result:=LPlataCrc(sp.BoardType,pd);
end;

function LArrayToCsv(const x:array of single; const sep:LongString=', '):LongString;
var i:Integer;
begin
 Result:='';
 for i:=Low(x) to High(x) do begin
  if (i>Low(x)) then Result:=Result+', ';
  Result:=Result+Format('%.7g',[x[i]]);
 end;
end;
function LArrayToCsv(const x:array of USHORT; const sep:LongString=', '):LongString;
var i:Integer;
begin
 Result:='';
 for i:=Low(x) to High(x) do begin
  if (i>Low(x)) then Result:=Result+sep;
  Result:=Result+Format('%u',[x[i]]);
 end;
end;
function LArrayToCsv(const x:array of ULONG; const sep:LongString=', '):LongString;
var i:Integer;
begin
 Result:='';
 for i:=Low(x) to High(x) do begin
  if (i>Low(x)) then Result:=Result+sep;
  Result:=Result+Format('%u',[x[i]]);
 end;
end;

function LPlataKoefAdcCsv(BoardType:ULONG; const pd:PLATA_DESCR_U2; const sep:LongString=', '):LongString;
begin
 case LBoardTypeTx(BoardType) of
  1:   Result:=LArrayToCsv(pd.t1.KoefADC,sep);
  2:   Result:=LArrayToCsv(pd.t2.KoefADC,sep);
  3:   Result:=LArrayToCsv(pd.t3.KoefADC,sep);
  4:   Result:=LArrayToCsv(pd.t4.KoefADC,sep);
  5:   Result:=LArrayToCsv(pd.t5.KoefADC,sep);
  6:   Result:=LArrayToCsv(pd.t6.KoefADC,sep);
  7:   Result:=LArrayToCsv(pd.t7.KoefADC,sep);
  else Result:='';
 end;
end;
function LPlataKoefAdcCsv(const sp:SLOT_PAR; const pd:PLATA_DESCR_U2; const sep:LongString=', '):LongString;
begin
 Result:=LPlataKoefAdcCsv(sp.BoardType,pd);
end;

function LPlataKoefDacCsv(BoardType:ULONG; const pd:PLATA_DESCR_U2; const sep:LongString=', '):LongString;
begin
 case LBoardTypeTx(BoardType) of
  1:   Result:=LArrayToCsv(pd.t1.KoefDAC,sep);
  2:   Result:=LArrayToCsv(pd.t2.KoefDAC,sep);
  3:   Result:=LArrayToCsv(pd.t3.KoefDAC,sep);
  4:   Result:=LArrayToCsv(pd.t4.KoefDAC,sep);
  5:   Result:=LArrayToCsv(pd.t5.KoefDAC,sep);
  6:   Result:=LArrayToCsv(pd.t6.KoefDAC,sep);
  7:   Result:=LArrayToCsv(pd.t7.KoefDAC,sep);
  else Result:='';
 end;
end;
function LPlataKoefDacCsv(const sp:SLOT_PAR; const pd:PLATA_DESCR_U2; const sep:LongString=', '):LongString;
begin
 Result:=LPlataKoefDacCsv(sp.BoardType,pd);
end;

function LPlataCustomCsv(BoardType:ULONG; const pd:PLATA_DESCR_U2; const sep:LongString=', '):LongString;
begin
 case LBoardTypeTx(BoardType) of
  1:   Result:=LArrayToCsv(pd.t1.Custom,sep);
  2:   Result:=LArrayToCsv(pd.t2.Custom,sep);
  3:   Result:=LArrayToCsv(pd.t3.Custom,sep);
  4:   Result:=LArrayToCsv(pd.t4.Custom,sep);
  5:   Result:=LArrayToCsv(pd.t5.Custom,sep);
  6:   Result:=LArrayToCsv(pd.t6.Custom,sep);
  7:   Result:=LArrayToCsv(pd.t7.Custom,sep);
  else Result:='';
 end;
end;
function LPlataCustomCsv(const sp:SLOT_PAR; const pd:PLATA_DESCR_U2; const sep:LongString=', '):LongString;
begin
 Result:=LPlataCustomCsv(sp.BoardType,pd);
end;

function LPlataCustomHex(BoardType:ULONG; const pd:PLATA_DESCR_U2):LongString;
begin
 case LBoardTypeTx(BoardType) of
  1:   Result:=hex_encode(Dump(pd.t1.Custom,SizeOf(pd.t1.Custom)));
  2:   Result:=hex_encode(Dump(pd.t2.Custom,SizeOf(pd.t2.Custom)));
  3:   Result:=hex_encode(Dump(pd.t3.Custom,SizeOf(pd.t3.Custom)));
  4:   Result:=hex_encode(Dump(pd.t4.Custom,SizeOf(pd.t4.Custom)));
  5:   Result:=hex_encode(Dump(pd.t5.Custom,SizeOf(pd.t5.Custom)));
  6:   Result:=hex_encode(Dump(pd.t6.Custom,SizeOf(pd.t6.Custom)));
  7:   Result:=hex_encode(Dump(pd.t7.Custom,SizeOf(pd.t7.Custom)));
  else Result:='';
 end;
end;
function LPlataCustomHex(const sp:SLOT_PAR; const pd:PLATA_DESCR_U2):LongString;
begin
 Result:=LPlataCustomHex(sp.BoardType,pd);
end;

/////////////////////////////
// Utilities from E440cmd.pas
/////////////////////////////

function LBIOS_OUTVAR(v:USHORT):ULONG;
var t:ULONG;
begin
 t:=v;
 Result:=(t shl 8);
end;

function LBIOS_INVAR(v:ULONG):USHORT;
begin
 Result:=(v shr 8);
end;

/////////////////////////////////////////
// NameSpace and kernel modules utilities
/////////////////////////////////////////

function LDevicePath(Slot:LongInt):LongString;
begin
 Result:='';
 if (Slot<L_MIN_SLOT) then Exit;
 if (Slot>L_MAX_SLOT) then Exit;
 if IsUnix then Result:='/dev/ldevice'+IntToStr(Slot);
 if IsWindows then Result:='\\.\ldev'+IntToStr(Slot);
end;

function LDeviceLink(Slot:LongInt):LongString;
begin
 Result:='';
 if (Slot<L_MIN_SLOT) then Exit;
 if (Slot>L_MAX_SLOT) then Exit;
 if IsUnix then Result:='/dev/ldev'+IntToStr(Slot);
 if IsWindows then Result:='\\.\ldev'+IntToStr(Slot);
end;

function wDeviceExists(Path:LongString):Boolean;
{$IFDEF WINDOWS}var hdev:System.THandle;{$ENDIF}
begin
 Result:=false;
 {$IFDEF WINDOWS}
 if (Path='') then Exit;
 hdev:=CreateFile(PChar(Path),GENERIC_READ,0,nil,OPEN_EXISTING,FILE_ATTRIBUTE_DEVICE,0);
 if (hdev=INVALID_HANDLE_VALUE) and (GetLastOsError=ERROR_ACCESS_DENIED) then Exit(true);
 if (hdev=INVALID_HANDLE_VALUE) then Exit;
 CloseHandle(hdev);
 Result:=true;
 {$ENDIF}
end;

function LDeviceExists(const Path:LongString):Boolean;
begin
 Result:=false;
 if IsUnix then Result:=FileExists(Path);
 if IsWindows then Result:=wDeviceExists(Path);
end;

function LKernelModulesLoaded:Boolean;
begin
 Result:=false;
 if IsUnix then begin
  // On Linux host - check /dev/ldevice0 exists.
  if LDeviceExists(LDevicePath(0)) then Result:=true;
 end;
 if IsWindows then begin
  // On Windows - check lcomp.dll exists in PATH.
  if (FileSearch(LCOMP_DLL_NAME,GetEnvironmentVariable('PATH'),false)<>'')
  then Result:=true;
 end;
end;

function LKernelModulesLoad:Boolean;
var s:LongString;
begin
 Result:=false;
 if LKernelModulesLoaded then Exit(true);
 if IsUnix and not HasSudoRights then Exit;
 if (Trim(LKernelModulesLoader)<>'')
 then RunCommand(LKernelModulesLoader,s);
 if LKernelModulesLoaded then Exit(true);
end;

///////////////////////////////////
// Interface factory implementation
///////////////////////////////////

type // NB: Required typecast to LUnknown
 TCreateInstance = function(Slot:ULONG):Pointer; cdecl;

var
 hModule         : THandle         = 0;
 FCreateInstance : TCreateInstance = nil;

function LoadLCreateInstance(name:PChar):Boolean;
begin
 if (hModule=0) then hModule:=SafeLoadLibrary(StrPas(name));
 if (hModule=0) then Exit(false); // Library not found or invalid.
 if not Assigned(FCreateInstance) then FCreateInstance:=GetProcAddress(hModule,'CreateInstance');
 if not Assigned(FCreateInstance) then Exit(false); // Function not found.
 Result:=true; // Succeed.
end;

function LCreateInstance(Slot:ULONG):LUnknown;
var P:Pointer;
begin
 if not LKernelModulesLoad then Exit(nil);
 if LoadLCreateInstance and Assigned(FCreateInstance)
 then P:=FCreateInstance(Slot)
 else P:=nil;
 Result:=LUnknown(P);
end;

////////////////////////
// LBoard implementation
////////////////////////

procedure LBoard.Reset;
begin
 Slot:=-1;
 Errno:=0;
 Sp:=Default(SLOT_PAR);
 Pd:=Default(PLATA_DESCR_U2);
 Ap:=Default(ASYNC_PAR);
 Adc:=Default(ADC_PAR);
 Dac:=Default(DAC_PAR);
end;

function LBoard.BoardType:ULONG;
begin
 Result:=Sp.BoardType;
end;

function LBoard.BoardName:LongString;
begin
 Result:=LBoardName(Sp.BoardType);
end;

function LBoard.BoardTypeName:LongString;
begin
 Result:=LBoardTypeName(Sp.BoardType);
end;

function LBoard.ErrorName(code:ULONG):LongString;
begin
 Result:=LErrorName(code);
end;

function LBoard.ErrorCodeName(code:ULONG):LongString;
begin
 Result:=LErrorCodeName(code);
end;

function LBoard.PlataBoardName:LongString;
begin
 Result:=LPlataBrdName(Sp,Pd);
end;

function LBoard.PlataSerNum:LongString;
begin
 Result:=LPlataSerNum(Sp,Pd);
end;

function LBoard.PlataRev:Char;
begin
 Result:=LPlataRev(Sp,Pd);
end;

function LBoard.PlataDspType:LongString;
begin
 Result:=LPlataDspType(Sp,Pd);
end;

function LBoard.PlataIsDacPresent:USHORT;
begin
 Result:=LPlataIsDacPresent(Sp,Pd);
end;

function LBoard.PlataQuartz:ULONG;
begin
 Result:=LPlataQuartz(Sp,Pd);
end;

function LBoard.PlataCrc:LongInt;
begin
 Result:=LPlataCrc(Sp,Pd);
end;

function LBoard.PlataKoefAdcCsv:LongString;
begin
 Result:=LPlataKoefAdcCsv(Sp,Pd);
end;

function LBoard.PlataKoefDacCsv:LongString;
begin
 Result:=LPlataKoefDacCsv(Sp,Pd);
end;

function LBoard.PlataCustomCsv:LongString;
begin
 Result:=LPlataCustomCsv(Sp,Pd);
end;

function LBoard.PlataCustomHex:LongString;
begin
 Result:=LPlataCustomHex(Sp,Pd);
end;

///////////////////////////////////////
// Unit initialization and finalization
///////////////////////////////////////

procedure Init_crw_lcard;
begin
end;

procedure Free_crw_lcard;
begin
end;

initialization

 Init_crw_lcard;

finalization

 Free_crw_lcard;

end.

//////////////
// END OF FILE
//////////////

