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