3 Copyright (c) 2007 - 2008, Intel Corporation
4 All rights reserved. This program and the accompanying materials
5 are licensed and made available under the terms and conditions of the BSD License
6 which accompanies this distribution. The full text of the license may be found at
7 http://opensource.org/licenses/bsd-license.php
9 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
10 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
18 Implementation of the USB mass storage Bulk-Only Transport protocol.
26 #include "UsbMassBot.h"
32 IN BOOLEAN ExtendedVerification
37 Initialize the USB mass storage class BOT transport protocol.
38 It will save its context which is a USB_BOT_PROTOCOL structure
39 in the Context if Context isn't NULL.
41 @param UsbIo The USB IO protocol to use
42 @param Context The variable to save the context to
44 @retval EFI_OUT_OF_RESOURCES Failed to allocate memory
45 @retval EFI_UNSUPPORTED The transport protocol doesn't support the device.
46 @retval EFI_SUCCESS The device is supported and protocol initialized.
52 IN EFI_USB_IO_PROTOCOL
* UsbIo
,
53 OUT VOID
**Context OPTIONAL
56 USB_BOT_PROTOCOL
*UsbBot
;
57 EFI_USB_INTERFACE_DESCRIPTOR
*Interface
;
58 EFI_USB_ENDPOINT_DESCRIPTOR EndPoint
;
63 // Allocate the BOT context, append two endpoint descriptors to it
65 UsbBot
= AllocateZeroPool (
66 sizeof (USB_BOT_PROTOCOL
) + 2 * sizeof (EFI_USB_ENDPOINT_DESCRIPTOR
)
69 return EFI_OUT_OF_RESOURCES
;
72 UsbBot
->UsbIo
= UsbIo
;
75 // Get the interface descriptor and validate that it
76 // is a USB MSC BOT interface.
78 Status
= UsbIo
->UsbGetInterfaceDescriptor (UsbIo
, &UsbBot
->Interface
);
80 if (EFI_ERROR (Status
)) {
81 DEBUG ((EFI_D_ERROR
, "UsbBotInit: Get invalid BOT interface (%r)\n", Status
));
85 Interface
= &UsbBot
->Interface
;
87 if (Interface
->InterfaceProtocol
!= USB_MASS_STORE_BOT
) {
88 Status
= EFI_UNSUPPORTED
;
93 // Locate and save the first bulk-in and bulk-out endpoint
95 for (Index
= 0; Index
< Interface
->NumEndpoints
; Index
++) {
96 Status
= UsbIo
->UsbGetEndpointDescriptor (UsbIo
, Index
, &EndPoint
);
98 if (EFI_ERROR (Status
) || !USB_IS_BULK_ENDPOINT (EndPoint
.Attributes
)) {
102 if (USB_IS_IN_ENDPOINT (EndPoint
.EndpointAddress
) &&
103 (UsbBot
->BulkInEndpoint
== NULL
)) {
105 UsbBot
->BulkInEndpoint
= (EFI_USB_ENDPOINT_DESCRIPTOR
*) (UsbBot
+ 1);
106 CopyMem(UsbBot
->BulkInEndpoint
, &EndPoint
, sizeof (EndPoint
));
109 if (USB_IS_OUT_ENDPOINT (EndPoint
.EndpointAddress
) &&
110 (UsbBot
->BulkOutEndpoint
== NULL
)) {
112 UsbBot
->BulkOutEndpoint
= (EFI_USB_ENDPOINT_DESCRIPTOR
*) (UsbBot
+ 1) + 1;
113 CopyMem(UsbBot
->BulkOutEndpoint
, &EndPoint
, sizeof(EndPoint
));
117 if ((UsbBot
->BulkInEndpoint
== NULL
) || (UsbBot
->BulkOutEndpoint
== NULL
)) {
118 DEBUG ((EFI_D_ERROR
, "UsbBotInit: In/Out Endpoint invalid\n"));
119 Status
= EFI_UNSUPPORTED
;
124 // The USB BOT protocol uses dCBWTag to match the CBW and CSW.
126 UsbBot
->CbwTag
= 0x01;
128 if (Context
!= NULL
) {
131 gBS
->FreePool (UsbBot
);
137 gBS
->FreePool (UsbBot
);
143 Send the command to the device using Bulk-Out endpoint
145 @param UsbBot The USB BOT device
146 @param Cmd The command to transfer to device
147 @param CmdLen the length of the command
148 @param DataDir The direction of the data
149 @param TransLen The expected length of the data
150 @param Lun The number of logic unit
152 @retval EFI_NOT_READY The device return NAK to the transfer
153 @retval EFI_SUCCESS The command is sent to the device.
154 @retval Others Failed to send the command to device
160 IN USB_BOT_PROTOCOL
*UsbBot
,
163 IN EFI_USB_DATA_DIRECTION DataDir
,
174 ASSERT ((CmdLen
> 0) && (CmdLen
<= USB_BOT_MAX_CMDLEN
));
177 // Fill in the CSW. Only the first LUN is supported now.
179 Cbw
.Signature
= USB_BOT_CBW_SIGNATURE
;
180 Cbw
.Tag
= UsbBot
->CbwTag
;
181 Cbw
.DataLen
= TransLen
;
182 Cbw
.Flag
= (UINT8
) ((DataDir
== EfiUsbDataIn
) ? 0x80 : 0);
186 ZeroMem (Cbw
.CmdBlock
, USB_BOT_MAX_CMDLEN
);
187 CopyMem (Cbw
.CmdBlock
, Cmd
, CmdLen
);
190 DataLen
= sizeof (USB_BOT_CBW
);
191 Timeout
= USB_BOT_SEND_CBW_TIMEOUT
/ USB_MASS_1_MILLISECOND
;
194 // Use the UsbIo to send the command to the device. The default
195 // time out is enough.
197 Status
= UsbBot
->UsbIo
->UsbBulkTransfer (
199 UsbBot
->BulkOutEndpoint
->EndpointAddress
,
206 // Respond to Bulk-Out endpoint stall with a Reset Recovery,
207 // see the spec section 5.3.1
209 if (EFI_ERROR (Status
)) {
210 if (USB_IS_ERROR (Result
, EFI_USB_ERR_STALL
) && DataDir
== EfiUsbDataOut
) {
211 UsbBotResetDevice (UsbBot
, FALSE
);
212 } else if (USB_IS_ERROR (Result
, EFI_USB_ERR_NAK
)) {
213 Status
= EFI_NOT_READY
;
222 Transfer the data between the device and host. BOT transfer
223 is composed of three phase, command, data, and status.
225 @param UsbBot The USB BOT device
226 @param DataDir The direction of the data
227 @param Data The buffer to hold data
228 @param TransLen The expected length of the data
229 @param Timeout The time to wait the command to complete
231 @retval EFI_SUCCESS The data is transferred
232 @retval Others Failed to transfer data
238 IN USB_BOT_PROTOCOL
*UsbBot
,
239 IN EFI_USB_DATA_DIRECTION DataDir
,
241 IN OUT UINTN
*TransLen
,
245 EFI_USB_ENDPOINT_DESCRIPTOR
*Endpoint
;
250 // It's OK if no data to transfer
252 if ((DataDir
== EfiUsbNoData
) || (*TransLen
== 0)) {
257 // Select the endpoint then issue the transfer
259 if (DataDir
== EfiUsbDataIn
) {
260 Endpoint
= UsbBot
->BulkInEndpoint
;
262 Endpoint
= UsbBot
->BulkOutEndpoint
;
266 Timeout
= Timeout
/ USB_MASS_1_MILLISECOND
;
268 Status
= UsbBot
->UsbIo
->UsbBulkTransfer (
270 Endpoint
->EndpointAddress
,
276 if (EFI_ERROR (Status
)) {
277 DEBUG ((EFI_D_ERROR
, "UsbBotDataTransfer: (%r)\n", Status
));
278 if (USB_IS_ERROR (Result
, EFI_USB_ERR_STALL
)) {
279 DEBUG ((EFI_D_ERROR
, "UsbBotDataTransfer: DataIn Stall\n"));
280 UsbClearEndpointStall (UsbBot
->UsbIo
, Endpoint
->EndpointAddress
);
281 } else if (USB_IS_ERROR (Result
, EFI_USB_ERR_NAK
)) {
282 Status
= EFI_NOT_READY
;
291 Get the command execution status from device. BOT transfer is
292 composed of three phase, command, data, and status.
293 This function return the transfer status of the BOT's CSW status,
294 and return the high level command execution result in Result. So
295 even it returns EFI_SUCCESS, the command may still have failed.
297 @param UsbBot The USB BOT device
298 @param TransLen The expected length of the data
299 @param Timeout The time to wait the command to complete
300 @param CmdStatus The result of the command execution.
302 @retval EFI_DEVICE_ERROR Failed to retrieve the command execute result
303 @retval EFI_SUCCESS Command execute result is retrieved and in the
310 IN USB_BOT_PROTOCOL
*UsbBot
,
320 EFI_USB_IO_PROTOCOL
*UsbIo
;
324 *CmdStatus
= USB_BOT_COMMAND_ERROR
;
325 Status
= EFI_DEVICE_ERROR
;
326 Endpoint
= UsbBot
->BulkInEndpoint
->EndpointAddress
;
327 UsbIo
= UsbBot
->UsbIo
;
328 Timeout
= USB_BOT_RECV_CSW_TIMEOUT
/ USB_MASS_1_MILLISECOND
;
330 for (Index
= 0; Index
< USB_BOT_RECV_CSW_RETRY
; Index
++) {
332 // Attemp to the read CSW from bulk in endpoint
334 ZeroMem (&Csw
, sizeof (USB_BOT_CSW
));
336 Len
= sizeof (USB_BOT_CSW
);
337 Status
= UsbIo
->UsbBulkTransfer (
345 if (EFI_ERROR(Status
)) {
346 DEBUG ((EFI_D_ERROR
, "UsbBotGetStatus (%r)\n", Status
));
347 if (USB_IS_ERROR (Result
, EFI_USB_ERR_STALL
)) {
348 DEBUG ((EFI_D_ERROR
, "UsbBotGetStatus: DataIn Stall\n"));
349 UsbClearEndpointStall (UsbIo
, Endpoint
);
354 if (Csw
.Signature
!= USB_BOT_CSW_SIGNATURE
) {
356 // Invalid Csw need perform reset recovery
358 DEBUG ((EFI_D_ERROR
, "UsbBotGetStatus: Device return a invalid signature\n"));
359 Status
= UsbBotResetDevice (UsbBot
, FALSE
);
360 } else if (Csw
.CmdStatus
== USB_BOT_COMMAND_ERROR
) {
362 // Respond phase error need perform reset recovery
364 DEBUG ((EFI_D_ERROR
, "UsbBotGetStatus: Device return a phase error\n"));
365 Status
= UsbBotResetDevice (UsbBot
, FALSE
);
368 *CmdStatus
= Csw
.CmdStatus
;
373 //The tag is increased even there is an error.
382 Call the Usb mass storage class transport protocol to issue
383 the command/data/status circle to execute the commands
385 @param Context The context of the BOT protocol, that is,
387 @param Cmd The high level command
388 @param CmdLen The command length
389 @param DataDir The direction of the data transfer
390 @param Data The buffer to hold data
391 @param DataLen The length of the data
392 @param Lun The number of logic unit
393 @param Timeout The time to wait command
394 @param CmdStatus The result of high level command execution
396 @retval EFI_DEVICE_ERROR Failed to excute command
397 @retval EFI_SUCCESS The command is executed OK, and result in CmdStatus
406 IN EFI_USB_DATA_DIRECTION DataDir
,
411 OUT UINT32
*CmdStatus
414 USB_BOT_PROTOCOL
*UsbBot
;
419 *CmdStatus
= USB_MASS_CMD_FAIL
;
420 UsbBot
= (USB_BOT_PROTOCOL
*) Context
;
423 // Send the command to the device. Return immediately if device
424 // rejects the command.
426 Status
= UsbBotSendCommand (UsbBot
, Cmd
, CmdLen
, DataDir
, DataLen
, Lun
);
427 if (EFI_ERROR (Status
)) {
428 DEBUG ((EFI_D_ERROR
, "UsbBotExecCommand: UsbBotSendCommand (%r)\n", Status
));
433 // Transfer the data. Don't return immediately even data transfer
434 // failed. The host should attempt to receive the CSW no matter
435 // whether it succeeds or failed.
437 TransLen
= (UINTN
) DataLen
;
438 UsbBotDataTransfer (UsbBot
, DataDir
, Data
, &TransLen
, Timeout
);
441 // Get the status, if that succeeds, interpret the result
443 Status
= UsbBotGetStatus (UsbBot
, DataLen
, &Result
);
444 if (EFI_ERROR (Status
)) {
445 DEBUG ((EFI_D_ERROR
, "UsbBotExecCommand: UsbBotGetStatus (%r)\n", Status
));
450 *CmdStatus
= USB_MASS_CMD_SUCCESS
;
458 Reset the mass storage device by BOT protocol
460 @param Context The context of the BOT protocol, that is,
463 @retval EFI_SUCCESS The device is reset
464 @retval Others Failed to reset the device.
471 IN BOOLEAN ExtendedVerification
474 USB_BOT_PROTOCOL
*UsbBot
;
475 EFI_USB_DEVICE_REQUEST Request
;
480 UsbBot
= (USB_BOT_PROTOCOL
*) Context
;
482 if (ExtendedVerification
) {
484 // If we need to do strictly reset, reset its parent hub port
486 Status
= UsbBot
->UsbIo
->UsbPortReset (UsbBot
->UsbIo
);
487 if (EFI_ERROR (Status
)) {
493 // Issue a class specific Bulk-Only Mass Storage Reset reqest.
494 // See the spec section 3.1
496 Request
.RequestType
= 0x21; // Class, Interface, Host to Device
497 Request
.Request
= USB_BOT_RESET_REQUEST
;
499 Request
.Index
= UsbBot
->Interface
.InterfaceNumber
;
501 Timeout
= USB_BOT_RESET_DEVICE_TIMEOUT
/ USB_MASS_1_MILLISECOND
;
503 Status
= UsbBot
->UsbIo
->UsbControlTransfer (
513 if (EFI_ERROR (Status
)) {
514 DEBUG ((EFI_D_ERROR
, "UsbBotResetDevice: (%r)\n", Status
));
519 // The device shall NAK the host's request until the reset is
520 // complete. We can use this to sync the device and host. For
521 // now just stall 100ms to wait the device.
523 gBS
->Stall (USB_BOT_RESET_DEVICE_STALL
);
526 // Clear the Bulk-In and Bulk-Out stall condition.
528 UsbClearEndpointStall (UsbBot
->UsbIo
, UsbBot
->BulkInEndpoint
->EndpointAddress
);
529 UsbClearEndpointStall (UsbBot
->UsbIo
, UsbBot
->BulkOutEndpoint
->EndpointAddress
);
537 Reset the mass storage device by BOT protocol
541 Context - The context of the BOT protocol, that is, USB_BOT_PROTOCOL
542 MaxLun - Return pointer to the max number of lun. Maxlun=1 means lun0 and
547 EFI_SUCCESS - The device is reset
548 Others - Failed to reset the device.
561 Reset the mass storage device by BOT protocol
565 Context - The context of the BOT protocol, that is, USB_BOT_PROTOCOL
566 MaxLun - Return pointer to the max number of lun. Maxlun=1 means lun0 and
571 EFI_SUCCESS - The device is reset
572 Others - Failed to reset the device.
576 USB_BOT_PROTOCOL
*UsbBot
;
577 EFI_USB_DEVICE_REQUEST Request
;
584 UsbBot
= (USB_BOT_PROTOCOL
*) Context
;
587 // Issue a class specific Bulk-Only Mass Storage get max lun reqest.
588 // See the spec section 3.2
590 Request
.RequestType
= 0xA1; // Class, Interface, Device to Host
591 Request
.Request
= USB_BOT_GETLUN_REQUEST
;
593 Request
.Index
= UsbBot
->Interface
.InterfaceNumber
;
595 Timeout
= USB_BOT_RESET_DEVICE_TIMEOUT
/ USB_MASS_1_MILLISECOND
;
597 Status
= UsbBot
->UsbIo
->UsbControlTransfer (
607 if (EFI_ERROR (Status
)) {
608 DEBUG ((EFI_D_ERROR
, "UsbBotGetMaxLun: (%r)\n", Status
));
615 Clean up the resource used by this BOT protocol
617 @param Context The context of the BOT protocol, that is,
620 @retval EFI_SUCCESS The resource is cleaned up.
629 gBS
->FreePool (Context
);