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