]> git.proxmox.com Git - mirror_edk2.git/blob - MdeModulePkg/Bus/Usb/UsbMassStorageDxe/UsbMassBoot.c
1. Added EFI_MEDIA_CHANGED and EFI_INVALID_PARAMETER returns in UsbMassReadBlocks().
[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 ((EFI_D_ERROR, "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 ((EFI_D_INFO, "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 ((EFI_D_INFO, "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
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 ((EFI_D_INFO, "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 Media = &UsbMass->BlockIoMedia;
544
545 ZeroMem (&ModeSenseCmd, sizeof (USB_SCSI_MODE_SENSE6_CMD));
546 ZeroMem (&ModeParaHeader, sizeof (USB_SCSI_MODE_SENSE6_PARA_HEADER));
547
548 //
549 // ModeSense6 command is defined in [SCSI2Spec-Page151]
550 //
551 ModeSenseCmd.OpCode = USB_SCSI_MODE_SENSE6_OPCODE;
552 ModeSenseCmd.Lun = (UINT8) USB_BOOT_LUN (UsbMass->Lun);
553 ModeSenseCmd.PageCode = 0x3F;
554 ModeSenseCmd.AllocateLen = (UINT8) sizeof (USB_SCSI_MODE_SENSE6_PARA_HEADER);
555
556 Status = UsbBootExecCmdWithRetry (
557 UsbMass,
558 &ModeSenseCmd,
559 sizeof (USB_SCSI_MODE_SENSE6_CMD),
560 EfiUsbDataIn,
561 &ModeParaHeader,
562 sizeof (USB_SCSI_MODE_SENSE6_PARA_HEADER),
563 USB_BOOT_GENERAL_CMD_TIMEOUT
564 );
565
566 //
567 // ModeSense(6) is used to get the information of WriteProtected. While only some of
568 // devices support this command, so have a try here.
569 //
570 if (!EFI_ERROR (Status)) {
571 Media->ReadOnly = (BOOLEAN) ((ModeParaHeader.DevicePara & 0x80) ? TRUE : FALSE);
572 }
573
574 return Status;
575 }
576
577
578 /**
579 Get the parameters for the USB mass storage media, including
580 the RemovableMedia, block size, and last block number. This
581 function is used both to initialize the media during the
582 DriverBindingStart and to re-initialize it when the media is
583 changed. Althought the RemoveableMedia is unlikely to change,
584 I include it here.
585
586 @param UsbMass The device to retireve disk gemotric.
587
588 @retval EFI_SUCCESS The disk gemotric is successfully retrieved.
589 @retval EFI_DEVICE_ERROR Something is inconsistent with the disk gemotric.
590
591 **/
592 EFI_STATUS
593 UsbBootGetParams (
594 IN USB_MASS_DEVICE *UsbMass
595 )
596 {
597 EFI_BLOCK_IO_MEDIA *Media;
598 EFI_STATUS Status;
599 UINT8 CmdSet;
600
601 Media = &(UsbMass->BlockIoMedia);
602 CmdSet = ((EFI_USB_INTERFACE_DESCRIPTOR *) (UsbMass->Context))->InterfaceSubClass;
603
604 Status = UsbBootInquiry (UsbMass);
605 if (EFI_ERROR (Status)) {
606 DEBUG ((EFI_D_ERROR, "UsbBootGetParams: UsbBootInquiry (%r)\n", Status));
607 return Status;
608 }
609
610 //
611 // Don't use the Removable bit in inquirydata to test whether the media
612 // is removable because many flash disks wrongly set this bit.
613 //
614 if ((UsbMass->Pdt == USB_PDT_CDROM) || (UsbMass->Pdt == USB_PDT_OPTICAL)) {
615 //
616 // CD-Rom device and Non-CD optical device
617 //
618 UsbMass->OpticalStorage = TRUE;
619 //
620 // Default value 2048 Bytes, in case no media present at first time
621 //
622 Media->BlockSize = 0x0800;
623 }
624
625 if ((UsbMass->Pdt != USB_PDT_CDROM) && (CmdSet == USB_MASS_STORE_SCSI)) {
626 //
627 // ModeSense is required for the device with PDT of 0x00/0x07/0x0E,
628 // which is from [MassStorageBootabilitySpec-Page7].
629 // ModeSense(10) is useless here, while ModeSense(6) defined in SCSI
630 // could get the information of WriteProtected.
631 // Since not all device support this command, so skip if fail.
632 //
633 UsbScsiModeSense (UsbMass);
634 }
635
636 return UsbBootReadCapacity (UsbMass);
637 }
638
639
640 /**
641 Detect whether the removable media is present and whether it has changed.
642 The Non-removable media doesn't need it.
643
644 @param UsbMass The device to retireve disk gemotric.
645
646 @retval EFI_SUCCESS The disk gemotric is successfully retrieved.
647 @retval EFI_DEVICE_ERROR Something is inconsistent with the disk gemotric.
648
649 **/
650 EFI_STATUS
651 UsbBootDetectMedia (
652 IN USB_MASS_DEVICE *UsbMass
653 )
654 {
655 EFI_BLOCK_IO_MEDIA OldMedia;
656 EFI_BLOCK_IO_MEDIA *Media;
657 UINT8 CmdSet;
658 EFI_TPL OldTpl;
659 EFI_STATUS Status;
660
661 Media = &UsbMass->BlockIoMedia;
662
663 CopyMem (
664 &OldMedia,
665 &(UsbMass->BlockIoMedia),
666 sizeof (EFI_BLOCK_IO_MEDIA)
667 );
668
669 CmdSet = ((EFI_USB_INTERFACE_DESCRIPTOR *) (UsbMass->Context))->InterfaceSubClass;
670
671 Status = UsbBootIsUnitReady (UsbMass);
672 if (EFI_ERROR (Status)) {
673 DEBUG ((EFI_D_ERROR, "UsbBootDetectMedia: UsbBootIsUnitReady (%r)\n", Status));
674 goto ON_ERROR;
675 }
676
677 if ((UsbMass->Pdt != USB_PDT_CDROM) && (CmdSet == USB_MASS_STORE_SCSI)) {
678 //
679 // ModeSense is required for the device with PDT of 0x00/0x07/0x0E,
680 // which is from [MassStorageBootabilitySpec-Page7].
681 // ModeSense(10) is useless here, while ModeSense(6) defined in SCSI
682 // could get the information of WriteProtected.
683 // Since not all device support this command, so skip if fail.
684 //
685 UsbScsiModeSense (UsbMass);
686 }
687
688 Status = UsbBootReadCapacity (UsbMass);
689 if (EFI_ERROR (Status)) {
690 DEBUG ((EFI_D_ERROR, "UsbBootDetectMedia: UsbBootReadCapacity (%r)\n", Status));
691 goto ON_ERROR;
692 }
693
694 return EFI_SUCCESS;
695
696 ON_ERROR:
697 //
698 // Detect whether it is necessary to reinstall the BlockIO
699 //
700 // MediaId may change in RequestSense for MediaChanged
701 // MediaPresent may change in RequestSense for NoMedia
702 // MediaReadOnly may change in RequestSense for WriteProtected or MediaChanged
703 // MediaPresent/BlockSize/LastBlock may change in ReadCapacity
704 //
705 if ((Media->MediaId != OldMedia.MediaId) ||
706 (Media->MediaPresent != OldMedia.MediaPresent) ||
707 (Media->ReadOnly != OldMedia.ReadOnly) ||
708 (Media->BlockSize != OldMedia.BlockSize) ||
709 (Media->LastBlock != OldMedia.LastBlock)) {
710
711 OldTpl = UsbGetCurrentTpl ();
712 DEBUG ((EFI_D_ERROR, "UsbBootDetectMedia: TPL before reinstall BlockIoProtocol is %d\n", OldTpl));
713
714 gBS->RestoreTPL (TPL_CALLBACK);
715
716 gBS->ReinstallProtocolInterface (
717 UsbMass->Controller,
718 &gEfiBlockIoProtocolGuid,
719 &UsbMass->BlockIo,
720 &UsbMass->BlockIo
721 );
722
723 DEBUG ((EFI_D_ERROR, "UsbBootDetectMedia: TPL after reinstall is %d\n", UsbGetCurrentTpl()));
724 ASSERT (UsbGetCurrentTpl () == TPL_CALLBACK);
725
726 gBS->RaiseTPL (OldTpl);
727
728 //
729 // Update MediaId after reinstall BLOCK_IO_PROTOCOL
730 //
731 if (Media->MediaPresent != OldMedia.MediaPresent) {
732 if (Media->MediaPresent == TRUE) {
733 Media->MediaId = 1;
734 } else {
735 Media->MediaId = 0;
736 }
737 }
738
739 if ((Media->ReadOnly != OldMedia.ReadOnly) ||
740 (Media->BlockSize != OldMedia.BlockSize) ||
741 (Media->LastBlock != OldMedia.LastBlock)) {
742 Media->MediaId++;
743 }
744 }
745
746 return Status;
747 }
748
749
750 /**
751 Read some blocks from the device.
752
753 @param UsbMass The USB mass storage device to read from
754 @param Lba The start block number
755 @param TotalBlock Total block number to read
756 @param Buffer The buffer to read to
757
758 @retval EFI_SUCCESS Data are read into the buffer
759 @retval Others Failed to read all the data
760
761 **/
762 EFI_STATUS
763 UsbBootReadBlocks (
764 IN USB_MASS_DEVICE *UsbMass,
765 IN UINT32 Lba,
766 IN UINTN TotalBlock,
767 OUT UINT8 *Buffer
768 )
769 {
770 USB_BOOT_READ10_CMD ReadCmd;
771 EFI_STATUS Status;
772 UINT16 Count;
773 UINT32 BlockSize;
774 UINT32 ByteSize;
775 UINT32 Timeout;
776
777 BlockSize = UsbMass->BlockIoMedia.BlockSize;
778 Status = EFI_SUCCESS;
779
780 while (TotalBlock > 0) {
781 //
782 // Split the total blocks into smaller pieces to ease the pressure
783 // on the device. We must split the total block because the READ10
784 // command only has 16 bit transfer length (in the unit of block).
785 //
786 Count = (UINT16)((TotalBlock < USB_BOOT_IO_BLOCKS) ? TotalBlock : USB_BOOT_IO_BLOCKS);
787 ByteSize = (UINT32)Count * BlockSize;
788
789 //
790 // USB command's upper limit timeout is 5s. [USB2.0-9.2.6.1]
791 //
792 Timeout = (UINT32) USB_BOOT_GENERAL_CMD_TIMEOUT;
793
794 //
795 // Fill in the command then execute
796 //
797 ZeroMem (&ReadCmd, sizeof (USB_BOOT_READ10_CMD));
798
799 ReadCmd.OpCode = USB_BOOT_READ10_OPCODE;
800 ReadCmd.Lun = (UINT8) (USB_BOOT_LUN (UsbMass->Lun));
801 UsbBootPutUint32 (ReadCmd.Lba, Lba);
802 UsbBootPutUint16 (ReadCmd.TransferLen, Count);
803
804 Status = UsbBootExecCmdWithRetry (
805 UsbMass,
806 &ReadCmd,
807 sizeof (USB_BOOT_READ10_CMD),
808 EfiUsbDataIn,
809 Buffer,
810 ByteSize,
811 Timeout
812 );
813 if (EFI_ERROR (Status)) {
814 return Status;
815 }
816
817 Lba += Count;
818 Buffer += Count * BlockSize;
819 TotalBlock -= Count;
820 }
821
822 return Status;
823 }
824
825
826 /**
827 Write some blocks to the device.
828
829 @param UsbMass The USB mass storage device to write to
830 @param Lba The start block number
831 @param TotalBlock Total block number to write
832 @param Buffer The buffer to write to
833
834 @retval EFI_SUCCESS Data are written into the buffer
835 @retval Others Failed to write all the data
836
837 **/
838 EFI_STATUS
839 UsbBootWriteBlocks (
840 IN USB_MASS_DEVICE *UsbMass,
841 IN UINT32 Lba,
842 IN UINTN TotalBlock,
843 OUT UINT8 *Buffer
844 )
845 {
846 USB_BOOT_WRITE10_CMD WriteCmd;
847 EFI_STATUS Status;
848 UINT16 Count;
849 UINT32 BlockSize;
850 UINT32 ByteSize;
851 UINT32 Timeout;
852
853 BlockSize = UsbMass->BlockIoMedia.BlockSize;
854 Status = EFI_SUCCESS;
855
856 while (TotalBlock > 0) {
857 //
858 // Split the total blocks into smaller pieces to ease the pressure
859 // on the device. We must split the total block because the WRITE10
860 // command only has 16 bit transfer length (in the unit of block).
861 //
862 Count = (UINT16)((TotalBlock < USB_BOOT_IO_BLOCKS) ? TotalBlock : USB_BOOT_IO_BLOCKS);
863 ByteSize = (UINT32)Count * BlockSize;
864
865 //
866 // USB command's upper limit timeout is 5s. [USB2.0-9.2.6.1]
867 //
868 Timeout = (UINT32) USB_BOOT_GENERAL_CMD_TIMEOUT;
869
870 //
871 // Fill in the write10 command block
872 //
873 ZeroMem (&WriteCmd, sizeof (USB_BOOT_WRITE10_CMD));
874
875 WriteCmd.OpCode = USB_BOOT_WRITE10_OPCODE;
876 WriteCmd.Lun = (UINT8) (USB_BOOT_LUN (UsbMass->Lun));
877 UsbBootPutUint32 (WriteCmd.Lba, Lba);
878 UsbBootPutUint16 (WriteCmd.TransferLen, Count);
879
880 Status = UsbBootExecCmdWithRetry (
881 UsbMass,
882 &WriteCmd,
883 sizeof (USB_BOOT_WRITE10_CMD),
884 EfiUsbDataOut,
885 Buffer,
886 ByteSize,
887 Timeout
888 );
889 if (EFI_ERROR (Status)) {
890 return Status;
891 }
892
893 Lba += Count;
894 Buffer += Count * BlockSize;
895 TotalBlock -= Count;
896 }
897
898 return Status;
899 }
900
901
902 /**
903 Use the USB clear feature control transfer to clear the endpoint
904 stall condition.
905
906 @param UsbIo The USB IO protocol to use
907 @param EndpointAddr The endpoint to clear stall for
908
909 @retval EFI_SUCCESS The endpoint stall condtion is clear
910 @retval Others Failed to clear the endpoint stall condtion
911
912 **/
913 EFI_STATUS
914 UsbClearEndpointStall (
915 IN EFI_USB_IO_PROTOCOL *UsbIo,
916 IN UINT8 EndpointAddr
917 )
918 {
919 EFI_USB_DEVICE_REQUEST Request;
920 EFI_STATUS Status;
921 UINT32 CmdResult;
922 UINT32 Timeout;
923
924 Request.RequestType = 0x02;
925 Request.Request = USB_REQ_CLEAR_FEATURE;
926 Request.Value = USB_FEATURE_ENDPOINT_HALT;
927 Request.Index = EndpointAddr;
928 Request.Length = 0;
929 Timeout = USB_BOOT_GENERAL_CMD_TIMEOUT / USB_MASS_1_MILLISECOND;
930
931 Status = UsbIo->UsbControlTransfer (
932 UsbIo,
933 &Request,
934 EfiUsbNoData,
935 Timeout,
936 NULL,
937 0,
938 &CmdResult
939 );
940
941 return Status;
942 }