unit IIOBindings; { Минимальный биндинг к libiio (классический API 0.x; в системе 0.26) с ДИНАМИЧЕСКОЙ загрузкой. Если libiio нет — IIOLoad возвращает False, и Pluto-бэкенд просто не участвует в discovery (приложение работает с HPSDR). Загружаем libiio.so / libiio.so.0 (Linux), iio.dll (Windows), libiio.dylib (macOS). Имена символов одинаковые на всех платформах. Покрыты функции для: scan (discovery), context create/destroy, поиск device/channel, чтение/запись атрибутов, буферы RX/TX. Этого набора хватает на фазы 1–4 (discovery → RX → управление → TX). } {$IFDEF FPC} {$MODE Delphi} {$ENDIF} interface uses dynlibs, ctypes, SysUtils; type // Непрозрачные указатели libiio Piio_scan_context = Pointer; Piio_context_info = Pointer; PPiio_context_info = ^Piio_context_info; // struct iio_context_info ** Piio_context = Pointer; Piio_device = Pointer; Piio_channel = Pointer; Piio_buffer = Pointer; var // ---- Scan / discovery ---- iio_create_scan_context: function(backend: PAnsiChar; flags: cuint): Piio_scan_context; cdecl; iio_scan_context_destroy: procedure(ctx: Piio_scan_context); cdecl; iio_scan_context_get_info_list: function(ctx: Piio_scan_context; var info: PPiio_context_info): ptrint; cdecl; iio_context_info_list_free: procedure(info: PPiio_context_info); cdecl; iio_context_info_get_description: function(info: Piio_context_info): PAnsiChar; cdecl; iio_context_info_get_uri: function(info: Piio_context_info): PAnsiChar; cdecl; // ---- Context ---- iio_create_context_from_uri: function(uri: PAnsiChar): Piio_context; cdecl; iio_context_destroy: procedure(ctx: Piio_context); cdecl; iio_context_get_name: function(ctx: Piio_context): PAnsiChar; cdecl; iio_context_find_device: function(ctx: Piio_context; name: PAnsiChar): Piio_device; cdecl; iio_context_get_attr_value: function(ctx: Piio_context; name: PAnsiChar): PAnsiChar; cdecl; // ---- Device / channel ---- // output — C '_Bool' (1 байт). Передаём явный байт 0/1 (ByteBool/True давал // некорректное значение → output-каналы не находились). iio_device_find_channel: function(dev: Piio_device; name: PAnsiChar; output: cuchar): Piio_channel; cdecl; iio_device_set_kernel_buffers_count: function(dev: Piio_device; nb: cuint): cint; cdecl; iio_channel_enable: procedure(chn: Piio_channel); cdecl; iio_channel_disable: procedure(chn: Piio_channel); cdecl; // ---- Атрибуты ---- iio_channel_attr_read_longlong: function(chn: Piio_channel; attr: PAnsiChar; out val: clonglong): cint; cdecl; iio_channel_attr_write_longlong: function(chn: Piio_channel; attr: PAnsiChar; val: clonglong): cint; cdecl; iio_channel_attr_write: function(chn: Piio_channel; attr: PAnsiChar; src: PAnsiChar): ptrint; cdecl; iio_channel_attr_write_bool: function(chn: Piio_channel; attr: PAnsiChar; val: ByteBool): cint; cdecl; iio_channel_attr_write_double: function(chn: Piio_channel; attr: PAnsiChar; val: cdouble): cint; cdecl; // ---- Буферы (RX refill / TX push) ---- iio_device_create_buffer: function(dev: Piio_device; samples_count: csize_t; cyclic: ByteBool): Piio_buffer; cdecl; iio_buffer_destroy: procedure(buf: Piio_buffer); cdecl; iio_buffer_cancel: procedure(buf: Piio_buffer); cdecl; // разблокирует refill/push iio_buffer_refill: function(buf: Piio_buffer): ptrint; cdecl; iio_buffer_push: function(buf: Piio_buffer): ptrint; cdecl; iio_buffer_first: function(buf: Piio_buffer; chn: Piio_channel): Pointer; cdecl; iio_buffer_step: function(buf: Piio_buffer): ptrint; cdecl; iio_buffer_end: function(buf: Piio_buffer): Pointer; cdecl; // ---- Утилиты ---- iio_strerror: procedure(err: cint; dst: PAnsiChar; len: csize_t); cdecl; // ---- libad9361 (опционально) ---- // ad9361_set_bb_rate: настраивает BBPLL + ЗАГРУЖАЕТ FIR-таблицу под частоту. // Без него прямая запись sampling_frequency на холодную даёт мусор (битый FIR). // nil если libad9361 не загружена. ad9361_set_bb_rate: function(dev: Piio_device; rate: culong): cint; cdecl; ad9361_set_trx_fir_enable: function(dev: Piio_device; enable: cint): cint; cdecl; // Загружает libiio (один раз). True — готово к использованию. Идемпотентно. function IIOLoad: Boolean; // True, если libiio загружена и символы разрешены. function IIOLoaded: Boolean; // Расшифровка кода ошибки libiio (err — отрицательный errno). function IIOStrError(err: cint): string; implementation var GLib: TLibHandle = NilHandle; GLibAD: TLibHandle = NilHandle; // libad9361 (опционально) GLoaded: Boolean = False; const {$IFDEF WINDOWS} IIO_LIBS: array[0..1] of string = ('libiio.dll', 'iio.dll'); AD_LIBS: array[0..1] of string = ('libad9361.dll', 'ad9361.dll'); {$ELSE} {$IFDEF DARWIN} IIO_LIBS: array[0..1] of string = ('libiio.dylib', 'libiio.1.dylib'); AD_LIBS: array[0..1] of string = ('libad9361.dylib', 'libad9361.0.dylib'); {$ELSE} IIO_LIBS: array[0..1] of string = ('libiio.so', 'libiio.so.0'); AD_LIBS: array[0..1] of string = ('libad9361.so', 'libad9361.so.0'); {$ENDIF} {$ENDIF} function Sym(const Name: string): Pointer; begin Result := GetProcedureAddress(GLib, Name); if Result = nil then raise Exception.CreateFmt('libiio: symbol "%s" not found', [Name]); end; function IIOLoaded: Boolean; begin Result := GLoaded; end; function IIOLoad: Boolean; var I: Integer; begin if GLoaded then Exit(True); if GLib = NilHandle then for I := 0 to High(IIO_LIBS) do begin GLib := LoadLibrary(IIO_LIBS[I]); if GLib <> NilHandle then Break; end; if GLib = NilHandle then Exit(False); try iio_create_scan_context := Sym('iio_create_scan_context'); iio_scan_context_destroy := Sym('iio_scan_context_destroy'); iio_scan_context_get_info_list := Sym('iio_scan_context_get_info_list'); iio_context_info_list_free := Sym('iio_context_info_list_free'); iio_context_info_get_description := Sym('iio_context_info_get_description'); iio_context_info_get_uri := Sym('iio_context_info_get_uri'); iio_create_context_from_uri := Sym('iio_create_context_from_uri'); iio_context_destroy := Sym('iio_context_destroy'); iio_context_get_name := Sym('iio_context_get_name'); iio_context_find_device := Sym('iio_context_find_device'); iio_context_get_attr_value := Sym('iio_context_get_attr_value'); iio_device_find_channel := Sym('iio_device_find_channel'); iio_device_set_kernel_buffers_count := Sym('iio_device_set_kernel_buffers_count'); iio_channel_enable := Sym('iio_channel_enable'); iio_channel_disable := Sym('iio_channel_disable'); iio_channel_attr_read_longlong := Sym('iio_channel_attr_read_longlong'); iio_channel_attr_write_longlong := Sym('iio_channel_attr_write_longlong'); iio_channel_attr_write := Sym('iio_channel_attr_write'); iio_channel_attr_write_bool := Sym('iio_channel_attr_write_bool'); iio_channel_attr_write_double := Sym('iio_channel_attr_write_double'); iio_device_create_buffer := Sym('iio_device_create_buffer'); iio_buffer_destroy := Sym('iio_buffer_destroy'); iio_buffer_cancel := Sym('iio_buffer_cancel'); iio_buffer_refill := Sym('iio_buffer_refill'); iio_buffer_push := Sym('iio_buffer_push'); iio_buffer_first := Sym('iio_buffer_first'); iio_buffer_step := Sym('iio_buffer_step'); iio_buffer_end := Sym('iio_buffer_end'); iio_strerror := Sym('iio_strerror'); // libad9361 — опционально (ad9361_set_bb_rate грузит FIR). Отсутствие не // фатально: ApplySampleRate откатится на прямую запись sampling_frequency. if GLibAD = NilHandle then for I := 0 to High(AD_LIBS) do begin GLibAD := LoadLibrary(AD_LIBS[I]); if GLibAD <> NilHandle then Break; end; if GLibAD <> NilHandle then begin ad9361_set_bb_rate := GetProcedureAddress(GLibAD, 'ad9361_set_bb_rate'); ad9361_set_trx_fir_enable := GetProcedureAddress(GLibAD, 'ad9361_set_trx_fir_enable'); end; GLoaded := True; except on E: Exception do begin UnloadLibrary(GLib); GLib := NilHandle; GLoaded := False; end; end; Result := GLoaded; end; function IIOStrError(err: cint): string; var Buf: array[0..255] of AnsiChar; begin if GLoaded and Assigned(iio_strerror) then begin FillChar(Buf, SizeOf(Buf), 0); iio_strerror(Abs(err), @Buf[0], SizeOf(Buf)); Result := string(PAnsiChar(@Buf[0])); end else Result := Format('iio error %d', [err]); end; finalization if GLibAD <> NilHandle then begin UnloadLibrary(GLibAD); GLibAD := NilHandle; end; if GLib <> NilHandle then begin UnloadLibrary(GLib); GLib := NilHandle; GLoaded := False; end; end.