123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217 |
- MODULE UsbStorageCbi; (** AUTHOR "cplattner/staubesv"; PURPOSE " CB/I transport layer of USB mass storage driver"; *)
- (**
- * References:
- *
- * - [1] Universal Serial Bus Mass Storage Class Control/Bulk/Interrupt (CBI) Transport, Revision 1.1, June 23, 2003
- * www.usb.org
- *
- * History:
- *
- * 09.02.2006 First release (staubesv)
- * 05.07.2006 Adapted to Usbdi (staubesv)
- * 07.08.2006 Cleanups, improved error case in transport, fixed transfer offset ignored (staubesv)
- *)
- IMPORT
- KernelLog,
- Base := UsbStorageBase, Usbdi, Debug := UsbDebug;
- CONST
- (* Interrupt Data Block coding, 3.4.3.1.1 in [1] *)
- Pass = 0;
- Fail = 1;
- PhaseError = 2;
- PersistentFailure = 3;
- TYPE
- (** USB Mass Storage Class Control/Bulk/Interrupt (CBI) and Control/Bulk (CB) transport layer *)
- CBITransport* = OBJECT(Base.StorageDriver);
- (**
- * The Accept Device-Specific Command class-specific request is uses by the CBI Command Transport
- * Protocol to send a command block from a host to a device.
- * @param cmdLen Length of the command
- * @param cmd Command
- * @param timeout in milliseconds
- * @return transport status of command block
- *)
- 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;
- (* After a Command Block Reset, the Stall condition and data toggle of the device's endpoints are undefined (2.2 in [1]) *)
- 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;
- b, c: Usbdi.BufferPtr; i: LONGINT;
- BEGIN
- IF Debug.Trace & Debug.traceScTransfers THEN KernelLog.String("UsbStorageCbi: Sending TransportCB/I Control"); KernelLog.Ln; END;
- NEW(c, cmdlen);
- FOR i := 0 TO cmdlen - 1 DO c[i] := cmd[i] END;
- NEW(b, bufferlen);
- FOR i := 0 TO bufferlen - 1 DO b[i] := buffer[ofs + i] END;
- status := AcceptCommand(c, 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; (* sense device *)
- 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; (* sense device *)
- 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, b);
- 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, b);
- 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; (* sense device *)
- 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; (* sense device *)
- 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
- (* UFI Inquiry + Sense do not change the sense data, so we cannot be sure that those commands succeded!!! *)
- (* just go on and hope the best! *)
- 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; (* just retry *)
- END;
- (* go on *)
- 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; (* try to recover by manual sensing *)
- ELSE
- (* Command completion interrupt. Error handling according 3.4.3.1.1. in [1] *)
- blockStatus := ORD(interruptData[1]) MOD 4;
- CASE blockStatus OF
- |Pass: (* command status ok *)
- |Fail: RETURN Base.ResError;
- |PhaseError: RETURN Base.ResFatalError; (* reset device *)
- |PersistentFailure: RETURN Base.ResSenseError; (* request sense *)
- 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.
|