]> git.proxmox.com Git - mirror_edk2.git/blob - MdeModulePkg/Bus/Usb/UsbMassStorageDxe/UsbMassBoot.c
1b200bb4ec9f89b20857de2811853ba1de066f98
[mirror_edk2.git] / MdeModulePkg / Bus / Usb / UsbMassStorageDxe / UsbMassBoot.c
1 /** @file
2 Implementation of the command set of USB Mass Storage Specification
3 for Bootability, Revision 1.0.
4
5 Copyright (c) 2007 - 2010, Intel Corporation
6 All rights reserved. This program and the accompanying materials
7 are licensed and made available under the terms and conditions of the BSD License
8 which accompanies this distribution. The full text of the license may be found at
9 http://opensource.org/licenses/bsd-license.php
10
11 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
12 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
13
14 **/
15
16 #include "UsbMassImpl.h"
17
18 /**
19 Execute REQUEST SENSE Command to retrieve sense data from device.
20
21 @param UsbMass The device whose sense data is requested.
22
23 @retval EFI_SUCCESS The command is excuted successfully.
24 @retval EFI_DEVICE_ERROR Failed to request sense.
25 @retval EFI_NO_RESPONSE The device media doesn't response this request.
26 @retval EFI_INVALID_PARAMETER The command has some invalid parameters.
27 @retval EFI_WRITE_PROTECTED The device is write protected.
28 @retval EFI_MEDIA_CHANGED The device media has been changed.
29
30 **/
31 EFI_STATUS
32 UsbBootRequestSense (
33 IN USB_MASS_DEVICE *UsbMass
34 )
35 {
36 USB_BOOT_REQUEST_SENSE_CMD SenseCmd;
37 USB_BOOT_REQUEST_SENSE_DATA SenseData;
38 EFI_BLOCK_IO_MEDIA *Media;
39 USB_MASS_TRANSPORT *Transport;
40 EFI_STATUS Status;
41 UINT32 CmdResult;
42
43 Transport = UsbMass->Transport;
44
45 //
46 // Request the sense data from the device
47 //
48 ZeroMem (&SenseCmd, sizeof (USB_BOOT_REQUEST_SENSE_CMD));
49 ZeroMem (&SenseData, sizeof (USB_BOOT_REQUEST_SENSE_DATA));
50
51 SenseCmd.OpCode = USB_BOOT_REQUEST_SENSE_OPCODE;
52 SenseCmd.Lun = (UINT8) (USB_BOOT_LUN (UsbMass->Lun));
53 SenseCmd.AllocLen = sizeof (USB_BOOT_REQUEST_SENSE_DATA);
54
55 Status = Transport->ExecCommand (
56 UsbMass->Context,
57 &SenseCmd,
58 sizeof (USB_BOOT_REQUEST_SENSE_CMD),
59 EfiUsbDataIn,
60 &SenseData,
61 sizeof (USB_BOOT_REQUEST_SENSE_DATA),
62 UsbMass->Lun,
63 USB_BOOT_GENERAL_CMD_TIMEOUT,
64 &CmdResult
65 );
66 if (EFI_ERROR (Status) || CmdResult != USB_MASS_CMD_SUCCESS) {
67 DEBUG ((EFI_D_ERROR, "UsbBootRequestSense: (%r) CmdResult=0x%x\n", Status, CmdResult));
68 if (!EFI_ERROR (Status)) {
69 Status = EFI_DEVICE_ERROR;
70 }
71 return Status;
72 }
73
74 //
75 // If sense data is retrieved successfully, interpret the sense data
76 // and update the media status if necessary.
77 //
78 Media = &UsbMass->BlockIoMedia;
79
80 switch (USB_BOOT_SENSE_KEY (SenseData.SenseKey)) {
81
82 case USB_BOOT_SENSE_NO_SENSE:
83 Status = EFI_NO_RESPONSE;
84 break;
85
86 case USB_BOOT_SENSE_RECOVERED:
87 //
88 // Suppose hardware can handle this case, and recover later by itself
89 //
90 Status = EFI_NOT_READY;
91 break;
92
93 case USB_BOOT_SENSE_NOT_READY:
94 Status = EFI_DEVICE_ERROR;
95 if (SenseData.Asc == USB_BOOT_ASC_NO_MEDIA) {
96 Media->MediaPresent = FALSE;
97 Status = EFI_NO_MEDIA;
98 } else if (SenseData.Asc == USB_BOOT_ASC_NOT_READY) {
99 Status = EFI_NOT_READY;
100 }
101 break;
102
103 case USB_BOOT_SENSE_ILLEGAL_REQUEST:
104 Status = EFI_INVALID_PARAMETER;
105 break;
106
107 case USB_BOOT_SENSE_UNIT_ATTENTION:
108 Status = EFI_DEVICE_ERROR;
109 if (SenseData.Asc == USB_BOOT_ASC_MEDIA_CHANGE) {
110 //
111 // If MediaChange, reset ReadOnly and new MediaId
112 //
113 Status = EFI_MEDIA_CHANGED;
114 Media->ReadOnly = FALSE;
115 Media->MediaId++;
116 }
117 break;
118
119 case USB_BOOT_SENSE_DATA_PROTECT:
120 Status = EFI_WRITE_PROTECTED;
121 Media->ReadOnly = TRUE;
122 break;
123
124 default:
125 Status = EFI_DEVICE_ERROR;
126 break;
127 }
128
129 DEBUG ((EFI_D_INFO, "UsbBootRequestSense: (%r) with sense key %x/%x/%x\n",
130 Status,
131 USB_BOOT_SENSE_KEY (SenseData.SenseKey),
132 SenseData.Asc,
133 SenseData.Ascq
134 ));
135
136 return Status;
137 }
138
139
140 /**
141 Execute the USB mass storage bootability commands.
142
143 This function executes the USB mass storage bootability commands.
144 If execution failed, retrieve the error by REQUEST_SENSE, then
145 update the device's status, such as ReadyOnly.
146
147 @param UsbMass The device to issue commands to
148 @param Cmd The command to execute
149 @param CmdLen The length of the command
150 @param DataDir The direction of data transfer
151 @param Data The buffer to hold the data
152 @param DataLen The length of expected data
153 @param Timeout The timeout used to transfer
154
155 @retval EFI_SUCCESS Command is excuted successfully
156 @retval Others Command execution failed.
157
158 **/
159 EFI_STATUS
160 UsbBootExecCmd (
161 IN USB_MASS_DEVICE *UsbMass,
162 IN VOID *Cmd,
163 IN UINT8 CmdLen,
164 IN EFI_USB_DATA_DIRECTION DataDir,
165 IN VOID *Data,
166 IN UINT32 DataLen,
167 IN UINT32 Timeout
168 )
169 {
170 USB_MASS_TRANSPORT *Transport;
171 EFI_STATUS Status;
172 UINT32 CmdResult;
173
174 Transport = UsbMass->Transport;
175 Status = Transport->ExecCommand (
176 UsbMass->Context,
177 Cmd,
178 CmdLen,
179 DataDir,
180 Data,
181 DataLen,
182 UsbMass->Lun,
183 Timeout,
184 &CmdResult
185 );
186 //
187 // If ExecCommand() returns no error and CmdResult is success,
188 // then the commnad transfer is successful.
189 //
190 if ((CmdResult == USB_MASS_CMD_SUCCESS) && !EFI_ERROR (Status)) {
191 return EFI_SUCCESS;
192 }
193
194 DEBUG ((EFI_D_INFO, "UsbBootExecCmd: Fail to Exec 0x%x Cmd /w %r\n",
195 *(UINT8 *)Cmd ,Status));
196
197 //
198 // If command execution failed, then retrieve error info via sense request.
199 //
200 return UsbBootRequestSense (UsbMass);
201 }
202
203
204 /**
205 Execute the USB mass storage bootability commands with retrial.
206
207 This function executes USB mass storage bootability commands.
208 If the device isn't ready, wait for it. If the device is ready
209 and error occurs, retry the command again until it exceeds the
210 limit of retrial times.
211
212 @param UsbMass The device to issue commands to
213 @param Cmd The command to execute
214 @param CmdLen The length of the command
215 @param DataDir The direction of data transfer
216 @param Data The buffer to hold the data
217 @param DataLen The length of expected data
218 @param Timeout The timeout used to transfer
219
220 @retval EFI_SUCCESS The command is executed successfully.
221 @retval EFI_MEDIA_CHANGED The device media has been changed.
222 @retval Others Command execution failed after retrial.
223
224 **/
225 EFI_STATUS
226 UsbBootExecCmdWithRetry (
227 IN USB_MASS_DEVICE *UsbMass,
228 IN VOID *Cmd,
229 IN UINT8 CmdLen,
230 IN EFI_USB_DATA_DIRECTION DataDir,
231 IN VOID *Data,
232 IN UINT32 DataLen,
233 IN UINT32 Timeout
234 )
235 {
236 EFI_STATUS Status;
237 UINTN Retry;
238 UINT8 Terminate;
239
240 Status = EFI_SUCCESS;
241
242 for (Retry = 0, Terminate = 0; Retry < USB_BOOT_COMMAND_RETRY; Retry++) {
243 Status = UsbBootExecCmd (
244 UsbMass,
245 Cmd,
246 CmdLen,
247 DataDir,
248 Data,
249 DataLen,
250 Timeout
251 );
252 if (Status == EFI_SUCCESS || Status == EFI_MEDIA_CHANGED) {
253 break;
254 }
255 //
256 // If the device isn't ready, just wait for it without limit on retrial times.
257 //
258 if (Status == EFI_NOT_READY && Terminate < 3) {
259 Retry = 0;
260 Terminate++;
261 }
262 }
263
264 return Status;
265 }
266
267
268 /**
269 Execute TEST UNIT READY command to check if the device is ready.
270
271 @param UsbMass The device to test
272
273 @retval EFI_SUCCESS The device is ready.
274 @retval Others Device not ready.
275
276 **/
277 EFI_STATUS
278 UsbBootIsUnitReady (
279 IN USB_MASS_DEVICE *UsbMass
280 )
281 {
282 USB_BOOT_TEST_UNIT_READY_CMD TestCmd;
283
284 ZeroMem (&TestCmd, sizeof (USB_BOOT_TEST_UNIT_READY_CMD));
285
286 TestCmd.OpCode = USB_BOOT_TEST_UNIT_READY_OPCODE;
287 TestCmd.Lun = (UINT8) (USB_BOOT_LUN (UsbMass->Lun));
288
289 return UsbBootExecCmdWithRetry (
290 UsbMass,
291 &TestCmd,
292 sizeof (USB_BOOT_TEST_UNIT_READY_CMD),
293 EfiUsbNoData,
294 NULL,
295 0,
296 USB_BOOT_GENERAL_CMD_TIMEOUT
297 );
298 }
299
300
301 /**
302 Execute INQUIRY Command to request information regarding parameters of
303 the device be sent to the host computer.
304
305 @param UsbMass The device to inquire.
306
307 @retval EFI_SUCCESS INQUIRY Command is executed successfully.
308 @retval Others INQUIRY Command is not executed successfully.
309
310 **/
311 EFI_STATUS
312 UsbBootInquiry (
313 IN USB_MASS_DEVICE *UsbMass
314 )
315 {
316 USB_BOOT_INQUIRY_CMD InquiryCmd;
317 USB_BOOT_INQUIRY_DATA InquiryData;
318 EFI_BLOCK_IO_MEDIA *Media;
319 EFI_STATUS Status;
320
321 Media = &(UsbMass->BlockIoMedia);
322
323 ZeroMem (&InquiryCmd, sizeof (USB_BOOT_INQUIRY_CMD));
324 ZeroMem (&InquiryData, sizeof (USB_BOOT_INQUIRY_DATA));
325
326 InquiryCmd.OpCode = USB_BOOT_INQUIRY_OPCODE;
327 InquiryCmd.Lun = (UINT8) (USB_BOOT_LUN (UsbMass->Lun));
328 InquiryCmd.AllocLen = sizeof (InquiryData);
329
330 Status = UsbBootExecCmdWithRetry (
331 UsbMass,
332 &InquiryCmd,
333 sizeof (USB_BOOT_INQUIRY_CMD),
334 EfiUsbDataIn,
335 &InquiryData,
336 sizeof (USB_BOOT_INQUIRY_DATA),
337 USB_BOOT_GENERAL_CMD_TIMEOUT
338 );
339 if (EFI_ERROR (Status)) {
340 return Status;
341 }
342
343 //
344 // Get information from PDT (Peripheral Device Type) field and Removable Medium Bit
345 // from the inquiry data.
346 //
347 UsbMass->Pdt = (UINT8) (USB_BOOT_PDT (InquiryData.Pdt));
348 Media->RemovableMedia = (BOOLEAN) (USB_BOOT_REMOVABLE (InquiryData.Removable));
349 //
350 // Set block size to the default value of 512 Bytes, in case no media is present at first time.
351 //
352 Media->BlockSize = 0x0200;
353
354 return Status;
355 }
356
357
358 /**
359 Execute READ CAPACITY command to request information regarding
360 the capacity of the installed medium of the device.
361
362 This function executes READ CAPACITY command to get the capacity
363 of the USB mass storage media, including the presence, block size,
364 and last block number.
365
366 @param UsbMass The device to retireve disk gemotric.
367
368 @retval EFI_SUCCESS The disk geometry is successfully retrieved.
369 @retval EFI_NOT_READY The returned block size is zero.
370 @retval Other READ CAPACITY command execution failed.
371
372 **/
373 EFI_STATUS
374 UsbBootReadCapacity (
375 IN USB_MASS_DEVICE *UsbMass
376 )
377 {
378 USB_BOOT_READ_CAPACITY_CMD CapacityCmd;
379 USB_BOOT_READ_CAPACITY_DATA CapacityData;
380 EFI_BLOCK_IO_MEDIA *Media;
381 EFI_STATUS Status;
382 UINT32 BlockSize;
383
384 Media = &UsbMass->BlockIoMedia;
385
386 ZeroMem (&CapacityCmd, sizeof (USB_BOOT_READ_CAPACITY_CMD));
387 ZeroMem (&CapacityData, sizeof (USB_BOOT_READ_CAPACITY_DATA));
388
389 CapacityCmd.OpCode = USB_BOOT_READ_CAPACITY_OPCODE;
390 CapacityCmd.Lun = (UINT8) (USB_BOOT_LUN (UsbMass->Lun));
391
392 Status = UsbBootExecCmdWithRetry (
393 UsbMass,
394 &CapacityCmd,
395 sizeof (USB_BOOT_READ_CAPACITY_CMD),
396 EfiUsbDataIn,
397 &CapacityData,
398 sizeof (USB_BOOT_READ_CAPACITY_DATA),
399 USB_BOOT_GENERAL_CMD_TIMEOUT
400 );
401 if (EFI_ERROR (Status)) {
402 return Status;
403 }
404
405 //
406 // Get the information on media presence, block size, and last block number
407 // from READ CAPACITY data.
408 //
409 Media->MediaPresent = TRUE;
410 Media->LastBlock = SwapBytes32 (ReadUnaligned32 ((CONST UINT32 *) CapacityData.LastLba));
411
412 BlockSize = SwapBytes32 (ReadUnaligned32 ((CONST UINT32 *) CapacityData.BlockLen));
413 if (BlockSize == 0) {
414 //
415 // Get sense data
416 //
417 return UsbBootRequestSense (UsbMass);
418 } else {
419 Media->BlockSize = BlockSize;
420 }
421
422 DEBUG ((EFI_D_INFO, "UsbBootReadCapacity Success LBA=%ld BlockSize=%d\n",
423 Media->LastBlock, Media->BlockSize));
424
425 return Status;
426 }
427
428 /**
429 Retrieves SCSI mode sense information via MODE SENSE(6) command.
430
431 @param UsbMass The device whose sense data is requested.
432
433 @retval EFI_SUCCESS SCSI mode sense information retrieved successfully.
434 @retval Other Command execution failed.
435
436 **/
437 EFI_STATUS
438 UsbScsiModeSense (
439 IN USB_MASS_DEVICE *UsbMass
440 )
441 {
442 EFI_STATUS Status;
443 USB_SCSI_MODE_SENSE6_CMD ModeSenseCmd;
444 USB_SCSI_MODE_SENSE6_PARA_HEADER ModeParaHeader;
445 EFI_BLOCK_IO_MEDIA *Media;
446
447 Media = &UsbMass->BlockIoMedia;
448
449 ZeroMem (&ModeSenseCmd, sizeof (USB_SCSI_MODE_SENSE6_CMD));
450 ZeroMem (&ModeParaHeader, sizeof (USB_SCSI_MODE_SENSE6_PARA_HEADER));
451
452 //
453 // MODE SENSE(6) command is defined in Section 8.2.10 of SCSI-2 Spec
454 //
455 ModeSenseCmd.OpCode = USB_SCSI_MODE_SENSE6_OPCODE;
456 ModeSenseCmd.Lun = (UINT8) USB_BOOT_LUN (UsbMass->Lun);
457 ModeSenseCmd.PageCode = 0x3F;
458 ModeSenseCmd.AllocateLen = (UINT8) sizeof (USB_SCSI_MODE_SENSE6_PARA_HEADER);
459
460 Status = UsbBootExecCmdWithRetry (
461 UsbMass,
462 &ModeSenseCmd,
463 sizeof (USB_SCSI_MODE_SENSE6_CMD),
464 EfiUsbDataIn,
465 &ModeParaHeader,
466 sizeof (USB_SCSI_MODE_SENSE6_PARA_HEADER),
467 USB_BOOT_GENERAL_CMD_TIMEOUT
468 );
469
470 //
471 // Format of device-specific parameter byte of the mode parameter header is defined in
472 // Section 8.2.10 of SCSI-2 Spec.
473 // BIT7 of this byte is indicates whether the medium is write protected.
474 //
475 if (!EFI_ERROR (Status)) {
476 Media->ReadOnly = (BOOLEAN) ((ModeParaHeader.DevicePara & BIT7) != 0);
477 }
478
479 return Status;
480 }
481
482
483 /**
484 Get the parameters for the USB mass storage media.
485
486 This function get the parameters for the USB mass storage media,
487 It is used both to initialize the media during the Start() phase
488 of Driver Binding Protocol and to re-initialize it when the media is
489 changed. Althought the RemoveableMedia is unlikely to change,
490 it is also included here.
491
492 @param UsbMass The device to retrieve disk gemotric.
493
494 @retval EFI_SUCCESS The disk gemotric is successfully retrieved.
495 @retval Other Failed to get the parameters.
496
497 **/
498 EFI_STATUS
499 UsbBootGetParams (
500 IN USB_MASS_DEVICE *UsbMass
501 )
502 {
503 EFI_BLOCK_IO_MEDIA *Media;
504 EFI_STATUS Status;
505 UINT8 CmdSet;
506
507 Media = &(UsbMass->BlockIoMedia);
508 CmdSet = ((EFI_USB_INTERFACE_DESCRIPTOR *) (UsbMass->Context))->InterfaceSubClass;
509
510 Status = UsbBootInquiry (UsbMass);
511 if (EFI_ERROR (Status)) {
512 DEBUG ((EFI_D_ERROR, "UsbBootGetParams: UsbBootInquiry (%r)\n", Status));
513 return Status;
514 }
515
516 //
517 // Don't use the Removable bit in inquiry data to test whether the media
518 // is removable because many flash disks wrongly set this bit.
519 //
520 if ((UsbMass->Pdt == USB_PDT_CDROM) || (UsbMass->Pdt == USB_PDT_OPTICAL)) {
521 //
522 // CD-Rom device and Non-CD optical device
523 //
524 UsbMass->OpticalStorage = TRUE;
525 //
526 // Default value 2048 Bytes, in case no media present at first time
527 //
528 Media->BlockSize = 0x0800;
529 }
530
531 if ((UsbMass->Pdt != USB_PDT_CDROM) && (CmdSet == USB_MASS_STORE_SCSI)) {
532 //
533 // ModeSense is required for the device with PDT of 0x00/0x07/0x0E,
534 // which is from [MassStorageBootabilitySpec-Page7].
535 // ModeSense(10) is useless here, while ModeSense(6) defined in SCSI
536 // could get the information of WriteProtected.
537 // Since not all device support this command, so skip if fail.
538 //
539 UsbScsiModeSense (UsbMass);
540 }
541
542 return UsbBootReadCapacity (UsbMass);
543 }
544
545
546 /**
547 Detect whether the removable media is present and whether it has changed.
548
549 @param UsbMass The device to check.
550
551 @retval EFI_SUCCESS The media status is successfully checked.
552 @retval Other Failed to detect media.
553
554 **/
555 EFI_STATUS
556 UsbBootDetectMedia (
557 IN USB_MASS_DEVICE *UsbMass
558 )
559 {
560 EFI_BLOCK_IO_MEDIA OldMedia;
561 EFI_BLOCK_IO_MEDIA *Media;
562 UINT8 CmdSet;
563 EFI_TPL OldTpl;
564 EFI_STATUS Status;
565
566 Media = &UsbMass->BlockIoMedia;
567
568 CopyMem (&OldMedia, &(UsbMass->BlockIoMedia), sizeof (EFI_BLOCK_IO_MEDIA));
569
570 CmdSet = ((EFI_USB_INTERFACE_DESCRIPTOR *) (UsbMass->Context))->InterfaceSubClass;
571
572 Status = UsbBootIsUnitReady (UsbMass);
573 if (EFI_ERROR (Status)) {
574 goto ON_ERROR;
575 }
576
577 if ((UsbMass->Pdt != USB_PDT_CDROM) && (CmdSet == USB_MASS_STORE_SCSI)) {
578 //
579 // MODE SENSE is required for the device with PDT of 0x00/0x07/0x0E,
580 // according to Section 4 of USB Mass Storage Specification for Bootability.
581 // MODE SENSE(10) is useless here, while MODE SENSE(6) defined in SCSI
582 // could get the information of Write Protected.
583 // Since not all device support this command, skip if fail.
584 //
585 UsbScsiModeSense (UsbMass);
586 }
587
588 Status = UsbBootReadCapacity (UsbMass);
589 if (EFI_ERROR (Status)) {
590 DEBUG ((EFI_D_ERROR, "UsbBootDetectMedia: UsbBootReadCapacity (%r)\n", Status));
591 goto ON_ERROR;
592 }
593
594 return EFI_SUCCESS;
595
596 ON_ERROR:
597 //
598 // Detect whether it is necessary to reinstall the Block I/O Protocol.
599 //
600 // MediaId may change in RequestSense for MediaChanged
601 // MediaPresent may change in RequestSense for NoMedia
602 // MediaReadOnly may change in RequestSense for WriteProtected or MediaChanged
603 // MediaPresent/BlockSize/LastBlock may change in ReadCapacity
604 //
605 if ((Media->MediaId != OldMedia.MediaId) ||
606 (Media->MediaPresent != OldMedia.MediaPresent) ||
607 (Media->ReadOnly != OldMedia.ReadOnly) ||
608 (Media->BlockSize != OldMedia.BlockSize) ||
609 (Media->LastBlock != OldMedia.LastBlock)) {
610
611 //
612 // This function is called by Block I/O Protocol APIs, which run at TPL_NOTIFY.
613 // Here we temporarily restore TPL to TPL_CALLBACK to invoke ReinstallProtocolInterface().
614 //
615 OldTpl = EfiGetCurrentTpl ();
616 gBS->RestoreTPL (TPL_CALLBACK);
617
618 gBS->ReinstallProtocolInterface (
619 UsbMass->Controller,
620 &gEfiBlockIoProtocolGuid,
621 &UsbMass->BlockIo,
622 &UsbMass->BlockIo
623 );
624
625 ASSERT (EfiGetCurrentTpl () == TPL_CALLBACK);
626 gBS->RaiseTPL (OldTpl);
627
628 //
629 // Update MediaId after reinstalling Block I/O Protocol.
630 //
631 if (Media->MediaPresent != OldMedia.MediaPresent) {
632 if (Media->MediaPresent) {
633 Media->MediaId = 1;
634 } else {
635 Media->MediaId = 0;
636 }
637 }
638
639 if ((Media->ReadOnly != OldMedia.ReadOnly) ||
640 (Media->BlockSize != OldMedia.BlockSize) ||
641 (Media->LastBlock != OldMedia.LastBlock)) {
642 Media->MediaId++;
643 }
644 }
645
646 return Status;
647 }
648
649
650 /**
651 Read some blocks from the device.
652
653 @param UsbMass The USB mass storage device to read from
654 @param Lba The start block number
655 @param TotalBlock Total block number to read
656 @param Buffer The buffer to read to
657
658 @retval EFI_SUCCESS Data are read into the buffer
659 @retval Others Failed to read all the data
660
661 **/
662 EFI_STATUS
663 UsbBootReadBlocks (
664 IN USB_MASS_DEVICE *UsbMass,
665 IN UINT32 Lba,
666 IN UINTN TotalBlock,
667 OUT UINT8 *Buffer
668 )
669 {
670 USB_BOOT_READ10_CMD ReadCmd;
671 EFI_STATUS Status;
672 UINT16 Count;
673 UINT32 BlockSize;
674 UINT32 ByteSize;
675 UINT32 Timeout;
676
677 BlockSize = UsbMass->BlockIoMedia.BlockSize;
678 Status = EFI_SUCCESS;
679
680 while (TotalBlock > 0) {
681 //
682 // Split the total blocks into smaller pieces to ease the pressure
683 // on the device. We must split the total block because the READ10
684 // command only has 16 bit transfer length (in the unit of block).
685 //
686 Count = (UINT16)((TotalBlock < USB_BOOT_IO_BLOCKS) ? TotalBlock : USB_BOOT_IO_BLOCKS);
687 ByteSize = (UINT32)Count * BlockSize;
688
689 //
690 // USB command's upper limit timeout is 5s. [USB2.0-9.2.6.1]
691 //
692 Timeout = (UINT32) USB_BOOT_GENERAL_CMD_TIMEOUT;
693
694 //
695 // Fill in the command then execute
696 //
697 ZeroMem (&ReadCmd, sizeof (USB_BOOT_READ10_CMD));
698
699 ReadCmd.OpCode = USB_BOOT_READ10_OPCODE;
700 ReadCmd.Lun = (UINT8) (USB_BOOT_LUN (UsbMass->Lun));
701 WriteUnaligned32 ((UINT32 *) ReadCmd.Lba, SwapBytes32 (Lba));
702 WriteUnaligned16 ((UINT16 *) ReadCmd.TransferLen, SwapBytes16 (Count));
703
704 Status = UsbBootExecCmdWithRetry (
705 UsbMass,
706 &ReadCmd,
707 sizeof (USB_BOOT_READ10_CMD),
708 EfiUsbDataIn,
709 Buffer,
710 ByteSize,
711 Timeout
712 );
713 if (EFI_ERROR (Status)) {
714 return Status;
715 }
716
717 Lba += Count;
718 Buffer += Count * BlockSize;
719 TotalBlock -= Count;
720 }
721
722 return Status;
723 }
724
725
726 /**
727 Write some blocks to the device.
728
729 @param UsbMass The USB mass storage device to write to
730 @param Lba The start block number
731 @param TotalBlock Total block number to write
732 @param Buffer Pointer to the source buffer for the data.
733
734 @retval EFI_SUCCESS Data are written into the buffer
735 @retval Others Failed to write all the data
736
737 **/
738 EFI_STATUS
739 UsbBootWriteBlocks (
740 IN USB_MASS_DEVICE *UsbMass,
741 IN UINT32 Lba,
742 IN UINTN TotalBlock,
743 IN UINT8 *Buffer
744 )
745 {
746 USB_BOOT_WRITE10_CMD WriteCmd;
747 EFI_STATUS Status;
748 UINT16 Count;
749 UINT32 BlockSize;
750 UINT32 ByteSize;
751 UINT32 Timeout;
752
753 BlockSize = UsbMass->BlockIoMedia.BlockSize;
754 Status = EFI_SUCCESS;
755
756 while (TotalBlock > 0) {
757 //
758 // Split the total blocks into smaller pieces to ease the pressure
759 // on the device. We must split the total block because the WRITE10
760 // command only has 16 bit transfer length (in the unit of block).
761 //
762 Count = (UINT16)((TotalBlock < USB_BOOT_IO_BLOCKS) ? TotalBlock : USB_BOOT_IO_BLOCKS);
763 ByteSize = (UINT32)Count * BlockSize;
764
765 //
766 // USB command's upper limit timeout is 5s. [USB2.0-9.2.6.1]
767 //
768 Timeout = (UINT32) USB_BOOT_GENERAL_CMD_TIMEOUT;
769
770 //
771 // Fill in the write10 command block
772 //
773 ZeroMem (&WriteCmd, sizeof (USB_BOOT_WRITE10_CMD));
774
775 WriteCmd.OpCode = USB_BOOT_WRITE10_OPCODE;
776 WriteCmd.Lun = (UINT8) (USB_BOOT_LUN (UsbMass->Lun));
777 WriteUnaligned32 ((UINT32 *) WriteCmd.Lba, SwapBytes32 (Lba));
778 WriteUnaligned16 ((UINT16 *) WriteCmd.TransferLen, SwapBytes16 (Count));
779
780 Status = UsbBootExecCmdWithRetry (
781 UsbMass,
782 &WriteCmd,
783 sizeof (USB_BOOT_WRITE10_CMD),
784 EfiUsbDataOut,
785 Buffer,
786 ByteSize,
787 Timeout
788 );
789 if (EFI_ERROR (Status)) {
790 return Status;
791 }
792
793 Lba += Count;
794 Buffer += Count * BlockSize;
795 TotalBlock -= Count;
796 }
797
798 return Status;
799 }
800
801 /**
802 Use the USB clear feature control transfer to clear the endpoint stall condition.
803
804 @param UsbIo The USB I/O Protocol instance
805 @param EndpointAddr The endpoint to clear stall for
806
807 @retval EFI_SUCCESS The endpoint stall condition is cleared.
808 @retval Others Failed to clear the endpoint stall condition.
809
810 **/
811 EFI_STATUS
812 UsbClearEndpointStall (
813 IN EFI_USB_IO_PROTOCOL *UsbIo,
814 IN UINT8 EndpointAddr
815 )
816 {
817 EFI_USB_DEVICE_REQUEST Request;
818 EFI_STATUS Status;
819 UINT32 CmdResult;
820 UINT32 Timeout;
821
822 Request.RequestType = 0x02;
823 Request.Request = USB_REQ_CLEAR_FEATURE;
824 Request.Value = USB_FEATURE_ENDPOINT_HALT;
825 Request.Index = EndpointAddr;
826 Request.Length = 0;
827 Timeout = USB_BOOT_GENERAL_CMD_TIMEOUT / USB_MASS_1_MILLISECOND;
828
829 Status = UsbIo->UsbControlTransfer (
830 UsbIo,
831 &Request,
832 EfiUsbNoData,
833 Timeout,
834 NULL,
835 0,
836 &CmdResult
837 );
838
839 return Status;
840 }