Oberon/ETH Oberon/PPPHDLC.Mod
Appearance
< Oberon | ETH Oberon
(* ETH Oberon, Copyright 1990-2003 Computer Systems Institute, ETH Zurich, CH-8092 Zurich.
Refer to the license.txt file provided with this distribution. *)
(* $VCS 1, Edgar.Schwarz@z.zgs.de, 28 Feb 99, 22:13:54 $ *)
MODULE PPPHDLC; (** non-portable *)
(* $Log$
$ 1, Edgar.Schwarz@z.zgs.de, 28 Feb 99, 22:13:54
version for PPP 1.0.0
*)
IMPORT
Debug := PPPDebug, SYSTEM, T:=PPPTools,
(*es*) Oberon, Ker:=Kernel, V24, PT := NetBase, Input, Out, Strings;
(* Ker:=PPCXOKernel, P:=Peripherals; *)
CONST
HDLCAddress=0FFX;
HDLCControl=03X;
HDLCHeaderLen* = 4; (* Flag + Address, Protocol (2 Byte) *)
EscSymbol = 7DX;
FlagSymbol = 7EX;
MTU*=1500;
StartPos*=4; (* even if we received a packed Packet, we can send back an unpacked one, using the same array *)
ArrayLength* = MTU+100; (* some bytes more needed *)
TYPE
Params* = POINTER TO ParamsDesc;
ParamsDesc* = RECORD END;
CallbackProc*=PROCEDURE (p:Params);
TimeOut = POINTER TO TimeOutDesc;
TimeOutDesc = RECORD
time: LONGINT;
callback: CallbackProc; params: Params;
next: TimeOut;
END;
PPPUnit* = POINTER TO PPPUnitDesc;
MyTask* = POINTER TO MyTaskDesc;
MyTaskDesc = RECORD
(*es*) (Oberon.TaskDesc)
(* (Ker.MainEventDesc) (* Oberon.TaskDesc *) *)
Config*:PPPUnit;
END;
PPPUnitDesc* = RECORD
MTU*, MRU*:INTEGER; (* Maximum Transmit Unit, how big our packets are; M Receive U, how big he may send*)
SendAsyncMap*:SET; (* Transmit AsyncMap; What characters have to be stuffed *)
(*es*) c*: LONGINT;
(* c*: P.SerialChannel; *)
cname*:ARRAY 32 OF CHAR;
task*: MyTask;
data, data2*: ARRAY ArrayLength OF CHAR;
TOqueue: TimeOut;
TOempty: BOOLEAN;
END;
VAR
(*es*)
debug*: BOOLEAN;
TOtask: Oberon.Task; (* check for timeout *)
TOunit: PPPUnit; (* instead of extending task *)
(*TOrecycle: TimeOut;*)
FrameCount: INTEGER; (* pe: number frames for reference. *)
(*es*) (* Save incomplete frames between calls to Receive *)
receiveFrame: RECORD
active: BOOLEAN; (* ready to receive data *)
(*deadline: LONGINT; (* until we want to receive the end *)*)
i: INTEGER; (* index of last received byte in receive buffer *)
esc: BOOLEAN; (* last byte received was EscSymbol *)
END;
(**)PPPHandleReceive*:
PROCEDURE (U:PPPUnit; prot:INTEGER; VAR p: ARRAY OF CHAR;
pos, len:INTEGER; VAR prno: INTEGER; VAR item: PT.Item);
PROCEDURE SendString(c: (*es*)LONGINT(*P.SerialChannel*);
VAR s: ARRAY OF CHAR);
VAR l0, l1, start: LONGINT;
(*es*)res: LONGINT;(**)
BEGIN
l0 := 0; WHILE s[l0] # 0X DO INC(l0) END; l1 := l0; start := 0;
(*es*) WHILE l0 > 0 DO
V24.SendBytes(c, s, l1, res);
DEC(l0, l1);
END
(* WHILE l0 > 0 DO
c.Write(s, start, l1);
DEC(l0, l1); INC(start, l1)
END *)
END SendString;
(* Init *)
PROCEDURE Init* (Config:PPPUnit;
c:(*es*)LONGINT(* P.SerialChannel*);
VAR cname, sstr, loginname, loginpasswd: ARRAY OF CHAR);
VAR to:TimeOut; l: LONGINT; temp: ARRAY 4 OF CHAR;
cr: ARRAY 2 OF CHAR;
(*es*)res: LONGINT;(**)
BEGIN
Config.c := c; COPY(cname, Config.cname);
(*es??? die V24 und Loginsachen macht doch der Dialer. Oder ?
(* V24 login is handled by the Dialer, isn't it? *)
WHILE V24.Available(c) > 0 DO V24.Receive(c, temp[0],res); END;
(* WHILE c.Available > 0 DO l := 1; c.Read(temp, 0, l) END; *)
(* send start string *)
IF sstr[0] # 0X THEN
SendString(c, sstr);
(* consume some bytes (they contain the reply string from server) *)
REPEAT
WHILE c.Available > 0 DO l := 1; c.Read(temp, 0, l) END;
Ker.Sleep(1, Ker.ONEsec)
UNTIL c.Available = 0
END;
IF loginname[0] # 0X THEN (* start normal unix-login procedure *)
cr[0] := 0DX; cr[1] := 0X;
(* send two CR *)
SendString(c, cr); Ker.Sleep(1, Ker.ONEsec);
SendString(c, cr); Ker.Sleep(5,Ker.ONEsec);
(* send loginname *)
SendString(c, loginname); SendString(c, cr);
Ker.Sleep(1, Ker.ONEsec);
(* send loginpasswd *)
SendString(c, loginpasswd); SendString(c, cr);
Ker.Sleep(1, Ker.ONEsec);
END;
REPEAT
WHILE c.Available > 0 DO
l := 1; c.Read(temp, 0, l);
Debug.Int(ORD(temp[0]),4);
IF (temp[0] >= 20X) & (temp[0] <= 7FX) THEN
Debug.Char(9X); Debug.Char(temp[0])
END;
Debug.Ln;
END;
Ker.Sleep(5,Ker.ONEsec);
Debug.Ln;
UNTIL c.Available = 0;
es*)
Debug.String("Starting receiving-loop"); Debug.Ln;
NEW(Config.task); Config.task.Config:=Config;
(*es???*) Debug.String("Pustekuchen, macht Devicepolling"); Debug.Ln;
(* pe *) Debug.String("Begin communicating with the remote system."); Debug.Ln;
Debug.String("Configure the timeout."); Debug.Ln;
(*Ker.InitMain(Config.task);*)
NEW(to); Config.TOqueue:=to; to.next:=to;
to.time:=MAX(LONGINT); Config.TOempty:=TRUE; (* sentinel *)
END Init;
PROCEDURE CheckTimer((*es*)me: Oberon.Task(*C: PPPUnit*));
(* Called only when TOqueue is not empty *)
VAR cur:TimeOut;
BEGIN
cur:=(*es*)TOunit(*C*).TOqueue;
IF cur.time < (*es*)Input.Time()(*Ker.Time()*) THEN
(*es*) Oberon.Remove(me);
IF TOunit.cname # "" THEN
(* output only if ppp is still running. See PPPMain.StopInst *)
Out.String("PPP timeout"); Out.Ln;
END;
(**)
(*es*)TOunit(*C*).TOempty:=TRUE;
cur.callback(cur.params);
END
END CheckTimer;
(* TimeOut Handling *)
PROCEDURE TIMEOUT* (C: PPPUnit; callb:CallbackProc; p:Params;
msec:LONGINT); (* msec: in microS-sec *)
VAR cur:TimeOut;
BEGIN
cur:=C.TOqueue; cur.callback:=callb; cur.params:=p;
cur.time:=(*es*) Input.Time(*Ker.Time*)()+msec;
C.TOempty:=FALSE;
(*es install timer task *)
TOtask.safe := FALSE; TOtask.time := 0;
TOtask.handle := CheckTimer;
Oberon.Install(TOtask);
TOunit := C;
(**)
END TIMEOUT;
PROCEDURE UNTIMEOUT* (C: PPPUnit; callb:CallbackProc);
BEGIN
C.TOempty:=TRUE;
(*es*) Oberon.Remove(TOtask); (**)
END UNTIMEOUT;
(* CheckPacket - Check a HDLC-Packet *)
PROCEDURE CheckPacket(Config: PPPUnit; VAR p: ARRAY OF CHAR;
pos, len:INTEGER; VAR prno: INTEGER; VAR item: PT.Item);
(*es*)VAR timeStr: ARRAY 16 OF CHAR; time, date: LONGINT;(**)
BEGIN
IF debug THEN
(* pe *) Debug.String('->->-> Frame '); Debug.Int(FrameCount, 5);
Debug.String(' received from peer.'); Debug.Ln;
INC(FrameCount); (* pe *)
Oberon.GetClock(time, date);
Strings.TimeToStr(time,timeStr);
Debug.String(timeStr);
Debug.String('CheckPacket: len = '); Debug.Int(len, 5); Debug.Ln;
T.OutPacket(p, pos, len);
END;
IF len > HDLCHeaderLen+2 THEN
(*[HDLC_Address+HDLC-Control+Protocol] + Checksum (2 Bytes)*)
IF T.CheckFCS(p, pos, len) THEN
(* CheckSum ok? The whole packet needed! *)
IF (p[pos] = HDLCAddress) & (p[pos+1] = HDLCControl) THEN
PPPHandleReceive(Config, T.GetInt(p, pos+2), p, pos+4, len-6,
prno, item);
ELSE (* silently discarded *)
IF debug THEN
Debug.String("Address, Code wrong"); Debug.Ln;
END;
END
ELSE
IF debug THEN Debug.String("Checksum failure"); Debug.Ln END
END
ELSE
IF debug & (len > 0) THEN
Debug.String("Length too short, length: ");
Debug.Int(len, 6); Debug.Ln;
END;
END
END CheckPacket;
PROCEDURE XOR20(ch:LONGINT):LONGINT;
BEGIN RETURN SYSTEM.VAL(LONGINT, (SYSTEM.VAL(SET, ch) / {5} ));
END XOR20;
(* ReceivePacket - ReceiveLoop: Task , needs extended Event
(including PPPUnit)*)
(*es*) (* ReceivePacket is indirectly called by NetBase.Polldevices when
characters at the serial interface are available. So just read the characters
into a buffer (Config.data) and return prno=0 (no handler installed for this
protocol hopefully). When you get end of frame (FlagSymbol) then check
it and return the stuff. *)
(**)PROCEDURE ReceivePacket*(Config: PPPUnit; VAR prno: INTEGER;
VAR item: PT.Item);
VAR (*es*) (*i:INTEGER;*)
ch: ARRAY 4 OF CHAR; l: LONGINT;
(*es*)c, res, endTime: LONGINT; timeOut : TimeOut;
(* c: P.SerialChannel; *)
BEGIN
c:=Config.c;
(*es*)(* i:=StartPos; ch[0]:=0X; (* not Esc-Symbol *) *)
(*es*)IF receiveFrame.active THEN
prno := 0;
ELSE
(*receiveFrame.deadline := Input.Time() + 10(*s*) * Input.TimeUnit;*)
receiveFrame.i := StartPos;
receiveFrame.active := TRUE;
receiveFrame.esc := FALSE;
ch[0]:=0X;
END;
IF receiveFrame.esc THEN ch[0] := EscSymbol ELSE ch[0]:=0X; END;
(**) LOOP
IF (*es*)V24.Available(c)(*c.Available*)>0 THEN
(* New chars in buffer ? *)
IF ch[0] (* last received *) #EscSymbol THEN
(*es*) V24.Receive(c, ch[0], res);
(*l := 1; c.Read(ch, 0, l);*)
IF ch[0] = FlagSymbol THEN (* end of frame received *)
CheckPacket(Config, Config.data, StartPos,
receiveFrame.i-StartPos, prno, item);
(*es*) (* data now in item *) receiveFrame.active := FALSE; RETURN;
(* receiveFrame.i:=StartPos; *)
ELSE
IF ch[0] # EscSymbol THEN
Config.data[receiveFrame.i]:=ch[0];
INC(receiveFrame.i) ;
(*es*) receiveFrame.esc := FALSE;
ELSE
receiveFrame.esc := TRUE;
(**) END
END;
ELSE
(*es*) V24.Receive(c, ch[0], res); receiveFrame.esc := FALSE;
(*l := 1; c.Read(ch, 0, l);*)
Config.data[receiveFrame.i]:=CHR(XOR20(ORD(ch[0])));
INC(receiveFrame.i); ch[0]:=0X; (* not Esc-,or Flag-Symbol *)
END;
IF receiveFrame.i>=ArrayLength THEN
Debug.String("Array Overflow!!");
(*HALT(99);*)
receiveFrame.active := FALSE; RETURN
END;
ELSE
RETURN; (* pause until more characters are available *)
(**)
END;
END
END ReceivePacket;
(* SendPacket - Send a packet to the V24 *)
PROCEDURE SendPacket* (Config: PPPUnit; prot:INTEGER;
VAR p:ARRAY OF CHAR; pos, len:INTEGER);
VAR minpos, code, k, begin: INTEGER; ch: ARRAY 4 OF CHAR; l0, l1, start: LONGINT; x: CHAR;
(*es*)c, res: LONGINT;
timeStr: ARRAY 16 OF CHAR; time, date: LONGINT;
(*c: P.SerialChannel;*)
BEGIN
c:=Config.c;
minpos:=HDLCHeaderLen;
IF pos<minpos THEN T.CopyString(p, pos, minpos, len); pos:=minpos; END; (* More space needed *)
DEC(pos, HDLCHeaderLen); INC(len, HDLCHeaderLen);
p[pos]:=0FFX; p[pos+1]:=03X; (* pos + 0: HDLC_Address = 0FFX; pos + 1: HDLC_Control =3X *)
T.PutInt(prot, p, pos+2); (* pos + 2: Protocol *)
code:=T.CalcFCS(p, pos, len);
p[pos+len]:=CHR(code MOD 256);
p[pos+len+1]:=CHR(SYSTEM.LSH(code, -8)); INC(len, 2);
(* insert the checksum *)
IF debug THEN
(* pe *) Debug.String('<-<-<- Frame '); Debug.Int(FrameCount, 5);
Debug.String(' going to peer.'); Debug.Ln;
INC(FrameCount); (* pe *)
Oberon.GetClock(time, date);
Strings.TimeToStr(time,timeStr);
Debug.String(timeStr);
Debug.String(' SendPacket: len = '); Debug.Int(len, 5); Debug.Ln;
T.OutPacket(p, pos, len); Debug.Ln
END;
(* send it to the V24 *)
ch[0]:=FlagSymbol;
l0 := 1; l1 := l0; start := 0;
WHILE l0 > 0 DO
(*es*)V24.Send(c,ch[start], res);
(* c.Write(ch, start, l1); *)
DEC(l0, l1); INC(start, l1)
END;
(* es auskommentiert: der Trick ist irgendwie alle Zeichen zu sammeln,
die man ohne Maskierung (EscSymbol) senden kann. Wenn man dann
auf eines trifft, verpackt man es und schickt die Dinger von zuvor
auf weg. Das ist mir etwas zu undurchsichtig.
Translation & remark from T.F.
Section commented out: The trick is to somehow collect all the
characters that can be sent without escaping (EscSymbol). Once we
come across a character that needs escaping, we send all the previously
collected characters followed by the escaped character.
It is probably some kind of "optimized code" that was translated and when
it did not work was finally replaced by the version below the
commented-out code. From what I can tell the active code is better
anyways (simpler). If optimization is needed, AosIO could be used to collect
the characters in a much simpler way. *)
(* begin:=pos;
FOR k:=pos TO pos+len-1 DO x:=p[k];
IF ((ORD(x)<20H) & (ORD(x) IN Config.SendAsyncMap))
OR (x=EscSymbol) OR (x=FlagSymbol) THEN
l0 :=k-begin; l1 := l0; start := begin;
WHILE l0 > 0 DO c.Write(p, start, l1); DEC(l0,l1); INC(start,l1);END;
begin:=k+1;
ch[0]:=EscSymbol; ch[1]:=CHR(XOR20(ORD(x)));
l0 := 2; l1 := l0; start := 0;
WHILE l0 > 0 DO c.Write(ch, start, l1); DEC(l0,l1); INC(start,l1);END;
END;
END;
p[pos+len]:=FlagSymbol; l0 := pos+len+1-begin; l1 := l0; start := begin;
WHILE l0 > 0 DO c.Write(p, start, l1); DEC(l0, l1); INC(start, l1); END;
*)
(*es*)
FOR k:=pos TO pos+len-1 DO
x:=p[k];
IF ((ORD(x)<20H) & (ORD(x) IN Config.SendAsyncMap))
OR (x=EscSymbol) OR (x=FlagSymbol) THEN
V24.Send(c,EscSymbol, res);
IF res # 0 THEN HALT(99); END;
V24.Send(c,CHR(XOR20(ORD(x))), res);
IF res # 0 THEN HALT(99); END;
ELSE
V24.Send(c, x, res);
IF res # 0 THEN HALT(99); END;
END;
END;
(* frame complete: send end flag *)
V24.Send(c,FlagSymbol, res);
IF res # 0 THEN HALT(99); END;
(**)
END SendPacket;
BEGIN
(* pe *) FrameCount := 0;
debug := FALSE;
(*es*) receiveFrame.active := FALSE;
NEW(TOtask);
(*NEW(TOrecycle); TOrecycle.next:=TOrecycle; (* Sentinel *)*)
END PPPHDLC.