]> git.proxmox.com Git - mirror_edk2.git/blame - MdeModulePkg/Bus/Usb/UsbMassStorageDxe/UsbMassBoot.c
Enhance the string formatting function to take "%p" to print pointer.
[mirror_edk2.git] / MdeModulePkg / Bus / Usb / UsbMassStorageDxe / UsbMassBoot.c
CommitLineData
e237e7ae 1/** @file\r
2\r
c7e39923 3Copyright (c) 2007 - 2008, Intel Corporation\r
e237e7ae 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
50fa1b3a 29EFI_TPL\r
30UsbGetCurrentTpl (\r
31 VOID\r
32 )\r
33/*++\r
34\r
35Routine Description:\r
36\r
37 return the current TPL, copied from the EDKII glue lib.\r
38\r
39Arguments:\r
40\r
41 VOID\r
42\r
43Returns:\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
e237e7ae 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
68STATIC\r
69UINT32\r
70UsbBootGetUint32 (\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
91STATIC\r
92VOID\r
93UsbBootPutUint32 (\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
113STATIC\r
114VOID\r
115UsbBootPutUint16 (\r
116 IN UINT8 *Buf,\r
117 IN UINT16 Data16\r
118 )\r
119{\r
c52fa98c 120 Data16 = (UINT16) (USB_BOOT_SWAP16 (Data16));\r
e237e7ae 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
135EFI_STATUS\r
136UsbBootRequestSense (\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
c52fa98c 156 SenseCmd.Lun = (UINT8) (USB_BOOT_LUN (UsbMass->Lun));\r
e237e7ae 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
c7e39923 166 UsbMass->Lun,\r
e237e7ae 167 USB_BOOT_GENERAL_CMD_TIMEOUT,\r
168 &CmdResult\r
169 );\r
170 if (EFI_ERROR (Status) || CmdResult != USB_MASS_CMD_SUCCESS) {\r
1c619535 171 DEBUG ((EFI_D_ERROR, "UsbBootRequestSense: (%r) CmdResult=0x%x\n", Status, CmdResult));\r
50fa1b3a 172 return Status;\r
e237e7ae 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
50fa1b3a 183 Status = EFI_NO_RESPONSE;\r
184 break;\r
185\r
e237e7ae 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
50fa1b3a 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
e237e7ae 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
50fa1b3a 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
e237e7ae 216 }\r
217 break;\r
218\r
219 case USB_BOOT_SNESE_DATA_PROTECT:\r
50fa1b3a 220 Status = EFI_WRITE_PROTECTED;\r
221 Media->ReadOnly = TRUE;\r
e237e7ae 222 break;\r
223\r
224 default:\r
225 Status = EFI_DEVICE_ERROR;\r
226 break;\r
227 }\r
228\r
1c619535 229 DEBUG ((EFI_D_INFO, "UsbBootRequestSense: (%r) with sense key %x/%x/%x\n",\r
e237e7ae 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
260STATIC\r
261EFI_STATUS\r
262UsbBootExecCmd (\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
c7e39923 284 UsbMass->Lun,\r
e237e7ae 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
1c619535 296 DEBUG ((EFI_D_INFO, "UsbBootExecCmd: Fail to Exec 0x%x Cmd /w %r\n",\r
50fa1b3a 297 *(UINT8 *)Cmd ,Status));\r
298\r
e237e7ae 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
322STATIC\r
323EFI_STATUS\r
324UsbBootExecCmdWithRetry (\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
1c619535 354 Timeout\r
e237e7ae 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
383EFI_STATUS\r
384UsbBootIsUnitReady (\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
c52fa98c 393 TestCmd.Lun = (UINT8) (USB_BOOT_LUN (UsbMass->Lun));\r
e237e7ae 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
417EFI_STATUS\r
418UsbBootInquiry (\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
c52fa98c 436 InquiryCmd.Lun = (UINT8) (USB_BOOT_LUN (UsbMass->Lun));\r
e237e7ae 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
50fa1b3a 446 USB_BOOT_GENERAL_CMD_TIMEOUT\r
e237e7ae 447 );\r
448 if (EFI_ERROR (Status)) {\r
449 return Status;\r
450 }\r
451\r
c52fa98c 452 UsbMass->Pdt = (UINT8) (USB_BOOT_PDT (InquiryData.Pdt));\r
453 Media->RemovableMedia = (BOOLEAN) (USB_BOOT_REMOVABLE (InquiryData.Removable));\r
e237e7ae 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
476EFI_STATUS\r
477UsbBootReadCapacity (\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
c52fa98c 495 CapacityCmd.Lun = (UINT8) (USB_BOOT_LUN (UsbMass->Lun));\r
e237e7ae 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
50fa1b3a 514 if (Media->BlockSize == 0) {\r
515 return EFI_NOT_READY;\r
516 }\r
517\r
1c619535 518 DEBUG ((EFI_D_INFO, "UsbBootReadCapacity Success LBA=%ld BlockSize=%d\n",\r
50fa1b3a 519 Media->LastBlock, Media->BlockSize));\r
e237e7ae 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
535EFI_STATUS\r
50fa1b3a 536UsbScsiModeSense (\r
e237e7ae 537 IN USB_MASS_DEVICE *UsbMass\r
538 )\r
539{\r
ca12415a 540 EFI_STATUS Status;\r
50fa1b3a 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
ca12415a 545 Media = &UsbMass->BlockIoMedia;\r
e237e7ae 546\r
50fa1b3a 547 ZeroMem (&ModeSenseCmd, sizeof (USB_SCSI_MODE_SENSE6_CMD));\r
548 ZeroMem (&ModeParaHeader, sizeof (USB_SCSI_MODE_SENSE6_PARA_HEADER));\r
e237e7ae 549\r
550 //\r
50fa1b3a 551 // ModeSense6 command is defined in [SCSI2Spec-Page151]\r
e237e7ae 552 //\r
50fa1b3a 553 ModeSenseCmd.OpCode = USB_SCSI_MODE_SENSE6_OPCODE;\r
687a2e5f 554 ModeSenseCmd.Lun = (UINT8) USB_BOOT_LUN (UsbMass->Lun);\r
50fa1b3a 555 ModeSenseCmd.PageCode = 0x3F;\r
556 ModeSenseCmd.AllocateLen = (UINT8) sizeof (USB_SCSI_MODE_SENSE6_PARA_HEADER);\r
e237e7ae 557\r
558 Status = UsbBootExecCmdWithRetry (\r
559 UsbMass,\r
560 &ModeSenseCmd,\r
50fa1b3a 561 sizeof (USB_SCSI_MODE_SENSE6_CMD),\r
e237e7ae 562 EfiUsbDataIn,\r
563 &ModeParaHeader,\r
50fa1b3a 564 sizeof (USB_SCSI_MODE_SENSE6_PARA_HEADER),\r
e237e7ae 565 USB_BOOT_GENERAL_CMD_TIMEOUT\r
566 );\r
50fa1b3a 567\r
e237e7ae 568 //\r
50fa1b3a 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
e237e7ae 571 //\r
50fa1b3a 572 if (!EFI_ERROR (Status)) {\r
687a2e5f 573 Media->ReadOnly = (BOOLEAN) ((ModeParaHeader.DevicePara & 0x80) ? TRUE : FALSE);\r
50fa1b3a 574 }\r
e237e7ae 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
594EFI_STATUS\r
595UsbBootGetParams (\r
596 IN USB_MASS_DEVICE *UsbMass\r
597 )\r
598{\r
599 EFI_BLOCK_IO_MEDIA *Media;\r
600 EFI_STATUS Status;\r
50fa1b3a 601 UINT8 CmdSet;\r
602\r
ca12415a 603 Media = &(UsbMass->BlockIoMedia);\r
50fa1b3a 604 CmdSet = ((EFI_USB_INTERFACE_DESCRIPTOR *) (UsbMass->Context))->InterfaceSubClass;\r
e237e7ae 605\r
606 Status = UsbBootInquiry (UsbMass);\r
607 if (EFI_ERROR (Status)) {\r
1c619535 608 DEBUG ((EFI_D_ERROR, "UsbBootGetParams: UsbBootInquiry (%r)\n", Status));\r
e237e7ae 609 return Status;\r
610 }\r
611\r
e237e7ae 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
50fa1b3a 618 // CD-Rom device and Non-CD optical device\r
e237e7ae 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
50fa1b3a 625 }\r
626\r
627 if ((UsbMass->Pdt != USB_PDT_CDROM) && (CmdSet == USB_MASS_STORE_SCSI)) {\r
e237e7ae 628 //\r
50fa1b3a 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
e237e7ae 634 //\r
50fa1b3a 635 UsbScsiModeSense (UsbMass);\r
e237e7ae 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
652EFI_STATUS\r
653UsbBootDetectMedia (\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
50fa1b3a 659 UINT8 CmdSet;\r
660 EFI_TPL OldTpl;\r
e237e7ae 661 EFI_STATUS Status;\r
662\r
663 Media = &UsbMass->BlockIoMedia;\r
e61d30b0 664\r
665 CopyMem (\r
666 &OldMedia,\r
667 &(UsbMass->BlockIoMedia),\r
668 sizeof (EFI_BLOCK_IO_MEDIA)\r
669 );\r
e237e7ae 670\r
50fa1b3a 671 CmdSet = ((EFI_USB_INTERFACE_DESCRIPTOR *) (UsbMass->Context))->InterfaceSubClass;\r
672\r
e237e7ae 673 Status = UsbBootIsUnitReady (UsbMass);\r
50fa1b3a 674 if (EFI_ERROR (Status)) {\r
1c619535 675 DEBUG ((EFI_D_ERROR, "UsbBootDetectMedia: UsbBootIsUnitReady (%r)\n", Status));\r
50fa1b3a 676 goto ON_ERROR;\r
e237e7ae 677 }\r
50fa1b3a 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
e237e7ae 691 if (EFI_ERROR (Status)) {\r
1c619535 692 DEBUG ((EFI_D_ERROR, "UsbBootDetectMedia: UsbBootReadCapacity (%r)\n", Status));\r
50fa1b3a 693 goto ON_ERROR;\r
e237e7ae 694 }\r
695\r
50fa1b3a 696 return EFI_SUCCESS;\r
697\r
698ON_ERROR:\r
e237e7ae 699 //\r
700 // Detect whether it is necessary to reinstall the BlockIO\r
701 //\r
50fa1b3a 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
e237e7ae 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
50fa1b3a 712\r
713 OldTpl = UsbGetCurrentTpl ();\r
1c619535 714 DEBUG ((EFI_D_ERROR, "UsbBootDetectMedia: TPL before reinstall BlockIoProtocol is %d\n", OldTpl));\r
50fa1b3a 715\r
716 gBS->RestoreTPL (TPL_CALLBACK);\r
717\r
e237e7ae 718 gBS->ReinstallProtocolInterface (\r
719 UsbMass->Controller,\r
720 &gEfiBlockIoProtocolGuid,\r
721 &UsbMass->BlockIo,\r
722 &UsbMass->BlockIo\r
723 );\r
50fa1b3a 724\r
1c619535 725 DEBUG ((EFI_D_ERROR, "UsbBootDetectMedia: TPL after reinstall is %d\n", UsbGetCurrentTpl()));\r
50fa1b3a 726 ASSERT (UsbGetCurrentTpl () == TPL_CALLBACK);\r
727\r
728 gBS->RaiseTPL (OldTpl);\r
729\r
e237e7ae 730 //\r
50fa1b3a 731 // Update MediaId after reinstall BLOCK_IO_PROTOCOL\r
e237e7ae 732 //\r
50fa1b3a 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
e237e7ae 739 }\r
50fa1b3a 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
e237e7ae 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
764EFI_STATUS\r
765UsbBootReadBlocks (\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
50fa1b3a 792 // USB command's upper limit timeout is 5s. [USB2.0-9.2.6.1]\r
e237e7ae 793 //\r
50fa1b3a 794 Timeout = (UINT32) USB_BOOT_GENERAL_CMD_TIMEOUT;\r
e237e7ae 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
c52fa98c 802 ReadCmd.Lun = (UINT8) (USB_BOOT_LUN (UsbMass->Lun));\r
e237e7ae 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
840EFI_STATUS\r
841UsbBootWriteBlocks (\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
50fa1b3a 868 // USB command's upper limit timeout is 5s. [USB2.0-9.2.6.1]\r
e237e7ae 869 //\r
50fa1b3a 870 Timeout = (UINT32) USB_BOOT_GENERAL_CMD_TIMEOUT;\r
e237e7ae 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
c52fa98c 878 WriteCmd.Lun = (UINT8) (USB_BOOT_LUN (UsbMass->Lun));\r
e237e7ae 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
915EFI_STATUS\r
916UsbClearEndpointStall (\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
41e8ff27 931 Timeout = USB_BOOT_GENERAL_CMD_TIMEOUT / USB_MASS_1_MILLISECOND;\r
e237e7ae 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