2 Implementation of the USB mass storage Bulk-Only Transport protocol,
3 according to USB Mass Storage Class Bulk-Only Transport, Revision 1.0.
5 Copyright (c) 2007 - 2017, Intel Corporation. All rights reserved.<BR>
6 This program and the accompanying materials
7 are licensed and made available under the terms and conditions of the BSD License
8 which accompanies this distribution. The full text of the license may be found at
9 http://opensource.org/licenses/bsd-license.php
11 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
12 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
19 // Definition of USB BOT Transport Protocol
21 USB_MASS_TRANSPORT mUsbBotTransport
= {
31 Initializes USB BOT protocol.
33 This function initializes the USB mass storage class BOT protocol.
34 It will save its context which is a USB_BOT_PROTOCOL structure
35 in the Context if Context isn't NULL.
37 @param UsbIo The USB I/O Protocol instance
38 @param Context The buffer to save the context to
40 @retval EFI_SUCCESS The device is successfully initialized.
41 @retval EFI_UNSUPPORTED The transport protocol doesn't support the device.
42 @retval Other The USB BOT initialization fails.
47 IN EFI_USB_IO_PROTOCOL
*UsbIo
,
48 OUT VOID
**Context OPTIONAL
51 USB_BOT_PROTOCOL
*UsbBot
;
52 EFI_USB_INTERFACE_DESCRIPTOR
*Interface
;
53 EFI_USB_ENDPOINT_DESCRIPTOR EndPoint
;
58 // Allocate the BOT context for USB_BOT_PROTOCOL and two endpoint descriptors.
60 UsbBot
= AllocateZeroPool (sizeof (USB_BOT_PROTOCOL
) + 2 * sizeof (EFI_USB_ENDPOINT_DESCRIPTOR
));
61 ASSERT (UsbBot
!= NULL
);
63 UsbBot
->UsbIo
= UsbIo
;
66 // Get the interface descriptor and validate that it
67 // is a USB Mass Storage BOT interface.
69 Status
= UsbIo
->UsbGetInterfaceDescriptor (UsbIo
, &UsbBot
->Interface
);
71 if (EFI_ERROR (Status
)) {
75 Interface
= &UsbBot
->Interface
;
77 if (Interface
->InterfaceProtocol
!= USB_MASS_STORE_BOT
) {
78 Status
= EFI_UNSUPPORTED
;
83 // Locate and save the first bulk-in and bulk-out endpoint
85 for (Index
= 0; Index
< Interface
->NumEndpoints
; Index
++) {
86 Status
= UsbIo
->UsbGetEndpointDescriptor (UsbIo
, Index
, &EndPoint
);
88 if (EFI_ERROR (Status
) || !USB_IS_BULK_ENDPOINT (EndPoint
.Attributes
)) {
92 if (USB_IS_IN_ENDPOINT (EndPoint
.EndpointAddress
) &&
93 (UsbBot
->BulkInEndpoint
== NULL
)) {
95 UsbBot
->BulkInEndpoint
= (EFI_USB_ENDPOINT_DESCRIPTOR
*) (UsbBot
+ 1);
96 CopyMem(UsbBot
->BulkInEndpoint
, &EndPoint
, sizeof (EndPoint
));
99 if (USB_IS_OUT_ENDPOINT (EndPoint
.EndpointAddress
) &&
100 (UsbBot
->BulkOutEndpoint
== NULL
)) {
102 UsbBot
->BulkOutEndpoint
= (EFI_USB_ENDPOINT_DESCRIPTOR
*) (UsbBot
+ 1) + 1;
103 CopyMem (UsbBot
->BulkOutEndpoint
, &EndPoint
, sizeof(EndPoint
));
108 // If bulk-in or bulk-out endpoint is not found, report error.
110 if ((UsbBot
->BulkInEndpoint
== NULL
) || (UsbBot
->BulkOutEndpoint
== NULL
)) {
111 Status
= EFI_UNSUPPORTED
;
116 // The USB BOT protocol uses CBWTag to match the CBW and CSW.
118 UsbBot
->CbwTag
= 0x01;
120 if (Context
!= NULL
) {
134 Send the command to the device using Bulk-Out endpoint.
136 This function sends the command to the device using Bulk-Out endpoint.
137 BOT transfer is composed of three phases: Command, Data, and Status.
138 This is the Command phase.
140 @param UsbBot The USB BOT device
141 @param Cmd The command to transfer to device
142 @param CmdLen The length of the command
143 @param DataDir The direction of the data
144 @param TransLen The expected length of the data
145 @param Lun The number of logic unit
147 @retval EFI_SUCCESS The command is sent to the device.
148 @retval EFI_NOT_READY The device return NAK to the transfer
149 @retval Others Failed to send the command to device
154 IN USB_BOT_PROTOCOL
*UsbBot
,
157 IN EFI_USB_DATA_DIRECTION DataDir
,
168 ASSERT ((CmdLen
> 0) && (CmdLen
<= USB_BOT_MAX_CMDLEN
));
171 // Fill in the Command Block Wrapper.
173 Cbw
.Signature
= USB_BOT_CBW_SIGNATURE
;
174 Cbw
.Tag
= UsbBot
->CbwTag
;
175 Cbw
.DataLen
= TransLen
;
176 Cbw
.Flag
= (UINT8
) ((DataDir
== EfiUsbDataIn
) ? BIT7
: 0);
180 ZeroMem (Cbw
.CmdBlock
, USB_BOT_MAX_CMDLEN
);
181 CopyMem (Cbw
.CmdBlock
, Cmd
, CmdLen
);
184 DataLen
= sizeof (USB_BOT_CBW
);
185 Timeout
= USB_BOT_SEND_CBW_TIMEOUT
/ USB_MASS_1_MILLISECOND
;
188 // Use USB I/O Protocol to send the Command Block Wrapper to the device.
190 Status
= UsbBot
->UsbIo
->UsbBulkTransfer (
192 UsbBot
->BulkOutEndpoint
->EndpointAddress
,
198 if (EFI_ERROR (Status
)) {
199 if (USB_IS_ERROR (Result
, EFI_USB_ERR_STALL
) && DataDir
== EfiUsbDataOut
) {
201 // Respond to Bulk-Out endpoint stall with a Reset Recovery,
202 // according to section 5.3.1 of USB Mass Storage Class Bulk-Only Transport Spec, v1.0.
204 UsbBotResetDevice (UsbBot
, FALSE
);
205 } else if (USB_IS_ERROR (Result
, EFI_USB_ERR_NAK
)) {
206 Status
= EFI_NOT_READY
;
215 Transfer the data between the device and host.
217 This function transfers the data between the device and host.
218 BOT transfer is composed of three phases: Command, Data, and Status.
219 This is the Data phase.
221 @param UsbBot The USB BOT device
222 @param DataDir The direction of the data
223 @param Data The buffer to hold data
224 @param TransLen The expected length of the data
225 @param Timeout The time to wait the command to complete
227 @retval EFI_SUCCESS The data is transferred
228 @retval EFI_SUCCESS No data to transfer
229 @retval EFI_NOT_READY The device return NAK to the transfer
230 @retval Others Failed to transfer data
235 IN USB_BOT_PROTOCOL
*UsbBot
,
236 IN EFI_USB_DATA_DIRECTION DataDir
,
238 IN OUT UINTN
*TransLen
,
242 EFI_USB_ENDPOINT_DESCRIPTOR
*Endpoint
;
247 // If no data to transfer, just return EFI_SUCCESS.
249 if ((DataDir
== EfiUsbNoData
) || (*TransLen
== 0)) {
254 // Select the endpoint then issue the transfer
256 if (DataDir
== EfiUsbDataIn
) {
257 Endpoint
= UsbBot
->BulkInEndpoint
;
259 Endpoint
= UsbBot
->BulkOutEndpoint
;
263 Timeout
= Timeout
/ USB_MASS_1_MILLISECOND
;
265 Status
= UsbBot
->UsbIo
->UsbBulkTransfer (
267 Endpoint
->EndpointAddress
,
273 if (EFI_ERROR (Status
)) {
274 if (USB_IS_ERROR (Result
, EFI_USB_ERR_STALL
)) {
275 DEBUG ((EFI_D_INFO
, "UsbBotDataTransfer: (%r)\n", Status
));
276 DEBUG ((EFI_D_INFO
, "UsbBotDataTransfer: DataIn Stall\n"));
277 UsbClearEndpointStall (UsbBot
->UsbIo
, Endpoint
->EndpointAddress
);
278 } else if (USB_IS_ERROR (Result
, EFI_USB_ERR_NAK
)) {
279 Status
= EFI_NOT_READY
;
281 DEBUG ((EFI_D_ERROR
, "UsbBotDataTransfer: (%r)\n", Status
));
283 if(Status
== EFI_TIMEOUT
){
284 UsbBotResetDevice(UsbBot
, FALSE
);
293 Get the command execution status from device.
295 This function gets the command execution status from device.
296 BOT transfer is composed of three phases: Command, Data, and Status.
297 This is the Status phase.
299 This function returns the transfer status of the BOT's CSW status,
300 and returns the high level command execution result in Result. So
301 even if EFI_SUCCESS is returned, the command may still have failed.
303 @param UsbBot The USB BOT device.
304 @param TransLen The expected length of the data.
305 @param CmdStatus The result of the command execution.
307 @retval EFI_SUCCESS Command execute result is retrieved and in the Result.
308 @retval Other Error occurred when trying to get status.
313 IN USB_BOT_PROTOCOL
*UsbBot
,
323 EFI_USB_IO_PROTOCOL
*UsbIo
;
327 *CmdStatus
= USB_BOT_COMMAND_ERROR
;
328 Status
= EFI_DEVICE_ERROR
;
329 Endpoint
= UsbBot
->BulkInEndpoint
->EndpointAddress
;
330 UsbIo
= UsbBot
->UsbIo
;
331 Timeout
= USB_BOT_RECV_CSW_TIMEOUT
/ USB_MASS_1_MILLISECOND
;
333 for (Index
= 0; Index
< USB_BOT_RECV_CSW_RETRY
; Index
++) {
335 // Attemp to the read Command Status Wrapper from bulk in endpoint
337 ZeroMem (&Csw
, sizeof (USB_BOT_CSW
));
339 Len
= sizeof (USB_BOT_CSW
);
340 Status
= UsbIo
->UsbBulkTransfer (
348 if (EFI_ERROR(Status
)) {
349 if (USB_IS_ERROR (Result
, EFI_USB_ERR_STALL
)) {
350 UsbClearEndpointStall (UsbIo
, Endpoint
);
355 if (Csw
.Signature
!= USB_BOT_CSW_SIGNATURE
) {
357 // CSW is invalid, so perform reset recovery
359 Status
= UsbBotResetDevice (UsbBot
, FALSE
);
360 } else if (Csw
.CmdStatus
== USB_BOT_COMMAND_ERROR
) {
362 // Respond phase error also needs reset recovery
364 Status
= UsbBotResetDevice (UsbBot
, FALSE
);
366 *CmdStatus
= Csw
.CmdStatus
;
371 //The tag is increased even if there is an error.
380 Call the USB Mass Storage Class BOT protocol to issue
381 the command/data/status circle to execute the commands.
383 @param Context The context of the BOT protocol, that is,
385 @param Cmd The high level command
386 @param CmdLen The command length
387 @param DataDir The direction of the data transfer
388 @param Data The buffer to hold data
389 @param DataLen The length of the data
390 @param Lun The number of logic unit
391 @param Timeout The time to wait command
392 @param CmdStatus The result of high level command execution
394 @retval EFI_SUCCESS The command is executed successfully.
395 @retval Other Failed to execute command
403 IN EFI_USB_DATA_DIRECTION DataDir
,
408 OUT UINT32
*CmdStatus
411 USB_BOT_PROTOCOL
*UsbBot
;
416 *CmdStatus
= USB_MASS_CMD_FAIL
;
417 UsbBot
= (USB_BOT_PROTOCOL
*) Context
;
420 // Send the command to the device. Return immediately if device
421 // rejects the command.
423 Status
= UsbBotSendCommand (UsbBot
, Cmd
, CmdLen
, DataDir
, DataLen
, Lun
);
424 if (EFI_ERROR (Status
)) {
425 DEBUG ((EFI_D_ERROR
, "UsbBotExecCommand: UsbBotSendCommand (%r)\n", Status
));
430 // Transfer the data. Don't return immediately even data transfer
431 // failed. The host should attempt to receive the CSW no matter
432 // whether it succeeds or fails.
434 TransLen
= (UINTN
) DataLen
;
435 UsbBotDataTransfer (UsbBot
, DataDir
, Data
, &TransLen
, Timeout
);
438 // Get the status, if that succeeds, interpret the result
440 Status
= UsbBotGetStatus (UsbBot
, DataLen
, &Result
);
441 if (EFI_ERROR (Status
)) {
442 DEBUG ((EFI_D_ERROR
, "UsbBotExecCommand: UsbBotGetStatus (%r)\n", Status
));
447 *CmdStatus
= USB_MASS_CMD_SUCCESS
;
455 Reset the USB mass storage device by BOT protocol.
457 @param Context The context of the BOT protocol, that is,
459 @param ExtendedVerification If FALSE, just issue Bulk-Only Mass Storage Reset request.
460 If TRUE, additionally reset parent hub port.
462 @retval EFI_SUCCESS The device is reset.
463 @retval Others Failed to reset the device..
469 IN BOOLEAN ExtendedVerification
472 USB_BOT_PROTOCOL
*UsbBot
;
473 EFI_USB_DEVICE_REQUEST Request
;
478 UsbBot
= (USB_BOT_PROTOCOL
*) Context
;
480 if (ExtendedVerification
) {
482 // If we need to do strictly reset, reset its parent hub port
484 Status
= UsbBot
->UsbIo
->UsbPortReset (UsbBot
->UsbIo
);
485 if (EFI_ERROR (Status
)) {
486 return EFI_DEVICE_ERROR
;
491 // Issue a class specific Bulk-Only Mass Storage Reset request,
492 // according to section 3.1 of USB Mass Storage Class Bulk-Only Transport Spec, v1.0.
494 Request
.RequestType
= 0x21;
495 Request
.Request
= USB_BOT_RESET_REQUEST
;
497 Request
.Index
= UsbBot
->Interface
.InterfaceNumber
;
499 Timeout
= USB_BOT_RESET_DEVICE_TIMEOUT
/ USB_MASS_1_MILLISECOND
;
501 Status
= UsbBot
->UsbIo
->UsbControlTransfer (
511 if (EFI_ERROR (Status
)) {
512 return EFI_DEVICE_ERROR
;
516 // The device shall NAK the host's request until the reset is
517 // complete. We can use this to sync the device and host. For
518 // now just stall 100ms to wait for the device.
520 gBS
->Stall (USB_BOT_RESET_DEVICE_STALL
);
523 // Clear the Bulk-In and Bulk-Out stall condition.
525 UsbClearEndpointStall (UsbBot
->UsbIo
, UsbBot
->BulkInEndpoint
->EndpointAddress
);
526 UsbClearEndpointStall (UsbBot
->UsbIo
, UsbBot
->BulkOutEndpoint
->EndpointAddress
);
533 Get the max LUN (Logical Unit Number) of USB mass storage device.
535 @param Context The context of the BOT protocol, that is, USB_BOT_PROTOCOL
536 @param MaxLun Return pointer to the max number of LUN. (e.g. MaxLun=1 means LUN0 and
539 @retval EFI_SUCCESS Max LUN is got successfully.
540 @retval Others Fail to execute this request.
549 USB_BOT_PROTOCOL
*UsbBot
;
550 EFI_USB_DEVICE_REQUEST Request
;
555 if (Context
== NULL
|| MaxLun
== NULL
) {
556 return EFI_INVALID_PARAMETER
;
559 UsbBot
= (USB_BOT_PROTOCOL
*) Context
;
562 // Issue a class specific Bulk-Only Mass Storage get max lun reqest.
563 // according to section 3.2 of USB Mass Storage Class Bulk-Only Transport Spec, v1.0.
565 Request
.RequestType
= 0xA1;
566 Request
.Request
= USB_BOT_GETLUN_REQUEST
;
568 Request
.Index
= UsbBot
->Interface
.InterfaceNumber
;
570 Timeout
= USB_BOT_RESET_DEVICE_TIMEOUT
/ USB_MASS_1_MILLISECOND
;
572 Status
= UsbBot
->UsbIo
->UsbControlTransfer (
581 if (EFI_ERROR (Status
) || *MaxLun
> USB_BOT_MAX_LUN
) {
583 // If the Get LUN request returns an error or the MaxLun is larger than
584 // the maximum LUN value (0x0f) supported by the USB Mass Storage Class
585 // Bulk-Only Transport Spec, then set MaxLun to 0.
587 // This improves compatibility with USB FLASH drives that have a single LUN
588 // and either do not return a max LUN value or return an invalid maximum LUN
598 Clean up the resource used by this BOT protocol.
600 @param Context The context of the BOT protocol, that is, USB_BOT_PROTOCOL.
602 @retval EFI_SUCCESS The resource is cleaned up.