123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239 |
- MODULE UsbStorageBot; (** AUTHOR "staubesv"; PURPOSE "Bulk-Only transport layer of USB mass storage driver"; *)
- (**
- * References:
- *
- * - Universal Serial Bus Mass Storage Class Bulk-Only Transport, Revision 1.0, September 31, 1999
- * www.usb.org
- *
- * History:
- *
- * 09.02.2006 First release (staubesv)
- * 05.07.2006 Adapted to Usbi (staubesv)
- *)
- IMPORT
- SYSTEM, KernelLog,
- Usbdi, Base := UsbStorageBase, Debug := UsbDebug;
- TYPE
- (* USB mass storage class bulk only transport layer *)
- BulkOnlyTransport* = OBJECT(Base.StorageDriver)
- VAR
- CBWbuffer : Usbdi.BufferPtr;
- CSWbuffer : Usbdi.BufferPtr;
- seqNbr : LONGINT;
- (* Perform reset recovery, i.e. send Bulk-Only Mass Storage Reset command via default control pipe and *)
- (* then clear the EndpointHalt feature of the bulk in and bulk out endpoints of the USB device. The request *)
- (* shall ready the device for the next CBW from the host. See [2], pages 7 & 16 *)
- 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;
- (* Mass storage devices request: Bulk-Only Mass Storage Reset *)
- 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;
- (* The Get Max LUN device request is used to determine the number of logical units supported by the device *)
- 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 (* Devices that do not suppoert multiple LUNs may stall this command *)
- 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;
- (* Process a bulk-only transfer. A three stage protocol is used: *)
- (* 1. Send command block wrapper (CBW) to device *)
- (* 2. Dataphase (optional) *)
- (* 3. Receive command status word (CSW) from device *)
- 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;
- b: Usbdi.Buffer;
- BEGIN (* No concurrency allowed *)
- ASSERT((cmdlen > 0) & (cmdlen <= 16)); (* [2], page 14 *)
- (* set up dCBWSignature *)
- CBWbuffer[0] := 55X; CBWbuffer[1] := 53X; CBWbuffer[2] := 42X; CBWbuffer[3] := 43X;
- (* set up dCBWTag - will be echoed by the device *)
- INC(seqNbr);
- CBWbuffer[4] := CHR(seqNbr);
- CBWbuffer[5] := CHR(LSH(seqNbr, -8));
- CBWbuffer[6] := CHR(LSH(seqNbr, -16));
- CBWbuffer[7] := CHR(LSH(seqNbr, -24));
- (* set up dCBWDataTransferLength *)
- CBWbuffer[8] := CHR(bufferlen);
- CBWbuffer[9] := CHR(LSH(bufferlen, -8));
- CBWbuffer[10] := CHR(LSH(bufferlen, -16));
- CBWbuffer[11] := CHR(LSH(bufferlen, -24));
- (* set up bmCBWFlags *)
- IF dir = Base.DataIn THEN CBWbuffer[12] := 80X; ELSE CBWbuffer[12] := 0X; END;
- (* set bCBWLUN *)
- CBWbuffer[13] := CHR(SYSTEM.VAL(LONGINT, LSH(SYSTEM.VAL(SET, ORD(cmd[1])) * {5..7}, -5)));
- (*set bCBWCBLength *)
- CBWbuffer[14] := CHR(cmdlen);
- FOR i := 15 TO 30 DO CBWbuffer[i] := 0X; END;
- (* copy CBWCB *)
- 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;
- (* send the CBW *)
- 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 (* sending the CBW failed -> Perform reset recovery [2], page 15 *)
- RETURN Base.ResFatalError;
- END;
- (* If there is data to send, enter the data stage *)
- IF bufferlen # 0 THEN
- NEW(b, bufferlen);
- FOR i := 0 TO bufferlen - 1 DO b[i] := buffer[ofs + i] END;
- 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, 0(*ofs*), b);
- 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, 0(*ofs*), b);
- tlen := bulkOutPipe.GetActLen();
- ELSE HALT(303);
- END;
- FOR i := 0 TO tlen - 1 DO buffer[ofs + i] := b[i] END;
- (* clear halt if STALL occured, but do not abort!!! *)
- IF status = Usbdi.Stalled THEN
- IF Debug.Level >= Debug.Warnings THEN KernelLog.String("UsbStorage: Stall on BulkOnly data phase"); KernelLog.Ln; END;
- (* only abort if clear halt fails *)
- 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
- (* allow short packets and stalls !!! *)
- 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;
- (* enter the status phase - Get the CSW *)
- 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;
- (* Host shall retry to get CSW ([2], page 19) *)
- 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;
- (* Check whether the CSW is valid. If it's not, perform a reset recovery ([2], page 18) *)
- (* Validity: Check CSW signature *)
- 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;
- (* Validity: check the dCSWTag *)
- 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;
- (* Check whether the CSW is meaningful *)
- residual := ORD(CSWbuffer[8]) + 10H*ORD(CSWbuffer[9]) + 100H*ORD(CSWbuffer[10]) + 1000H*ORD(CSWbuffer[11]);
- (* Meaning: bCSWStatus *)
- IF (CSWbuffer[12] = 0X) & (residual <= bufferlen) THEN (* CSW meaningful; Command Passed *)
- IF residual # 0 THEN
- tlen := bufferlen - residual;
- RETURN Base.ResShortTransfer;
- ELSE
- RETURN Base.ResOk;
- END;
- ELSIF (CSWbuffer[12] = 1X) & (residual <= bufferlen) THEN (* CSW meaningful; Command Error *)
- IF Debug.Level >= Debug.Warnings THEN KernelLog.String("UsbStorage: CSW reports Error"); KernelLog.Ln; END;
- RETURN Base.ResError;
- ELSIF CSWbuffer[12] = 2X THEN (* Phase Error: Perform reset recovery [2], page 16*)
- IF Debug.Trace & Debug.Trace & Debug.traceCSWs THEN KernelLog.String("UsbStorage: CSW reports Phase Error"); KernelLog.Ln; END;
- RETURN Base.ResFatalError;
- ELSE (* CSW not meaningful -> Perform reset recovery *)
- 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.
|