3 Copyright (c) 2007, 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 Controller The controller to init
43 @param Context The variable to save the context to
45 @retval EFI_OUT_OF_RESOURCES Failed to allocate memory
46 @retval EFI_UNSUPPORTED The transport protocol doesn't support the device.
47 @retval EFI_SUCCESS The device is supported and protocol initialized.
53 IN EFI_USB_IO_PROTOCOL
* UsbIo
,
54 IN EFI_HANDLE Controller
,
55 OUT VOID
**Context OPTIONAL
58 USB_BOT_PROTOCOL
*UsbBot
;
59 EFI_USB_INTERFACE_DESCRIPTOR
*Interface
;
60 EFI_USB_ENDPOINT_DESCRIPTOR EndPoint
;
65 // Allocate the BOT context, append two endpoint descriptors to it
67 UsbBot
= AllocateZeroPool (
68 sizeof (USB_BOT_PROTOCOL
) + 2 * sizeof (EFI_USB_ENDPOINT_DESCRIPTOR
)
71 return EFI_OUT_OF_RESOURCES
;
74 UsbBot
->UsbIo
= UsbIo
;
77 // Get the interface descriptor and validate that it
78 // is a USB MSC BOT interface.
80 Status
= UsbIo
->UsbGetInterfaceDescriptor (UsbIo
, &UsbBot
->Interface
);
82 if (EFI_ERROR (Status
)) {
83 DEBUG ((EFI_D_ERROR
, "UsbBotInit: Get invalid BOT interface (%r)\n", Status
));
87 Interface
= &UsbBot
->Interface
;
89 if (Interface
->InterfaceProtocol
!= USB_MASS_STORE_BOT
) {
90 Status
= EFI_UNSUPPORTED
;
95 // Locate and save the first bulk-in and bulk-out endpoint
97 for (Index
= 0; Index
< Interface
->NumEndpoints
; Index
++) {
98 Status
= UsbIo
->UsbGetEndpointDescriptor (UsbIo
, Index
, &EndPoint
);
100 if (EFI_ERROR (Status
) || !USB_IS_BULK_ENDPOINT (EndPoint
.Attributes
)) {
104 if (USB_IS_IN_ENDPOINT (EndPoint
.EndpointAddress
) &&
105 (UsbBot
->BulkInEndpoint
== NULL
)) {
107 UsbBot
->BulkInEndpoint
= (EFI_USB_ENDPOINT_DESCRIPTOR
*) (UsbBot
+ 1);
108 CopyMem(UsbBot
->BulkInEndpoint
, &EndPoint
, sizeof (EndPoint
));
111 if (USB_IS_OUT_ENDPOINT (EndPoint
.EndpointAddress
) &&
112 (UsbBot
->BulkOutEndpoint
== NULL
)) {
114 UsbBot
->BulkOutEndpoint
= (EFI_USB_ENDPOINT_DESCRIPTOR
*) (UsbBot
+ 1) + 1;
115 CopyMem(UsbBot
->BulkOutEndpoint
, &EndPoint
, sizeof(EndPoint
));
119 if ((UsbBot
->BulkInEndpoint
== NULL
) || (UsbBot
->BulkOutEndpoint
== NULL
)) {
120 DEBUG ((EFI_D_ERROR
, "UsbBotInit: In/Out Endpoint invalid\n"));
121 Status
= EFI_UNSUPPORTED
;
126 // The USB BOT protocol uses dCBWTag to match the CBW and CSW.
128 UsbBot
->CbwTag
= 0x01;
130 if (Context
!= NULL
) {
133 gBS
->FreePool (UsbBot
);
139 gBS
->FreePool (UsbBot
);
145 Send the command to the device using Bulk-Out endpoint
147 @param UsbBot The USB BOT device
148 @param Cmd The command to transfer to device
149 @param CmdLen the length of the command
150 @param DataDir The direction of the data
151 @param TransLen The expected length of the data
153 @retval EFI_NOT_READY The device return NAK to the transfer
154 @retval EFI_SUCCESS The command is sent to the device.
155 @retval Others Failed to send the command to device
161 IN USB_BOT_PROTOCOL
*UsbBot
,
164 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 Timeout The time to wait command
393 @param CmdStatus The result of high level command execution
395 @retval EFI_DEVICE_ERROR Failed to excute command
396 @retval EFI_SUCCESS The command is executed OK, and result in CmdStatus
405 IN EFI_USB_DATA_DIRECTION DataDir
,
409 OUT UINT32
*CmdStatus
412 USB_BOT_PROTOCOL
*UsbBot
;
417 *CmdStatus
= USB_MASS_CMD_FAIL
;
418 UsbBot
= (USB_BOT_PROTOCOL
*) Context
;
421 // Send the command to the device. Return immediately if device
422 // rejects the command.
424 Status
= UsbBotSendCommand (UsbBot
, Cmd
, CmdLen
, DataDir
, DataLen
);
425 if (EFI_ERROR (Status
)) {
426 DEBUG ((EFI_D_ERROR
, "UsbBotExecCommand: UsbBotSendCommand (%r)\n", Status
));
431 // Transfer the data. Don't return immediately even data transfer
432 // failed. The host should attempt to receive the CSW no matter
433 // whether it succeeds or failed.
435 TransLen
= (UINTN
) DataLen
;
436 UsbBotDataTransfer (UsbBot
, DataDir
, Data
, &TransLen
, Timeout
);
439 // Get the status, if that succeeds, interpret the result
441 Status
= UsbBotGetStatus (UsbBot
, DataLen
, &Result
);
442 if (EFI_ERROR (Status
)) {
443 DEBUG ((EFI_D_ERROR
, "UsbBotExecCommand: UsbBotGetStatus (%r)\n", Status
));
448 *CmdStatus
= USB_MASS_CMD_SUCCESS
;
456 Reset the mass storage device by BOT protocol
458 @param Context The context of the BOT protocol, that is,
461 @retval EFI_SUCCESS The device is reset
462 @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
)) {
491 // Issue a class specific "Bulk-Only Mass Storage Reset reqest.
492 // See the spec section 3.1
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 DEBUG ((EFI_D_ERROR
, "UsbBotResetDevice: (%r)\n", Status
));
517 // The device shall NAK the host's request until the reset is
518 // complete. We can use this to sync the device and host. For
519 // now just stall 100ms to wait the device.
521 gBS
->Stall (USB_BOT_RESET_DEVICE_STALL
);
524 // Clear the Bulk-In and Bulk-Out stall condition.
526 UsbClearEndpointStall (UsbBot
->UsbIo
, UsbBot
->BulkInEndpoint
->EndpointAddress
);
527 UsbClearEndpointStall (UsbBot
->UsbIo
, UsbBot
->BulkOutEndpoint
->EndpointAddress
);
533 Clean up the resource used by this BOT protocol
535 @param Context The context of the BOT protocol, that is,
538 @retval EFI_SUCCESS The resource is cleaned up.
547 gBS
->FreePool (Context
);