]> git.proxmox.com Git - mirror_edk2.git/blob - MdeModulePkg/Bus/Usb/UsbMassStorageDxe/UsbMassBot.c
MdeModulePkg: Change use of EFI_D_* to DEBUG_*
[mirror_edk2.git] / MdeModulePkg / Bus / Usb / UsbMassStorageDxe / UsbMassBot.c
1 /** @file
2 Implementation of the USB mass storage Bulk-Only Transport protocol,
3 according to USB Mass Storage Class Bulk-Only Transport, Revision 1.0.
4
5 Copyright (c) 2007 - 2018, Intel Corporation. All rights reserved.<BR>
6 SPDX-License-Identifier: BSD-2-Clause-Patent
7
8 **/
9
10 #include "UsbMass.h"
11
12 //
13 // Definition of USB BOT Transport Protocol
14 //
15 USB_MASS_TRANSPORT mUsbBotTransport = {
16 USB_MASS_STORE_BOT,
17 UsbBotInit,
18 UsbBotExecCommand,
19 UsbBotResetDevice,
20 UsbBotGetMaxLun,
21 UsbBotCleanUp
22 };
23
24 /**
25 Initializes USB BOT protocol.
26
27 This function initializes the USB mass storage class BOT protocol.
28 It will save its context which is a USB_BOT_PROTOCOL structure
29 in the Context if Context isn't NULL.
30
31 @param UsbIo The USB I/O Protocol instance
32 @param Context The buffer to save the context to
33
34 @retval EFI_SUCCESS The device is successfully initialized.
35 @retval EFI_UNSUPPORTED The transport protocol doesn't support the device.
36 @retval Other The USB BOT initialization fails.
37
38 **/
39 EFI_STATUS
40 UsbBotInit (
41 IN EFI_USB_IO_PROTOCOL *UsbIo,
42 OUT VOID **Context OPTIONAL
43 )
44 {
45 USB_BOT_PROTOCOL *UsbBot;
46 EFI_USB_INTERFACE_DESCRIPTOR *Interface;
47 EFI_USB_ENDPOINT_DESCRIPTOR EndPoint;
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
122 ON_ERROR:
123 FreePool (UsbBot);
124 return Status;
125 }
126
127 /**
128 Send the command to the device using Bulk-Out endpoint.
129
130 This function sends the command to the device using Bulk-Out endpoint.
131 BOT transfer is composed of three phases: Command, Data, and Status.
132 This is the Command phase.
133
134 @param UsbBot The USB BOT device
135 @param Cmd The command to transfer to device
136 @param CmdLen The length of the command
137 @param DataDir The direction of the data
138 @param TransLen The expected length of the data
139 @param Lun The number of logic unit
140
141 @retval EFI_SUCCESS The command is sent to the device.
142 @retval EFI_NOT_READY The device return NAK to the transfer
143 @retval Others Failed to send the command to device
144
145 **/
146 EFI_STATUS
147 UsbBotSendCommand (
148 IN USB_BOT_PROTOCOL *UsbBot,
149 IN UINT8 *Cmd,
150 IN UINT8 CmdLen,
151 IN EFI_USB_DATA_DIRECTION DataDir,
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
207
208 /**
209 Transfer the data between the device and host.
210
211 This function transfers the data between the device and host.
212 BOT transfer is composed of three phases: Command, Data, and Status.
213 This is the Data phase.
214
215 @param UsbBot The USB BOT device
216 @param DataDir The direction of the data
217 @param Data The buffer to hold data
218 @param TransLen The expected length of the data
219 @param Timeout The time to wait the command to complete
220
221 @retval EFI_SUCCESS The data is transferred
222 @retval EFI_SUCCESS No data to transfer
223 @retval EFI_NOT_READY The device return NAK to the transfer
224 @retval Others Failed to transfer data
225
226 **/
227 EFI_STATUS
228 UsbBotDataTransfer (
229 IN USB_BOT_PROTOCOL *UsbBot,
230 IN EFI_USB_DATA_DIRECTION DataDir,
231 IN OUT UINT8 *Data,
232 IN OUT UINTN *TransLen,
233 IN UINT32 Timeout
234 )
235 {
236 EFI_USB_ENDPOINT_DESCRIPTOR *Endpoint;
237 EFI_STATUS Status;
238 UINT32 Result;
239
240 //
241 // If no data to transfer, just return EFI_SUCCESS.
242 //
243 if ((DataDir == EfiUsbNoData) || (*TransLen == 0)) {
244 return EFI_SUCCESS;
245 }
246
247 //
248 // Select the endpoint then issue the transfer
249 //
250 if (DataDir == EfiUsbDataIn) {
251 Endpoint = UsbBot->BulkInEndpoint;
252 } else {
253 Endpoint = UsbBot->BulkOutEndpoint;
254 }
255
256 Result = 0;
257 Timeout = Timeout / USB_MASS_1_MILLISECOND;
258
259 Status = UsbBot->UsbIo->UsbBulkTransfer (
260 UsbBot->UsbIo,
261 Endpoint->EndpointAddress,
262 Data,
263 TransLen,
264 Timeout,
265 &Result
266 );
267 if (EFI_ERROR (Status)) {
268 if (USB_IS_ERROR (Result, EFI_USB_ERR_STALL)) {
269 DEBUG ((DEBUG_INFO, "UsbBotDataTransfer: (%r)\n", Status));
270 DEBUG ((DEBUG_INFO, "UsbBotDataTransfer: DataIn Stall\n"));
271 UsbClearEndpointStall (UsbBot->UsbIo, Endpoint->EndpointAddress);
272 } else if (USB_IS_ERROR (Result, EFI_USB_ERR_NAK)) {
273 Status = EFI_NOT_READY;
274 } else {
275 DEBUG ((DEBUG_ERROR, "UsbBotDataTransfer: (%r)\n", Status));
276 }
277 if(Status == EFI_TIMEOUT){
278 UsbBotResetDevice(UsbBot, FALSE);
279 }
280 }
281
282 return Status;
283 }
284
285
286 /**
287 Get the command execution status from device.
288
289 This function gets the command execution status from device.
290 BOT transfer is composed of three phases: Command, Data, and Status.
291 This is the Status phase.
292
293 This function returns the transfer status of the BOT's CSW status,
294 and returns the high level command execution result in Result. So
295 even if EFI_SUCCESS is returned, the command may still have failed.
296
297 @param UsbBot The USB BOT device.
298 @param TransLen The expected length of the data.
299 @param CmdStatus The result of the command execution.
300
301 @retval EFI_SUCCESS Command execute result is retrieved and in the Result.
302 @retval Other Error occurred when trying to get status.
303
304 **/
305 EFI_STATUS
306 UsbBotGetStatus (
307 IN USB_BOT_PROTOCOL *UsbBot,
308 IN UINT32 TransLen,
309 OUT UINT8 *CmdStatus
310 )
311 {
312 USB_BOT_CSW Csw;
313 UINTN Len;
314 UINT8 Endpoint;
315 EFI_STATUS Status;
316 UINT32 Result;
317 EFI_USB_IO_PROTOCOL *UsbIo;
318 UINT32 Index;
319 UINTN Timeout;
320
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;
326
327 for (Index = 0; Index < USB_BOT_RECV_CSW_RETRY; Index++) {
328 //
329 // Attempt to the read Command Status Wrapper from bulk in endpoint
330 //
331 ZeroMem (&Csw, sizeof (USB_BOT_CSW));
332 Result = 0;
333 Len = sizeof (USB_BOT_CSW);
334 Status = UsbIo->UsbBulkTransfer (
335 UsbIo,
336 Endpoint,
337 &Csw,
338 &Len,
339 Timeout,
340 &Result
341 );
342 if (EFI_ERROR(Status)) {
343 if (USB_IS_ERROR (Result, EFI_USB_ERR_STALL)) {
344 UsbClearEndpointStall (UsbIo, Endpoint);
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 //The tag is increased even if there is an error.
366 //
367 UsbBot->CbwTag++;
368
369 return Status;
370 }
371
372
373 /**
374 Call the USB Mass Storage Class BOT protocol to issue
375 the command/data/status circle to execute the commands.
376
377 @param Context The context of the BOT protocol, that is,
378 USB_BOT_PROTOCOL
379 @param Cmd The high level command
380 @param CmdLen The command length
381 @param DataDir The direction of the data transfer
382 @param Data The buffer to hold data
383 @param DataLen The length of the data
384 @param Lun The number of logic unit
385 @param Timeout The time to wait command
386 @param CmdStatus The result of high level command execution
387
388 @retval EFI_SUCCESS The command is executed successfully.
389 @retval Other Failed to execute command
390
391 **/
392 EFI_STATUS
393 UsbBotExecCommand (
394 IN VOID *Context,
395 IN VOID *Cmd,
396 IN UINT8 CmdLen,
397 IN EFI_USB_DATA_DIRECTION DataDir,
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
447
448 /**
449 Reset the USB mass storage device by BOT protocol.
450
451 @param Context The context of the BOT protocol, that is,
452 USB_BOT_PROTOCOL.
453 @param ExtendedVerification If FALSE, just issue Bulk-Only Mass Storage Reset request.
454 If TRUE, additionally reset parent hub port.
455
456 @retval EFI_SUCCESS The device is reset.
457 @retval Others Failed to reset the device..
458
459 **/
460 EFI_STATUS
461 UsbBotResetDevice (
462 IN VOID *Context,
463 IN BOOLEAN ExtendedVerification
464 )
465 {
466 USB_BOT_PROTOCOL *UsbBot;
467 EFI_USB_DEVICE_REQUEST Request;
468 EFI_STATUS Status;
469 UINT32 Result;
470 UINT32 Timeout;
471
472 UsbBot = (USB_BOT_PROTOCOL *) Context;
473
474 if (ExtendedVerification) {
475 //
476 // If we need to do strictly reset, reset its parent hub port
477 //
478 Status = UsbBot->UsbIo->UsbPortReset (UsbBot->UsbIo);
479 if (EFI_ERROR (Status)) {
480 return EFI_DEVICE_ERROR;
481 }
482 }
483
484 //
485 // Issue a class specific Bulk-Only Mass Storage Reset request,
486 // according to section 3.1 of USB Mass Storage Class Bulk-Only Transport Spec, v1.0.
487 //
488 Request.RequestType = 0x21;
489 Request.Request = USB_BOT_RESET_REQUEST;
490 Request.Value = 0;
491 Request.Index = UsbBot->Interface.InterfaceNumber;
492 Request.Length = 0;
493 Timeout = USB_BOT_RESET_DEVICE_TIMEOUT / USB_MASS_1_MILLISECOND;
494
495 Status = UsbBot->UsbIo->UsbControlTransfer (
496 UsbBot->UsbIo,
497 &Request,
498 EfiUsbNoData,
499 Timeout,
500 NULL,
501 0,
502 &Result
503 );
504
505 if (EFI_ERROR (Status)) {
506 return EFI_DEVICE_ERROR;
507 }
508
509 //
510 // The device shall NAK the host's request until the reset is
511 // complete. We can use this to sync the device and host. For
512 // now just stall 100ms to wait for the device.
513 //
514 gBS->Stall (USB_BOT_RESET_DEVICE_STALL);
515
516 //
517 // Clear the Bulk-In and Bulk-Out stall condition.
518 //
519 UsbClearEndpointStall (UsbBot->UsbIo, UsbBot->BulkInEndpoint->EndpointAddress);
520 UsbClearEndpointStall (UsbBot->UsbIo, UsbBot->BulkOutEndpoint->EndpointAddress);
521
522 return Status;
523 }
524
525
526 /**
527 Get the max LUN (Logical Unit Number) of USB mass storage device.
528
529 @param Context The context of the BOT protocol, that is, USB_BOT_PROTOCOL
530 @param MaxLun Return pointer to the max number of LUN. (e.g. MaxLun=1 means LUN0 and
531 LUN1 in all.)
532
533 @retval EFI_SUCCESS Max LUN is got successfully.
534 @retval Others Fail to execute this request.
535
536 **/
537 EFI_STATUS
538 UsbBotGetMaxLun (
539 IN VOID *Context,
540 OUT UINT8 *MaxLun
541 )
542 {
543 USB_BOT_PROTOCOL *UsbBot;
544 EFI_USB_DEVICE_REQUEST Request;
545 EFI_STATUS Status;
546 UINT32 Result;
547 UINT32 Timeout;
548
549 if (Context == NULL || MaxLun == NULL) {
550 return EFI_INVALID_PARAMETER;
551 }
552
553 UsbBot = (USB_BOT_PROTOCOL *) Context;
554
555 //
556 // Issue a class specific Bulk-Only Mass Storage get max lun request.
557 // according to section 3.2 of USB Mass Storage Class Bulk-Only Transport Spec, v1.0.
558 //
559 Request.RequestType = 0xA1;
560 Request.Request = USB_BOT_GETLUN_REQUEST;
561 Request.Value = 0;
562 Request.Index = UsbBot->Interface.InterfaceNumber;
563 Request.Length = 1;
564 Timeout = USB_BOT_RESET_DEVICE_TIMEOUT / USB_MASS_1_MILLISECOND;
565
566 Status = UsbBot->UsbIo->UsbControlTransfer (
567 UsbBot->UsbIo,
568 &Request,
569 EfiUsbDataIn,
570 Timeout,
571 (VOID *) MaxLun,
572 1,
573 &Result
574 );
575 if (EFI_ERROR (Status) || *MaxLun > USB_BOT_MAX_LUN) {
576 //
577 // If the Get LUN request returns an error or the MaxLun is larger than
578 // the maximum LUN value (0x0f) supported by the USB Mass Storage Class
579 // Bulk-Only Transport Spec, then set MaxLun to 0.
580 //
581 // This improves compatibility with USB FLASH drives that have a single LUN
582 // and either do not return a max LUN value or return an invalid maximum LUN
583 // value.
584 //
585 *MaxLun = 0;
586 }
587
588 return EFI_SUCCESS;
589 }
590
591 /**
592 Clean up the resource used by this BOT protocol.
593
594 @param Context The context of the BOT protocol, that is, USB_BOT_PROTOCOL.
595
596 @retval EFI_SUCCESS The resource is cleaned up.
597
598 **/
599 EFI_STATUS
600 UsbBotCleanUp (
601 IN VOID *Context
602 )
603 {
604 FreePool (Context);
605 return EFI_SUCCESS;
606 }