MODULE BluetoothUART;	(** AUTHOR "be"; PURPOSE "HCI UART transport layer"; *)

IMPORT
	KernelLog, Streams, Bluetooth, Objects;

(* HCI command packet format (RS-232)
	| 01X | opcode (2bytes) | total parameter length | par0 | ... | parN |; and opcode = OGF (6bit) || OCF (10bit)

	HCI ACL data packet format (RS-232)
	| 02X |

	HCI SCO data packet format (RS-232)
	| 03X |

	HCI event packet format (RS-232)
	| 04X | Event Code | total parameter length | par0 | ... | parN |

	Error message packed format (RS-232)
	| 05X |

	Negotiation packet format (RS-232)
	| 06X |
*)

CONST

(*
	TraceSend = FALSE;
	TraceReceive = FALSE;
*)

	ModuleName = "[BTUART]";

	uartCommand = 01X;
	uartACLData = 02X;
	uartSCOData = 03X;
	uartEvent = 04X;

TYPE

	TransportLayer* = OBJECT(Bluetooth.TransportLayer)

		VAR
			TraceReceive*, TraceSend*: BOOLEAN;
			dead-: BOOLEAN;

		PROCEDURE &Init*(name: ARRAY OF CHAR; sender: Streams.Sender; receiver: Streams.Receiver);
		BEGIN
			Init^(name, sender, receiver);
			NEW(out, sender, 512); NEW(in, receiver, 512);
			dead := FALSE;
			TraceReceive := FALSE;	TraceSend := FALSE;
		END Init;

		PROCEDURE Close*;
		BEGIN {EXCLUSIVE}
			dead := TRUE
		END Close;

		PROCEDURE IsOpen*() :BOOLEAN;
		BEGIN {EXCLUSIVE}
			RETURN ~dead;
		END IsOpen;

		PROCEDURE ReadACLPacket() : Bluetooth.Packet;
		VAR acl: Bluetooth.ACLPacket; i : LONGINT;
		BEGIN
			NEW(acl);
			i := ORD(in.Get()) + ORD(in.Get())*100H;
			acl.handle := i  MOD 1000H;
			acl.PB := (i DIV 1000H) MOD 4;
			acl.BC := (i DIV 4000H) MOD 4;
			acl.len := ORD(in.Get()) + ORD(in.Get())*100H;
			(*ASSERT(acl.len <= Bluetooth.MaxACLDataLen);*)
			IF (acl.len > Bluetooth.MaxACLDataLen) THEN
				KernelLog.String(ModuleName);
				KernelLog.String("TransportLayer.ReadACLPacket: acl.len > Bluetooth.MaxACLDataLen");
				KernelLog.Ln;
				KernelLog.String("acl.len= "); KernelLog.Int(acl.len, 0);
				KernelLog.String("; BluetoothMaxACLDataLen= "); KernelLog.Int(Bluetooth.MaxACLDataLen, 0);
				KernelLog.String("; in.res= 0x"); KernelLog.Hex(in.res, -2);
				KernelLog.Ln;
				RETURN NIL;
			END;
			FOR i := 0 TO acl.len-1 DO
				acl.data[i] := in.Get();
			END;
			IF (in.res # 0) THEN
				KernelLog.String(ModuleName);
				KernelLog.String("TransportLayer.ReadACLPacket: UART failure; in.res= 0x"); KernelLog.Hex(in.res, -2);
				KernelLog.Ln;
				RETURN NIL;
			END;
			IF TraceReceive THEN
				KernelLog.String(ModuleName);
				KernelLog.String("TransportLayer.ReadACLPacket: reading ACL data");
				KernelLog.Ln;
				KernelLog.String("handle= 0x");KernelLog.Hex(acl.handle,-2);
				KernelLog.String("; packet boundary= 0x");KernelLog.Hex(acl.PB,-2);
				KernelLog.String("; broadcast= 0x"); KernelLog.Hex(acl.BC,-2);
				KernelLog.String("; payload length= 0x"); KernelLog.Int(acl.len,0);
				KernelLog.String("; acl.data= ");
				FOR i := 0 TO  acl.len-1 DO
					KernelLog.String(" 0x"); KernelLog.Hex(ORD(acl.data[i]),-2);
				END;
				KernelLog.Ln;
			END;
			RETURN acl;
		END ReadACLPacket;

		PROCEDURE ReadSCOPacket() : Bluetooth.Packet;
		VAR sco: Bluetooth.SCOPacket; i : LONGINT;
		BEGIN
			KernelLog.String(ModuleName);
			KernelLog.String("TransportLayer.ReadSCOPacket: uartSCOData received!! continue ....");
			KernelLog.Ln;
			NEW(sco);
			i := ORD(in.Get()) + ORD(in.Get())*100H;
			sco.handle := i MOD 1000H;
			sco.len := ORD(in.Get());
			(*ASSERT(sco.len <= Bluetooth.MaxSCODataLen);*)
			IF (sco.len > Bluetooth.MaxSCODataLen) THEN
				KernelLog.String(ModuleName);
				KernelLog.String("TransportLayer.ReadSCOPacket: sco.len > Bluetooth.MaxSCODataLen");
				KernelLog.Ln;
				KernelLog.String("sco.len= "); KernelLog.Int(sco.len, 0);
				KernelLog.String("; BluetoothMaxACLDataLen= "); KernelLog.Int(Bluetooth.MaxSCODataLen, 0);
				KernelLog.String("; in.res= 0x"); KernelLog.Hex(in.res, -2);
				KernelLog.Ln;
				RETURN NIL;
			END;
			FOR i := 0 TO sco.len-1 DO
				sco.data[i] := in.Get();
			END;
			IF (in.res # 0) THEN
				KernelLog.String(ModuleName);
				KernelLog.String("TransportLayer.ReadSCOPacket: UART failure; in.res= 0x"); KernelLog.Hex(in.res, -2);
				KernelLog.Ln;
				RETURN NIL;
			END;
			IF TraceReceive THEN
				KernelLog.String(ModuleName);
				KernelLog.String("TransportLayer.ReadSCOPacket: reading SCO data");
				KernelLog.Ln;
				KernelLog.String("handle= 0x");KernelLog.Hex(sco.handle,-2);
				KernelLog.String(" payload length= 0x"); KernelLog.Int(sco.len,0);
				KernelLog.String("; sco.data= ");
				FOR i := 0 TO sco.len-1 DO
					KernelLog.String(" 0x"); KernelLog.Hex(ORD(sco.data[i]),-2);
				END;
				KernelLog.Ln;
			END;
			RETURN sco;
		END ReadSCOPacket;

		PROCEDURE ReadEventPacket() : Bluetooth.Packet;
		VAR event: Bluetooth.EventPacket; i : LONGINT;
		BEGIN
			NEW(event);
			event.code :=in. Get();
			event.paramLen := ORD(in.Get());
			(*ASSERT(event.paramLen < Bluetooth.MaxEventParamLen);*)
			IF (event.paramLen > Bluetooth.MaxEventParamLen) THEN
				KernelLog.String(ModuleName);
				KernelLog.String("TransportLayer.ReadEventPacket: paramLen > MaxParamLen");
				KernelLog.Ln;
				KernelLog.String("paramLen= "); KernelLog.Int(event.paramLen, 0);
				KernelLog.String("; MaxParamLen= "); KernelLog.Int(Bluetooth.MaxEventParamLen, 0);
				KernelLog.String("; in.res= 0x"); KernelLog.Hex(in.res, -2);
				KernelLog.Ln;
			END;
			FOR i := 0 TO event.paramLen-1 DO
				event.params[i] := in.Get();
			END;
			IF (in.res # 0) THEN
				KernelLog.String(ModuleName);
				KernelLog.String("TransportLayer.ReadEventPacket: UART failure; in.res= 0x"); KernelLog.Hex(in.res, -2);
				KernelLog.Ln;
				RETURN NIL;
			END;
			IF TraceReceive THEN
				KernelLog.String(ModuleName);
				KernelLog.String("TransportLayer.ReadEventPacket: reading event Data");
				KernelLog.Ln;
				KernelLog.String("event.code = 0x"); KernelLog.Hex(ORD(event.code),-2);
				KernelLog.String("; event.paramLen= "); KernelLog.Int(event.paramLen,0);
				KernelLog.String("; event.params= ");
				FOR i := 0 TO event.paramLen-1 DO
					KernelLog.String(" 0x"); KernelLog.Hex(ORD(event.params[i]),-2);
				END;
				KernelLog.Ln;
			END;
			RETURN event;
		END ReadEventPacket;

		PROCEDURE ReadUnknownPacket() : Bluetooth.Packet;
		VAR unknown: Bluetooth.UnknownPacket; ch : CHAR;
		BEGIN
			KernelLog.String(ModuleName);
			KernelLog.String("TransportLayer.ReadUnknownPacket: unknown/invalid packet ch= 0x"); KernelLog.Hex(ORD(ch),-2);
			KernelLog.String("; in.res= 0x"); KernelLog.Hex(in.res, -2);
			KernelLog.String("; in.Available()= "); KernelLog.Int(in.Available(), 0);
			KernelLog.Ln;
			NEW(unknown);
			unknown.len := 0;
			WHILE ((in.Available() > 0) & (in.res = 0)) DO
				IF(unknown.len < Bluetooth.MaxUnknownDataLen) THEN
					unknown.data[unknown.len] := in.Get();
					KernelLog.String("unknown.data["); KernelLog.Int(unknown.len,0); KernelLog.String("]= 0x");
					KernelLog.Hex(ORD(unknown.data[unknown.len]),-2);
					KernelLog.String("; in.res= 0x"); KernelLog.Hex(in.res, -2);
					KernelLog.Ln;
					INC(unknown.len);
				ELSE
					ch := in.Get();
					KernelLog.String("discard ch= 0x"); KernelLog.Hex(ORD(ch),-2);
					KernelLog.String("; in.res= 0x"); KernelLog.Hex(in.res, -2);
					KernelLog.Ln;
				END;
			END;
			RETURN unknown;
		END ReadUnknownPacket;


		PROCEDURE Read;
		VAR
			ch: CHAR;
			queue: Bluetooth.Queue;
			packet: Bluetooth.Packet;
		BEGIN
			ch := in.Get();
			IF (in.res # 0) THEN
				KernelLog.String(ModuleName);
				KernelLog.String("TransportLayer.Read: UART failure in.res= 0x"); KernelLog.Hex(in.res, -2);
				KernelLog.String("; closing layer");
				KernelLog.Ln;
				Close;
				RETURN;
			END;
			IF (ch = uartCommand) THEN 	(* HCI command packet *)
				(*HALT(100)	(* not sent by host controller *)*)
				KernelLog.String(ModuleName);
				KernelLog.String("TransportLayer.Read: uartCommand received! closing layer");
				KernelLog.Ln;
				Close;
				RETURN;
			ELSIF (ch = uartACLData) THEN	(* HCI ACL data packet *)
				packet := ReadACLPacket();
				queue := sink[Bluetooth.ACL];
			ELSIF (ch = uartSCOData) THEN	(* HCI SCO data packet *)
				packet := ReadSCOPacket();
				queue := sink[Bluetooth.SCO];
			ELSIF (ch = uartEvent) THEN		(* HCI event packet *)
				packet := ReadEventPacket();
				queue := sink[Bluetooth.Event];
			ELSE 							(* unknown/invalid packet *)
				packet := ReadUnknownPacket();
				queue := sink[Bluetooth.Default];
			END;
			IF (packet = NIL) THEN
				KernelLog.String(ModuleName);
				KernelLog.String("TransportLayer.Read: error while reading packet; ch= "); KernelLog.Char(ch);
				KernelLog.String("; in.res= "); KernelLog.Int(in.res, 0);
				KernelLog.String("; closing layer");
				KernelLog.Ln;
				Close;
				RETURN;
			ELSE
				queue.Add(packet);
			END;
			IF TraceReceive THEN
				KernelLog.String(ModuleName);
				KernelLog.String("TransportLayer.Read done.");
				KernelLog.String(" in.Available()= "); KernelLog.Int(in.Available(), 0);
				KernelLog.Ln;
			END;
		END Read;

		PROCEDURE GetPacketType(type: LONGINT; VAR c: CHAR): BOOLEAN;
		VAR res: BOOLEAN;
		BEGIN
			res := TRUE;
			CASE type OF
				| Bluetooth.Command: c := uartCommand
				| Bluetooth.ACL: c := uartACLData
				| Bluetooth.SCO: c := uartSCOData
				ELSE res := FALSE
			END;
			RETURN res
		END GetPacketType;

		PROCEDURE Send*(type: LONGINT; VAR data: ARRAY OF CHAR; ofs, len: LONGINT; VAR res: LONGINT);
		VAR  pt: CHAR; i: LONGINT;
		BEGIN {EXCLUSIVE}
			IF ~GetPacketType(type, pt) THEN
				KernelLog.String(ModuleName);
				KernelLog.String("TransportLayer.Send: wrong packet type= 0x"); KernelLog.Hex(type,-2);
				KernelLog.Ln;
				res := Bluetooth.ErrInvalidParameters;
				RETURN
			END;
			IF TraceSend THEN
				KernelLog.String(ModuleName);
				KernelLog.String("TransportLayer.Send: packet type= 0x"); KernelLog.Hex(ORD(pt), -2);
				FOR i := 0 TO len-1 DO
					KernelLog.Char(" ");
					KernelLog.Hex(ORD(data[ofs+i]), -2);
				END;
				KernelLog.Ln
			END;
			out.Char(pt); out.Bytes(data, ofs, len); out.Update;
			res := out.res
		END Send;

		PROCEDURE Send1H*(type: LONGINT; VAR hdr: ARRAY OF CHAR; hdrlen: LONGINT; VAR data: ARRAY OF CHAR; ofs, len: LONGINT; VAR res: LONGINT);
		VAR pt: CHAR; i: LONGINT;
		BEGIN
			IF ~GetPacketType(type, pt) THEN
				KernelLog.String(ModuleName);
				KernelLog.String("TransportLayer.Send1H: wrong packet type= 0x"); KernelLog.Hex(type,-2);
				KernelLog.Ln;
				res := Bluetooth.ErrInvalidParameters;
				RETURN
			END;
			IF TraceSend THEN
				KernelLog.String(ModuleName);
				KernelLog.String("TransportLayer.Send1H: packet type= 0x"); KernelLog.Hex(ORD(pt), -2);
				FOR i := 0 TO hdrlen-1 DO
					 KernelLog.Char(" "); KernelLog.Hex(ORD(hdr[i]), -2);
				END;
				FOR i := 0 TO len-1 DO
					KernelLog.Char(" "); KernelLog.Hex(ORD(data[ofs+i]), -2);
				END;
				KernelLog.Ln
			END;
			out.Char(pt); out.Bytes(hdr, 0, hdrlen); out.Bytes(data, ofs, len); out.Update;
			res := out.res
		END Send1H;

		PROCEDURE Send2H*(type: LONGINT; VAR hdr1: ARRAY OF CHAR; hdr1len: LONGINT;
								VAR hdr2: ARRAY OF CHAR; hdr2len: LONGINT;
								VAR data: ARRAY OF CHAR; ofs, len: LONGINT; VAR res: LONGINT);
		VAR pt: CHAR; i: LONGINT;
		BEGIN
			IF ~GetPacketType(type, pt) THEN
				KernelLog.String(ModuleName);
				KernelLog.String("TransportLayer.Send2H: wrong packet type= 0x"); KernelLog.Hex(type,-2);
				KernelLog.Ln;
				res := Bluetooth.ErrInvalidParameters;
				RETURN
			END;
			IF TraceSend THEN
				KernelLog.String(ModuleName);
				KernelLog.String("TransportLayer.Send2H: packet type= 0x"); KernelLog.Hex(ORD(pt), -2);
				FOR i := 0 TO hdr1len-1 DO
					 KernelLog.Char(" "); KernelLog.Hex(ORD(hdr1[i]), -2);
				END;
				FOR i := 0 TO hdr2len-1 DO
					KernelLog.Char(" "); KernelLog.Hex(ORD(hdr2[ofs+i]), -2);
				END;
				FOR i := 0 TO len-1 DO
					KernelLog.Char(" "); KernelLog.Hex(ORD(data[ofs+i]), -2);
				END;
				KernelLog.Ln
			END;
			out.Char(pt); out.Bytes(hdr1, 0, hdr1len); out.Bytes(hdr2, 0, hdr2len); out.Bytes(data, ofs, len); out.Update;
			res := out.res
		END Send2H;

	BEGIN {ACTIVE}
		Objects.SetPriority(3);
		REPEAT
			Read;
		UNTIL dead
	END TransportLayer;

END BluetoothUART.