diff --git a/synaser.pas b/synaser.pas index b645337..9523177 100644 --- a/synaser.pas +++ b/synaser.pas @@ -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;