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