MODULE UsbStorageBot;
IMPORT
SYSTEM, KernelLog,
Usbdi, Base := UsbStorageBase, Debug := UsbDebug;
TYPE
BulkOnlyTransport* = OBJECT(Base.StorageDriver)
VAR
CBWbuffer : Usbdi.BufferPtr;
CSWbuffer : Usbdi.BufferPtr;
seqNbr : LONGINT;
PROCEDURE Reset*(timeout : LONGINT) : LONGINT;
VAR critical : BOOLEAN; status : Usbdi.Status;
BEGIN
IF Debug.Trace & Debug.traceScRequests THEN KernelLog.String("UsbStorage: Doing reset recovery ... "); END;
status := device.Request(Usbdi.ToDevice + Usbdi.Class + Usbdi.Interface, 255, 0, interface.bInterfaceNumber, 0, Usbdi.NoData);
IF (status = Usbdi.Disconnected) THEN
RETURN Base.ResDisconnected;
ELSIF status # Usbdi.Ok THEN
IF Debug.Level >= Debug.Errors THEN KernelLog.String("UsbStorage: Mass storage reset failed."); KernelLog.Ln; END;
RETURN Base.ResFatalError;
END;
IF bulkInPipe.IsHalted() THEN critical := ~bulkInPipe.ClearHalt(); END;
IF bulkOutPipe.IsHalted() THEN critical := critical OR ~bulkOutPipe.ClearHalt(); END;
IF critical THEN
IF Debug.Level >= Debug.Errors THEN KernelLog.String("UsbStorage: Failure on BulkOnly reset ClearHalt"); KernelLog.Ln; END;
RETURN Base.ResFatalError;
END;
IF Debug.Trace & Debug.traceScRequests THEN KernelLog.String("UsbStorage: Reset recovery succeeded."); KernelLog.Ln; END;
RETURN Base.ResOk;
END Reset;
PROCEDURE GetMaxLun*(VAR maxlun : LONGINT): LONGINT;
VAR buffer : Usbdi.BufferPtr; status : Usbdi.Status;
BEGIN
IF Debug.Trace & Debug.traceScRequests THEN KernelLog.String("UsbStorage: GetMaxLUN.... "); END;
NEW(buffer, 1);
status := device.Request(Usbdi.ToHost + Usbdi.Class + Usbdi.Interface, 254, 0, interface.bInterfaceNumber, 1, buffer^);
IF status = Usbdi.Ok THEN
maxlun := ORD(buffer[0]);
IF Debug.Trace & Debug.traceScRequests THEN KernelLog.String("MaxLUN is: "); KernelLog.Int(maxlun, 0); KernelLog.Ln; END;
RETURN Base.ResOk;
ELSIF status = Usbdi.Stalled THEN
maxlun := 0;
IF Debug.Trace & Debug.traceScRequests THEN KernelLog.String("MaxLUN request not supported (STALL)"); KernelLog.Ln; END;
RETURN Base.ResOk;
ELSIF status = Usbdi.Disconnected THEN
RETURN Base.ResDisconnected;
ELSE
IF Debug.Level >= Debug.Errors THEN KernelLog.String("UsbStorage: GetMaxLUN request failed."); KernelLog.Ln; END;
RETURN Base.ResFatalError;
END;
END GetMaxLun;
PROCEDURE Transport*(cmd : ARRAY OF CHAR; cmdlen : LONGINT; dir : SET;
VAR buffer : ARRAY OF CHAR; ofs, bufferlen : LONGINT; VAR tlen : LONGINT; timeout : LONGINT) : LONGINT;
VAR status : Usbdi.Status; i, residual : LONGINT;
BEGIN
ASSERT((cmdlen > 0) & (cmdlen <= 16));
CBWbuffer[0] := 55X; CBWbuffer[1] := 53X; CBWbuffer[2] := 42X; CBWbuffer[3] := 43X;
INC(seqNbr);
CBWbuffer[4] := CHR(seqNbr);
CBWbuffer[5] := CHR(SYSTEM.LSH(seqNbr, -8));
CBWbuffer[6] := CHR(SYSTEM.LSH(seqNbr, -16));
CBWbuffer[7] := CHR(SYSTEM.LSH(seqNbr, -24));
CBWbuffer[8] := CHR(bufferlen);
CBWbuffer[9] := CHR(SYSTEM.LSH(bufferlen, -8));
CBWbuffer[10] := CHR(SYSTEM.LSH(bufferlen, -16));
CBWbuffer[11] := CHR(SYSTEM.LSH(bufferlen, -24));
IF dir = Base.DataIn THEN CBWbuffer[12] := 80X; ELSE CBWbuffer[12] := 0X; END;
CBWbuffer[13] := CHR(SYSTEM.VAL(LONGINT, SYSTEM.LSH(SYSTEM.VAL(SET, ORD(cmd[1])) * {5..7}, -5)));
CBWbuffer[14] := CHR(cmdlen);
FOR i := 15 TO 30 DO CBWbuffer[i] := 0X; END;
FOR i := 0 TO cmdlen-1 DO CBWbuffer[15+i] := cmd[i]; END;
IF Debug.Trace & Debug.traceCBWs THEN
KernelLog.String("Sending CBW: "); FOR i := 0 TO LEN(CBWbuffer)-1 DO KernelLog.Hex(ORD(CBWbuffer[i]), -2); KernelLog.Char(" "); END; KernelLog.Ln;
END;
IF Base.performance = Usbdi.MaxPerformance THEN bulkOutPipe.mode := Usbdi.MaxPerformance; ELSE bulkOutPipe.mode := Usbdi.MinCpu; END;
status := bulkOutPipe.Transfer(31, 0, CBWbuffer^);
IF (status = Usbdi.Disconnected) THEN
RETURN Base.ResDisconnected;
ELSIF status # Usbdi.Ok THEN
RETURN Base.ResFatalError;
END;
IF bufferlen # 0 THEN
IF Debug.Trace & Debug.traceScTransfers THEN
KernelLog.String("UsbStorage: Data Phase: Transfering "); KernelLog.Int(bufferlen, 0); KernelLog.String(" Bytes to ");
IF dir = Base.DataIn THEN KernelLog.String("Host Controller"); ELSIF dir = Base.DataOut THEN KernelLog.String("Device"); ELSE HALT(301); END;
KernelLog.Ln;
END;
IF dir = Base.DataIn THEN
IF Base.performance = Usbdi.MaxPerformance THEN bulkInPipe.mode := Usbdi.Normal; ELSE bulkInPipe.mode := Usbdi.MinCpu; END;
bulkInPipe.SetTimeout(timeout);
status := bulkInPipe.Transfer(bufferlen, ofs, buffer);
tlen := bulkInPipe.GetActLen();
ELSIF dir = Base.DataOut THEN
bulkOutPipe.SetTimeout(timeout);
IF Base.performance = Usbdi.MaxPerformance THEN bulkOutPipe.mode := Usbdi.Normal; ELSE bulkOutPipe.mode := Usbdi.MinCpu; END;
status := bulkOutPipe.Transfer(bufferlen, ofs, buffer);
tlen := bulkOutPipe.GetActLen();
ELSE HALT(303);
END;
IF status = Usbdi.Stalled THEN
IF Debug.Level >= Debug.Warnings THEN KernelLog.String("UsbStorage: Stall on BulkOnly data phase"); KernelLog.Ln; END;
IF ((dir = Base.DataIn) & (~bulkInPipe.ClearHalt())) OR ((dir = Base.DataOut) & (~bulkOutPipe.ClearHalt())) THEN
IF Debug.Level >= Debug.Errors THEN KernelLog.String("UsbStorage: Failure on BulkOnly clear halt"); KernelLog.Ln; END;
RETURN Base.ResFatalError;
END;
ELSIF status = Usbdi.InProgress THEN
IF Debug.Level >= Debug.Errors THEN KernelLog.String("UsbStorage: Timeout on BulkOnly data phase"); KernelLog.Ln; END;
RETURN Base.ResTimeout;
ELSIF status = Usbdi.Disconnected THEN
RETURN Base.ResDisconnected;
ELSIF status = Usbdi.Error THEN
IF Debug.Level >= Debug.Errors THEN KernelLog.String("UsbStorage: Failure on BulkOnly data phase"); KernelLog.Ln; END;
RETURN Base.ResFatalError;
END;
ELSE
tlen := 0;
END;
IF Debug.Trace & Debug.traceScTransfers THEN KernelLog.String("UsbStorage: Getting BulkOnly CSW"); KernelLog.Ln; END;
IF Base.performance = Usbdi.MaxPerformance THEN bulkInPipe.mode := Usbdi.MaxPerformance; ELSE bulkInPipe.mode := Usbdi.MinCpu; END;
bulkInPipe.SetTimeout(timeout);
status := bulkInPipe.Transfer(13, 0, CSWbuffer^);
IF status = Usbdi.InProgress THEN
IF Debug.Level >= Debug.Errors THEN KernelLog.String("UsbStorage: Timeout in CSW phase"); KernelLog.Ln; END;
RETURN Base.ResFatalError
ELSIF status = Usbdi.Disconnected THEN
RETURN Base.ResDisconnected;
ELSIF status # Usbdi.Ok THEN
IF Debug.Level >= Debug.Warnings THEN KernelLog.String("UsbStorage: Could not get CSW, must retry CSW phase"); KernelLog.Ln; END;
IF (status = Usbdi.Stalled) & ~bulkInPipe.ClearHalt() THEN
IF Debug.Level >= Debug.Errors THEN KernelLog.String("UsbStorage: Failure on BulkOnly clear halt"); KernelLog.Ln; END;
RETURN Base.ResFatalError;
END;
IF Debug.Level >= Debug.Warnings THEN KernelLog.String("UsbStorage: Retrying BulkOnly CSW"); KernelLog.Ln; END;
status := bulkInPipe.Transfer(13, 0, CSWbuffer^);
IF (status = Usbdi.Disconnected) THEN
RETURN Base.ResDisconnected;
ELSIF status # Usbdi.Ok THEN
IF Debug.Level >= Debug.Errors THEN KernelLog.String("UsbStorage: 2nd try to get CSW failed"); KernelLog.Ln; END;
RETURN Base.ResFatalError;
END;
END;
IF Debug.Trace & Debug.traceCSWs THEN
KernelLog.String("Received CSW: "); FOR i := 0 TO 12 DO KernelLog.Hex(ORD(CSWbuffer[i]), -2); KernelLog.Char(" "); END; KernelLog.Ln;
END;
IF (CSWbuffer[0] # 55X) OR (CSWbuffer[1] # 53X) OR (CSWbuffer[2] # 42X) OR (CSWbuffer[3] # 53X) THEN
IF Debug.Level >= Debug.Errors THEN KernelLog.String("UsbStorage: Device did not send a valid CSW! (wrong signature)"); KernelLog.Ln; END;
RETURN Base.ResFatalError;
END;
IF (CSWbuffer[4] # CBWbuffer[4]) OR (CSWbuffer[5] # CBWbuffer[5]) OR (CSWbuffer[6] # CBWbuffer[6]) OR (CSWbuffer[7] # CBWbuffer[7]) THEN
IF Debug.Level >= Debug.Errors THEN KernelLog.String("UsbStorage: Device sent wrong tag in CSW"); KernelLog.Ln; END;
RETURN Base.ResFatalError;
END;
residual := ORD(CSWbuffer[8]) + 10H*ORD(CSWbuffer[9]) + 100H*ORD(CSWbuffer[10]) + 1000H*ORD(CSWbuffer[11]);
IF (CSWbuffer[12] = 0X) & (residual <= bufferlen) THEN
IF residual # 0 THEN
tlen := bufferlen - residual;
RETURN Base.ResShortTransfer;
ELSE
RETURN Base.ResOk;
END;
ELSIF (CSWbuffer[12] = 1X) & (residual <= bufferlen) THEN
IF Debug.Level >= Debug.Warnings THEN KernelLog.String("UsbStorage: CSW reports Error"); KernelLog.Ln; END;
RETURN Base.ResError;
ELSIF CSWbuffer[12] = 2X THEN
IF Debug.Trace & Debug.Trace & Debug.traceCSWs THEN KernelLog.String("UsbStorage: CSW reports Phase Error"); KernelLog.Ln; END;
RETURN Base.ResFatalError;
ELSE
IF Debug.Trace THEN KernelLog.String("UsbStorage: CSW not meaningful"); KernelLog.Ln; END;
RETURN Base.ResFatalError;
END;
END Transport;
PROCEDURE &Init*;
BEGIN
Init^; NEW(CBWbuffer, 31); NEW(CSWbuffer, 13); seqNbr := 0;
END Init;
END BulkOnlyTransport;
END UsbStorageBot.