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