]> git.proxmox.com Git - mirror_edk2.git/blob - MdeModulePkg/Bus/Usb/UsbMassStorageDxe/UsbMassBoot.c
980f8b289510854a88f826021d37efd7e98e87cb
[mirror_edk2.git] / MdeModulePkg / Bus / Usb / UsbMassStorageDxe / UsbMassBoot.c
1 /** @file
2
3 Copyright (c) 2007, 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 UsbMassBoot.c
15
16 Abstract:
17
18 This file implement the command set of "USB Mass Storage Specification
19 for Bootability".
20
21 Revision History
22
23
24 **/
25
26 #include "UsbMassImpl.h"
27
28
29 /**
30 Read an UINT32 from the buffer to avoid byte alignment problems, then
31 convert that to the little endia. The USB mass storage bootability spec
32 use big endia
33
34 @param Buf The buffer contains the first byte of the UINT32
35 in big endia.
36
37 @return The UINT32 value read from the buffer in little endia.
38
39 **/
40 STATIC
41 UINT32
42 UsbBootGetUint32 (
43 IN UINT8 *Buf
44 )
45 {
46 UINT32 Value;
47
48 CopyMem (&Value, Buf, sizeof (UINT32));
49 return USB_BOOT_SWAP32 (Value);
50 }
51
52
53 /**
54 Put an UINT32 in little endia to the buffer. The data is converted to
55 big endia before writing.
56
57 @param Buf The buffer to write data to
58 @param Data32 The data to write.
59
60 @return None
61
62 **/
63 STATIC
64 VOID
65 UsbBootPutUint32 (
66 IN UINT8 *Buf,
67 IN UINT32 Data32
68 )
69 {
70 Data32 = USB_BOOT_SWAP32 (Data32);
71 CopyMem (Buf, &Data32, sizeof (UINT32));
72 }
73
74
75 /**
76 Put an UINT16 in little endia to the buffer. The data is converted to
77 big endia before writing.
78
79 @param Buf The buffer to write data to
80 @param Data16 The data to write
81
82 @return None
83
84 **/
85 STATIC
86 VOID
87 UsbBootPutUint16 (
88 IN UINT8 *Buf,
89 IN UINT16 Data16
90 )
91 {
92 Data16 = USB_BOOT_SWAP16 (Data16);
93 CopyMem (Buf, &Data16, sizeof (UINT16));
94 }
95
96
97 /**
98 Request sense information via sending Request Sense
99 Packet Command.
100
101 @param UsbMass The device to be requested sense data
102
103 @retval EFI_DEVICE_ERROR Hardware error
104 @retval EFI_SUCCESS Success
105
106 **/
107 EFI_STATUS
108 UsbBootRequestSense (
109 IN USB_MASS_DEVICE *UsbMass
110 )
111 {
112 USB_BOOT_REQUEST_SENSE_CMD SenseCmd;
113 USB_BOOT_REQUEST_SENSE_DATA SenseData;
114 EFI_BLOCK_IO_MEDIA *Media;
115 USB_MASS_TRANSPORT *Transport;
116 EFI_STATUS Status;
117 UINT32 CmdResult;
118
119 Transport = UsbMass->Transport;
120
121 //
122 // Request the sense data from the device if command failed
123 //
124 ZeroMem (&SenseCmd, sizeof (USB_BOOT_REQUEST_SENSE_CMD));
125 ZeroMem (&SenseData, sizeof (USB_BOOT_REQUEST_SENSE_DATA));
126
127 SenseCmd.OpCode = USB_BOOT_REQUEST_SENSE_OPCODE;
128 SenseCmd.Lun = USB_BOOT_LUN (UsbMass->Lun);
129 SenseCmd.AllocLen = sizeof (USB_BOOT_REQUEST_SENSE_DATA);
130
131 Status = Transport->ExecCommand (
132 UsbMass->Context,
133 &SenseCmd,
134 sizeof (USB_BOOT_REQUEST_SENSE_CMD),
135 EfiUsbDataIn,
136 &SenseData,
137 sizeof (USB_BOOT_REQUEST_SENSE_DATA),
138 USB_BOOT_GENERAL_CMD_TIMEOUT,
139 &CmdResult
140 );
141 if (EFI_ERROR (Status) || CmdResult != USB_MASS_CMD_SUCCESS) {
142 DEBUG ((mUsbMscError, "UsbBootRequestSense: (%r) CmdResult=0x%x\n", Status, CmdResult));
143 return EFI_DEVICE_ERROR;
144 }
145
146 //
147 // Interpret the sense data and update the media status if necessary.
148 //
149 Media = &UsbMass->BlockIoMedia;
150
151 switch (USB_BOOT_SENSE_KEY (SenseData.SenseKey)) {
152
153 case USB_BOOT_SENSE_NO_SENSE:
154 case USB_BOOT_SENSE_RECOVERED:
155 //
156 // Suppose hardware can handle this case, and recover later by itself
157 //
158 Status = EFI_NOT_READY;
159 break;
160
161 case USB_BOOT_SENSE_NOT_READY:
162 switch (SenseData.ASC) {
163 case USB_BOOT_ASC_NO_MEDIA:
164 Status = EFI_NO_MEDIA;
165 Media->MediaPresent = FALSE;
166 break;
167
168 case USB_BOOT_ASC_MEDIA_UPSIDE_DOWN:
169 Status = EFI_DEVICE_ERROR;
170 Media->MediaPresent = FALSE;
171 break;
172
173 case USB_BOOT_ASC_NOT_READY:
174 if (SenseData.ASCQ == USB_BOOT_ASCQ_IN_PROGRESS ||
175 SenseData.ASCQ == USB_BOOT_ASCQ_DEVICE_BUSY) {
176 //
177 // Regular timeout, and need retry once more
178 //
179 DEBUG ((mUsbMscInfo, "UsbBootRequestSense: Not ready and need retry once more\n"));
180 Status = EFI_NOT_READY;
181 }
182 }
183 break;
184
185 case USB_BOOT_SENSE_ILLEGAL_REQUEST:
186 Status = EFI_INVALID_PARAMETER;
187 break;
188
189 case USB_BOOT_SENSE_UNIT_ATTENTION:
190 Status = EFI_DEVICE_ERROR;
191 if (SenseData.ASC == USB_BOOT_ASC_MEDIA_CHANGE) {
192 Status = EFI_MEDIA_CHANGED;
193 UsbMass->BlockIoMedia.MediaId++;
194 }
195 break;
196
197 case USB_BOOT_SNESE_DATA_PROTECT:
198 Status = EFI_WRITE_PROTECTED;
199 UsbMass->BlockIoMedia.ReadOnly = TRUE;
200 break;
201
202 default:
203 Status = EFI_DEVICE_ERROR;
204 break;
205 }
206
207 DEBUG ((mUsbMscInfo, "UsbBootRequestSense: (%r) with sense key %x/%x/%x\n",
208 Status,
209 USB_BOOT_SENSE_KEY (SenseData.SenseKey),
210 SenseData.ASC,
211 SenseData.ASCQ
212 ));
213
214 return Status;
215 }
216
217
218 /**
219 Execute the USB mass storage bootability commands. If execution
220 failed, retrieve the error by REQUEST_SENSE then update the device's
221 status, such as ReadyOnly.
222
223 @param UsbMass The device to issue commands to
224 @param Cmd The command to execute
225 @param CmdLen The length of the command
226 @param DataDir The direction of data transfer
227 @param Data The buffer to hold the data
228 @param DataLen The length of expected data
229 @param Timeout The timeout used to transfer
230
231 @retval EFI_SUCCESS The command is excuted OK
232 @retval EFI_DEVICE_ERROR Failed to request sense
233 @retval EFI_INVALID_PARAMETER The command has some invalid parameters
234 @retval EFI_WRITE_PROTECTED The device is write protected
235 @retval EFI_MEDIA_CHANGED The device media has been changed
236
237 **/
238 STATIC
239 EFI_STATUS
240 UsbBootExecCmd (
241 IN USB_MASS_DEVICE *UsbMass,
242 IN VOID *Cmd,
243 IN UINT8 CmdLen,
244 IN EFI_USB_DATA_DIRECTION DataDir,
245 IN VOID *Data,
246 IN UINT32 DataLen,
247 IN UINT32 Timeout
248 )
249 {
250 USB_MASS_TRANSPORT *Transport;
251 EFI_STATUS Status;
252 UINT32 CmdResult;
253
254 Transport = UsbMass->Transport;
255 Status = Transport->ExecCommand (
256 UsbMass->Context,
257 Cmd,
258 CmdLen,
259 DataDir,
260 Data,
261 DataLen,
262 Timeout,
263 &CmdResult
264 );
265 //
266 // ExecCommand return success and get the right CmdResult means
267 // the commnad transfer is OK.
268 //
269 if ((CmdResult == USB_MASS_CMD_SUCCESS) && !EFI_ERROR(Status)) {
270 return EFI_SUCCESS;
271 }
272
273 return UsbBootRequestSense (UsbMass);
274 }
275
276
277 /**
278 Execute the USB mass storage bootability commands. If execution
279 failed, retrieve the error by REQUEST_SENSE then update the device's
280 status, such as ReadyOnly.
281
282 @param UsbMass The device to issue commands to
283 @param Cmd The command to execute
284 @param CmdLen The length of the command
285 @param DataDir The direction of data transfer
286 @param Data The buffer to hold the data
287 @param DataLen The length of expected data
288
289 @retval EFI_SUCCESS The command is excuted OK
290 @retval EFI_DEVICE_ERROR Failed to request sense
291 @retval EFI_INVALID_PARAMETER The command has some invalid parameters
292 @retval EFI_WRITE_PROTECTED The device is write protected
293 @retval EFI_MEDIA_CHANGED The device media has been changed
294
295 **/
296 STATIC
297 EFI_STATUS
298 UsbBootExecCmdWithRetry (
299 IN USB_MASS_DEVICE *UsbMass,
300 IN VOID *Cmd,
301 IN UINT8 CmdLen,
302 IN EFI_USB_DATA_DIRECTION DataDir,
303 IN VOID *Data,
304 IN UINT32 DataLen,
305 IN UINT32 Timeout
306 )
307 {
308 EFI_STATUS Status;
309 INT16 Index;
310
311 //
312 // If the device isn't ready, wait some time. If the device is ready,
313 // retry the command again.
314 //
315 Status = EFI_SUCCESS;
316
317 for (Index = 0; Index < USB_BOOT_COMMAND_RETRY; Index++) {
318 //
319 // Execute the command with an increasingly larger timeout value.
320 //
321 Status = UsbBootExecCmd (
322 UsbMass,
323 Cmd,
324 CmdLen,
325 DataDir,
326 Data,
327 DataLen,
328 Timeout * (Index + 1)
329 );
330 if (Status == EFI_SUCCESS ||
331 Status == EFI_MEDIA_CHANGED) {
332 break;
333 }
334 //
335 // Need retry once more, so reset index
336 //
337 if (Status == EFI_NOT_READY) {
338 Index = 0;
339 }
340 }
341
342 return Status;
343 }
344
345
346
347 /**
348 Use the TEST UNIT READY command to check whether it is ready.
349 If it is ready, update the parameters.
350
351 @param UsbMass The device to test
352
353 @retval EFI_SUCCESS The device is ready and parameters are updated.
354 @retval Others Device not ready.
355
356 **/
357 EFI_STATUS
358 UsbBootIsUnitReady (
359 IN USB_MASS_DEVICE *UsbMass
360 )
361 {
362 USB_BOOT_TEST_UNIT_READY_CMD TestCmd;
363
364 ZeroMem (&TestCmd, sizeof (USB_BOOT_TEST_UNIT_READY_CMD));
365
366 TestCmd.OpCode = USB_BOOT_TEST_UNIT_READY_OPCODE;
367 TestCmd.Lun = USB_BOOT_LUN (UsbMass->Lun);
368
369 return UsbBootExecCmdWithRetry (
370 UsbMass,
371 &TestCmd,
372 sizeof (USB_BOOT_TEST_UNIT_READY_CMD),
373 EfiUsbNoData,
374 NULL,
375 0,
376 USB_BOOT_GENERAL_CMD_TIMEOUT
377 );
378 }
379
380
381 /**
382 Inquiry Command requests that information regrarding parameters of
383 the Device be sent to the Host.
384
385 @param UsbMass The device to inquiry.
386
387 @retval EFI_SUCCESS The device is ready and parameters are updated.
388 @retval Others Device not ready.
389
390 **/
391 EFI_STATUS
392 UsbBootInquiry (
393 IN USB_MASS_DEVICE *UsbMass
394 )
395 {
396 USB_BOOT_INQUIRY_CMD InquiryCmd;
397 USB_BOOT_INQUIRY_DATA InquiryData;
398 EFI_BLOCK_IO_MEDIA *Media;
399 EFI_STATUS Status;
400
401 Media = &(UsbMass->BlockIoMedia);
402
403 //
404 // Use the Inquiry command to get the RemovableMedia setting.
405 //
406 ZeroMem (&InquiryCmd, sizeof (USB_BOOT_INQUIRY_CMD));
407 ZeroMem (&InquiryData, sizeof (USB_BOOT_INQUIRY_DATA));
408
409 InquiryCmd.OpCode = USB_BOOT_INQUIRY_OPCODE;
410 InquiryCmd.Lun = USB_BOOT_LUN (UsbMass->Lun);
411 InquiryCmd.AllocLen = sizeof (InquiryData);
412
413 Status = UsbBootExecCmdWithRetry (
414 UsbMass,
415 &InquiryCmd,
416 sizeof (USB_BOOT_INQUIRY_CMD),
417 EfiUsbDataIn,
418 &InquiryData,
419 sizeof (USB_BOOT_INQUIRY_DATA),
420 USB_BOOT_INQUIRY_CMD_TIMEOUT
421 );
422 if (EFI_ERROR (Status)) {
423 return Status;
424 }
425
426 UsbMass->Pdt = USB_BOOT_PDT (InquiryData.Pdt);
427 Media->RemovableMedia = USB_BOOT_REMOVABLE (InquiryData.Removable);
428 //
429 // Default value 512 Bytes, in case no media present at first time
430 //
431 Media->BlockSize = 0x0200;
432
433 return Status;
434 }
435
436
437 /**
438 Get the capacity of the USB mass storage media, including
439 the presentation, block size, and last block number. This
440 function is used to get the disk parameters at the start if
441 it is a non-removable media or to detect the media if it is
442 removable.
443
444 @param UsbMass The device to retireve disk gemotric.
445
446 @retval EFI_SUCCESS The disk gemotric is successfully retrieved.
447 @retval EFI_DEVICE_ERROR Something is inconsistent with the disk gemotric.
448
449 **/
450 EFI_STATUS
451 UsbBootReadCapacity (
452 IN USB_MASS_DEVICE *UsbMass
453 )
454 {
455 USB_BOOT_READ_CAPACITY_CMD CapacityCmd;
456 USB_BOOT_READ_CAPACITY_DATA CapacityData;
457 EFI_BLOCK_IO_MEDIA *Media;
458 EFI_STATUS Status;
459
460 Media = &UsbMass->BlockIoMedia;
461
462 //
463 // Use the READ CAPACITY command to get the block length and last blockno
464 //
465 ZeroMem (&CapacityCmd, sizeof (USB_BOOT_READ_CAPACITY_CMD));
466 ZeroMem (&CapacityData, sizeof (USB_BOOT_READ_CAPACITY_DATA));
467
468 CapacityCmd.OpCode = USB_BOOT_READ_CAPACITY_OPCODE;
469 CapacityCmd.Lun = USB_BOOT_LUN (UsbMass->Lun);
470
471 Status = UsbBootExecCmdWithRetry (
472 UsbMass,
473 &CapacityCmd,
474 sizeof (USB_BOOT_READ_CAPACITY_CMD),
475 EfiUsbDataIn,
476 &CapacityData,
477 sizeof (USB_BOOT_READ_CAPACITY_DATA),
478 USB_BOOT_GENERAL_CMD_TIMEOUT
479 );
480 if (EFI_ERROR (Status)) {
481 return Status;
482 }
483
484 Media->MediaPresent = TRUE;
485 Media->LastBlock = UsbBootGetUint32 (CapacityData.LastLba);
486 Media->BlockSize = UsbBootGetUint32 (CapacityData.BlockLen);
487
488 DEBUG ((mUsbMscInfo, "UsbBootReadCapacity Success LBA=%d BlockSize=%d\n",
489 Media->LastBlock, Media->BlockSize));
490
491 return EFI_SUCCESS;
492 }
493
494
495 /**
496 Retrieves mode sense information via sending Mode Sense
497 Packet Command.
498
499 @param UsbMass The USB_FLOPPY_DEV instance.
500
501 @retval EFI_DEVICE_ERROR Hardware error
502 @retval EFI_SUCCESS Success
503
504 **/
505 EFI_STATUS
506 UsbBootModeSense (
507 IN USB_MASS_DEVICE *UsbMass
508 )
509 {
510 EFI_STATUS Status;
511 USB_BOOT_MODE_SENSE_CMD ModeSenseCmd;
512 USB_BOOT_MODE_PARA_HEADER ModeParaHeader;
513 UINT8 CommandSet;
514
515 ZeroMem (&ModeSenseCmd, sizeof (USB_BOOT_MODE_SENSE_CMD));
516 ZeroMem (&ModeParaHeader, sizeof (USB_BOOT_MODE_PARA_HEADER));
517
518 //
519 // overuse Context Pointer, the first field of Bot or Cbi is EFI_USB_INTERFACE_DESCRIPTOR
520 //
521 CommandSet = ((EFI_USB_INTERFACE_DESCRIPTOR *) (UsbMass->Context))->InterfaceSubClass;
522
523 if (CommandSet == USB_MASS_STORE_SCSI) {
524 //
525 // Not UFI Command Set, no ModeSense Command
526 //
527 return EFI_SUCCESS;
528 }
529
530 ModeSenseCmd.OpCode = USB_BOOT_MODE_SENSE10_OPCODE;
531 ModeSenseCmd.PageCode = 0x3f;
532 ModeSenseCmd.ParaListLenLsb = (UINT8) sizeof (USB_BOOT_MODE_PARA_HEADER);
533
534 Status = UsbBootExecCmdWithRetry (
535 UsbMass,
536 &ModeSenseCmd,
537 sizeof (USB_BOOT_MODE_SENSE_CMD),
538 EfiUsbDataIn,
539 &ModeParaHeader,
540 sizeof (USB_BOOT_MODE_PARA_HEADER),
541 USB_BOOT_GENERAL_CMD_TIMEOUT
542 );
543 //
544 // Did nothing with the Header here
545 // But probably should
546 //
547
548 return Status;
549 }
550
551
552 /**
553 Get the parameters for the USB mass storage media, including
554 the RemovableMedia, block size, and last block number. This
555 function is used both to initialize the media during the
556 DriverBindingStart and to re-initialize it when the media is
557 changed. Althought the RemoveableMedia is unlikely to change,
558 I include it here.
559
560 @param UsbMass The device to retireve disk gemotric.
561
562 @retval EFI_SUCCESS The disk gemotric is successfully retrieved.
563 @retval EFI_DEVICE_ERROR Something is inconsistent with the disk gemotric.
564
565 **/
566 EFI_STATUS
567 UsbBootGetParams (
568 IN USB_MASS_DEVICE *UsbMass
569 )
570 {
571 EFI_BLOCK_IO_MEDIA *Media;
572 EFI_STATUS Status;
573
574 Status = UsbBootInquiry (UsbMass);
575 if (EFI_ERROR (Status)) {
576 DEBUG ((mUsbMscError, "UsbBootGetParams: UsbBootInquiry (%r)\n", Status));
577 return Status;
578 }
579
580 Media = &(UsbMass->BlockIoMedia);
581 //
582 // Don't use the Removable bit in inquirydata to test whether the media
583 // is removable because many flash disks wrongly set this bit.
584 //
585 if ((UsbMass->Pdt == USB_PDT_CDROM) || (UsbMass->Pdt == USB_PDT_OPTICAL)) {
586 //
587 // CD-Rom or Optical device
588 //
589 UsbMass->OpticalStorage = TRUE;
590 //
591 // Default value 2048 Bytes, in case no media present at first time
592 //
593 Media->BlockSize = 0x0800;
594 } else {
595 //
596 // Non CD-Rom device need ModeSenseCmd between InquiryCmd and ReadCapacityCmd
597 //
598 Status = UsbBootModeSense (UsbMass);
599 if (EFI_ERROR (Status)) {
600 DEBUG ((mUsbMscError, "UsbBootGetParams: UsbBootModeSense (%r)\n", Status));
601 return Status;
602 }
603 }
604
605 return UsbBootReadCapacity (UsbMass);
606 }
607
608
609 /**
610 Detect whether the removable media is present and whether it has changed.
611 The Non-removable media doesn't need it.
612
613 @param UsbMass The device to retireve disk gemotric.
614
615 @retval EFI_SUCCESS The disk gemotric is successfully retrieved.
616 @retval EFI_DEVICE_ERROR Something is inconsistent with the disk gemotric.
617
618 **/
619 EFI_STATUS
620 UsbBootDetectMedia (
621 IN USB_MASS_DEVICE *UsbMass
622 )
623 {
624 EFI_BLOCK_IO_MEDIA OldMedia;
625 EFI_BLOCK_IO_MEDIA *Media;
626 EFI_STATUS Status;
627
628 Media = &UsbMass->BlockIoMedia;
629 OldMedia = UsbMass->BlockIoMedia;
630
631 //
632 // First test whether the device is ready and get status
633 // If media changed or ready, need read the device's capacity
634 //
635 Status = UsbBootIsUnitReady (UsbMass);
636 if ((Status == EFI_SUCCESS && Media->MediaPresent) ||
637 (Status == EFI_MEDIA_CHANGED)) {
638 if ((UsbMass->Pdt != USB_PDT_CDROM) &&
639 (UsbMass->Pdt != USB_PDT_OPTICAL)) {
640 //
641 // Non CD-Rom device need ModeSenseCmd between InquiryCmd and ReadCapacityCmd
642 //
643 UsbBootModeSense (UsbMass);
644 }
645 DEBUG ((mUsbMscInfo, "UsbBootDetectMedia: Need Read Capacity\n"));
646 Status = UsbBootReadCapacity (UsbMass);
647 }
648 if (EFI_ERROR (Status)) {
649 return Status;
650 }
651
652 //
653 // Detect whether it is necessary to reinstall the BlockIO
654 //
655 if ((Media->MediaId != OldMedia.MediaId) ||
656 (Media->MediaPresent != OldMedia.MediaPresent) ||
657 (Media->ReadOnly != OldMedia.ReadOnly) ||
658 (Media->BlockSize != OldMedia.BlockSize) ||
659 (Media->LastBlock != OldMedia.LastBlock)) {
660 DEBUG ((mUsbMscInfo, "UsbBootDetectMedia: Need reinstall BlockIoProtocol\n"));
661 Media->MediaId++;
662 gBS->ReinstallProtocolInterface (
663 UsbMass->Controller,
664 &gEfiBlockIoProtocolGuid,
665 &UsbMass->BlockIo,
666 &UsbMass->BlockIo
667 );
668 //
669 // Check whether media present or media changed or write protected
670 //
671 if (Media->MediaPresent == FALSE) {
672 Status = EFI_NO_MEDIA;
673 }
674 if (Media->MediaId != OldMedia.MediaId) {
675 Status = EFI_MEDIA_CHANGED;
676 }
677 if (Media->ReadOnly != OldMedia.ReadOnly) {
678 Status = EFI_WRITE_PROTECTED;
679 }
680 }
681
682 return Status;
683 }
684
685
686 /**
687 Read some blocks from the device.
688
689 @param UsbMass The USB mass storage device to read from
690 @param Lba The start block number
691 @param TotalBlock Total block number to read
692 @param Buffer The buffer to read to
693
694 @retval EFI_SUCCESS Data are read into the buffer
695 @retval Others Failed to read all the data
696
697 **/
698 EFI_STATUS
699 UsbBootReadBlocks (
700 IN USB_MASS_DEVICE *UsbMass,
701 IN UINT32 Lba,
702 IN UINTN TotalBlock,
703 OUT UINT8 *Buffer
704 )
705 {
706 USB_BOOT_READ10_CMD ReadCmd;
707 EFI_STATUS Status;
708 UINT16 Count;
709 UINT32 BlockSize;
710 UINT32 ByteSize;
711 UINT32 Timeout;
712
713 BlockSize = UsbMass->BlockIoMedia.BlockSize;
714 Status = EFI_SUCCESS;
715
716 while (TotalBlock > 0) {
717 //
718 // Split the total blocks into smaller pieces to ease the pressure
719 // on the device. We must split the total block because the READ10
720 // command only has 16 bit transfer length (in the unit of block).
721 //
722 Count = (UINT16)((TotalBlock < USB_BOOT_IO_BLOCKS) ? TotalBlock : USB_BOOT_IO_BLOCKS);
723 ByteSize = (UINT32)Count * BlockSize;
724
725 //
726 // Optical device need longer timeout than other device
727 //
728 if (UsbMass->OpticalStorage == TRUE) {
729 Timeout = (UINT32)Count * USB_BOOT_OPTICAL_BLOCK_TIMEOUT;
730 } else {
731 Timeout = (UINT32)Count * USB_BOOT_GENERAL_BLOCK_TIMEOUT;
732 }
733
734 //
735 // Fill in the command then execute
736 //
737 ZeroMem (&ReadCmd, sizeof (USB_BOOT_READ10_CMD));
738
739 ReadCmd.OpCode = USB_BOOT_READ10_OPCODE;
740 ReadCmd.Lun = USB_BOOT_LUN (UsbMass->Lun);
741 UsbBootPutUint32 (ReadCmd.Lba, Lba);
742 UsbBootPutUint16 (ReadCmd.TransferLen, Count);
743
744 Status = UsbBootExecCmdWithRetry (
745 UsbMass,
746 &ReadCmd,
747 sizeof (USB_BOOT_READ10_CMD),
748 EfiUsbDataIn,
749 Buffer,
750 ByteSize,
751 Timeout
752 );
753 if (EFI_ERROR (Status)) {
754 return Status;
755 }
756
757 Lba += Count;
758 Buffer += Count * BlockSize;
759 TotalBlock -= Count;
760 }
761
762 return Status;
763 }
764
765
766 /**
767 Write some blocks to the device.
768
769 @param UsbMass The USB mass storage device to write to
770 @param Lba The start block number
771 @param TotalBlock Total block number to write
772 @param Buffer The buffer to write to
773
774 @retval EFI_SUCCESS Data are written into the buffer
775 @retval Others Failed to write all the data
776
777 **/
778 EFI_STATUS
779 UsbBootWriteBlocks (
780 IN USB_MASS_DEVICE *UsbMass,
781 IN UINT32 Lba,
782 IN UINTN TotalBlock,
783 OUT UINT8 *Buffer
784 )
785 {
786 USB_BOOT_WRITE10_CMD WriteCmd;
787 EFI_STATUS Status;
788 UINT16 Count;
789 UINT32 BlockSize;
790 UINT32 ByteSize;
791 UINT32 Timeout;
792
793 BlockSize = UsbMass->BlockIoMedia.BlockSize;
794 Status = EFI_SUCCESS;
795
796 while (TotalBlock > 0) {
797 //
798 // Split the total blocks into smaller pieces to ease the pressure
799 // on the device. We must split the total block because the WRITE10
800 // command only has 16 bit transfer length (in the unit of block).
801 //
802 Count = (UINT16)((TotalBlock < USB_BOOT_IO_BLOCKS) ? TotalBlock : USB_BOOT_IO_BLOCKS);
803 ByteSize = (UINT32)Count * BlockSize;
804
805 //
806 // Optical device need longer timeout than other device
807 //
808 if (UsbMass->OpticalStorage == TRUE) {
809 Timeout = (UINT32)Count * USB_BOOT_OPTICAL_BLOCK_TIMEOUT;
810 } else {
811 Timeout = (UINT32)Count * USB_BOOT_GENERAL_BLOCK_TIMEOUT;
812 }
813
814 //
815 // Fill in the write10 command block
816 //
817 ZeroMem (&WriteCmd, sizeof (USB_BOOT_WRITE10_CMD));
818
819 WriteCmd.OpCode = USB_BOOT_WRITE10_OPCODE;
820 WriteCmd.Lun = USB_BOOT_LUN (UsbMass->Lun);
821 UsbBootPutUint32 (WriteCmd.Lba, Lba);
822 UsbBootPutUint16 (WriteCmd.TransferLen, Count);
823
824 Status = UsbBootExecCmdWithRetry (
825 UsbMass,
826 &WriteCmd,
827 sizeof (USB_BOOT_WRITE10_CMD),
828 EfiUsbDataOut,
829 Buffer,
830 ByteSize,
831 Timeout
832 );
833 if (EFI_ERROR (Status)) {
834 return Status;
835 }
836
837 Lba += Count;
838 Buffer += Count * BlockSize;
839 TotalBlock -= Count;
840 }
841
842 return Status;
843 }
844
845
846 /**
847 Use the USB clear feature control transfer to clear the endpoint
848 stall condition.
849
850 @param UsbIo The USB IO protocol to use
851 @param EndpointAddr The endpoint to clear stall for
852
853 @retval EFI_SUCCESS The endpoint stall condtion is clear
854 @retval Others Failed to clear the endpoint stall condtion
855
856 **/
857 EFI_STATUS
858 UsbClearEndpointStall (
859 IN EFI_USB_IO_PROTOCOL *UsbIo,
860 IN UINT8 EndpointAddr
861 )
862 {
863 EFI_USB_DEVICE_REQUEST Request;
864 EFI_STATUS Status;
865 UINT32 CmdResult;
866 UINT32 Timeout;
867
868 Request.RequestType = 0x02;
869 Request.Request = USB_REQ_CLEAR_FEATURE;
870 Request.Value = USB_FEATURE_ENDPOINT_HALT;
871 Request.Index = EndpointAddr;
872 Request.Length = 0;
873 Timeout = USB_BOOT_GENERAL_CMD_TIMEOUT / USB_MASS_STALL_1_MS;
874
875 Status = UsbIo->UsbControlTransfer (
876 UsbIo,
877 &Request,
878 EfiUsbNoData,
879 Timeout,
880 NULL,
881 0,
882 &CmdResult
883 );
884
885 return Status;
886 }