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