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