980f8b289510854a88f826021d37efd7e98e87cb
[people/mcb30/edk2.git] / edk2 / MdeModulePkg / Bus / Usb / UsbMassStorageDxe / UsbMassBoot.c
1 /** @file\r
2 \r
3 Copyright (c) 2007, 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 /**\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
40 STATIC\r
41 UINT32\r
42 UsbBootGetUint32 (\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
63 STATIC\r
64 VOID\r
65 UsbBootPutUint32 (\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
85 STATIC\r
86 VOID\r
87 UsbBootPutUint16 (\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
107 EFI_STATUS\r
108 UsbBootRequestSense (\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
238 STATIC\r
239 EFI_STATUS\r
240 UsbBootExecCmd (\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
296 STATIC\r
297 EFI_STATUS\r
298 UsbBootExecCmdWithRetry (\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
357 EFI_STATUS\r
358 UsbBootIsUnitReady (\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
391 EFI_STATUS\r
392 UsbBootInquiry (\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
450 EFI_STATUS\r
451 UsbBootReadCapacity (\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
505 EFI_STATUS\r
506 UsbBootModeSense (\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
566 EFI_STATUS\r
567 UsbBootGetParams (\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
619 EFI_STATUS\r
620 UsbBootDetectMedia (\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   OldMedia = UsbMass->BlockIoMedia;\r
630 \r
631   //\r
632   // First test whether the device is ready and get status\r
633   // If media changed or ready, need read the device's capacity\r
634   //\r
635   Status = UsbBootIsUnitReady (UsbMass);\r
636   if ((Status == EFI_SUCCESS && Media->MediaPresent) ||\r
637       (Status == EFI_MEDIA_CHANGED)) {\r
638     if ((UsbMass->Pdt != USB_PDT_CDROM) &&\r
639         (UsbMass->Pdt != USB_PDT_OPTICAL)) {\r
640       //\r
641       // Non CD-Rom device need ModeSenseCmd between InquiryCmd and ReadCapacityCmd\r
642       //\r
643       UsbBootModeSense (UsbMass);\r
644     }\r
645     DEBUG ((mUsbMscInfo, "UsbBootDetectMedia: Need Read Capacity\n"));\r
646     Status = UsbBootReadCapacity (UsbMass);\r
647   }\r
648   if (EFI_ERROR (Status)) {\r
649     return Status;\r
650   }\r
651 \r
652   //\r
653   // Detect whether it is necessary to reinstall the BlockIO\r
654   //\r
655   if ((Media->MediaId != OldMedia.MediaId) ||\r
656       (Media->MediaPresent != OldMedia.MediaPresent) ||\r
657       (Media->ReadOnly != OldMedia.ReadOnly) ||\r
658       (Media->BlockSize != OldMedia.BlockSize) ||\r
659       (Media->LastBlock != OldMedia.LastBlock)) {\r
660     DEBUG ((mUsbMscInfo, "UsbBootDetectMedia: Need reinstall BlockIoProtocol\n"));\r
661     Media->MediaId++;\r
662     gBS->ReinstallProtocolInterface (\r
663            UsbMass->Controller,\r
664            &gEfiBlockIoProtocolGuid,\r
665            &UsbMass->BlockIo,\r
666            &UsbMass->BlockIo\r
667            );\r
668     //\r
669     // Check whether media present or media changed or write protected\r
670     //\r
671     if (Media->MediaPresent == FALSE) {\r
672       Status = EFI_NO_MEDIA;\r
673     }\r
674     if (Media->MediaId != OldMedia.MediaId) {\r
675       Status = EFI_MEDIA_CHANGED;\r
676     }\r
677     if (Media->ReadOnly != OldMedia.ReadOnly) {\r
678       Status = EFI_WRITE_PROTECTED;\r
679     }\r
680   }\r
681 \r
682   return Status;\r
683 }\r
684 \r
685 \r
686 /**\r
687   Read some blocks from the device.\r
688 \r
689   @param  UsbMass                The USB mass storage device to read from\r
690   @param  Lba                    The start block number\r
691   @param  TotalBlock             Total block number to read\r
692   @param  Buffer                 The buffer to read to\r
693 \r
694   @retval EFI_SUCCESS            Data are read into the buffer\r
695   @retval Others                 Failed to read all the data\r
696 \r
697 **/\r
698 EFI_STATUS\r
699 UsbBootReadBlocks (\r
700   IN  USB_MASS_DEVICE       *UsbMass,\r
701   IN  UINT32                Lba,\r
702   IN  UINTN                 TotalBlock,\r
703   OUT UINT8                 *Buffer\r
704   )\r
705 {\r
706   USB_BOOT_READ10_CMD       ReadCmd;\r
707   EFI_STATUS                Status;\r
708   UINT16                    Count;\r
709   UINT32                    BlockSize;\r
710   UINT32                    ByteSize;\r
711   UINT32                    Timeout;\r
712 \r
713   BlockSize = UsbMass->BlockIoMedia.BlockSize;\r
714   Status    = EFI_SUCCESS;\r
715 \r
716   while (TotalBlock > 0) {\r
717     //\r
718     // Split the total blocks into smaller pieces to ease the pressure\r
719     // on the device. We must split the total block because the READ10\r
720     // command only has 16 bit transfer length (in the unit of block).\r
721     //\r
722     Count     = (UINT16)((TotalBlock < USB_BOOT_IO_BLOCKS) ? TotalBlock : USB_BOOT_IO_BLOCKS);\r
723     ByteSize  = (UINT32)Count * BlockSize;\r
724 \r
725     //\r
726     // Optical device need longer timeout than other device\r
727     //\r
728     if (UsbMass->OpticalStorage == TRUE) {\r
729       Timeout = (UINT32)Count * USB_BOOT_OPTICAL_BLOCK_TIMEOUT;\r
730     } else {\r
731       Timeout = (UINT32)Count * USB_BOOT_GENERAL_BLOCK_TIMEOUT;\r
732     }\r
733 \r
734     //\r
735     // Fill in the command then execute\r
736     //\r
737     ZeroMem (&ReadCmd, sizeof (USB_BOOT_READ10_CMD));\r
738 \r
739     ReadCmd.OpCode  = USB_BOOT_READ10_OPCODE;\r
740     ReadCmd.Lun     = USB_BOOT_LUN (UsbMass->Lun);\r
741     UsbBootPutUint32 (ReadCmd.Lba, Lba);\r
742     UsbBootPutUint16 (ReadCmd.TransferLen, Count);\r
743 \r
744     Status = UsbBootExecCmdWithRetry (\r
745                UsbMass,\r
746                &ReadCmd,\r
747                sizeof (USB_BOOT_READ10_CMD),\r
748                EfiUsbDataIn,\r
749                Buffer,\r
750                ByteSize,\r
751                Timeout\r
752                );\r
753     if (EFI_ERROR (Status)) {\r
754       return Status;\r
755     }\r
756 \r
757     Lba        += Count;\r
758     Buffer     += Count * BlockSize;\r
759     TotalBlock -= Count;\r
760   }\r
761 \r
762   return Status;\r
763 }\r
764 \r
765 \r
766 /**\r
767   Write some blocks to the device.\r
768 \r
769   @param  UsbMass                The USB mass storage device to write to\r
770   @param  Lba                    The start block number\r
771   @param  TotalBlock             Total block number to write\r
772   @param  Buffer                 The buffer to write to\r
773 \r
774   @retval EFI_SUCCESS            Data are written into the buffer\r
775   @retval Others                 Failed to write all the data\r
776 \r
777 **/\r
778 EFI_STATUS\r
779 UsbBootWriteBlocks (\r
780   IN  USB_MASS_DEVICE         *UsbMass,\r
781   IN  UINT32                  Lba,\r
782   IN  UINTN                   TotalBlock,\r
783   OUT UINT8                   *Buffer\r
784   )\r
785 {\r
786   USB_BOOT_WRITE10_CMD  WriteCmd;\r
787   EFI_STATUS            Status;\r
788   UINT16                Count;\r
789   UINT32                BlockSize;\r
790   UINT32                ByteSize;\r
791   UINT32                Timeout;\r
792 \r
793   BlockSize = UsbMass->BlockIoMedia.BlockSize;\r
794   Status    = EFI_SUCCESS;\r
795 \r
796   while (TotalBlock > 0) {\r
797     //\r
798     // Split the total blocks into smaller pieces to ease the pressure\r
799     // on the device. We must split the total block because the WRITE10\r
800     // command only has 16 bit transfer length (in the unit of block).\r
801     //\r
802     Count     = (UINT16)((TotalBlock < USB_BOOT_IO_BLOCKS) ? TotalBlock : USB_BOOT_IO_BLOCKS);\r
803     ByteSize  = (UINT32)Count * BlockSize;\r
804 \r
805     //\r
806     // Optical device need longer timeout than other device\r
807     //\r
808     if (UsbMass->OpticalStorage == TRUE) {\r
809       Timeout = (UINT32)Count * USB_BOOT_OPTICAL_BLOCK_TIMEOUT;\r
810     } else {\r
811       Timeout = (UINT32)Count * USB_BOOT_GENERAL_BLOCK_TIMEOUT;\r
812     }\r
813 \r
814     //\r
815     // Fill in the write10 command block\r
816     //\r
817     ZeroMem (&WriteCmd, sizeof (USB_BOOT_WRITE10_CMD));\r
818 \r
819     WriteCmd.OpCode = USB_BOOT_WRITE10_OPCODE;\r
820     WriteCmd.Lun    = USB_BOOT_LUN (UsbMass->Lun);\r
821     UsbBootPutUint32 (WriteCmd.Lba, Lba);\r
822     UsbBootPutUint16 (WriteCmd.TransferLen, Count);\r
823 \r
824     Status = UsbBootExecCmdWithRetry (\r
825                UsbMass,\r
826                &WriteCmd,\r
827                sizeof (USB_BOOT_WRITE10_CMD),\r
828                EfiUsbDataOut,\r
829                Buffer,\r
830                ByteSize,\r
831                Timeout\r
832                );\r
833     if (EFI_ERROR (Status)) {\r
834       return Status;\r
835     }\r
836 \r
837     Lba        += Count;\r
838     Buffer     += Count * BlockSize;\r
839     TotalBlock -= Count;\r
840   }\r
841 \r
842   return Status;\r
843 }\r
844 \r
845 \r
846 /**\r
847   Use the USB clear feature control transfer to clear the endpoint\r
848   stall condition.\r
849 \r
850   @param  UsbIo                  The USB IO protocol to use\r
851   @param  EndpointAddr           The endpoint to clear stall for\r
852 \r
853   @retval EFI_SUCCESS            The endpoint stall condtion is clear\r
854   @retval Others                 Failed to clear the endpoint stall condtion\r
855 \r
856 **/\r
857 EFI_STATUS\r
858 UsbClearEndpointStall (\r
859   IN EFI_USB_IO_PROTOCOL    *UsbIo,\r
860   IN UINT8                  EndpointAddr\r
861   )\r
862 {\r
863   EFI_USB_DEVICE_REQUEST    Request;\r
864   EFI_STATUS                Status;\r
865   UINT32                    CmdResult;\r
866   UINT32                    Timeout;\r
867 \r
868   Request.RequestType = 0x02;\r
869   Request.Request     = USB_REQ_CLEAR_FEATURE;\r
870   Request.Value       = USB_FEATURE_ENDPOINT_HALT;\r
871   Request.Index       = EndpointAddr;\r
872   Request.Length      = 0;\r
873   Timeout             = USB_BOOT_GENERAL_CMD_TIMEOUT / USB_MASS_STALL_1_MS;\r
874 \r
875   Status = UsbIo->UsbControlTransfer (\r
876                     UsbIo,\r
877                     &Request,\r
878                     EfiUsbNoData,\r
879                     Timeout,\r
880                     NULL,\r
881                     0,\r
882                     &CmdResult\r
883                     );\r
884 \r
885   return Status;\r
886 }\r