]> git.proxmox.com Git - mirror_edk2.git/blob - MdeModulePkg/Bus/Usb/UsbMassStorageDxe/UsbMassBot.c
4c29b611213dcbd0901b78b47fe3d988e3c050a5
[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 - 2016, Intel Corporation. All rights reserved.<BR>
6 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
10
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.
13
14 **/
15
16 #include "UsbMass.h"
17
18 //
19 // Definition of USB BOT Transport Protocol
20 //
21 USB_MASS_TRANSPORT mUsbBotTransport = {
22 USB_MASS_STORE_BOT,
23 UsbBotInit,
24 UsbBotExecCommand,
25 UsbBotResetDevice,
26 UsbBotGetMaxLun,
27 UsbBotCleanUp
28 };
29
30 /**
31 Initializes USB BOT protocol.
32
33 This function initializes the USB mass storage class BOT protocol.
34 It will save its context which is a USB_BOT_PROTOCOL structure
35 in the Context if Context isn't NULL.
36
37 @param UsbIo The USB I/O Protocol instance
38 @param Context The buffer to save the context to
39
40 @retval EFI_SUCCESS The device is successfully initialized.
41 @retval EFI_UNSUPPORTED The transport protocol doesn't support the device.
42 @retval Other The USB BOT initialization fails.
43
44 **/
45 EFI_STATUS
46 UsbBotInit (
47 IN EFI_USB_IO_PROTOCOL *UsbIo,
48 OUT VOID **Context OPTIONAL
49 )
50 {
51 USB_BOT_PROTOCOL *UsbBot;
52 EFI_USB_INTERFACE_DESCRIPTOR *Interface;
53 EFI_USB_ENDPOINT_DESCRIPTOR EndPoint;
54 EFI_STATUS Status;
55 UINT8 Index;
56
57 //
58 // Allocate the BOT context for USB_BOT_PROTOCOL and two endpoint descriptors.
59 //
60 UsbBot = AllocateZeroPool (sizeof (USB_BOT_PROTOCOL) + 2 * sizeof (EFI_USB_ENDPOINT_DESCRIPTOR));
61 ASSERT (UsbBot != NULL);
62
63 UsbBot->UsbIo = UsbIo;
64
65 //
66 // Get the interface descriptor and validate that it
67 // is a USB Mass Storage BOT interface.
68 //
69 Status = UsbIo->UsbGetInterfaceDescriptor (UsbIo, &UsbBot->Interface);
70
71 if (EFI_ERROR (Status)) {
72 goto ON_ERROR;
73 }
74
75 Interface = &UsbBot->Interface;
76
77 if (Interface->InterfaceProtocol != USB_MASS_STORE_BOT) {
78 Status = EFI_UNSUPPORTED;
79 goto ON_ERROR;
80 }
81
82 //
83 // Locate and save the first bulk-in and bulk-out endpoint
84 //
85 for (Index = 0; Index < Interface->NumEndpoints; Index++) {
86 Status = UsbIo->UsbGetEndpointDescriptor (UsbIo, Index, &EndPoint);
87
88 if (EFI_ERROR (Status) || !USB_IS_BULK_ENDPOINT (EndPoint.Attributes)) {
89 continue;
90 }
91
92 if (USB_IS_IN_ENDPOINT (EndPoint.EndpointAddress) &&
93 (UsbBot->BulkInEndpoint == NULL)) {
94
95 UsbBot->BulkInEndpoint = (EFI_USB_ENDPOINT_DESCRIPTOR *) (UsbBot + 1);
96 CopyMem(UsbBot->BulkInEndpoint, &EndPoint, sizeof (EndPoint));
97 }
98
99 if (USB_IS_OUT_ENDPOINT (EndPoint.EndpointAddress) &&
100 (UsbBot->BulkOutEndpoint == NULL)) {
101
102 UsbBot->BulkOutEndpoint = (EFI_USB_ENDPOINT_DESCRIPTOR *) (UsbBot + 1) + 1;
103 CopyMem (UsbBot->BulkOutEndpoint, &EndPoint, sizeof(EndPoint));
104 }
105 }
106
107 //
108 // If bulk-in or bulk-out endpoint is not found, report error.
109 //
110 if ((UsbBot->BulkInEndpoint == NULL) || (UsbBot->BulkOutEndpoint == NULL)) {
111 Status = EFI_UNSUPPORTED;
112 goto ON_ERROR;
113 }
114
115 //
116 // The USB BOT protocol uses CBWTag to match the CBW and CSW.
117 //
118 UsbBot->CbwTag = 0x01;
119
120 if (Context != NULL) {
121 *Context = UsbBot;
122 } else {
123 FreePool (UsbBot);
124 }
125
126 return EFI_SUCCESS;
127
128 ON_ERROR:
129 FreePool (UsbBot);
130 return Status;
131 }
132
133 /**
134 Send the command to the device using Bulk-Out endpoint.
135
136 This function sends the command to the device using Bulk-Out endpoint.
137 BOT transfer is composed of three phases: Command, Data, and Status.
138 This is the Command phase.
139
140 @param UsbBot The USB BOT device
141 @param Cmd The command to transfer to device
142 @param CmdLen The length of the command
143 @param DataDir The direction of the data
144 @param TransLen The expected length of the data
145 @param Lun The number of logic unit
146
147 @retval EFI_SUCCESS The command is sent to the device.
148 @retval EFI_NOT_READY The device return NAK to the transfer
149 @retval Others Failed to send the command to device
150
151 **/
152 EFI_STATUS
153 UsbBotSendCommand (
154 IN USB_BOT_PROTOCOL *UsbBot,
155 IN UINT8 *Cmd,
156 IN UINT8 CmdLen,
157 IN EFI_USB_DATA_DIRECTION DataDir,
158 IN UINT32 TransLen,
159 IN UINT8 Lun
160 )
161 {
162 USB_BOT_CBW Cbw;
163 EFI_STATUS Status;
164 UINT32 Result;
165 UINTN DataLen;
166 UINTN Timeout;
167
168 ASSERT ((CmdLen > 0) && (CmdLen <= USB_BOT_MAX_CMDLEN));
169
170 //
171 // Fill in the Command Block Wrapper.
172 //
173 Cbw.Signature = USB_BOT_CBW_SIGNATURE;
174 Cbw.Tag = UsbBot->CbwTag;
175 Cbw.DataLen = TransLen;
176 Cbw.Flag = (UINT8) ((DataDir == EfiUsbDataIn) ? BIT7 : 0);
177 Cbw.Lun = Lun;
178 Cbw.CmdLen = CmdLen;
179
180 ZeroMem (Cbw.CmdBlock, USB_BOT_MAX_CMDLEN);
181 CopyMem (Cbw.CmdBlock, Cmd, CmdLen);
182
183 Result = 0;
184 DataLen = sizeof (USB_BOT_CBW);
185 Timeout = USB_BOT_SEND_CBW_TIMEOUT / USB_MASS_1_MILLISECOND;
186
187 //
188 // Use USB I/O Protocol to send the Command Block Wrapper to the device.
189 //
190 Status = UsbBot->UsbIo->UsbBulkTransfer (
191 UsbBot->UsbIo,
192 UsbBot->BulkOutEndpoint->EndpointAddress,
193 &Cbw,
194 &DataLen,
195 Timeout,
196 &Result
197 );
198 if (EFI_ERROR (Status)) {
199 if (USB_IS_ERROR (Result, EFI_USB_ERR_STALL) && DataDir == EfiUsbDataOut) {
200 //
201 // Respond to Bulk-Out endpoint stall with a Reset Recovery,
202 // according to section 5.3.1 of USB Mass Storage Class Bulk-Only Transport Spec, v1.0.
203 //
204 UsbBotResetDevice (UsbBot, FALSE);
205 } else if (USB_IS_ERROR (Result, EFI_USB_ERR_NAK)) {
206 Status = EFI_NOT_READY;
207 }
208 }
209
210 return Status;
211 }
212
213
214 /**
215 Transfer the data between the device and host.
216
217 This function transfers the data between the device and host.
218 BOT transfer is composed of three phases: Command, Data, and Status.
219 This is the Data phase.
220
221 @param UsbBot The USB BOT device
222 @param DataDir The direction of the data
223 @param Data The buffer to hold data
224 @param TransLen The expected length of the data
225 @param Timeout The time to wait the command to complete
226
227 @retval EFI_SUCCESS The data is transferred
228 @retval EFI_SUCCESS No data to transfer
229 @retval EFI_NOT_READY The device return NAK to the transfer
230 @retval Others Failed to transfer data
231
232 **/
233 EFI_STATUS
234 UsbBotDataTransfer (
235 IN USB_BOT_PROTOCOL *UsbBot,
236 IN EFI_USB_DATA_DIRECTION DataDir,
237 IN OUT UINT8 *Data,
238 IN OUT UINTN *TransLen,
239 IN UINT32 Timeout
240 )
241 {
242 EFI_USB_ENDPOINT_DESCRIPTOR *Endpoint;
243 EFI_STATUS Status;
244 UINT32 Result;
245
246 //
247 // If no data to transfer, just return EFI_SUCCESS.
248 //
249 if ((DataDir == EfiUsbNoData) || (*TransLen == 0)) {
250 return EFI_SUCCESS;
251 }
252
253 //
254 // Select the endpoint then issue the transfer
255 //
256 if (DataDir == EfiUsbDataIn) {
257 Endpoint = UsbBot->BulkInEndpoint;
258 } else {
259 Endpoint = UsbBot->BulkOutEndpoint;
260 }
261
262 Result = 0;
263 Timeout = Timeout / USB_MASS_1_MILLISECOND;
264
265 Status = UsbBot->UsbIo->UsbBulkTransfer (
266 UsbBot->UsbIo,
267 Endpoint->EndpointAddress,
268 Data,
269 TransLen,
270 Timeout,
271 &Result
272 );
273 if (EFI_ERROR (Status)) {
274 if (USB_IS_ERROR (Result, EFI_USB_ERR_STALL)) {
275 DEBUG ((EFI_D_INFO, "UsbBotDataTransfer: (%r)\n", Status));
276 DEBUG ((EFI_D_INFO, "UsbBotDataTransfer: DataIn Stall\n"));
277 UsbClearEndpointStall (UsbBot->UsbIo, Endpoint->EndpointAddress);
278 } else if (USB_IS_ERROR (Result, EFI_USB_ERR_NAK)) {
279 Status = EFI_NOT_READY;
280 } else {
281 DEBUG ((EFI_D_ERROR, "UsbBotDataTransfer: (%r)\n", Status));
282 }
283 if(Status == EFI_TIMEOUT){
284 UsbBotResetDevice(UsbBot, FALSE);
285 }
286 }
287
288 return Status;
289 }
290
291
292 /**
293 Get the command execution status from device.
294
295 This function gets the command execution status from device.
296 BOT transfer is composed of three phases: Command, Data, and Status.
297 This is the Status phase.
298
299 This function returns the transfer status of the BOT's CSW status,
300 and returns the high level command execution result in Result. So
301 even if EFI_SUCCESS is returned, the command may still have failed.
302
303 @param UsbBot The USB BOT device.
304 @param TransLen The expected length of the data.
305 @param CmdStatus The result of the command execution.
306
307 @retval EFI_SUCCESS Command execute result is retrieved and in the Result.
308 @retval Other Error occurred when trying to get status.
309
310 **/
311 EFI_STATUS
312 UsbBotGetStatus (
313 IN USB_BOT_PROTOCOL *UsbBot,
314 IN UINT32 TransLen,
315 OUT UINT8 *CmdStatus
316 )
317 {
318 USB_BOT_CSW Csw;
319 UINTN Len;
320 UINT8 Endpoint;
321 EFI_STATUS Status;
322 UINT32 Result;
323 EFI_USB_IO_PROTOCOL *UsbIo;
324 UINT32 Index;
325 UINTN Timeout;
326
327 *CmdStatus = USB_BOT_COMMAND_ERROR;
328 Status = EFI_DEVICE_ERROR;
329 Endpoint = UsbBot->BulkInEndpoint->EndpointAddress;
330 UsbIo = UsbBot->UsbIo;
331 Timeout = USB_BOT_RECV_CSW_TIMEOUT / USB_MASS_1_MILLISECOND;
332
333 for (Index = 0; Index < USB_BOT_RECV_CSW_RETRY; Index++) {
334 //
335 // Attemp to the read Command Status Wrapper from bulk in endpoint
336 //
337 ZeroMem (&Csw, sizeof (USB_BOT_CSW));
338 Result = 0;
339 Len = sizeof (USB_BOT_CSW);
340 Status = UsbIo->UsbBulkTransfer (
341 UsbIo,
342 Endpoint,
343 &Csw,
344 &Len,
345 Timeout,
346 &Result
347 );
348 if (EFI_ERROR(Status)) {
349 if (USB_IS_ERROR (Result, EFI_USB_ERR_STALL)) {
350 UsbClearEndpointStall (UsbIo, Endpoint);
351 }
352 continue;
353 }
354
355 if (Csw.Signature != USB_BOT_CSW_SIGNATURE) {
356 //
357 // CSW is invalid, so perform reset recovery
358 //
359 Status = UsbBotResetDevice (UsbBot, FALSE);
360 } else if (Csw.CmdStatus == USB_BOT_COMMAND_ERROR) {
361 //
362 // Respond phase error also needs reset recovery
363 //
364 Status = UsbBotResetDevice (UsbBot, FALSE);
365 } else {
366 *CmdStatus = Csw.CmdStatus;
367 break;
368 }
369 }
370 //
371 //The tag is increased even if there is an error.
372 //
373 UsbBot->CbwTag++;
374
375 return Status;
376 }
377
378
379 /**
380 Call the USB Mass Storage Class BOT protocol to issue
381 the command/data/status circle to execute the commands.
382
383 @param Context The context of the BOT protocol, that is,
384 USB_BOT_PROTOCOL
385 @param Cmd The high level command
386 @param CmdLen The command length
387 @param DataDir The direction of the data transfer
388 @param Data The buffer to hold data
389 @param DataLen The length of the data
390 @param Lun The number of logic unit
391 @param Timeout The time to wait command
392 @param CmdStatus The result of high level command execution
393
394 @retval EFI_SUCCESS The command is executed successfully.
395 @retval Other Failed to execute command
396
397 **/
398 EFI_STATUS
399 UsbBotExecCommand (
400 IN VOID *Context,
401 IN VOID *Cmd,
402 IN UINT8 CmdLen,
403 IN EFI_USB_DATA_DIRECTION DataDir,
404 IN VOID *Data,
405 IN UINT32 DataLen,
406 IN UINT8 Lun,
407 IN UINT32 Timeout,
408 OUT UINT32 *CmdStatus
409 )
410 {
411 USB_BOT_PROTOCOL *UsbBot;
412 EFI_STATUS Status;
413 UINTN TransLen;
414 UINT8 Result;
415
416 *CmdStatus = USB_MASS_CMD_FAIL;
417 UsbBot = (USB_BOT_PROTOCOL *) Context;
418
419 //
420 // Send the command to the device. Return immediately if device
421 // rejects the command.
422 //
423 Status = UsbBotSendCommand (UsbBot, Cmd, CmdLen, DataDir, DataLen, Lun);
424 if (EFI_ERROR (Status)) {
425 DEBUG ((EFI_D_ERROR, "UsbBotExecCommand: UsbBotSendCommand (%r)\n", Status));
426 return Status;
427 }
428
429 //
430 // Transfer the data. Don't return immediately even data transfer
431 // failed. The host should attempt to receive the CSW no matter
432 // whether it succeeds or fails.
433 //
434 TransLen = (UINTN) DataLen;
435 Status = UsbBotDataTransfer (UsbBot, DataDir, Data, &TransLen, Timeout);
436 if (Status == EFI_DEVICE_ERROR) {
437 DEBUG ((EFI_D_ERROR, "UsbBotExecCommand: UsbBotDataTransfer (%r)\n", Status));
438 return Status;
439 }
440
441 //
442 // Get the status, if that succeeds, interpret the result
443 //
444 Status = UsbBotGetStatus (UsbBot, DataLen, &Result);
445 if (EFI_ERROR (Status)) {
446 DEBUG ((EFI_D_ERROR, "UsbBotExecCommand: UsbBotGetStatus (%r)\n", Status));
447 return Status;
448 }
449
450 if (Result == 0) {
451 *CmdStatus = USB_MASS_CMD_SUCCESS;
452 }
453
454 return EFI_SUCCESS;
455 }
456
457
458 /**
459 Reset the USB mass storage device by BOT protocol.
460
461 @param Context The context of the BOT protocol, that is,
462 USB_BOT_PROTOCOL.
463 @param ExtendedVerification If FALSE, just issue Bulk-Only Mass Storage Reset request.
464 If TRUE, additionally reset parent hub port.
465
466 @retval EFI_SUCCESS The device is reset.
467 @retval Others Failed to reset the device..
468
469 **/
470 EFI_STATUS
471 UsbBotResetDevice (
472 IN VOID *Context,
473 IN BOOLEAN ExtendedVerification
474 )
475 {
476 USB_BOT_PROTOCOL *UsbBot;
477 EFI_USB_DEVICE_REQUEST Request;
478 EFI_STATUS Status;
479 UINT32 Result;
480 UINT32 Timeout;
481
482 UsbBot = (USB_BOT_PROTOCOL *) Context;
483
484 if (ExtendedVerification) {
485 //
486 // If we need to do strictly reset, reset its parent hub port
487 //
488 Status = UsbBot->UsbIo->UsbPortReset (UsbBot->UsbIo);
489 if (EFI_ERROR (Status)) {
490 return EFI_DEVICE_ERROR;
491 }
492 }
493
494 //
495 // Issue a class specific Bulk-Only Mass Storage Reset request,
496 // according to section 3.1 of USB Mass Storage Class Bulk-Only Transport Spec, v1.0.
497 //
498 Request.RequestType = 0x21;
499 Request.Request = USB_BOT_RESET_REQUEST;
500 Request.Value = 0;
501 Request.Index = UsbBot->Interface.InterfaceNumber;
502 Request.Length = 0;
503 Timeout = USB_BOT_RESET_DEVICE_TIMEOUT / USB_MASS_1_MILLISECOND;
504
505 Status = UsbBot->UsbIo->UsbControlTransfer (
506 UsbBot->UsbIo,
507 &Request,
508 EfiUsbNoData,
509 Timeout,
510 NULL,
511 0,
512 &Result
513 );
514
515 if (EFI_ERROR (Status)) {
516 return EFI_DEVICE_ERROR;
517 }
518
519 //
520 // The device shall NAK the host's request until the reset is
521 // complete. We can use this to sync the device and host. For
522 // now just stall 100ms to wait for the device.
523 //
524 gBS->Stall (USB_BOT_RESET_DEVICE_STALL);
525
526 //
527 // Clear the Bulk-In and Bulk-Out stall condition.
528 //
529 UsbClearEndpointStall (UsbBot->UsbIo, UsbBot->BulkInEndpoint->EndpointAddress);
530 UsbClearEndpointStall (UsbBot->UsbIo, UsbBot->BulkOutEndpoint->EndpointAddress);
531
532 return Status;
533 }
534
535
536 /**
537 Get the max LUN (Logical Unit Number) of USB mass storage device.
538
539 @param Context The context of the BOT protocol, that is, USB_BOT_PROTOCOL
540 @param MaxLun Return pointer to the max number of LUN. (e.g. MaxLun=1 means LUN0 and
541 LUN1 in all.)
542
543 @retval EFI_SUCCESS Max LUN is got successfully.
544 @retval Others Fail to execute this request.
545
546 **/
547 EFI_STATUS
548 UsbBotGetMaxLun (
549 IN VOID *Context,
550 OUT UINT8 *MaxLun
551 )
552 {
553 USB_BOT_PROTOCOL *UsbBot;
554 EFI_USB_DEVICE_REQUEST Request;
555 EFI_STATUS Status;
556 UINT32 Result;
557 UINT32 Timeout;
558
559 ASSERT (Context);
560
561 UsbBot = (USB_BOT_PROTOCOL *) Context;
562
563 //
564 // Issue a class specific Bulk-Only Mass Storage get max lun reqest.
565 // according to section 3.2 of USB Mass Storage Class Bulk-Only Transport Spec, v1.0.
566 //
567 Request.RequestType = 0xA1;
568 Request.Request = USB_BOT_GETLUN_REQUEST;
569 Request.Value = 0;
570 Request.Index = UsbBot->Interface.InterfaceNumber;
571 Request.Length = 1;
572 Timeout = USB_BOT_RESET_DEVICE_TIMEOUT / USB_MASS_1_MILLISECOND;
573
574 Status = UsbBot->UsbIo->UsbControlTransfer (
575 UsbBot->UsbIo,
576 &Request,
577 EfiUsbDataIn,
578 Timeout,
579 (VOID *) MaxLun,
580 1,
581 &Result
582 );
583
584 return Status;
585 }
586
587 /**
588 Clean up the resource used by this BOT protocol.
589
590 @param Context The context of the BOT protocol, that is, USB_BOT_PROTOCOL.
591
592 @retval EFI_SUCCESS The resource is cleaned up.
593
594 **/
595 EFI_STATUS
596 UsbBotCleanUp (
597 IN VOID *Context
598 )
599 {
600 FreePool (Context);
601 return EFI_SUCCESS;
602 }
603