MODULE UsbStorageCbi;
IMPORT
KernelLog,
Base := UsbStorageBase, Usbdi, Debug := UsbDebug;
CONST
Pass = 0;
Fail = 1;
PhaseError = 2;
PersistentFailure = 3;
TYPE
CBITransport* = OBJECT(Base.StorageDriver);
PROCEDURE AcceptCommand(cmd : Usbdi.Buffer; cmdlen, timeout : LONGINT) : Usbdi.Status;
BEGIN
ASSERT(LEN(cmd) >= cmdlen);
defaultPipe.SetTimeout(timeout);
RETURN device.Request(Usbdi.ToDevice + Usbdi.Class + Usbdi.Interface, 0, 0, interface.bInterfaceNumber, cmdlen, cmd);
END AcceptCommand;
PROCEDURE Reset*(timeout : LONGINT) : LONGINT;
VAR buffer, interruptData : Usbdi.BufferPtr; status : Usbdi.Status; i : LONGINT;
BEGIN
IF Debug.Trace & Debug.traceScRequests THEN KernelLog.String("UsbStorageCbi: Sending CB/I reset ControlTransfer"); KernelLog.Ln; END;
NEW(buffer, 12);
buffer[0] := CHR(1DH);
buffer[1] := CHR(4);
FOR i := 2 TO 11 DO buffer[i] := CHR(255) END;
status := AcceptCommand(buffer^, 12, timeout);
IF (status = Usbdi.Disconnected) THEN
RETURN Base.ResDisconnected;
ELSIF (status # Usbdi.Ok) THEN
IF Debug.Level >= Debug.Errors THEN KernelLog.String("UsbStorageCbi: Failure on TransportCB/I-Reset Control"); KernelLog.Ln; END;
RETURN Base.ResFatalError;
END;
IF transportMethod = Base.MethodCBI THEN
IF Debug.Trace & Debug.traceScRequests THEN KernelLog.String("UsbStorageCbi: Sending CB/I reset InterruptTransfer"); KernelLog.Ln; END;
NEW(interruptData, 8);
interruptPipe.SetTimeout(timeout);
status := interruptPipe.Transfer(2, 0, interruptData^);
IF status = Usbdi.Stalled THEN
IF Debug.Level >= Debug.Errors THEN KernelLog.String("UsbStorageCbi: Stall on TransportCB/I-Reset Interrupt"); KernelLog.Ln; END;
IF ~interruptPipe.ClearHalt() THEN RETURN Base.ResError END;
IF Debug.Level >= Debug.Errors THEN KernelLog.String("UsbStorageCbi: Failure on TransportCB/I-Reset clear halt on Interruptpipe"); KernelLog.Ln; END;
RETURN Base.ResFatalError;
ELSIF status = Usbdi.InProgress THEN
IF Debug.Level >= Debug.Errors THEN KernelLog.String("UsbStorageCbi: Timeout on TransportCB/I-Reset Interrupt"); KernelLog.Ln; END;
RETURN Base.ResTimeout;
ELSIF status = Usbdi.Disconnected THEN
RETURN Base.ResDisconnected;
ELSIF status # Usbdi.Ok THEN
IF Debug.Level >= Debug.Errors THEN KernelLog.String("UsbStorageCbi: Failure on TransportCB/I-Reset Interrupt"); KernelLog.Ln; END;
RETURN Base.ResFatalError;
END;
END;
IF ~bulkInPipe.ClearHalt() OR ~bulkOutPipe.ClearHalt() THEN
IF Debug.Level >= Debug.Errors THEN KernelLog.String("UsbStorageCbi: Failure on CB/I reset ClearHalt"); KernelLog.Ln; END;
RETURN Base.ResFatalError;
END;
IF Debug.Trace & Debug.traceScRequests THEN KernelLog.String("UsbStorageCbi: CB/I reset OK"); KernelLog.Ln; END;
RETURN Base.ResOk;
END Reset;
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; interruptData : Usbdi.BufferPtr; blockStatus : LONGINT;
BEGIN
IF Debug.Trace & Debug.traceScTransfers THEN KernelLog.String("UsbStorageCbi: Sending TransportCB/I Control"); KernelLog.Ln; END;
status := AcceptCommand(cmd, cmdlen, timeout);
IF status = Usbdi.Stalled THEN
IF Debug.Level >= Debug.Warnings THEN KernelLog.String("UsbStorageCbi: Stall on TransportCB/I Control"); KernelLog.Ln; END;
RETURN Base.ResError;
ELSIF status = Usbdi.InProgress THEN
IF Debug.Level >= Debug.Warnings THEN KernelLog.String("UsbStorageCbi: Timeout on TransportCB/I Control"); KernelLog.Ln; END;
RETURN Base.ResTimeout;
ELSIF status = Usbdi.Disconnected THEN
RETURN Base.ResDisconnected;
ELSIF status # Usbdi.Ok THEN
IF Debug.Level >= Debug.Warnings THEN
KernelLog.String("UsbStorageCbi: Failure on TransportCB/I Control, status :"); KernelLog.Int(status, 0); KernelLog.Ln;
END;
RETURN Base.ResError;
END;
IF (bufferlen # 0) THEN
IF dir = Base.DataIn THEN
IF Debug.Trace & Debug.traceScTransfers THEN
KernelLog.String("UsbStorageCbi: Get "); KernelLog.Int(bufferlen, 0); KernelLog.String(" bytes from device"); KernelLog.Ln;
END;
bulkInPipe.SetTimeout(timeout);
status := bulkInPipe.Transfer(bufferlen, ofs, buffer);
tlen := bulkInPipe.GetActLen();
ELSIF dir = Base.DataOut THEN
IF Debug.Trace & Debug.traceScTransfers THEN
KernelLog.String("UsbStorageCbi: Send "); KernelLog.Int(bufferlen, 0); KernelLog.String(" bytes to device"); KernelLog.Ln;
END;
bulkOutPipe.SetTimeout(timeout);
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("UsbStorageCbi: Stall on TransportCB/I Bulk"); KernelLog.Ln; END;
IF ((dir = Base.DataIn) & ~bulkInPipe.ClearHalt()) OR ((dir = Base.DataOut) & ~bulkOutPipe.ClearHalt()) THEN
IF Debug.Level >= Debug.Warnings THEN KernelLog.String("UsbStorage: Failure on TransportCB/I clear halt on Bulkpipe"); KernelLog.Ln; END;
RETURN Base.ResFatalError
END;
RETURN Base.ResError;
ELSIF status = Usbdi.InProgress THEN
IF Debug.Level >= Debug.Warnings THEN KernelLog.String("UsbStorageCbi: Timeout on TransportCB/I Bulk"); KernelLog.Ln; END;
RETURN Base.ResTimeout;
ELSIF status = Usbdi.Disconnected THEN
RETURN Base.ResDisconnected;
ELSIF status # Usbdi.Ok THEN
IF Debug.Level >= Debug.Warnings THEN KernelLog.String("UsbStorageCbi: Failure on TransportCB/I Bulk"); KernelLog.Ln; END;
RETURN Base.ResError;
END;
ELSE
tlen := 0;
END;
IF transportMethod = Base.MethodCBI THEN
IF Debug.Trace & Debug.traceScRequests THEN KernelLog.String("UsbStorageCbi: Sending TransportCB/I Interrupt"); KernelLog.Ln; END;
NEW(interruptData, 2);
interruptPipe.SetTimeout(timeout);
status := interruptPipe.Transfer(2, 0, interruptData^);
IF status = Usbdi.Stalled THEN
IF Debug.Level >= Debug.Errors THEN KernelLog.String("UsbStorageCbi: Stall on TransportCB/I Interrupt"); KernelLog.Ln; END;
IF interruptPipe.ClearHalt() THEN RETURN Base.ResError END;
IF Debug.Level >= Debug.Errors THEN KernelLog.String("UsbStorageCbi: Failure on TransportCB/I clear halt on Interruptpipe"); KernelLog.Ln; END;
RETURN Base.ResFatalError
ELSIF status = Usbdi.InProgress THEN
IF Debug.Level >= Debug.Errors THEN KernelLog.String("UsbStorageCbi: Timeout on TransportCB/I Interrupt"); KernelLog.Ln; END;
RETURN Base.ResTimeout;
ELSIF status = Usbdi.Disconnected THEN
RETURN Base.ResDisconnected;
ELSIF status # Usbdi.Ok THEN
IF Debug.Level >= Debug.Errors THEN KernelLog.String("UsbStorageCbi: Failure on TransportCB/I Interrupt"); KernelLog.Ln; END;
RETURN Base.ResFatalError;
END;
IF (transportProtocol = Base.ProtocolUFI) THEN
IF (cmd[0] = 12X) OR (cmd[0] = 03X) THEN
ELSIF (interruptData[0] # 0X) THEN
IF Debug.Level >= Debug.Errors THEN
KernelLog.String("UsbStorageCbi: Error on CBI/UFI, asc = "); KernelLog.Hex(ORD(interruptData[0]), 0);
KernelLog.String(" ascq = "); KernelLog.Hex(ORD(interruptData[1]), 0); KernelLog.Ln;
END;
RETURN Base.ResSenseError;
END;
ELSIF interruptData[0] # 0X THEN
IF Debug.Level >= Debug.Errors THEN KernelLog.String("UsbStorageCbi: CBI returned invalid interupt data block"); KernelLog.Ln; END;
RETURN Base.ResSenseError;
ELSE
blockStatus := ORD(interruptData[1]) MOD 4;
CASE blockStatus OF
|Pass:
|Fail: RETURN Base.ResError;
|PhaseError: RETURN Base.ResFatalError;
|PersistentFailure: RETURN Base.ResSenseError;
ELSE
HALT(99);
END;
END;
END;
IF tlen # bufferlen THEN RETURN Base.ResShortTransfer; END;
RETURN Base.ResOk;
END Transport;
PROCEDURE &Init*;
BEGIN
Init^;
END Init;
END CBITransport;
END UsbStorageCbi.