UsbStorageCbi.Mod 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217
  1. MODULE UsbStorageCbi; (** AUTHOR "cplattner/staubesv"; PURPOSE " CB/I transport layer of USB mass storage driver"; *)
  2. (**
  3. * References:
  4. *
  5. * - [1] Universal Serial Bus Mass Storage Class Control/Bulk/Interrupt (CBI) Transport, Revision 1.1, June 23, 2003
  6. * www.usb.org
  7. *
  8. * History:
  9. *
  10. * 09.02.2006 First release (staubesv)
  11. * 05.07.2006 Adapted to Usbdi (staubesv)
  12. * 07.08.2006 Cleanups, improved error case in transport, fixed transfer offset ignored (staubesv)
  13. *)
  14. IMPORT
  15. KernelLog,
  16. Base := UsbStorageBase, Usbdi, Debug := UsbDebug;
  17. CONST
  18. (* Interrupt Data Block coding, 3.4.3.1.1 in [1] *)
  19. Pass = 0;
  20. Fail = 1;
  21. PhaseError = 2;
  22. PersistentFailure = 3;
  23. TYPE
  24. (** USB Mass Storage Class Control/Bulk/Interrupt (CBI) and Control/Bulk (CB) transport layer *)
  25. CBITransport* = OBJECT(Base.StorageDriver);
  26. (**
  27. * The Accept Device-Specific Command class-specific request is uses by the CBI Command Transport
  28. * Protocol to send a command block from a host to a device.
  29. * @param cmdLen Length of the command
  30. * @param cmd Command
  31. * @param timeout in milliseconds
  32. * @return transport status of command block
  33. *)
  34. PROCEDURE AcceptCommand(cmd : Usbdi.Buffer; cmdlen, timeout : LONGINT) : Usbdi.Status;
  35. BEGIN
  36. ASSERT(LEN(cmd) >= cmdlen);
  37. defaultPipe.SetTimeout(timeout);
  38. RETURN device.Request(Usbdi.ToDevice + Usbdi.Class + Usbdi.Interface, 0, 0, interface.bInterfaceNumber, cmdlen, cmd);
  39. END AcceptCommand;
  40. PROCEDURE Reset*(timeout : LONGINT) : LONGINT;
  41. VAR buffer, interruptData : Usbdi.BufferPtr; status : Usbdi.Status; i : LONGINT;
  42. BEGIN
  43. IF Debug.Trace & Debug.traceScRequests THEN KernelLog.String("UsbStorageCbi: Sending CB/I reset ControlTransfer"); KernelLog.Ln; END;
  44. NEW(buffer, 12);
  45. buffer[0] := CHR(1DH);
  46. buffer[1] := CHR(4);
  47. FOR i := 2 TO 11 DO buffer[i] := CHR(255) END;
  48. status := AcceptCommand(buffer, 12, timeout);
  49. IF (status = Usbdi.Disconnected) THEN
  50. RETURN Base.ResDisconnected;
  51. ELSIF (status # Usbdi.Ok) THEN
  52. IF Debug.Level >= Debug.Errors THEN KernelLog.String("UsbStorageCbi: Failure on TransportCB/I-Reset Control"); KernelLog.Ln; END;
  53. RETURN Base.ResFatalError;
  54. END;
  55. IF transportMethod = Base.MethodCBI THEN
  56. IF Debug.Trace & Debug.traceScRequests THEN KernelLog.String("UsbStorageCbi: Sending CB/I reset InterruptTransfer"); KernelLog.Ln; END;
  57. NEW(interruptData, 8);
  58. interruptPipe.SetTimeout(timeout);
  59. status := interruptPipe.Transfer(2, 0, interruptData);
  60. IF status = Usbdi.Stalled THEN
  61. IF Debug.Level >= Debug.Errors THEN KernelLog.String("UsbStorageCbi: Stall on TransportCB/I-Reset Interrupt"); KernelLog.Ln; END;
  62. IF ~interruptPipe.ClearHalt() THEN RETURN Base.ResError END;
  63. IF Debug.Level >= Debug.Errors THEN KernelLog.String("UsbStorageCbi: Failure on TransportCB/I-Reset clear halt on Interruptpipe"); KernelLog.Ln; END;
  64. RETURN Base.ResFatalError;
  65. ELSIF status = Usbdi.InProgress THEN
  66. IF Debug.Level >= Debug.Errors THEN KernelLog.String("UsbStorageCbi: Timeout on TransportCB/I-Reset Interrupt"); KernelLog.Ln; END;
  67. RETURN Base.ResTimeout;
  68. ELSIF status = Usbdi.Disconnected THEN
  69. RETURN Base.ResDisconnected;
  70. ELSIF status # Usbdi.Ok THEN
  71. IF Debug.Level >= Debug.Errors THEN KernelLog.String("UsbStorageCbi: Failure on TransportCB/I-Reset Interrupt"); KernelLog.Ln; END;
  72. RETURN Base.ResFatalError;
  73. END;
  74. END;
  75. (* After a Command Block Reset, the Stall condition and data toggle of the device's endpoints are undefined (2.2 in [1]) *)
  76. IF ~bulkInPipe.ClearHalt() OR ~bulkOutPipe.ClearHalt() THEN
  77. IF Debug.Level >= Debug.Errors THEN KernelLog.String("UsbStorageCbi: Failure on CB/I reset ClearHalt"); KernelLog.Ln; END;
  78. RETURN Base.ResFatalError;
  79. END;
  80. IF Debug.Trace & Debug.traceScRequests THEN KernelLog.String("UsbStorageCbi: CB/I reset OK"); KernelLog.Ln; END;
  81. RETURN Base.ResOk;
  82. END Reset;
  83. PROCEDURE Transport*(cmd : ARRAY OF CHAR; cmdlen : LONGINT; dir : SET;
  84. VAR buffer : ARRAY OF CHAR; ofs, bufferlen : LONGINT; VAR tlen : LONGINT; timeout : LONGINT) : LONGINT;
  85. VAR status : Usbdi.Status; interruptData : Usbdi.BufferPtr; blockStatus : LONGINT;
  86. b, c: Usbdi.BufferPtr; i: LONGINT;
  87. BEGIN
  88. IF Debug.Trace & Debug.traceScTransfers THEN KernelLog.String("UsbStorageCbi: Sending TransportCB/I Control"); KernelLog.Ln; END;
  89. NEW(c, cmdlen);
  90. FOR i := 0 TO cmdlen - 1 DO c[i] := cmd[i] END;
  91. NEW(b, bufferlen);
  92. FOR i := 0 TO bufferlen - 1 DO b[i] := buffer[ofs + i] END;
  93. status := AcceptCommand(c, cmdlen, timeout);
  94. IF status = Usbdi.Stalled THEN
  95. IF Debug.Level >= Debug.Warnings THEN KernelLog.String("UsbStorageCbi: Stall on TransportCB/I Control"); KernelLog.Ln; END;
  96. RETURN Base.ResError; (* sense device *)
  97. ELSIF status = Usbdi.InProgress THEN
  98. IF Debug.Level >= Debug.Warnings THEN KernelLog.String("UsbStorageCbi: Timeout on TransportCB/I Control"); KernelLog.Ln; END;
  99. RETURN Base.ResTimeout;
  100. ELSIF status = Usbdi.Disconnected THEN
  101. RETURN Base.ResDisconnected;
  102. ELSIF status # Usbdi.Ok THEN
  103. IF Debug.Level >= Debug.Warnings THEN
  104. KernelLog.String("UsbStorageCbi: Failure on TransportCB/I Control, status :"); KernelLog.Int(status, 0); KernelLog.Ln;
  105. END;
  106. RETURN Base.ResError; (* sense device *)
  107. END;
  108. IF (bufferlen # 0) THEN
  109. IF dir = Base.DataIn THEN
  110. IF Debug.Trace & Debug.traceScTransfers THEN
  111. KernelLog.String("UsbStorageCbi: Get "); KernelLog.Int(bufferlen, 0); KernelLog.String(" bytes from device"); KernelLog.Ln;
  112. END;
  113. bulkInPipe.SetTimeout(timeout);
  114. status := bulkInPipe.Transfer(bufferlen, ofs, b);
  115. tlen := bulkInPipe.GetActLen();
  116. ELSIF dir = Base.DataOut THEN
  117. IF Debug.Trace & Debug.traceScTransfers THEN
  118. KernelLog.String("UsbStorageCbi: Send "); KernelLog.Int(bufferlen, 0); KernelLog.String(" bytes to device"); KernelLog.Ln;
  119. END;
  120. bulkOutPipe.SetTimeout(timeout);
  121. status := bulkOutPipe.Transfer(bufferlen, ofs, b);
  122. tlen := bulkOutPipe.GetActLen();
  123. ELSE HALT(303);
  124. END;
  125. IF status = Usbdi.Stalled THEN
  126. IF Debug.Level >= Debug.Warnings THEN KernelLog.String("UsbStorageCbi: Stall on TransportCB/I Bulk"); KernelLog.Ln; END;
  127. IF ((dir = Base.DataIn) & ~bulkInPipe.ClearHalt()) OR ((dir = Base.DataOut) & ~bulkOutPipe.ClearHalt()) THEN
  128. IF Debug.Level >= Debug.Warnings THEN KernelLog.String("UsbStorage: Failure on TransportCB/I clear halt on Bulkpipe"); KernelLog.Ln; END;
  129. RETURN Base.ResFatalError
  130. END;
  131. RETURN Base.ResError; (* sense device *)
  132. ELSIF status = Usbdi.InProgress THEN
  133. IF Debug.Level >= Debug.Warnings THEN KernelLog.String("UsbStorageCbi: Timeout on TransportCB/I Bulk"); KernelLog.Ln; END;
  134. RETURN Base.ResTimeout;
  135. ELSIF status = Usbdi.Disconnected THEN
  136. RETURN Base.ResDisconnected;
  137. ELSIF status # Usbdi.Ok THEN
  138. IF Debug.Level >= Debug.Warnings THEN KernelLog.String("UsbStorageCbi: Failure on TransportCB/I Bulk"); KernelLog.Ln; END;
  139. RETURN Base.ResError; (* sense device *)
  140. END;
  141. ELSE
  142. tlen := 0;
  143. END;
  144. IF transportMethod = Base.MethodCBI THEN
  145. IF Debug.Trace & Debug.traceScRequests THEN KernelLog.String("UsbStorageCbi: Sending TransportCB/I Interrupt"); KernelLog.Ln; END;
  146. NEW(interruptData, 2);
  147. interruptPipe.SetTimeout(timeout);
  148. status := interruptPipe.Transfer(2, 0, interruptData);
  149. IF status = Usbdi.Stalled THEN
  150. IF Debug.Level >= Debug.Errors THEN KernelLog.String("UsbStorageCbi: Stall on TransportCB/I Interrupt"); KernelLog.Ln; END;
  151. IF interruptPipe.ClearHalt() THEN RETURN Base.ResError END;
  152. IF Debug.Level >= Debug.Errors THEN KernelLog.String("UsbStorageCbi: Failure on TransportCB/I clear halt on Interruptpipe"); KernelLog.Ln; END;
  153. RETURN Base.ResFatalError
  154. ELSIF status = Usbdi.InProgress THEN
  155. IF Debug.Level >= Debug.Errors THEN KernelLog.String("UsbStorageCbi: Timeout on TransportCB/I Interrupt"); KernelLog.Ln; END;
  156. RETURN Base.ResTimeout;
  157. ELSIF status = Usbdi.Disconnected THEN
  158. RETURN Base.ResDisconnected;
  159. ELSIF status # Usbdi.Ok THEN
  160. IF Debug.Level >= Debug.Errors THEN KernelLog.String("UsbStorageCbi: Failure on TransportCB/I Interrupt"); KernelLog.Ln; END;
  161. RETURN Base.ResFatalError;
  162. END;
  163. IF (transportProtocol = Base.ProtocolUFI) THEN
  164. IF (cmd[0] = 12X) OR (cmd[0] = 03X) THEN
  165. (* UFI Inquiry + Sense do not change the sense data, so we cannot be sure that those commands succeded!!! *)
  166. (* just go on and hope the best! *)
  167. ELSIF (interruptData[0] # 0X) THEN
  168. IF Debug.Level >= Debug.Errors THEN
  169. KernelLog.String("UsbStorageCbi: Error on CBI/UFI, asc = "); KernelLog.Hex(ORD(interruptData[0]), 0);
  170. KernelLog.String(" ascq = "); KernelLog.Hex(ORD(interruptData[1]), 0); KernelLog.Ln;
  171. END;
  172. RETURN Base.ResSenseError; (* just retry *)
  173. END;
  174. (* go on *)
  175. ELSIF interruptData[0] # 0X THEN
  176. IF Debug.Level >= Debug.Errors THEN KernelLog.String("UsbStorageCbi: CBI returned invalid interupt data block"); KernelLog.Ln; END;
  177. RETURN Base.ResSenseError; (* try to recover by manual sensing *)
  178. ELSE
  179. (* Command completion interrupt. Error handling according 3.4.3.1.1. in [1] *)
  180. blockStatus := ORD(interruptData[1]) MOD 4;
  181. CASE blockStatus OF
  182. |Pass: (* command status ok *)
  183. |Fail: RETURN Base.ResError;
  184. |PhaseError: RETURN Base.ResFatalError; (* reset device *)
  185. |PersistentFailure: RETURN Base.ResSenseError; (* request sense *)
  186. ELSE
  187. HALT(99);
  188. END;
  189. END;
  190. END;
  191. IF tlen # bufferlen THEN RETURN Base.ResShortTransfer; END;
  192. RETURN Base.ResOk;
  193. END Transport;
  194. PROCEDURE &Init*;
  195. BEGIN
  196. Init^;
  197. END Init;
  198. END CBITransport;
  199. END UsbStorageCbi.