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