3 Implementation of the USB mass storage Bulk-Only Transport protocol.
5 Copyright (c) 2007 - 2008, Intel Corporation
6 All rights reserved. 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.
17 #include "UsbMassBot.h"
20 Reset the mass storage device by BOT protocol.
22 @param Context The context of the BOT protocol, that is,
24 @param ExtendedVerification The flag controlling the rule of reset dev.
26 @retval EFI_SUCCESS The device is reset.
27 @retval Others Failed to reset the device..
33 IN BOOLEAN ExtendedVerification
38 Initialize the USB mass storage class BOT transport protocol.
39 It will save its context which is a USB_BOT_PROTOCOL structure
40 in the Context if Context isn't NULL.
42 @param UsbIo The USB IO protocol to use
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.
48 @retval Other The UBS BOT initialization fails.
53 IN EFI_USB_IO_PROTOCOL
* UsbIo
,
54 OUT VOID
**Context OPTIONAL
57 USB_BOT_PROTOCOL
*UsbBot
;
58 EFI_USB_INTERFACE_DESCRIPTOR
*Interface
;
59 EFI_USB_ENDPOINT_DESCRIPTOR EndPoint
;
64 // Allocate the BOT context, append two endpoint descriptors to it
66 UsbBot
= AllocateZeroPool (
67 sizeof (USB_BOT_PROTOCOL
) + 2 * sizeof (EFI_USB_ENDPOINT_DESCRIPTOR
)
70 return EFI_OUT_OF_RESOURCES
;
73 UsbBot
->UsbIo
= UsbIo
;
76 // Get the interface descriptor and validate that it
77 // is a USB MSC BOT interface.
79 Status
= UsbIo
->UsbGetInterfaceDescriptor (UsbIo
, &UsbBot
->Interface
);
81 if (EFI_ERROR (Status
)) {
82 DEBUG ((EFI_D_ERROR
, "UsbBotInit: Get invalid BOT interface (%r)\n", Status
));
86 Interface
= &UsbBot
->Interface
;
88 if (Interface
->InterfaceProtocol
!= USB_MASS_STORE_BOT
) {
89 Status
= EFI_UNSUPPORTED
;
94 // Locate and save the first bulk-in and bulk-out endpoint
96 for (Index
= 0; Index
< Interface
->NumEndpoints
; Index
++) {
97 Status
= UsbIo
->UsbGetEndpointDescriptor (UsbIo
, Index
, &EndPoint
);
99 if (EFI_ERROR (Status
) || !USB_IS_BULK_ENDPOINT (EndPoint
.Attributes
)) {
103 if (USB_IS_IN_ENDPOINT (EndPoint
.EndpointAddress
) &&
104 (UsbBot
->BulkInEndpoint
== NULL
)) {
106 UsbBot
->BulkInEndpoint
= (EFI_USB_ENDPOINT_DESCRIPTOR
*) (UsbBot
+ 1);
107 CopyMem(UsbBot
->BulkInEndpoint
, &EndPoint
, sizeof (EndPoint
));
110 if (USB_IS_OUT_ENDPOINT (EndPoint
.EndpointAddress
) &&
111 (UsbBot
->BulkOutEndpoint
== NULL
)) {
113 UsbBot
->BulkOutEndpoint
= (EFI_USB_ENDPOINT_DESCRIPTOR
*) (UsbBot
+ 1) + 1;
114 CopyMem(UsbBot
->BulkOutEndpoint
, &EndPoint
, sizeof(EndPoint
));
118 if ((UsbBot
->BulkInEndpoint
== NULL
) || (UsbBot
->BulkOutEndpoint
== NULL
)) {
119 DEBUG ((EFI_D_ERROR
, "UsbBotInit: In/Out Endpoint invalid\n"));
120 Status
= EFI_UNSUPPORTED
;
125 // The USB BOT protocol uses dCBWTag to match the CBW and CSW.
127 UsbBot
->CbwTag
= 0x01;
129 if (Context
!= NULL
) {
132 gBS
->FreePool (UsbBot
);
138 gBS
->FreePool (UsbBot
);
144 Send the command to the device using Bulk-Out endpoint.
146 @param UsbBot The USB BOT device
147 @param Cmd The command to transfer to device
148 @param CmdLen the length of the command
149 @param DataDir The direction of the data
150 @param TransLen The expected length of the data
151 @param Lun The number of logic unit
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
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
237 IN USB_BOT_PROTOCOL
*UsbBot
,
238 IN EFI_USB_DATA_DIRECTION DataDir
,
240 IN OUT UINTN
*TransLen
,
244 EFI_USB_ENDPOINT_DESCRIPTOR
*Endpoint
;
249 // It's OK if no data to transfer
251 if ((DataDir
== EfiUsbNoData
) || (*TransLen
== 0)) {
256 // Select the endpoint then issue the transfer
258 if (DataDir
== EfiUsbDataIn
) {
259 Endpoint
= UsbBot
->BulkInEndpoint
;
261 Endpoint
= UsbBot
->BulkOutEndpoint
;
265 Timeout
= Timeout
/ USB_MASS_1_MILLISECOND
;
267 Status
= UsbBot
->UsbIo
->UsbBulkTransfer (
269 Endpoint
->EndpointAddress
,
275 if (EFI_ERROR (Status
)) {
276 DEBUG ((EFI_D_ERROR
, "UsbBotDataTransfer: (%r)\n", Status
));
277 if (USB_IS_ERROR (Result
, EFI_USB_ERR_STALL
)) {
278 DEBUG ((EFI_D_ERROR
, "UsbBotDataTransfer: DataIn Stall\n"));
279 UsbClearEndpointStall (UsbBot
->UsbIo
, Endpoint
->EndpointAddress
);
280 } else if (USB_IS_ERROR (Result
, EFI_USB_ERR_NAK
)) {
281 Status
= EFI_NOT_READY
;
290 Get the command execution status from device. BOT transfer is
291 composed of three phase, command, data, and status.
292 This function return the transfer status of the BOT's CSW status,
293 and return the high level command execution result in Result. So
294 even it returns EFI_SUCCESS, the command may still have failed.
296 @param UsbBot The USB BOT device.
297 @param TransLen The expected length of the data.
298 @param CmdStatus The result of the command execution.
300 @retval EFI_SUCCESS Command execute result is retrieved and in the
302 @retval Other Failed to get status.
307 IN USB_BOT_PROTOCOL
*UsbBot
,
317 EFI_USB_IO_PROTOCOL
*UsbIo
;
321 *CmdStatus
= USB_BOT_COMMAND_ERROR
;
322 Status
= EFI_DEVICE_ERROR
;
323 Endpoint
= UsbBot
->BulkInEndpoint
->EndpointAddress
;
324 UsbIo
= UsbBot
->UsbIo
;
325 Timeout
= USB_BOT_RECV_CSW_TIMEOUT
/ USB_MASS_1_MILLISECOND
;
327 for (Index
= 0; Index
< USB_BOT_RECV_CSW_RETRY
; Index
++) {
329 // Attemp to the read CSW from bulk in endpoint
331 ZeroMem (&Csw
, sizeof (USB_BOT_CSW
));
333 Len
= sizeof (USB_BOT_CSW
);
334 Status
= UsbIo
->UsbBulkTransfer (
342 if (EFI_ERROR(Status
)) {
343 DEBUG ((EFI_D_ERROR
, "UsbBotGetStatus (%r)\n", Status
));
344 if (USB_IS_ERROR (Result
, EFI_USB_ERR_STALL
)) {
345 DEBUG ((EFI_D_ERROR
, "UsbBotGetStatus: DataIn Stall\n"));
346 UsbClearEndpointStall (UsbIo
, Endpoint
);
351 if (Csw
.Signature
!= USB_BOT_CSW_SIGNATURE
) {
353 // Invalid Csw need perform reset recovery
355 DEBUG ((EFI_D_ERROR
, "UsbBotGetStatus: Device return a invalid signature\n"));
356 Status
= UsbBotResetDevice (UsbBot
, FALSE
);
357 } else if (Csw
.CmdStatus
== USB_BOT_COMMAND_ERROR
) {
359 // Respond phase error need perform reset recovery
361 DEBUG ((EFI_D_ERROR
, "UsbBotGetStatus: Device return a phase error\n"));
362 Status
= UsbBotResetDevice (UsbBot
, FALSE
);
365 *CmdStatus
= Csw
.CmdStatus
;
370 //The tag is increased even there is an error.
379 Call the Usb mass storage class transport protocol to issue
380 the command/data/status circle to execute the commands.
382 @param Context The context of the BOT protocol, that is,
384 @param Cmd The high level command
385 @param CmdLen The command length
386 @param DataDir The direction of the data transfer
387 @param Data The buffer to hold data
388 @param DataLen The length of the data
389 @param Lun The number of logic unit
390 @param Timeout The time to wait command
391 @param CmdStatus The result of high level command execution
393 @retval EFI_SUCCESS The command is executed OK, and result in CmdStatus
394 @retval Other Failed to excute command
402 IN EFI_USB_DATA_DIRECTION DataDir
,
407 OUT UINT32
*CmdStatus
410 USB_BOT_PROTOCOL
*UsbBot
;
415 *CmdStatus
= USB_MASS_CMD_FAIL
;
416 UsbBot
= (USB_BOT_PROTOCOL
*) Context
;
419 // Send the command to the device. Return immediately if device
420 // rejects the command.
422 Status
= UsbBotSendCommand (UsbBot
, Cmd
, CmdLen
, DataDir
, DataLen
, Lun
);
423 if (EFI_ERROR (Status
)) {
424 DEBUG ((EFI_D_ERROR
, "UsbBotExecCommand: UsbBotSendCommand (%r)\n", Status
));
429 // Transfer the data. Don't return immediately even data transfer
430 // failed. The host should attempt to receive the CSW no matter
431 // whether it succeeds or failed.
433 TransLen
= (UINTN
) DataLen
;
434 UsbBotDataTransfer (UsbBot
, DataDir
, Data
, &TransLen
, Timeout
);
437 // Get the status, if that succeeds, interpret the result
439 Status
= UsbBotGetStatus (UsbBot
, DataLen
, &Result
);
440 if (EFI_ERROR (Status
)) {
441 DEBUG ((EFI_D_ERROR
, "UsbBotExecCommand: UsbBotGetStatus (%r)\n", Status
));
446 *CmdStatus
= USB_MASS_CMD_SUCCESS
;
454 Reset the mass storage device by BOT protocol.
456 @param Context The context of the BOT protocol, that is,
458 @param ExtendedVerification The flag controlling the rule of reset dev.
460 @retval EFI_SUCCESS The device is reset.
461 @retval Others Failed to reset the device..
467 IN BOOLEAN ExtendedVerification
470 USB_BOT_PROTOCOL
*UsbBot
;
471 EFI_USB_DEVICE_REQUEST Request
;
476 UsbBot
= (USB_BOT_PROTOCOL
*) Context
;
478 if (ExtendedVerification
) {
480 // If we need to do strictly reset, reset its parent hub port
482 Status
= UsbBot
->UsbIo
->UsbPortReset (UsbBot
->UsbIo
);
483 if (EFI_ERROR (Status
)) {
489 // Issue a class specific Bulk-Only Mass Storage Reset reqest.
490 // See the spec section 3.1
492 Request
.RequestType
= 0x21; // Class, Interface, Host to Device
493 Request
.Request
= USB_BOT_RESET_REQUEST
;
495 Request
.Index
= UsbBot
->Interface
.InterfaceNumber
;
497 Timeout
= USB_BOT_RESET_DEVICE_TIMEOUT
/ USB_MASS_1_MILLISECOND
;
499 Status
= UsbBot
->UsbIo
->UsbControlTransfer (
509 if (EFI_ERROR (Status
)) {
510 DEBUG ((EFI_D_ERROR
, "UsbBotResetDevice: (%r)\n", Status
));
515 // The device shall NAK the host's request until the reset is
516 // complete. We can use this to sync the device and host. For
517 // now just stall 100ms to wait the device.
519 gBS
->Stall (USB_BOT_RESET_DEVICE_STALL
);
522 // Clear the Bulk-In and Bulk-Out stall condition.
524 UsbClearEndpointStall (UsbBot
->UsbIo
, UsbBot
->BulkInEndpoint
->EndpointAddress
);
525 UsbClearEndpointStall (UsbBot
->UsbIo
, UsbBot
->BulkOutEndpoint
->EndpointAddress
);
531 Get the max lun of mass storage device.
533 @param Context The context of the BOT protocol, that is, USB_BOT_PROTOCOL
534 @param MaxLun Return pointer to the max number of lun. Maxlun=1 means lun0 and
537 @retval EFI_SUCCESS Get max lun success.
538 @retval Others Failed to execute this request.
547 USB_BOT_PROTOCOL
*UsbBot
;
548 EFI_USB_DEVICE_REQUEST Request
;
555 UsbBot
= (USB_BOT_PROTOCOL
*) Context
;
558 // Issue a class specific Bulk-Only Mass Storage get max lun reqest.
559 // See the spec section 3.2
561 Request
.RequestType
= 0xA1; // Class, Interface, Device to Host
562 Request
.Request
= USB_BOT_GETLUN_REQUEST
;
564 Request
.Index
= UsbBot
->Interface
.InterfaceNumber
;
566 Timeout
= USB_BOT_RESET_DEVICE_TIMEOUT
/ USB_MASS_1_MILLISECOND
;
568 Status
= UsbBot
->UsbIo
->UsbControlTransfer (
578 if (EFI_ERROR (Status
)) {
579 DEBUG ((EFI_D_ERROR
, "UsbBotGetMaxLun: (%r)\n", Status
));
586 Clean up the resource used by this BOT protocol.
588 @param Context The context of the BOT protocol, that is,
591 @retval EFI_SUCCESS The resource is cleaned up.
599 gBS
->FreePool (Context
);