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