TianoCore EDK2 master
Loading...
Searching...
No Matches
UsbMassBot.c
Go to the documentation of this file.
1
10#include "UsbMass.h"
11
12//
13// Definition of USB BOT Transport Protocol
14//
15USB_MASS_TRANSPORT mUsbBotTransport = {
22};
23
42 OUT VOID **Context OPTIONAL
43 )
44{
45 USB_BOT_PROTOCOL *UsbBot;
48 EFI_STATUS Status;
49 UINT8 Index;
50
51 //
52 // Allocate the BOT context for USB_BOT_PROTOCOL and two endpoint descriptors.
53 //
54 UsbBot = AllocateZeroPool (sizeof (USB_BOT_PROTOCOL) + 2 * sizeof (EFI_USB_ENDPOINT_DESCRIPTOR));
55 ASSERT (UsbBot != NULL);
56
57 UsbBot->UsbIo = UsbIo;
58
59 //
60 // Get the interface descriptor and validate that it
61 // is a USB Mass Storage BOT interface.
62 //
63 Status = UsbIo->UsbGetInterfaceDescriptor (UsbIo, &UsbBot->Interface);
64
65 if (EFI_ERROR (Status)) {
66 goto ON_ERROR;
67 }
68
69 Interface = &UsbBot->Interface;
70
71 if (Interface->InterfaceProtocol != USB_MASS_STORE_BOT) {
72 Status = EFI_UNSUPPORTED;
73 goto ON_ERROR;
74 }
75
76 //
77 // Locate and save the first bulk-in and bulk-out endpoint
78 //
79 for (Index = 0; Index < Interface->NumEndpoints; Index++) {
80 Status = UsbIo->UsbGetEndpointDescriptor (UsbIo, Index, &EndPoint);
81
82 if (EFI_ERROR (Status) || !USB_IS_BULK_ENDPOINT (EndPoint.Attributes)) {
83 continue;
84 }
85
86 if (USB_IS_IN_ENDPOINT (EndPoint.EndpointAddress) &&
87 (UsbBot->BulkInEndpoint == NULL))
88 {
89 UsbBot->BulkInEndpoint = (EFI_USB_ENDPOINT_DESCRIPTOR *)(UsbBot + 1);
90 CopyMem (UsbBot->BulkInEndpoint, &EndPoint, sizeof (EndPoint));
91 }
92
93 if (USB_IS_OUT_ENDPOINT (EndPoint.EndpointAddress) &&
94 (UsbBot->BulkOutEndpoint == NULL))
95 {
96 UsbBot->BulkOutEndpoint = (EFI_USB_ENDPOINT_DESCRIPTOR *)(UsbBot + 1) + 1;
97 CopyMem (UsbBot->BulkOutEndpoint, &EndPoint, sizeof (EndPoint));
98 }
99 }
100
101 //
102 // If bulk-in or bulk-out endpoint is not found, report error.
103 //
104 if ((UsbBot->BulkInEndpoint == NULL) || (UsbBot->BulkOutEndpoint == NULL)) {
105 Status = EFI_UNSUPPORTED;
106 goto ON_ERROR;
107 }
108
109 //
110 // The USB BOT protocol uses CBWTag to match the CBW and CSW.
111 //
112 UsbBot->CbwTag = 0x01;
113
114 if (Context != NULL) {
115 *Context = UsbBot;
116 } else {
117 FreePool (UsbBot);
118 }
119
120 return EFI_SUCCESS;
121
122ON_ERROR:
123 FreePool (UsbBot);
124 return Status;
125}
126
148 IN USB_BOT_PROTOCOL *UsbBot,
149 IN UINT8 *Cmd,
150 IN UINT8 CmdLen,
152 IN UINT32 TransLen,
153 IN UINT8 Lun
154 )
155{
156 USB_BOT_CBW Cbw;
157 EFI_STATUS Status;
158 UINT32 Result;
159 UINTN DataLen;
160 UINTN Timeout;
161
162 ASSERT ((CmdLen > 0) && (CmdLen <= USB_BOT_MAX_CMDLEN));
163
164 //
165 // Fill in the Command Block Wrapper.
166 //
167 Cbw.Signature = USB_BOT_CBW_SIGNATURE;
168 Cbw.Tag = UsbBot->CbwTag;
169 Cbw.DataLen = TransLen;
170 Cbw.Flag = (UINT8)((DataDir == EfiUsbDataIn) ? BIT7 : 0);
171 Cbw.Lun = Lun;
172 Cbw.CmdLen = CmdLen;
173
174 ZeroMem (Cbw.CmdBlock, USB_BOT_MAX_CMDLEN);
175 CopyMem (Cbw.CmdBlock, Cmd, CmdLen);
176
177 Result = 0;
178 DataLen = sizeof (USB_BOT_CBW);
179 Timeout = USB_BOT_SEND_CBW_TIMEOUT / USB_MASS_1_MILLISECOND;
180
181 //
182 // Use USB I/O Protocol to send the Command Block Wrapper to the device.
183 //
184 Status = UsbBot->UsbIo->UsbBulkTransfer (
185 UsbBot->UsbIo,
186 UsbBot->BulkOutEndpoint->EndpointAddress,
187 &Cbw,
188 &DataLen,
189 Timeout,
190 &Result
191 );
192 if (EFI_ERROR (Status)) {
193 if (USB_IS_ERROR (Result, EFI_USB_ERR_STALL) && (DataDir == EfiUsbDataOut)) {
194 //
195 // Respond to Bulk-Out endpoint stall with a Reset Recovery,
196 // according to section 5.3.1 of USB Mass Storage Class Bulk-Only Transport Spec, v1.0.
197 //
198 UsbBotResetDevice (UsbBot, FALSE);
199 } else if (USB_IS_ERROR (Result, EFI_USB_ERR_NAK)) {
200 Status = EFI_NOT_READY;
201 }
202 }
203
204 return Status;
205}
206
228 IN USB_BOT_PROTOCOL *UsbBot,
230 IN OUT UINT8 *Data,
231 IN OUT UINTN *TransLen,
232 IN UINT32 Timeout
233 )
234{
236 EFI_STATUS Status;
237 UINT32 Result;
238
239 //
240 // If no data to transfer, just return EFI_SUCCESS.
241 //
242 if ((DataDir == EfiUsbNoData) || (*TransLen == 0)) {
243 return EFI_SUCCESS;
244 }
245
246 //
247 // Select the endpoint then issue the transfer
248 //
249 if (DataDir == EfiUsbDataIn) {
250 Endpoint = UsbBot->BulkInEndpoint;
251 } else {
252 Endpoint = UsbBot->BulkOutEndpoint;
253 }
254
255 Result = 0;
256 Timeout = Timeout / USB_MASS_1_MILLISECOND;
257
258 Status = UsbBot->UsbIo->UsbBulkTransfer (
259 UsbBot->UsbIo,
260 Endpoint->EndpointAddress,
261 Data,
262 TransLen,
263 Timeout,
264 &Result
265 );
266 if (EFI_ERROR (Status)) {
267 if (USB_IS_ERROR (Result, EFI_USB_ERR_STALL)) {
268 DEBUG ((DEBUG_INFO, "UsbBotDataTransfer: (%r)\n", Status));
269 DEBUG ((DEBUG_INFO, "UsbBotDataTransfer: DataIn Stall\n"));
270 UsbClearEndpointStall (UsbBot->UsbIo, Endpoint->EndpointAddress);
271 } else if (USB_IS_ERROR (Result, EFI_USB_ERR_NAK)) {
272 Status = EFI_NOT_READY;
273 } else {
274 DEBUG ((DEBUG_ERROR, "UsbBotDataTransfer: (%r)\n", Status));
275 }
276
277 if (Status == EFI_TIMEOUT) {
278 UsbBotResetDevice (UsbBot, FALSE);
279 }
280 }
281
282 return Status;
283}
284
306 IN USB_BOT_PROTOCOL *UsbBot,
307 IN UINT32 TransLen,
308 OUT UINT8 *CmdStatus
309 )
310{
311 USB_BOT_CSW Csw;
312 UINTN Len;
313 UINT8 Endpoint;
314 EFI_STATUS Status;
315 UINT32 Result;
316 EFI_USB_IO_PROTOCOL *UsbIo;
317 UINT32 Index;
318 UINTN Timeout;
319
320 *CmdStatus = USB_BOT_COMMAND_ERROR;
321 Status = EFI_DEVICE_ERROR;
322 Endpoint = UsbBot->BulkInEndpoint->EndpointAddress;
323 UsbIo = UsbBot->UsbIo;
324 Timeout = USB_BOT_RECV_CSW_TIMEOUT / USB_MASS_1_MILLISECOND;
325
326 for (Index = 0; Index < USB_BOT_RECV_CSW_RETRY; Index++) {
327 //
328 // Attempt to the read Command Status Wrapper from bulk in endpoint
329 //
330 ZeroMem (&Csw, sizeof (USB_BOT_CSW));
331 Result = 0;
332 Len = sizeof (USB_BOT_CSW);
333 Status = UsbIo->UsbBulkTransfer (
334 UsbIo,
335 Endpoint,
336 &Csw,
337 &Len,
338 Timeout,
339 &Result
340 );
341 if (EFI_ERROR (Status)) {
342 if (USB_IS_ERROR (Result, EFI_USB_ERR_STALL)) {
343 UsbClearEndpointStall (UsbIo, Endpoint);
344 }
345
346 continue;
347 }
348
349 if (Csw.Signature != USB_BOT_CSW_SIGNATURE) {
350 //
351 // CSW is invalid, so perform reset recovery
352 //
353 Status = UsbBotResetDevice (UsbBot, FALSE);
354 } else if (Csw.CmdStatus == USB_BOT_COMMAND_ERROR) {
355 //
356 // Respond phase error also needs reset recovery
357 //
358 Status = UsbBotResetDevice (UsbBot, FALSE);
359 } else {
360 *CmdStatus = Csw.CmdStatus;
361 break;
362 }
363 }
364
365 //
366 // The tag is increased even if there is an error.
367 //
368 UsbBot->CbwTag++;
369
370 return Status;
371}
372
394 IN VOID *Context,
395 IN VOID *Cmd,
396 IN UINT8 CmdLen,
398 IN VOID *Data,
399 IN UINT32 DataLen,
400 IN UINT8 Lun,
401 IN UINT32 Timeout,
402 OUT UINT32 *CmdStatus
403 )
404{
405 USB_BOT_PROTOCOL *UsbBot;
406 EFI_STATUS Status;
407 UINTN TransLen;
408 UINT8 Result;
409
410 *CmdStatus = USB_MASS_CMD_FAIL;
411 UsbBot = (USB_BOT_PROTOCOL *)Context;
412
413 //
414 // Send the command to the device. Return immediately if device
415 // rejects the command.
416 //
417 Status = UsbBotSendCommand (UsbBot, Cmd, CmdLen, DataDir, DataLen, Lun);
418 if (EFI_ERROR (Status)) {
419 DEBUG ((DEBUG_ERROR, "UsbBotExecCommand: UsbBotSendCommand (%r)\n", Status));
420 return Status;
421 }
422
423 //
424 // Transfer the data. Don't return immediately even data transfer
425 // failed. The host should attempt to receive the CSW no matter
426 // whether it succeeds or fails.
427 //
428 TransLen = (UINTN)DataLen;
429 UsbBotDataTransfer (UsbBot, DataDir, Data, &TransLen, Timeout);
430
431 //
432 // Get the status, if that succeeds, interpret the result
433 //
434 Status = UsbBotGetStatus (UsbBot, DataLen, &Result);
435 if (EFI_ERROR (Status)) {
436 DEBUG ((DEBUG_ERROR, "UsbBotExecCommand: UsbBotGetStatus (%r)\n", Status));
437 return Status;
438 }
439
440 if (Result == 0) {
441 *CmdStatus = USB_MASS_CMD_SUCCESS;
442 }
443
444 return EFI_SUCCESS;
445}
446
461 IN VOID *Context,
462 IN BOOLEAN ExtendedVerification
463 )
464{
465 USB_BOT_PROTOCOL *UsbBot;
467 EFI_STATUS Status;
468 UINT32 Result;
469 UINT32 Timeout;
470
471 UsbBot = (USB_BOT_PROTOCOL *)Context;
472
473 if (ExtendedVerification) {
474 //
475 // If we need to do strictly reset, reset its parent hub port
476 //
477 Status = UsbBot->UsbIo->UsbPortReset (UsbBot->UsbIo);
478 if (EFI_ERROR (Status)) {
479 return EFI_DEVICE_ERROR;
480 }
481 }
482
483 //
484 // Issue a class specific Bulk-Only Mass Storage Reset request,
485 // according to section 3.1 of USB Mass Storage Class Bulk-Only Transport Spec, v1.0.
486 //
487 Request.RequestType = 0x21;
488 Request.Request = USB_BOT_RESET_REQUEST;
489 Request.Value = 0;
490 Request.Index = UsbBot->Interface.InterfaceNumber;
491 Request.Length = 0;
492 Timeout = USB_BOT_RESET_DEVICE_TIMEOUT / USB_MASS_1_MILLISECOND;
493
494 Status = UsbBot->UsbIo->UsbControlTransfer (
495 UsbBot->UsbIo,
496 &Request,
497 EfiUsbNoData,
498 Timeout,
499 NULL,
500 0,
501 &Result
502 );
503
504 if (EFI_ERROR (Status)) {
505 return EFI_DEVICE_ERROR;
506 }
507
508 //
509 // The device shall NAK the host's request until the reset is
510 // complete. We can use this to sync the device and host. For
511 // now just stall 100ms to wait for the device.
512 //
513 gBS->Stall (USB_BOT_RESET_DEVICE_STALL);
514
515 //
516 // Clear the Bulk-In and Bulk-Out stall condition.
517 //
518 UsbClearEndpointStall (UsbBot->UsbIo, UsbBot->BulkInEndpoint->EndpointAddress);
519 UsbClearEndpointStall (UsbBot->UsbIo, UsbBot->BulkOutEndpoint->EndpointAddress);
520
521 return Status;
522}
523
537 IN VOID *Context,
538 OUT UINT8 *MaxLun
539 )
540{
541 USB_BOT_PROTOCOL *UsbBot;
543 EFI_STATUS Status;
544 UINT32 Result;
545 UINT32 Timeout;
546
547 if ((Context == NULL) || (MaxLun == NULL)) {
548 return EFI_INVALID_PARAMETER;
549 }
550
551 UsbBot = (USB_BOT_PROTOCOL *)Context;
552
553 //
554 // Issue a class specific Bulk-Only Mass Storage get max lun request.
555 // according to section 3.2 of USB Mass Storage Class Bulk-Only Transport Spec, v1.0.
556 //
557 Request.RequestType = 0xA1;
558 Request.Request = USB_BOT_GETLUN_REQUEST;
559 Request.Value = 0;
560 Request.Index = UsbBot->Interface.InterfaceNumber;
561 Request.Length = 1;
562 Timeout = USB_BOT_RESET_DEVICE_TIMEOUT / USB_MASS_1_MILLISECOND;
563
564 Status = UsbBot->UsbIo->UsbControlTransfer (
565 UsbBot->UsbIo,
566 &Request,
567 EfiUsbDataIn,
568 Timeout,
569 (VOID *)MaxLun,
570 1,
571 &Result
572 );
573 if (EFI_ERROR (Status) || (*MaxLun > USB_BOT_MAX_LUN)) {
574 //
575 // If the Get LUN request returns an error or the MaxLun is larger than
576 // the maximum LUN value (0x0f) supported by the USB Mass Storage Class
577 // Bulk-Only Transport Spec, then set MaxLun to 0.
578 //
579 // This improves compatibility with USB FLASH drives that have a single LUN
580 // and either do not return a max LUN value or return an invalid maximum LUN
581 // value.
582 //
583 *MaxLun = 0;
584 }
585
586 return EFI_SUCCESS;
587}
588
599 IN VOID *Context
600 )
601{
602 FreePool (Context);
603 return EFI_SUCCESS;
604}
UINT64 UINTN
VOID *EFIAPI CopyMem(OUT VOID *DestinationBuffer, IN CONST VOID *SourceBuffer, IN UINTN Length)
VOID *EFIAPI ZeroMem(OUT VOID *Buffer, IN UINTN Length)
VOID *EFIAPI AllocateZeroPool(IN UINTN AllocationSize)
VOID EFIAPI FreePool(IN VOID *Buffer)
#define NULL
Definition: Base.h:319
#define FALSE
Definition: Base.h:307
#define IN
Definition: Base.h:279
#define OUT
Definition: Base.h:284
#define DEBUG(Expression)
Definition: DebugLib.h:434
EFI_USB_DATA_DIRECTION
Definition: UsbIo.h:44
RETURN_STATUS EFI_STATUS
Definition: UefiBaseType.h:29
#define EFI_SUCCESS
Definition: UefiBaseType.h:112
EFI_BOOT_SERVICES * gBS
#define USB_MASS_STORE_BOT
Bulk-Only Transport.
Definition: Usb.h:38
EFI_STATUS UsbClearEndpointStall(IN EFI_USB_IO_PROTOCOL *UsbIo, IN UINT8 EndpointAddr)
Definition: UsbMassBoot.c:977
EFI_STATUS UsbBotSendCommand(IN USB_BOT_PROTOCOL *UsbBot, IN UINT8 *Cmd, IN UINT8 CmdLen, IN EFI_USB_DATA_DIRECTION DataDir, IN UINT32 TransLen, IN UINT8 Lun)
Definition: UsbMassBot.c:147
EFI_STATUS UsbBotExecCommand(IN VOID *Context, IN VOID *Cmd, IN UINT8 CmdLen, IN EFI_USB_DATA_DIRECTION DataDir, IN VOID *Data, IN UINT32 DataLen, IN UINT8 Lun, IN UINT32 Timeout, OUT UINT32 *CmdStatus)
Definition: UsbMassBot.c:393
EFI_STATUS UsbBotInit(IN EFI_USB_IO_PROTOCOL *UsbIo, OUT VOID **Context OPTIONAL)
Definition: UsbMassBot.c:40
EFI_STATUS UsbBotCleanUp(IN VOID *Context)
Definition: UsbMassBot.c:598
EFI_STATUS UsbBotDataTransfer(IN USB_BOT_PROTOCOL *UsbBot, IN EFI_USB_DATA_DIRECTION DataDir, IN OUT UINT8 *Data, IN OUT UINTN *TransLen, IN UINT32 Timeout)
Definition: UsbMassBot.c:227
EFI_STATUS UsbBotResetDevice(IN VOID *Context, IN BOOLEAN ExtendedVerification)
Definition: UsbMassBot.c:460
EFI_STATUS UsbBotGetStatus(IN USB_BOT_PROTOCOL *UsbBot, IN UINT32 TransLen, OUT UINT8 *CmdStatus)
Definition: UsbMassBot.c:305
EFI_STATUS UsbBotGetMaxLun(IN VOID *Context, OUT UINT8 *MaxLun)
Definition: UsbMassBot.c:536
#define USB_BOT_COMMAND_ERROR
Phase error, need to reset the device.
Definition: UsbMassBot.h:31
#define USB_BOT_RESET_REQUEST
Bulk-Only Mass Storage Reset.
Definition: UsbMassBot.h:19
#define USB_BOT_MAX_LUN
Lun number is from 0 to 15.
Definition: UsbMassBot.h:23
#define USB_BOT_MAX_CMDLEN
Maximum number of command from command set.
Definition: UsbMassBot.h:24
#define USB_BOT_GETLUN_REQUEST
Get Max Lun.
Definition: UsbMassBot.h:20
#define USB_BOT_CSW_SIGNATURE
dCSWSignature, tag the packet as CSW
Definition: UsbMassBot.h:22
#define USB_BOT_CBW_SIGNATURE
dCBWSignature, tag the packet as CBW
Definition: UsbMassBot.h:21
UINT8 CmdLen
Length of the command. Bits 0~4 are used.
Definition: UsbMassBot.h:60
UINT8 Flag
Bit 7, 0 ~ Data-Out, 1 ~ Data-In.
Definition: UsbMassBot.h:58
UINT32 DataLen
Length of data between CBW and CSW.
Definition: UsbMassBot.h:57
UINT8 Lun
Lun number. Bits 0~3 are used.
Definition: UsbMassBot.h:59