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