123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468 |
- MODULE V24; (** AUTHOR "fof"; PURPOSE "V24 (V24/RS-232 driver) for WinAos"; **)
- IMPORT Kernel32, KernelLog, Heaps, Kernel, Commands, Serials, Strings;
- CONST
- MaxPortNo = 32; (* Up to 8 serial ports supported *)
- (*
- TYPE
- DCB32 = RECORD (* DCB structure for Win32 *)
- DCBlength: LONGINT; (* sizeof(DCB) *)
- BaudRate: LONGINT; (* current baud rate*)
- flags: SET; (* bits in flags:
- DWORD fBinary: 1; (* binary mode, no EOF check *)
- DWORD fParity: 1; (* enable parity checking *)
- DWORD fOutxCtsFlow:1; (* CTS output flow control *)
- DWORD fOutxDsrFlow:1; (* DSR output flow control *)
- DWORD fDtrControl:2; (* DTR flow control type *)
- DWORD fDsrSensitivity:1; (* DSR sensitivity *)
- DWORD fTXContinueOnXoff:1; (* XOFF continues Tx *)
- DWORD fOutX: 1; (* XON/XOFF out flow control *)
- DWORD fInX: 1; (* XON/XOFF in flow control *)
- DWORD fErrorChar: 1; (* enable error replacement *)
- DWORD fNull: 1; (* enable null stripping *)
- DWORD fRtsControl:2; (* RTS flow control *)
- DWORD fAbortOnError:1; (* abort reads/writes on error *)
- DWORD fDummy2:17; (* reserved *) *)
- wReserved: INTEGER; (* not currently used *)
- XonLim: INTEGER; (* transmit XON threshold *)
- XoffLim: INTEGER; (* transmit XOFF threshold *)
- ByteSize: SHORTINT; (* number of bits/byte, 4-8 *)
- Parity: SHORTINT; (* 0-4=no,odd,even,mark,space *)
- StopBits: SHORTINT; (* 0, 1, 2 = 1, 1.5, 2 *)
- XonChar: CHAR; (* Tx and Rx XON character *)
- XoffChar: CHAR; (* Tx and Rx XOFF character *)
- ErrorChar: CHAR; (* error replacement character *)
- EofChar: CHAR; (* end of input character *)
- EvtChar: CHAR (* received event character *)
- END;
- COMMTIMEOUTS = RECORD (* COMMTIMEOUTS *)
- ReadInterval: LONGINT;
- ReadTotalMultiplier: LONGINT;
- ReadTotalConstant: LONGINT;
- WriteTotalMultiplier: LONGINT;
- WriteTotalConstant: LONGINT
- END;
- COMSTAT32 = RECORD (* COMSTAT for Win32 *)
- status: SET; (* fields in status:
- DWORD fCtsHold : 1; (* Tx waiting for CTS signal *)
- DWORD fDsrHold : 1; (* Tx waiting for DSR signal *)
- DWORD fRlsdHold : 1; (* Tx waiting for RLSD signal *)
- DWORD fXoffHold : 1; (* Tx waiting, XOFF char rec'd *)
- DWORD fXoffSent : 1; (* Tx waiting, XOFF char sent *)
- DWORD fEof : 1; (* EOF character sent *)
- DWORD fTxim : 1; (* character waiting for Tx *)
- DWORD fReserved : 25; (* reserved *) *)
- cbInQueue: LONGINT; (* bytes in input buffer *)
- cbOutQueue: LONGINT (* bytes in output buffer *)
- END;
- *)
- (* Handle = POINTER TO RECORD (Kernel32.Object)
- (*
- recBuf: ARRAY BufSize OF S.BYTE;
- recBufSize, recBufPos: LONGINT;
- *)
- port: LONGINT
- END;
- *)
- TYPE
- Port* = OBJECT (Serials.Port)
- VAR
- handle: Kernel32.HANDLE;
- portname: ARRAY 6 OF CHAR; (* Name COM1 to COM8 must terminate with a 0X *)
- timer : Kernel.Timer;
-
- rOverlapped, wOverlapped: Kernel32.Overlapped;
- PROCEDURE & Init*( port: LONGINT );
- VAR fn: Heaps.FinalizerNode;
- BEGIN
- NEW(timer);
- COPY( "COM ", portname );
- IF port < 10 THEN
- portname[3] := CHR( ORD( "0" ) + port );
- portname[4]:= 0X;
- ELSE
- portname[3] := CHR( ORD( "0" ) + (port DIV 10) );
- portname[4] := CHR( ORD( "0" ) + (port MOD 10) );
- portname[5] := 0X;
- END;
- handle := Kernel32.InvalidHandleValue;
- NEW( fn ); fn.finalizer := SELF.FinalizePort; Heaps.AddFinalizer( SELF, fn );
- END Init;
- PROCEDURE Open*( bps, data, parity, stop: LONGINT; VAR res: LONGINT );
- BEGIN {EXCLUSIVE}
- IF handle = Kernel32.InvalidHandleValue THEN
- SetPortState( bps, data, parity, stop, res );
- IF res = Serials.Ok THEN
- rOverlapped.hEvent := Kernel32.CreateEvent(NIL,Kernel32.True,Kernel32.False,NIL);
- ASSERT(rOverlapped.hEvent # NIL);
- wOverlapped.hEvent := Kernel32.CreateEvent(NIL,Kernel32.True,Kernel32.False,NIL);
- ASSERT(wOverlapped.hEvent # NIL);
- KernelLog.String( portname ); KernelLog.String( " opened" ); KernelLog.Ln
- END;
- ELSE
- res := Serials.PortInUse;
- END;
- END Open;
- (** Send len characters from buf to output, starting at ofs. res is non-zero on error. *)
- PROCEDURE Send*( CONST buf: ARRAY OF CHAR; ofs, len: LONGINT; propagate: BOOLEAN; VAR res: LONGINT );
- VAR written: LONGINT; ret: Kernel32.BOOL;
- BEGIN
- ASSERT ( LEN( buf ) >= ofs + len ); (* array bound check not implemented in Kernel32.WriteFile *)
- IF (handle # Kernel32.InvalidHandleValue) THEN
- written := 0;
- ret := Kernel32.WriteFile( handle, buf[ofs], len, written, wOverlapped );
- IF ret = Kernel32.False THEN
- ret := Kernel32.GetOverlappedResult(handle,wOverlapped,written,Kernel32.True);
- END;
- INC(charactersSent,written);
- IF (ret # Kernel32.False) & (written = len) THEN
- res := Serials.Ok;
- ELSE
- res := Serials.TransportError;
- END;
- ELSE
- res := Serials.Closed;
- END
- END Send;
- (** Send a single character to the UART. *)
- PROCEDURE SendChar*( ch: CHAR; VAR res: LONGINT );
- VAR ret: Kernel32.BOOL; written: LONGINT;
- BEGIN
- IF handle # Kernel32.InvalidHandleValue THEN
- written := 0;
- ret := Kernel32.WriteFile( handle, ch, 1, written, wOverlapped );
- IF ret = Kernel32.False THEN
- ret := Kernel32.GetOverlappedResult(handle,wOverlapped,written,Kernel32.True);
- END;
- INC(charactersSent,written);
- IF (ret # Kernel32.False) & (written=1) THEN
- res := Serials.Ok;
- ELSE
- res := Serials.TransportError;
- END;
- ELSE
- res := Serials.Closed;
- END
- END SendChar;
- (** Receive size characters into buf, starting at ofs and return the effective number of bytes read in len.
- Wait until at least min bytes (possibly zero) are available.
- res is non-zero on error. *)
- PROCEDURE Receive*( VAR buf: ARRAY OF CHAR; ofs, size, min: LONGINT; VAR len, res: LONGINT );
- VAR i, l, read, ret: LONGINT;
- BEGIN
- ASSERT ( LEN( buf ) >= ofs + size );
- ASSERT ( LEN( buf ) >= ofs + min ); (* array bound check not implemented in Kernel32.ReadFile *)
- IF handle # Kernel32.InvalidHandleValue THEN
-
- res := Serials.Ok; len := 0;
-
- (* blocking read of the minimally required amount of data *)
- IF min > 0 THEN
- read := 0;
- ret := Kernel32.ReadFile( handle, buf[ofs], min, read, rOverlapped );
- IF ret = Kernel32.False THEN
- ret := Kernel32.GetOverlappedResult(handle,rOverlapped,read,Kernel32.True);
- END;
- INC(ofs,read); INC(len,read); INC(charactersReceived,read); DEC(size,read);
- IF ret = Kernel32.False THEN
- res := Serials.TransportError; RETURN;
- END;
- END;
-
- (* Non-blocking read of available data *)
- l := MIN(size,Available());
- IF l > 0 THEN
- read := 0;
- ret := Kernel32.ReadFile( handle, buf[ofs], l, read, rOverlapped );
- IF ret = Kernel32.False THEN
- ret := Kernel32.GetOverlappedResult(handle,rOverlapped,read,Kernel32.True);
- END;
- INC(len,read); INC(charactersReceived,read);
- IF ret = Kernel32.False THEN (* we've already received <min> bytes, but there was an error and we are going to report it anyway! *)
- res := Serials.TransportError;
- END;
- END;
- ELSE
- res := Serials.Closed;
- END;
- END Receive;
- (** Wait for the next character is received in the input buffer. *)
- PROCEDURE ReceiveChar*( VAR ch: CHAR; VAR res: LONGINT );
- VAR l, ret, read: LONGINT;
- BEGIN
- IF handle # Kernel32.InvalidHandleValue THEN
- read := 0;
- ret := Kernel32.ReadFile( handle, ch, 1, read, rOverlapped );
- IF ret = Kernel32.False THEN
- ret := Kernel32.GetOverlappedResult(handle,rOverlapped,read,Kernel32.True);
- END;
- INC(charactersReceived,read);
- IF (ret # Kernel32.False) & (read = 1) THEN
- res := Serials.Ok;
- ELSE
- res := Serials.TransportError;
- END;
- ELSE
- res := Serials.Closed;
- END
- END ReceiveChar;
- PROCEDURE Available*( ): LONGINT;
- VAR errors: Kernel32.DWORD; stat: Kernel32.ComStat; res: Kernel32.BOOL;
- BEGIN
- IF handle # Kernel32.InvalidHandleValue THEN
- res := Kernel32.ClearCommError( handle, errors, stat );
- IF res # Kernel32.False THEN RETURN stat.cbInQue END
- END;
- RETURN 0
- END Available;
- (** Open a serial port (numbered from 0) connection. bps is the required bits per second.
- data is the number of bits per communication unit. parity is the parity mode.
- stop is the number of stop bits.
- res: Ok, PortInUse, NoSuchPort, WrongBPS, WrongData, WrongParity, WrongStop, Failed *)
- PROCEDURE SetPortState( bps, data, parity, stop: LONGINT; VAR res: LONGINT );
- VAR
- hFile: Kernel32.HANDLE; dcb: Kernel32.DCB;
- ret: Kernel32.BOOL; err: LONGINT;
- windowsComName: ARRAY 16 OF CHAR;
- BEGIN
- res := Serials.Ok;
- windowsComName := "\\.\";
- Strings.Append(windowsComName, portname);
- hFile := Kernel32.CreateFile( windowsComName,
- Kernel32.SetToDW({Kernel32.GenericRead, Kernel32.GenericWrite}), Kernel32.SetToDW({}),
- NIL , Kernel32.OpenExisting, Kernel32.SetToDW({}), Kernel32.NULL );
- IF hFile # Kernel32.InvalidHandleValue THEN
- ret := Kernel32.GetCommState( hFile, dcb );
- IF ret # Kernel32.False THEN
- dcb.BaudRate := bps;
- IF (data >= 4) & (data <= 8) THEN dcb.ByteSize := CHR( data )
- ELSE res := Serials.WrongData
- END;
- CASE stop OF
- | Serials.Stop1:
- dcb.StopBits := Kernel32.OneStopBit
- | Serials.Stop1dot5:
- dcb.StopBits := Kernel32.One5StopBits
- | Serials.Stop2:
- dcb.StopBits := Kernel32.TwoStopBits
- ELSE
- res := Serials.WrongStop
- END;
- CASE parity OF
- | Serials.ParNo:
- dcb.Parity := Kernel32.NoParity
- | Serials.ParOdd:
- dcb.Parity := Kernel32.OddParity
- | Serials.ParEven:
- dcb.Parity := Kernel32.EvenParity
- | Serials.ParMark:
- dcb.Parity := Kernel32.MarkParity
- | Serials.ParSpace:
- dcb.Parity := Kernel32.SpaceParity
- ELSE
- res := Serials.WrongParity
- END;
- IF res = Serials.Ok THEN
- ret := Kernel32.SetCommState( hFile, dcb );
- IF ret # Kernel32.False THEN
- ret := Kernel32.PurgeComm( hFile, Kernel32.SetToDW({Kernel32.PurgeTXClear, Kernel32.PurgeRXClear}) );
- ret := Kernel32.SetupComm( hFile, 800H, 800H );
- handle := hFile;
- RETURN
- END
- END
- END;
- ret := Kernel32.CloseHandle( hFile )
- END;
- IF res = Serials.Ok THEN
- err := Kernel32.GetLastError();
- CASE err OF
- Kernel32.ErrorFileNotFound:
- res := Serials.NoSuchPort
- | Kernel32.ErrorAccessDenied:
- res := Serials.PortInUse
- | Kernel32.ErrorInvalidParameter:
- res := Serials.WrongBPS
- ELSE res := Serials.TransportError
- END
- END
- END SetPortState;
- PROCEDURE FinalizePort( ptr: ANY );
- BEGIN
- Close();
- END FinalizePort;
- (** Get the port state: speed, no. of data bits, parity, no. of stop bits (only valid if openstat is TRUE) *)
- PROCEDURE GetPortState*( VAR openstat: BOOLEAN; VAR bps, data, parity, stop: LONGINT );
- VAR dcb: Kernel32.DCB; ret: Kernel32.BOOL;
- BEGIN {EXCLUSIVE}
- openstat := FALSE;
- IF handle # Kernel32.InvalidHandleValue THEN
- ret := Kernel32.GetCommState(handle, dcb );
- IF ret # Kernel32.False THEN
- openstat := TRUE;
- bps := dcb.BaudRate;
- data := ORD(dcb.ByteSize);
- CASE dcb.StopBits OF
- |Kernel32.OneStopBit: stop := Serials.Stop1;
- |Kernel32.One5StopBits: stop := Serials.Stop1dot5;
- |Kernel32.TwoStopBits: stop := Serials.Stop2;
- ELSE
- KernelLog.String("Win32.V24.GetPortState: Wrong stops bits"); KernelLog.Ln;
- END;
- CASE dcb.Parity OF
- |Kernel32.NoParity: parity := Serials.ParNo;
- |Kernel32.OddParity: parity := Serials.ParOdd;
- |Kernel32.EvenParity: parity := Serials.ParEven;
- |Kernel32.MarkParity: parity := Serials.ParMark;
- |Kernel32.SpaceParity: parity := Serials.ParSpace;
- ELSE
- KernelLog.String("Win32.V24.GetPortState: Wrong parity mode"); KernelLog.Ln;
- END;
- END;
- END;
- END GetPortState;
- (** ClearMC - Clear the specified modem control lines. s may contain DTR, RTS & Break. *)
- PROCEDURE ClearMC*( s: SET );
- BEGIN {EXCLUSIVE}
- IF (handle # Kernel32.InvalidHandleValue) THEN
- IF Serials.Break IN s THEN Kernel32.ClearCommBreak( handle ) END;
- IF Serials.DTR IN s THEN Kernel32.EscapeCommFunction( handle, Kernel32.CLRDTR ) END;
- IF Serials.RTS IN s THEN Kernel32.EscapeCommFunction( handle, Kernel32.CLRRTS ) END
- END
- END ClearMC;
- (** SetMC - Set the specified modem control lines. s may contain DTR, RTS & Break. *)
- PROCEDURE SetMC*( s: SET );
- VAR res: LONGINT;
- BEGIN {EXCLUSIVE}
- IF handle # Kernel32.InvalidHandleValue THEN
- IF Serials.Break IN s THEN res := Kernel32.SetCommBreak( handle ) END;
- IF Serials.DTR IN s THEN
- res := Kernel32.EscapeCommFunction( handle, Kernel32.SETDTR )
- END;
- IF Serials.RTS IN s THEN
- res := Kernel32.EscapeCommFunction( handle, Kernel32.SETRTS )
- END
- END
- END SetMC;
- (** GetMC - Return the state of the specified modem control lines. s contains
- the current state of DSR, CTS, RI, DCD & Break Interrupt. *)
- PROCEDURE GetMC*( VAR s: SET );
- VAR state: Kernel32.DWORD; res: LONGINT;
- BEGIN {EXCLUSIVE}
- s := {};
- IF handle # Kernel32.InvalidHandleValue THEN
- res := Kernel32.GetCommModemStatus( handle, state );
- IF Kernel32.MSCTSOn IN Kernel32.DWToSet(state) THEN INCL( s, Serials.CTS ) END;
- IF Kernel32.MSDSROn IN Kernel32.DWToSet(state) THEN INCL( s, Serials.DSR ) END;
- IF Kernel32.MSRingOn IN Kernel32.DWToSet(state) THEN INCL( s, Serials.RI ) END;
- IF Kernel32.MSRLSDOn IN Kernel32.DWToSet(state) THEN INCL( s, Serials.DCD ) END
- END
- END GetMC;
- PROCEDURE Wait;
- BEGIN {EXCLUSIVE}
- timer.Sleep(1);
- END Wait;
- PROCEDURE Close*;
- BEGIN {EXCLUSIVE}
- IF handle # Kernel32.InvalidHandleValue THEN
- Kernel32.CancelIoEx(handle, NIL);
- Kernel32.CloseHandle( handle ); handle := Kernel32.InvalidHandleValue;
- IF rOverlapped.hEvent # NIL THEN Kernel32.CloseHandle(rOverlapped.hEvent); rOverlapped.hEvent := NIL; END;
- IF wOverlapped.hEvent # NIL THEN Kernel32.CloseHandle(wOverlapped.hEvent); wOverlapped.hEvent := NIL; END;
- END
- END Close;
- END Port;
- (** Scan the installed serial ports *)
- PROCEDURE Scan*(context : Commands.Context);
- VAR i, ret: LONGINT; name,winname: ARRAY 256 OF CHAR;
- strNumber: ARRAY 4 OF CHAR; found: BOOLEAN;
- BEGIN
- context.out.String( "Serial port detection and inspection (WinAos):" ); context.out.Ln;
- found := FALSE;
- FOR i := 1 TO MaxPortNo DO
- COPY( "COM", name );
- Strings.IntToStr(i, strNumber);
- Strings.Append(name, strNumber);
- ret := Kernel32.QueryDosDevice( name, winname, LEN( name ) );
- IF ret # Kernel32.False THEN
- found := TRUE;
- context.out.String( name ); context.out.String( ":" );
- context.out.String( winname ); context.out.String( "." );
- context.out.Ln
- END;
- END;
- IF ~found THEN context.out.String("no ports found"); context.out.Ln END;
- END Scan;
- PROCEDURE Install*(context: Commands.Context);
- END Install;
- (** Create a port object for each windows COM port *)
- PROCEDURE Init*;
- VAR
- i, ret: LONGINT;
- name : ARRAY 8 OF CHAR;
- winname: ARRAY 256 OF CHAR;
- port: Port;
- strNumber: ARRAY 4 OF CHAR;
- serialsPort: Serials.Port;
- BEGIN
- FOR i := 1 TO MaxPortNo DO
- COPY( "COM", name );
- Strings.IntToStr(i, strNumber);
- Strings.Append(name, strNumber);
- ret := Kernel32.QueryDosDevice(name, winname, LEN( winname ) );
- IF ret # 0 THEN
- serialsPort := Serials.GetPort(i);
- IF (serialsPort = NIL) OR (serialsPort.description # winname) THEN
- IF serialsPort # NIL THEN Serials.UnRegisterPort(serialsPort) END;
- NEW(port, i);
- Serials.RegisterOnboardPort( i , port, name, winname );
- END;
- END;
- END;
- END Init;
- BEGIN
- Init();
- END V24.
- V24.Install ~
- V24.Scan ~
- SystemTools.Free V24 ~
- Serials.Show ~
- SystemTools.Free Serials ~
|