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