synaser.pas - POSIX and Android support by ACBr

git-svn-id: https://svn.code.sf.net/p/synalist/code/trunk@253 7c85be65-684b-0410-a082-b2ed4fbef004
This commit is contained in:
geby 2022-01-14 17:08:30 +00:00
parent aa1fdbacc4
commit 49454ea6e1

View File

@ -1,9 +1,9 @@
{==============================================================================|
| Project : Ararat Synapse | 007.006.002 |
| Project : Ararat Synapse | 007.007.000 |
|==============================================================================|
| Content: Serial port support |
|==============================================================================|
| Copyright (c)2001-2021, Lukas Gebauer |
| Copyright (c)2001-2022, Lukas Gebauer |
| All rights reserved. |
| |
| Redistribution and use in source and binary forms, with or without |
@ -83,6 +83,15 @@ case with my USB modem):
{$ENDIF}
{$ENDIF}
{$IFDEF UNIX}
{$DEFINE USE_LINUX_LOCK}
{$ENDIF}
{$IFDEF ANDROID}
{$DEFINE UNIX}
{$UNDEF USE_LINUX_LOCK}
{$ENDIF}
{$IFDEF FPC}
{$MODE DELPHI}
{$IFDEF MSWINDOWS}
@ -96,6 +105,17 @@ case with my USB modem):
{define working mode w/o LIBC for fpc}
{$DEFINE NO_LIBC}
{$ENDIF}
{$IFDEF POSIX}
{$WARN UNIT_PLATFORM OFF}
{$WARN SYMBOL_PLATFORM OFF}
{$ENDIF}
{$IFDEF NEXTGEN}
{$LEGACYIFEND ON}
{$ZEROBASEDSTRINGS OFF}
{$ENDIF}
{$Q-}
{$H+}
{$M+}
@ -106,11 +126,18 @@ interface
uses
{$IFNDEF MSWINDOWS}
{$IFNDEF NO_LIBC}
Libc,
KernelIoctl,
{$IFDEF POSIX}
Posix.Termios, Posix.Fcntl, Posix.Unistd, Posix.Stropts, Posix.SysSelect, Posix.SysTime,
{$IFDEF LINUX}
Linuxapi.KernelIoctl,
{$ENDIF}
{$ELSE}
termio, baseunix, unix,
{$IFNDEF NO_LIBC}
Libc,
KernelIoctl,
{$ELSE}
termio, baseunix, unix,
{$ENDIF}
{$ENDIF}
{$IFNDEF FPC}
Types,
@ -132,6 +159,7 @@ const
LockfileDirectory = '/var/lock'; {HGJ}
PortIsClosed = -1; {HGJ}
ErrAccessDenied = 9990; {DSA}
ErrAlreadyOwned = 9991; {HGJ}
ErrAlreadyInUse = 9992; {HGJ}
ErrWrongParameter = 9993; {HGJ}
@ -203,7 +231,7 @@ type
const
{$IFDEF UNIX}
{$IFDEF BSD}
MaxRates = 18; //MAC
MaxRates = 18; //MAC
{$ELSE}
{$IFDEF CPUARM}
MaxRates = 19; //CPUARM
@ -261,10 +289,27 @@ const // From fcntl.h
O_SYNC = $0080; { synchronous writes }
{$ENDIF}
{$IFDEF ANDROID}
const
TIOCMSET = $5418;
TIOCMGET = $5415;
TCSBRK = $5409;
{$ENDIF}
const
sOK = 0;
sErr = integer(-1);
{$IFDEF POSIX}
const
TIOCM_DTR = $002;
TIOCM_RTS = $004;
TIOCM_CTS = $020;
TIOCM_CAR = $040;
TIOCM_RNG = $080;
TIOCM_DSR = $100;
{$ENDIF}
type
{:Possible status event types for @link(THookSerialStatus)}
@ -341,9 +386,11 @@ type
procedure GetComNr(Value: string); virtual;
function PreTestFailing: boolean; virtual;{HGJ}
function TestCtrlLine: Boolean; virtual;
{$IFDEF UNIX}
{$IFNDEF MSWINDOWS}
procedure DcbToTermios(const dcb: TDCB; var term: termios); virtual;
procedure TermiosToDcb(const term: termios; var dcb: TDCB); virtual;
{$ENDIF}
{$IFDEF USE_LINUX_LOCK}
function ReadLockfile: integer; virtual;
function LockfileName: String; virtual;
procedure CreateLockfile(PidNr: integer); virtual;
@ -354,7 +401,7 @@ type
{: data Control Block with communication parameters. Usable only when you
need to call API directly.}
DCB: Tdcb;
{$IFDEF UNIX}
{$IFNDEF MSWINDOWS}
TermiosStruc: termios;
{$ENDIF}
{:Object constructor.}
@ -632,7 +679,7 @@ type
{:Raise Synaser error with ErrNumber code. Usually used by internal routines.}
procedure RaiseSynaError(ErrNumber: integer); virtual;
{$IFDEF UNIX}
{$IFDEF USE_LINUX_LOCK}
function cpomComportAccessible: boolean; virtual;{HGJ}
procedure cpomReleaseComport; virtual; {HGJ}
{$ENDIF}
@ -807,7 +854,7 @@ begin
end;
if InstanceActive then
begin
{$IFDEF UNIX}
{$IFDEF USE_LINUX_LOCK}
if FLinuxLock then
cpomReleaseComport;
{$ENDIF}
@ -882,7 +929,7 @@ begin
sleep(x);
end;
end;
Next := GetTick + Trunc((Length / MaxB) * 1000);
Next := GetTick + LongWord(Trunc((Length / MaxB) * 1000));
end;
end;
@ -946,23 +993,34 @@ begin
{$IFNDEF MSWINDOWS}
if FComNr <> PortIsClosed then
FDevice := '/dev/ttyS' + IntToStr(FComNr);
// Comport already owned by another process? {HGJ}
if FLinuxLock then
if not cpomComportAccessible then
begin
RaiseSynaError(ErrAlreadyOwned);
Exit;
end;
{$IFNDEF FPC}
FHandle := THandle(Libc.open(pchar(FDevice), O_RDWR or O_SYNC));
{$ELSE}
FHandle := THandle(fpOpen(FDevice, O_RDWR or O_SYNC));
{$ENDIF}
{$IFDEF USE_LINUX_LOCK}
// Comport already owned by another process? {HGJ}
if FLinuxLock then
if not cpomComportAccessible then
begin
if FileExists(LockfileName) then
RaiseSynaError(ErrAlreadyOwned)
else
RaiseSynaError(ErrAccessDenied);
Exit;
end;
{$ENDIF}
{$IFNDEF FPC}
{$IFDEF POSIX}
FHandle := open(MarshaledAString(AnsiString(FDevice)), O_RDWR or O_SYNC);
{$ELSE}
FHandle := THandle(Libc.open(pchar(FDevice), O_RDWR or O_SYNC));
{$ENDIF}
{$ELSE}
FHandle := THandle(fpOpen(FDevice, O_RDWR or O_SYNC));
{$ENDIF}
if FHandle = INVALID_HANDLE_VALUE then //because THandle is not integer on all platforms!
SerialCheck(-1)
else
SerialCheck(0);
{$IFDEF UNIX}
{$IFDEF USE_LINUX_LOCK}
if FLastError <> sOK then
if FLinuxLock then
cpomReleaseComport;
@ -999,7 +1057,7 @@ begin
begin
SetSynaError(ErrNoDeviceAnswer);
FileClose(FHandle); {HGJ}
{$IFDEF UNIX}
{$IFDEF USE_LINUX_LOCK}
if FLinuxLock then
cpomReleaseComport; {HGJ}
{$ENDIF} {HGJ}
@ -1648,7 +1706,11 @@ end;
procedure TBlockSerial.SetCommState;
begin
DcbToTermios(dcb, termiosstruc);
SerialCheck(tcsetattr(FHandle, TCSANOW, termiosstruc));
{$IfDef POSIX}
ioctl(Fhandle, TCSANOW, PInteger(@TermiosStruc));
{$Else}
SerialCheck(tcsetattr(FHandle, TCSANOW, termiosstruc));
{$EndIf}
ExceptCheck;
end;
{$ELSE}
@ -1811,7 +1873,7 @@ end;
{$IFNDEF MSWINDOWS}
function TBlockSerial.CanRead(Timeout: integer): boolean;
var
FDSet: TFDSet;
FDSet: {$IFDEF POSIX}FD_Set{$ELSE}TFDSet{$ENDIF};
TimeVal: PTimeVal;
TimeV: TTimeVal;
x: Integer;
@ -1823,7 +1885,7 @@ begin
TimeVal := nil;
{$IFNDEF FPC}
FD_ZERO(FDSet);
FD_SET(FHandle, FDSet);
{$IFDEF POSIX}_FD_SET{$ELSE}FD_SET{$ENDIF}(FHandle, FDSet);
x := Select(FHandle + 1, @FDSet, nil, nil, TimeVal);
{$ELSE}
fpFD_ZERO(FDSet);
@ -1853,7 +1915,7 @@ end;
{$IFNDEF MSWINDOWS}
function TBlockSerial.CanWrite(Timeout: integer): boolean;
var
FDSet: TFDSet;
FDSet: {$IFDEF POSIX}FD_Set{$ELSE}TFDSet{$ENDIF};
TimeVal: PTimeVal;
TimeV: TTimeVal;
x: Integer;
@ -1865,7 +1927,7 @@ begin
TimeVal := nil;
{$IFNDEF FPC}
FD_ZERO(FDSet);
FD_SET(FHandle, FDSet);
{$IFDEF POSIX}_FD_SET{$ELSE}FD_SET{$ENDIF}(FHandle, FDSet);
x := Select(FHandle + 1, nil, @FDSet, nil, TimeVal);
{$ELSE}
fpFD_ZERO(FDSet);
@ -1882,8 +1944,10 @@ begin
end;
{$ELSE}
function TBlockSerial.CanWrite(Timeout: integer): boolean;
{$IFDEF WIN32}
var
t: LongWord;
{$ENDIF}
begin
Result := SendingData = 0;
if not Result then
@ -1942,7 +2006,11 @@ end;
procedure TBlockSerial.Flush;
begin
{$IFNDEF MSWINDOWS}
SerialCheck(tcdrain(FHandle));
{$IFDEF ANDROID}
ioctl(FHandle, TCSBRK, 1);
{$ELSE}
SerialCheck(tcdrain(FHandle));
{$ENDIF}
{$ELSE}
SetSynaError(sOK);
if not Flushfilebuffers(FHandle) then
@ -2197,7 +2265,7 @@ end;
Ownership Manager.
}
{$IFDEF UNIX}
{$IFDEF USE_LINUX_LOCK}
function TBlockSerial.LockfileName: String;
var
@ -2209,8 +2277,13 @@ end;
procedure TBlockSerial.CreateLockfile(PidNr: integer);
var
f: TextFile;
s: string;
{$IFDEF FPC}
m: Word;
FS: TFileStream;
{$ELSE}
f: TextFile;
{$ENDIF}
begin
// Create content for file
s := IntToStr(PidNr);
@ -2218,6 +2291,7 @@ begin
s := ' ' + s;
// Create file
try
{$IFNDEF FPC}
AssignFile(f, LockfileName);
try
Rewrite(f);
@ -2225,6 +2299,21 @@ begin
finally
CloseFile(f);
end;
{$ELSE}
s := s + sLineBreak;
if FileExists(LockfileName) then
m := fmOpenReadWrite
else
m := fmCreate;
FS := TFileStream.Create(LockfileName, m or fmShareDenyWrite);
try
FS.Seek(0, soEnd);
FS.Write(Pointer(s)^, Length(s));
finally
FS.Free ;
end;
{$ENDIF}
// Allow all users to enjoy the benefits of cpom
s := 'chmod a+rw ' + LockfileName;
{$IFNDEF FPC}
@ -2263,7 +2352,7 @@ var
begin
Filename := LockfileName;
{$IFNDEF FPC}
MyPid := Libc.getpid;
MyPid := {$IFNDEF POSIX}Libc.{$ENDIF}getpid;
{$ELSE}
MyPid := fpGetPid;
{$ENDIF}
@ -2271,20 +2360,20 @@ begin
if not DirectoryExists(LockfileDirectory) then
CreateDir(LockfileDirectory);
// Check the Lockfile
if not FileExists (Filename) then
if not FileExists(Filename) then
begin // comport is not locked. Lock it for us.
CreateLockfile(MyPid);
result := true;
result := FileExists(Filename);
exit; // done.
end;
// Is port owned by orphan? Then it's time for error recovery.
//FPC forgot to add getsid.. :-(
{$IFNDEF FPC}
if Libc.getsid(ReadLockfile) = -1 then
if {$IFNDEF POSIX}Libc.{$ENDIF}getsid(ReadLockfile) = -1 then
begin // Lockfile was left from former desaster
DeleteFile(Filename); // error recovery
CreateLockfile(MyPid);
result := true;
result := FileExists(Filename);
exit;
end;
{$ENDIF}
@ -2330,13 +2419,15 @@ end;
{$ENDIF}
{$IFNDEF MSWINDOWS}
function GetSerialPortNames: string;
const
ATTR = {$IFDEF POSIX}$7FFFFFFF{$ELSE}$FFFFFFFF{$ENDIF};
var
sr : TSearchRec;
begin
Result := '';
if FindFirst('/dev/ttyS*', $FFFFFFFF, sr) = 0 then
if FindFirst('/dev/ttyS*', ATTR, sr) = 0 then
repeat
if (sr.Attr and $FFFFFFFF) = Sr.Attr then
if (sr.Attr and ATTR) = Sr.Attr then
begin
if Result <> '' then
Result := Result + ',';
@ -2344,18 +2435,18 @@ begin
end;
until FindNext(sr) <> 0;
FindClose(sr);
if FindFirst('/dev/ttyUSB*', $FFFFFFFF, sr) = 0 then begin
if FindFirst('/dev/ttyUSB*', ATTR, sr) = 0 then begin
repeat
if (sr.Attr and $FFFFFFFF) = Sr.Attr then begin
if (sr.Attr and ATTR) = Sr.Attr then begin
if Result <> '' then Result := Result + ',';
Result := Result + '/dev/' + sr.Name;
end;
until FindNext(sr) <> 0;
end;
FindClose(sr);
if FindFirst('/dev/ttyAM*', $FFFFFFFF, sr) = 0 then begin
if FindFirst('/dev/ttyAM*', ATTR, sr) = 0 then begin
repeat
if (sr.Attr and $FFFFFFFF) = Sr.Attr then begin
if (sr.Attr and ATTR) = Sr.Attr then begin
if Result <> '' then Result := Result + ',';
Result := Result + '/dev/' + sr.Name;
end;