TianoCore EDK2 master
Loading...
Searching...
No Matches
UsbMassCbi.c
Go to the documentation of this file.
1
12#include "UsbMass.h"
13
14//
15// Definition of USB CBI0 Transport Protocol
16//
17USB_MASS_TRANSPORT mUsbCbi0Transport = {
22 NULL,
24};
25
26//
27// Definition of USB CBI1 Transport Protocol
28//
29USB_MASS_TRANSPORT mUsbCbi1Transport = {
34 NULL,
36};
37
56 OUT VOID **Context OPTIONAL
57 )
58{
59 USB_CBI_PROTOCOL *UsbCbi;
62 EFI_STATUS Status;
63 UINT8 Index;
64
65 //
66 // Allocate the CBI context for USB_CBI_PROTOCOL and 3 endpoint descriptors.
67 //
68 UsbCbi = AllocateZeroPool (
69 sizeof (USB_CBI_PROTOCOL) + 3 * sizeof (EFI_USB_ENDPOINT_DESCRIPTOR)
70 );
71 ASSERT (UsbCbi != NULL);
72
73 UsbCbi->UsbIo = UsbIo;
74
75 //
76 // Get the interface descriptor and validate that it
77 // is a USB Mass Storage CBI interface.
78 //
79 Status = UsbIo->UsbGetInterfaceDescriptor (UsbIo, &UsbCbi->Interface);
80 if (EFI_ERROR (Status)) {
81 goto ON_ERROR;
82 }
83
84 Interface = &UsbCbi->Interface;
85 if ( (Interface->InterfaceProtocol != USB_MASS_STORE_CBI0)
86 && (Interface->InterfaceProtocol != USB_MASS_STORE_CBI1))
87 {
88 Status = EFI_UNSUPPORTED;
89 goto ON_ERROR;
90 }
91
92 //
93 // Locate and save the bulk-in, bulk-out, and interrupt endpoint
94 //
95 for (Index = 0; Index < Interface->NumEndpoints; Index++) {
96 Status = UsbIo->UsbGetEndpointDescriptor (UsbIo, Index, &EndPoint);
97 if (EFI_ERROR (Status)) {
98 continue;
99 }
100
101 if (USB_IS_BULK_ENDPOINT (EndPoint.Attributes)) {
102 //
103 // Use the first Bulk-In and Bulk-Out endpoints
104 //
105 if (USB_IS_IN_ENDPOINT (EndPoint.EndpointAddress) &&
106 (UsbCbi->BulkInEndpoint == NULL))
107 {
108 UsbCbi->BulkInEndpoint = (EFI_USB_ENDPOINT_DESCRIPTOR *)(UsbCbi + 1);
109 CopyMem (UsbCbi->BulkInEndpoint, &EndPoint, sizeof (EndPoint));
110 }
111
112 if (USB_IS_OUT_ENDPOINT (EndPoint.EndpointAddress) &&
113 (UsbCbi->BulkOutEndpoint == NULL))
114 {
115 UsbCbi->BulkOutEndpoint = (EFI_USB_ENDPOINT_DESCRIPTOR *)(UsbCbi + 1) + 1;
116 CopyMem (UsbCbi->BulkOutEndpoint, &EndPoint, sizeof (EndPoint));
117 }
118 } else if (USB_IS_INTERRUPT_ENDPOINT (EndPoint.Attributes)) {
119 //
120 // Use the first interrupt endpoint if it is CBI0
121 //
122 if ((Interface->InterfaceProtocol == USB_MASS_STORE_CBI0) &&
123 (UsbCbi->InterruptEndpoint == NULL))
124 {
125 UsbCbi->InterruptEndpoint = (EFI_USB_ENDPOINT_DESCRIPTOR *)(UsbCbi + 1) + 2;
126 CopyMem (UsbCbi->InterruptEndpoint, &EndPoint, sizeof (EndPoint));
127 }
128 }
129 }
130
131 if ((UsbCbi->BulkInEndpoint == NULL) || (UsbCbi->BulkOutEndpoint == NULL)) {
132 Status = EFI_UNSUPPORTED;
133 goto ON_ERROR;
134 }
135
136 if ((Interface->InterfaceProtocol == USB_MASS_STORE_CBI0) && (UsbCbi->InterruptEndpoint == NULL)) {
137 Status = EFI_UNSUPPORTED;
138 goto ON_ERROR;
139 }
140
141 if (Context != NULL) {
142 *Context = UsbCbi;
143 } else {
144 FreePool (UsbCbi);
145 }
146
147 return EFI_SUCCESS;
148
149ON_ERROR:
150 FreePool (UsbCbi);
151 return Status;
152}
153
171 IN USB_CBI_PROTOCOL *UsbCbi,
172 IN UINT8 *Cmd,
173 IN UINT8 CmdLen,
174 IN UINT32 Timeout
175 )
176{
178 EFI_STATUS Status;
179 UINT32 TransStatus;
180 UINTN DataLen;
181 INTN Retry;
182
183 //
184 // Fill in the device request, CBI use the "Accept Device-Specific
185 // Cmd" (ADSC) class specific request to send commands.
186 //
187 Request.RequestType = 0x21;
188 Request.Request = 0;
189 Request.Value = 0;
190 Request.Index = UsbCbi->Interface.InterfaceNumber;
191 Request.Length = CmdLen;
192
193 Status = EFI_SUCCESS;
194 Timeout = Timeout / USB_MASS_1_MILLISECOND;
195
196 for (Retry = 0; Retry < USB_CBI_MAX_RETRY; Retry++) {
197 //
198 // Use USB I/O Protocol to send the command to the device
199 //
200 TransStatus = 0;
201 DataLen = CmdLen;
202
203 Status = UsbCbi->UsbIo->UsbControlTransfer (
204 UsbCbi->UsbIo,
205 &Request,
206 EfiUsbDataOut,
207 Timeout,
208 Cmd,
209 DataLen,
210 &TransStatus
211 );
212 //
213 // The device can fail the command by STALL the control endpoint.
214 // It can delay the command by NAK the data or status stage, this
215 // is a "class-specific exemption to the USB specification". Retry
216 // if the command is NAKed.
217 //
218 if (EFI_ERROR (Status) && (TransStatus == EFI_USB_ERR_NAK)) {
219 continue;
220 }
221
222 break;
223 }
224
225 return Status;
226}
227
248 IN USB_CBI_PROTOCOL *UsbCbi,
250 IN OUT UINT8 *Data,
251 IN OUT UINTN *TransLen,
252 IN UINT32 Timeout
253 )
254{
256 EFI_STATUS Status;
257 UINT32 TransStatus;
258 UINTN Remain;
259 UINTN Increment;
260 UINT8 *Next;
261 UINTN Retry;
262
263 //
264 // If no data to transfer, just return EFI_SUCCESS.
265 //
266 if ((DataDir == EfiUsbNoData) || (*TransLen == 0)) {
267 return EFI_SUCCESS;
268 }
269
270 //
271 // Select the endpoint then issue the transfer
272 //
273 if (DataDir == EfiUsbDataIn) {
274 Endpoint = UsbCbi->BulkInEndpoint;
275 } else {
276 Endpoint = UsbCbi->BulkOutEndpoint;
277 }
278
279 Next = Data;
280 Remain = *TransLen;
281 Retry = 0;
282 Status = EFI_SUCCESS;
283 Timeout = Timeout / USB_MASS_1_MILLISECOND;
284
285 //
286 // Transfer the data with a loop. The length of data transferred once is restricted.
287 //
288 while (Remain > 0) {
289 TransStatus = 0;
290
291 if (Remain > (UINTN)USB_CBI_MAX_PACKET_NUM * Endpoint->MaxPacketSize) {
292 Increment = USB_CBI_MAX_PACKET_NUM * Endpoint->MaxPacketSize;
293 } else {
294 Increment = Remain;
295 }
296
297 Status = UsbCbi->UsbIo->UsbBulkTransfer (
298 UsbCbi->UsbIo,
299 Endpoint->EndpointAddress,
300 Next,
301 &Increment,
302 Timeout,
303 &TransStatus
304 );
305 if (EFI_ERROR (Status)) {
306 if (TransStatus == EFI_USB_ERR_NAK) {
307 //
308 // The device can NAK the host if either the data/buffer isn't
309 // available or the command is in-progress.
310 // If data are partially transferred, we just ignore NAK and continue.
311 // If all data have been transferred and status is NAK, then we retry for several times.
312 // If retry exceeds the USB_CBI_MAX_RETRY, then return error status.
313 //
314 if (Increment == 0) {
315 if (++Retry > USB_CBI_MAX_RETRY) {
316 goto ON_EXIT;
317 }
318 } else {
319 Next += Increment;
320 Remain -= Increment;
321 Retry = 0;
322 }
323
324 continue;
325 }
326
327 //
328 // The device can fail the command by STALL the bulk endpoint.
329 // Clear the stall if that is the case.
330 //
331 if (TransStatus == EFI_USB_ERR_STALL) {
332 UsbClearEndpointStall (UsbCbi->UsbIo, Endpoint->EndpointAddress);
333 }
334
335 goto ON_EXIT;
336 }
337
338 Next += Increment;
339 Remain -= Increment;
340 }
341
342ON_EXIT:
343 *TransLen -= Remain;
344 return Status;
345}
346
365 IN USB_CBI_PROTOCOL *UsbCbi,
366 IN UINT32 Timeout,
367 OUT USB_CBI_STATUS *Result
368 )
369{
370 UINTN Len;
371 UINT8 Endpoint;
372 EFI_STATUS Status;
373 UINT32 TransStatus;
374 INTN Retry;
375
376 Endpoint = UsbCbi->InterruptEndpoint->EndpointAddress;
377 Status = EFI_SUCCESS;
378 Timeout = Timeout / USB_MASS_1_MILLISECOND;
379
380 //
381 // Attempt to the read the result from interrupt endpoint
382 //
383 for (Retry = 0; Retry < USB_CBI_MAX_RETRY; Retry++) {
384 TransStatus = 0;
385 Len = sizeof (USB_CBI_STATUS);
386
387 Status = UsbCbi->UsbIo->UsbSyncInterruptTransfer (
388 UsbCbi->UsbIo,
389 Endpoint,
390 Result,
391 &Len,
392 Timeout,
393 &TransStatus
394 );
395 //
396 // The CBI can NAK the interrupt endpoint if the command is in-progress.
397 //
398 if (EFI_ERROR (Status) && (TransStatus == EFI_USB_ERR_NAK)) {
399 continue;
400 }
401
402 break;
403 }
404
405 return Status;
406}
407
427 IN VOID *Context,
428 IN VOID *Cmd,
429 IN UINT8 CmdLen,
431 IN VOID *Data,
432 IN UINT32 DataLen,
433 IN UINT8 Lun,
434 IN UINT32 Timeout,
435 OUT UINT32 *CmdStatus
436 )
437{
438 USB_CBI_PROTOCOL *UsbCbi;
439 USB_CBI_STATUS Result;
440 EFI_STATUS Status;
441 UINTN TransLen;
442
443 *CmdStatus = USB_MASS_CMD_SUCCESS;
444 UsbCbi = (USB_CBI_PROTOCOL *)Context;
445
446 //
447 // Send the command to the device. Return immediately if device
448 // rejects the command.
449 //
450 Status = UsbCbiSendCommand (UsbCbi, Cmd, CmdLen, Timeout);
451 if (EFI_ERROR (Status)) {
452 gBS->Stall (10 * USB_MASS_1_MILLISECOND);
453 DEBUG ((DEBUG_ERROR, "UsbCbiExecCommand: UsbCbiSendCommand (%r)\n", Status));
454 return Status;
455 }
456
457 //
458 // Transfer the data. Return this status if no interrupt endpoint
459 // is used to report the transfer status.
460 //
461 TransLen = (UINTN)DataLen;
462
463 Status = UsbCbiDataTransfer (UsbCbi, DataDir, Data, &TransLen, Timeout);
464 if (UsbCbi->InterruptEndpoint == NULL) {
465 DEBUG ((DEBUG_ERROR, "UsbCbiExecCommand: UsbCbiDataTransfer (%r)\n", Status));
466 return Status;
467 }
468
469 //
470 // Get the status. If it succeeds, interpret the result.
471 //
472 Status = UsbCbiGetStatus (UsbCbi, Timeout, &Result);
473 if (EFI_ERROR (Status)) {
474 DEBUG ((DEBUG_ERROR, "UsbCbiExecCommand: UsbCbiGetStatus (%r)\n", Status));
475 return Status;
476 }
477
478 if (UsbCbi->Interface.InterfaceSubClass == USB_MASS_STORE_UFI) {
479 //
480 // For UFI device, ASC and ASCQ are returned.
481 //
482 // Do not set the USB_MASS_CMD_FAIL for a request sense command
483 // as a bad result type doesn't mean a cmd failure
484 //
485 if ((Result.Type != 0) && (*(UINT8 *)Cmd != 0x03)) {
486 *CmdStatus = USB_MASS_CMD_FAIL;
487 }
488 } else {
489 //
490 // Check page 27, CBI spec 1.1 for vaious reture status.
491 //
492 switch (Result.Value & 0x03) {
493 case 0x00:
494 //
495 // Pass
496 //
497 *CmdStatus = USB_MASS_CMD_SUCCESS;
498 break;
499
500 case 0x02:
501 //
502 // Phase Error, response with reset.
503 // No break here to fall through to "Fail".
504 //
505 UsbCbiResetDevice (UsbCbi, FALSE);
506
507 case 0x01:
508 //
509 // Fail
510 //
511 *CmdStatus = USB_MASS_CMD_FAIL;
512 break;
513
514 case 0x03:
515 //
516 // Persistent Fail. Need to send REQUEST SENSE.
517 //
518 *CmdStatus = USB_MASS_CMD_PERSISTENT;
519 break;
520 }
521 }
522
523 return EFI_SUCCESS;
524}
525
543 IN VOID *Context,
544 IN BOOLEAN ExtendedVerification
545 )
546{
547 UINT8 ResetCmd[USB_CBI_RESET_CMD_LEN];
548 USB_CBI_PROTOCOL *UsbCbi;
549 USB_CBI_STATUS Result;
550 EFI_STATUS Status;
551 UINT32 Timeout;
552
553 UsbCbi = (USB_CBI_PROTOCOL *)Context;
554
555 //
556 // Fill in the reset command.
557 //
558 SetMem (ResetCmd, USB_CBI_RESET_CMD_LEN, 0xFF);
559
560 ResetCmd[0] = 0x1D;
561 ResetCmd[1] = 0x04;
562 Timeout = USB_CBI_RESET_DEVICE_TIMEOUT / USB_MASS_1_MILLISECOND;
563
564 //
565 // Send the command to the device. Don't use UsbCbiExecCommand here.
566 //
567 Status = UsbCbiSendCommand (UsbCbi, ResetCmd, USB_CBI_RESET_CMD_LEN, Timeout);
568 if (EFI_ERROR (Status)) {
569 return EFI_DEVICE_ERROR;
570 }
571
572 //
573 // Just retrieve the status and ignore that. Then stall
574 // 50ms to wait for it to complete.
575 //
576 UsbCbiGetStatus (UsbCbi, Timeout, &Result);
577 gBS->Stall (USB_CBI_RESET_DEVICE_STALL);
578
579 //
580 // Clear the Bulk-In and Bulk-Out stall condition and init data toggle.
581 //
582 UsbClearEndpointStall (UsbCbi->UsbIo, UsbCbi->BulkInEndpoint->EndpointAddress);
583 UsbClearEndpointStall (UsbCbi->UsbIo, UsbCbi->BulkOutEndpoint->EndpointAddress);
584
585 return Status;
586}
587
598 IN VOID *Context
599 )
600{
601 FreePool (Context);
602 return EFI_SUCCESS;
603}
UINT64 UINTN
INT64 INTN
VOID *EFIAPI CopyMem(OUT VOID *DestinationBuffer, IN CONST VOID *SourceBuffer, IN UINTN Length)
VOID *EFIAPI SetMem(OUT VOID *Buffer, IN UINTN Length, IN UINT8 Value)
Definition: SetMemWrapper.c:38
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_UFI
Typically a floppy disk driver device.
Definition: Usb.h:29
#define USB_MASS_STORE_CBI0
CBI protocol with command completion interrupt.
Definition: Usb.h:36
#define USB_MASS_STORE_CBI1
CBI protocol without command completion interrupt.
Definition: Usb.h:37
EFI_STATUS UsbClearEndpointStall(IN EFI_USB_IO_PROTOCOL *UsbIo, IN UINT8 EndpointAddr)
Definition: UsbMassBoot.c:977
EFI_STATUS UsbCbiDataTransfer(IN USB_CBI_PROTOCOL *UsbCbi, IN EFI_USB_DATA_DIRECTION DataDir, IN OUT UINT8 *Data, IN OUT UINTN *TransLen, IN UINT32 Timeout)
Definition: UsbMassCbi.c:247
EFI_STATUS UsbCbiCleanUp(IN VOID *Context)
Definition: UsbMassCbi.c:597
EFI_STATUS UsbCbiResetDevice(IN VOID *Context, IN BOOLEAN ExtendedVerification)
Definition: UsbMassCbi.c:542
EFI_STATUS UsbCbiGetStatus(IN USB_CBI_PROTOCOL *UsbCbi, IN UINT32 Timeout, OUT USB_CBI_STATUS *Result)
Definition: UsbMassCbi.c:364
EFI_STATUS UsbCbiExecCommand(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: UsbMassCbi.c:426
EFI_STATUS UsbCbiInit(IN EFI_USB_IO_PROTOCOL *UsbIo, OUT VOID **Context OPTIONAL)
Definition: UsbMassCbi.c:54
EFI_STATUS UsbCbiSendCommand(IN USB_CBI_PROTOCOL *UsbCbi, IN UINT8 *Cmd, IN UINT8 CmdLen, IN UINT32 Timeout)
Definition: UsbMassCbi.c:170