]> git.proxmox.com Git - mirror_edk2.git/blame_incremental - MdeModulePkg/Bus/Scsi/ScsiDiskDxe/ScsiDisk.c
ArmPlatformPkg/LcdPlatformNullLib: fix incorrect library class
[mirror_edk2.git] / MdeModulePkg / Bus / Scsi / ScsiDiskDxe / ScsiDisk.c
... / ...
CommitLineData
1/** @file\r
2 SCSI disk driver that layers on every SCSI IO protocol in the system.\r
3\r
4Copyright (c) 2006 - 2017, Intel Corporation. All rights reserved.<BR>\r
5This program and the accompanying materials\r
6are licensed and made available under the terms and conditions of the BSD License\r
7which accompanies this distribution. The full text of the license may be found at\r
8http://opensource.org/licenses/bsd-license.php\r
9\r
10THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
11WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
12\r
13**/\r
14\r
15\r
16#include "ScsiDisk.h"\r
17\r
18EFI_DRIVER_BINDING_PROTOCOL gScsiDiskDriverBinding = {\r
19 ScsiDiskDriverBindingSupported,\r
20 ScsiDiskDriverBindingStart,\r
21 ScsiDiskDriverBindingStop,\r
22 0xa,\r
23 NULL,\r
24 NULL\r
25};\r
26\r
27EFI_DISK_INFO_PROTOCOL gScsiDiskInfoProtocolTemplate = {\r
28 EFI_DISK_INFO_SCSI_INTERFACE_GUID,\r
29 ScsiDiskInfoInquiry,\r
30 ScsiDiskInfoIdentify,\r
31 ScsiDiskInfoSenseData,\r
32 ScsiDiskInfoWhichIde\r
33};\r
34\r
35/**\r
36 Allocates an aligned buffer for SCSI disk.\r
37\r
38 This function allocates an aligned buffer for the SCSI disk to perform\r
39 SCSI IO operations. The alignment requirement is from SCSI IO interface.\r
40\r
41 @param ScsiDiskDevice The SCSI disk involved for the operation.\r
42 @param BufferSize The request buffer size.\r
43\r
44 @return A pointer to the aligned buffer or NULL if the allocation fails.\r
45\r
46**/\r
47VOID *\r
48AllocateAlignedBuffer (\r
49 IN SCSI_DISK_DEV *ScsiDiskDevice,\r
50 IN UINTN BufferSize\r
51 )\r
52{\r
53 return AllocateAlignedPages (EFI_SIZE_TO_PAGES (BufferSize), ScsiDiskDevice->ScsiIo->IoAlign);\r
54}\r
55\r
56/**\r
57 Frees an aligned buffer for SCSI disk.\r
58\r
59 This function frees an aligned buffer for the SCSI disk to perform\r
60 SCSI IO operations.\r
61\r
62 @param Buffer The aligned buffer to be freed.\r
63 @param BufferSize The request buffer size.\r
64\r
65**/\r
66VOID\r
67FreeAlignedBuffer (\r
68 IN VOID *Buffer,\r
69 IN UINTN BufferSize\r
70 )\r
71{\r
72 if (Buffer != NULL) {\r
73 FreeAlignedPages (Buffer, EFI_SIZE_TO_PAGES (BufferSize));\r
74 }\r
75}\r
76\r
77/**\r
78 The user Entry Point for module ScsiDisk.\r
79\r
80 The user code starts with this function.\r
81\r
82 @param ImageHandle The firmware allocated handle for the EFI image. \r
83 @param SystemTable A pointer to the EFI System Table.\r
84 \r
85 @retval EFI_SUCCESS The entry point is executed successfully.\r
86 @retval other Some error occurs when executing this entry point.\r
87\r
88**/\r
89EFI_STATUS\r
90EFIAPI\r
91InitializeScsiDisk(\r
92 IN EFI_HANDLE ImageHandle,\r
93 IN EFI_SYSTEM_TABLE *SystemTable\r
94 )\r
95{\r
96 EFI_STATUS Status;\r
97\r
98 //\r
99 // Install driver model protocol(s).\r
100 //\r
101 Status = EfiLibInstallDriverBindingComponentName2 (\r
102 ImageHandle,\r
103 SystemTable,\r
104 &gScsiDiskDriverBinding,\r
105 ImageHandle,\r
106 &gScsiDiskComponentName,\r
107 &gScsiDiskComponentName2\r
108 );\r
109 ASSERT_EFI_ERROR (Status);\r
110\r
111\r
112 return Status;\r
113}\r
114\r
115/**\r
116 Test to see if this driver supports ControllerHandle.\r
117\r
118 This service is called by the EFI boot service ConnectController(). In order\r
119 to make drivers as small as possible, there are a few calling restrictions for\r
120 this service. ConnectController() must follow these calling restrictions.\r
121 If any other agent wishes to call Supported() it must also follow these\r
122 calling restrictions.\r
123\r
124 @param This Protocol instance pointer.\r
125 @param ControllerHandle Handle of device to test\r
126 @param RemainingDevicePath Optional parameter use to pick a specific child\r
127 device to start.\r
128\r
129 @retval EFI_SUCCESS This driver supports this device\r
130 @retval EFI_ALREADY_STARTED This driver is already running on this device\r
131 @retval other This driver does not support this device\r
132\r
133**/\r
134EFI_STATUS\r
135EFIAPI\r
136ScsiDiskDriverBindingSupported (\r
137 IN EFI_DRIVER_BINDING_PROTOCOL *This,\r
138 IN EFI_HANDLE Controller,\r
139 IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL\r
140 )\r
141{\r
142 EFI_STATUS Status;\r
143 EFI_SCSI_IO_PROTOCOL *ScsiIo;\r
144 UINT8 DeviceType;\r
145\r
146 Status = gBS->OpenProtocol (\r
147 Controller,\r
148 &gEfiScsiIoProtocolGuid,\r
149 (VOID **) &ScsiIo,\r
150 This->DriverBindingHandle,\r
151 Controller,\r
152 EFI_OPEN_PROTOCOL_BY_DRIVER\r
153 );\r
154 if (EFI_ERROR (Status)) {\r
155 return Status;\r
156 }\r
157\r
158 Status = ScsiIo->GetDeviceType (ScsiIo, &DeviceType);\r
159 if (!EFI_ERROR (Status)) {\r
160 if ((DeviceType == EFI_SCSI_TYPE_DISK) || (DeviceType == EFI_SCSI_TYPE_CDROM)) {\r
161 Status = EFI_SUCCESS;\r
162 } else {\r
163 Status = EFI_UNSUPPORTED;\r
164 }\r
165 }\r
166\r
167 gBS->CloseProtocol (\r
168 Controller,\r
169 &gEfiScsiIoProtocolGuid,\r
170 This->DriverBindingHandle,\r
171 Controller\r
172 );\r
173 return Status;\r
174}\r
175\r
176\r
177/**\r
178 Start this driver on ControllerHandle.\r
179\r
180 This service is called by the EFI boot service ConnectController(). In order\r
181 to make drivers as small as possible, there are a few calling restrictions for\r
182 this service. ConnectController() must follow these calling restrictions. If\r
183 any other agent wishes to call Start() it must also follow these calling\r
184 restrictions.\r
185\r
186 @param This Protocol instance pointer.\r
187 @param ControllerHandle Handle of device to bind driver to\r
188 @param RemainingDevicePath Optional parameter use to pick a specific child\r
189 device to start.\r
190\r
191 @retval EFI_SUCCESS This driver is added to ControllerHandle\r
192 @retval EFI_ALREADY_STARTED This driver is already running on ControllerHandle\r
193 @retval other This driver does not support this device\r
194\r
195**/\r
196EFI_STATUS\r
197EFIAPI\r
198ScsiDiskDriverBindingStart (\r
199 IN EFI_DRIVER_BINDING_PROTOCOL *This,\r
200 IN EFI_HANDLE Controller,\r
201 IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL\r
202 )\r
203{\r
204 EFI_STATUS Status;\r
205 EFI_SCSI_IO_PROTOCOL *ScsiIo;\r
206 SCSI_DISK_DEV *ScsiDiskDevice;\r
207 BOOLEAN Temp;\r
208 UINT8 Index;\r
209 UINT8 MaxRetry;\r
210 BOOLEAN NeedRetry;\r
211 BOOLEAN MustReadCapacity;\r
212\r
213 MustReadCapacity = TRUE;\r
214\r
215 ScsiDiskDevice = (SCSI_DISK_DEV *) AllocateZeroPool (sizeof (SCSI_DISK_DEV));\r
216 if (ScsiDiskDevice == NULL) {\r
217 return EFI_OUT_OF_RESOURCES;\r
218 }\r
219\r
220 Status = gBS->OpenProtocol (\r
221 Controller,\r
222 &gEfiScsiIoProtocolGuid,\r
223 (VOID **) &ScsiIo,\r
224 This->DriverBindingHandle,\r
225 Controller,\r
226 EFI_OPEN_PROTOCOL_BY_DRIVER\r
227 );\r
228 if (EFI_ERROR (Status)) {\r
229 FreePool (ScsiDiskDevice);\r
230 return Status;\r
231 }\r
232\r
233 ScsiDiskDevice->Signature = SCSI_DISK_DEV_SIGNATURE;\r
234 ScsiDiskDevice->ScsiIo = ScsiIo;\r
235 ScsiDiskDevice->BlkIo.Revision = EFI_BLOCK_IO_PROTOCOL_REVISION3;\r
236 ScsiDiskDevice->BlkIo.Media = &ScsiDiskDevice->BlkIoMedia;\r
237 ScsiDiskDevice->BlkIo.Media->IoAlign = ScsiIo->IoAlign;\r
238 ScsiDiskDevice->BlkIo.Reset = ScsiDiskReset;\r
239 ScsiDiskDevice->BlkIo.ReadBlocks = ScsiDiskReadBlocks;\r
240 ScsiDiskDevice->BlkIo.WriteBlocks = ScsiDiskWriteBlocks;\r
241 ScsiDiskDevice->BlkIo.FlushBlocks = ScsiDiskFlushBlocks;\r
242 ScsiDiskDevice->BlkIo2.Media = &ScsiDiskDevice->BlkIoMedia;\r
243 ScsiDiskDevice->BlkIo2.Reset = ScsiDiskResetEx;\r
244 ScsiDiskDevice->BlkIo2.ReadBlocksEx = ScsiDiskReadBlocksEx;\r
245 ScsiDiskDevice->BlkIo2.WriteBlocksEx = ScsiDiskWriteBlocksEx;\r
246 ScsiDiskDevice->BlkIo2.FlushBlocksEx = ScsiDiskFlushBlocksEx;\r
247 ScsiDiskDevice->EraseBlock.Revision = EFI_ERASE_BLOCK_PROTOCOL_REVISION;\r
248 ScsiDiskDevice->EraseBlock.EraseLengthGranularity = 1;\r
249 ScsiDiskDevice->EraseBlock.EraseBlocks = ScsiDiskEraseBlocks;\r
250 ScsiDiskDevice->UnmapInfo.MaxBlkDespCnt = 1;\r
251 ScsiDiskDevice->BlockLimitsVpdSupported = FALSE;\r
252 ScsiDiskDevice->Handle = Controller;\r
253 InitializeListHead (&ScsiDiskDevice->AsyncTaskQueue);\r
254\r
255 ScsiIo->GetDeviceType (ScsiIo, &(ScsiDiskDevice->DeviceType));\r
256 switch (ScsiDiskDevice->DeviceType) {\r
257 case EFI_SCSI_TYPE_DISK:\r
258 ScsiDiskDevice->BlkIo.Media->BlockSize = 0x200;\r
259 MustReadCapacity = TRUE;\r
260 break;\r
261\r
262 case EFI_SCSI_TYPE_CDROM:\r
263 ScsiDiskDevice->BlkIo.Media->BlockSize = 0x800;\r
264 ScsiDiskDevice->BlkIo.Media->ReadOnly = TRUE;\r
265 MustReadCapacity = FALSE;\r
266 break;\r
267 }\r
268 //\r
269 // The Sense Data Array's initial size is 6\r
270 //\r
271 ScsiDiskDevice->SenseDataNumber = 6;\r
272 ScsiDiskDevice->SenseData = (EFI_SCSI_SENSE_DATA *) AllocateZeroPool (\r
273 sizeof (EFI_SCSI_SENSE_DATA) * ScsiDiskDevice->SenseDataNumber\r
274 );\r
275 if (ScsiDiskDevice->SenseData == NULL) {\r
276 gBS->CloseProtocol (\r
277 Controller,\r
278 &gEfiScsiIoProtocolGuid,\r
279 This->DriverBindingHandle,\r
280 Controller\r
281 );\r
282 FreePool (ScsiDiskDevice);\r
283 return EFI_OUT_OF_RESOURCES;\r
284 }\r
285\r
286 //\r
287 // Retrieve device information\r
288 //\r
289 MaxRetry = 2;\r
290 for (Index = 0; Index < MaxRetry; Index++) {\r
291 Status = ScsiDiskInquiryDevice (ScsiDiskDevice, &NeedRetry);\r
292 if (!EFI_ERROR (Status)) {\r
293 break;\r
294 }\r
295\r
296 if (!NeedRetry) {\r
297 FreePool (ScsiDiskDevice->SenseData);\r
298 gBS->CloseProtocol (\r
299 Controller,\r
300 &gEfiScsiIoProtocolGuid,\r
301 This->DriverBindingHandle,\r
302 Controller\r
303 );\r
304 FreePool (ScsiDiskDevice);\r
305 return EFI_DEVICE_ERROR;\r
306 }\r
307 }\r
308 //\r
309 // The second parameter "TRUE" means must\r
310 // retrieve media capacity\r
311 //\r
312 Status = ScsiDiskDetectMedia (ScsiDiskDevice, MustReadCapacity, &Temp);\r
313 if (!EFI_ERROR (Status)) {\r
314 //\r
315 // Determine if Block IO & Block IO2 should be produced on this controller\r
316 // handle\r
317 //\r
318 if (DetermineInstallBlockIo(Controller)) {\r
319 InitializeInstallDiskInfo(ScsiDiskDevice, Controller);\r
320 Status = gBS->InstallMultipleProtocolInterfaces (\r
321 &Controller,\r
322 &gEfiBlockIoProtocolGuid,\r
323 &ScsiDiskDevice->BlkIo,\r
324 &gEfiBlockIo2ProtocolGuid,\r
325 &ScsiDiskDevice->BlkIo2,\r
326 &gEfiDiskInfoProtocolGuid,\r
327 &ScsiDiskDevice->DiskInfo,\r
328 NULL\r
329 );\r
330 if (!EFI_ERROR(Status)) {\r
331 if (DetermineInstallEraseBlock(ScsiDiskDevice, Controller)) {\r
332 Status = gBS->InstallProtocolInterface (\r
333 &Controller,\r
334 &gEfiEraseBlockProtocolGuid,\r
335 EFI_NATIVE_INTERFACE,\r
336 &ScsiDiskDevice->EraseBlock\r
337 );\r
338 if (EFI_ERROR(Status)) {\r
339 DEBUG ((EFI_D_ERROR, "ScsiDisk: Failed to install the Erase Block Protocol! Status = %r\n", Status));\r
340 }\r
341 }\r
342 ScsiDiskDevice->ControllerNameTable = NULL;\r
343 AddUnicodeString2 (\r
344 "eng",\r
345 gScsiDiskComponentName.SupportedLanguages,\r
346 &ScsiDiskDevice->ControllerNameTable,\r
347 L"SCSI Disk Device",\r
348 TRUE\r
349 );\r
350 AddUnicodeString2 (\r
351 "en",\r
352 gScsiDiskComponentName2.SupportedLanguages,\r
353 &ScsiDiskDevice->ControllerNameTable,\r
354 L"SCSI Disk Device",\r
355 FALSE\r
356 );\r
357 return EFI_SUCCESS;\r
358 }\r
359 } \r
360 }\r
361\r
362 gBS->FreePool (ScsiDiskDevice->SenseData);\r
363 gBS->FreePool (ScsiDiskDevice);\r
364 gBS->CloseProtocol (\r
365 Controller,\r
366 &gEfiScsiIoProtocolGuid,\r
367 This->DriverBindingHandle,\r
368 Controller\r
369 );\r
370 return Status;\r
371 \r
372}\r
373\r
374\r
375/**\r
376 Stop this driver on ControllerHandle.\r
377\r
378 This service is called by the EFI boot service DisconnectController().\r
379 In order to make drivers as small as possible, there are a few calling\r
380 restrictions for this service. DisconnectController() must follow these\r
381 calling restrictions. If any other agent wishes to call Stop() it must\r
382 also follow these calling restrictions.\r
383 \r
384 @param This Protocol instance pointer.\r
385 @param ControllerHandle Handle of device to stop driver on\r
386 @param NumberOfChildren Number of Handles in ChildHandleBuffer. If number of\r
387 children is zero stop the entire bus driver.\r
388 @param ChildHandleBuffer List of Child Handles to Stop.\r
389\r
390 @retval EFI_SUCCESS This driver is removed ControllerHandle\r
391 @retval other This driver was not removed from this device\r
392\r
393**/\r
394EFI_STATUS\r
395EFIAPI\r
396ScsiDiskDriverBindingStop (\r
397 IN EFI_DRIVER_BINDING_PROTOCOL *This,\r
398 IN EFI_HANDLE Controller,\r
399 IN UINTN NumberOfChildren,\r
400 IN EFI_HANDLE *ChildHandleBuffer OPTIONAL\r
401 )\r
402{\r
403 EFI_BLOCK_IO_PROTOCOL *BlkIo;\r
404 EFI_ERASE_BLOCK_PROTOCOL *EraseBlock;\r
405 SCSI_DISK_DEV *ScsiDiskDevice;\r
406 EFI_STATUS Status;\r
407\r
408 Status = gBS->OpenProtocol (\r
409 Controller,\r
410 &gEfiBlockIoProtocolGuid,\r
411 (VOID **) &BlkIo,\r
412 This->DriverBindingHandle,\r
413 Controller,\r
414 EFI_OPEN_PROTOCOL_GET_PROTOCOL\r
415 );\r
416 if (EFI_ERROR (Status)) {\r
417 return Status;\r
418 }\r
419\r
420 ScsiDiskDevice = SCSI_DISK_DEV_FROM_BLKIO (BlkIo);\r
421\r
422 //\r
423 // Wait for the BlockIo2 requests queue to become empty\r
424 //\r
425 while (!IsListEmpty (&ScsiDiskDevice->AsyncTaskQueue));\r
426\r
427 //\r
428 // If Erase Block Protocol is installed, then uninstall this protocol.\r
429 //\r
430 Status = gBS->OpenProtocol (\r
431 Controller,\r
432 &gEfiEraseBlockProtocolGuid,\r
433 (VOID **) &EraseBlock,\r
434 This->DriverBindingHandle,\r
435 Controller,\r
436 EFI_OPEN_PROTOCOL_GET_PROTOCOL\r
437 );\r
438\r
439 if (!EFI_ERROR (Status)) {\r
440 Status = gBS->UninstallProtocolInterface (\r
441 Controller,\r
442 &gEfiEraseBlockProtocolGuid,\r
443 &ScsiDiskDevice->EraseBlock\r
444 );\r
445 if (EFI_ERROR (Status)) {\r
446 return Status;\r
447 }\r
448 }\r
449\r
450 Status = gBS->UninstallMultipleProtocolInterfaces (\r
451 Controller,\r
452 &gEfiBlockIoProtocolGuid,\r
453 &ScsiDiskDevice->BlkIo,\r
454 &gEfiBlockIo2ProtocolGuid,\r
455 &ScsiDiskDevice->BlkIo2,\r
456 &gEfiDiskInfoProtocolGuid,\r
457 &ScsiDiskDevice->DiskInfo,\r
458 NULL\r
459 );\r
460 if (!EFI_ERROR (Status)) {\r
461 gBS->CloseProtocol (\r
462 Controller,\r
463 &gEfiScsiIoProtocolGuid,\r
464 This->DriverBindingHandle,\r
465 Controller\r
466 );\r
467\r
468 ReleaseScsiDiskDeviceResources (ScsiDiskDevice);\r
469\r
470 return EFI_SUCCESS;\r
471 }\r
472 //\r
473 // errors met\r
474 //\r
475 return Status;\r
476}\r
477\r
478/**\r
479 Reset SCSI Disk.\r
480\r
481\r
482 @param This The pointer of EFI_BLOCK_IO_PROTOCOL\r
483 @param ExtendedVerification The flag about if extend verificate\r
484\r
485 @retval EFI_SUCCESS The device was reset.\r
486 @retval EFI_DEVICE_ERROR The device is not functioning properly and could\r
487 not be reset.\r
488 @return EFI_STATUS is returned from EFI_SCSI_IO_PROTOCOL.ResetDevice().\r
489\r
490**/\r
491EFI_STATUS\r
492EFIAPI\r
493ScsiDiskReset (\r
494 IN EFI_BLOCK_IO_PROTOCOL *This,\r
495 IN BOOLEAN ExtendedVerification\r
496 )\r
497{\r
498 EFI_TPL OldTpl;\r
499 SCSI_DISK_DEV *ScsiDiskDevice;\r
500 EFI_STATUS Status;\r
501\r
502 OldTpl = gBS->RaiseTPL (TPL_CALLBACK);\r
503\r
504 ScsiDiskDevice = SCSI_DISK_DEV_FROM_BLKIO (This);\r
505\r
506 Status = ScsiDiskDevice->ScsiIo->ResetDevice (ScsiDiskDevice->ScsiIo);\r
507\r
508 if (EFI_ERROR (Status)) {\r
509 if (Status == EFI_UNSUPPORTED) {\r
510 Status = EFI_SUCCESS;\r
511 } else {\r
512 Status = EFI_DEVICE_ERROR;\r
513 goto Done;\r
514 }\r
515 }\r
516\r
517 if (!ExtendedVerification) {\r
518 goto Done;\r
519 }\r
520\r
521 Status = ScsiDiskDevice->ScsiIo->ResetBus (ScsiDiskDevice->ScsiIo);\r
522\r
523 if (EFI_ERROR (Status)) {\r
524 Status = EFI_DEVICE_ERROR;\r
525 goto Done;\r
526 }\r
527\r
528Done:\r
529 gBS->RestoreTPL (OldTpl);\r
530 return Status;\r
531}\r
532\r
533/**\r
534 The function is to Read Block from SCSI Disk.\r
535\r
536 @param This The pointer of EFI_BLOCK_IO_PROTOCOL.\r
537 @param MediaId The Id of Media detected\r
538 @param Lba The logic block address\r
539 @param BufferSize The size of Buffer\r
540 @param Buffer The buffer to fill the read out data\r
541\r
542 @retval EFI_SUCCESS Successfully to read out block.\r
543 @retval EFI_DEVICE_ERROR Fail to detect media.\r
544 @retval EFI_NO_MEDIA Media is not present.\r
545 @retval EFI_MEDIA_CHANGED Media has changed.\r
546 @retval EFI_BAD_BUFFER_SIZE The Buffer was not a multiple of the block size of the device.\r
547 @retval EFI_INVALID_PARAMETER Invalid parameter passed in.\r
548\r
549**/\r
550EFI_STATUS\r
551EFIAPI\r
552ScsiDiskReadBlocks (\r
553 IN EFI_BLOCK_IO_PROTOCOL *This,\r
554 IN UINT32 MediaId,\r
555 IN EFI_LBA Lba,\r
556 IN UINTN BufferSize,\r
557 OUT VOID *Buffer\r
558 )\r
559{\r
560 SCSI_DISK_DEV *ScsiDiskDevice;\r
561 EFI_BLOCK_IO_MEDIA *Media;\r
562 EFI_STATUS Status;\r
563 UINTN BlockSize;\r
564 UINTN NumberOfBlocks;\r
565 BOOLEAN MediaChange;\r
566 EFI_TPL OldTpl;\r
567\r
568 MediaChange = FALSE;\r
569 OldTpl = gBS->RaiseTPL (TPL_CALLBACK);\r
570 ScsiDiskDevice = SCSI_DISK_DEV_FROM_BLKIO (This);\r
571\r
572 if (!IS_DEVICE_FIXED(ScsiDiskDevice)) {\r
573\r
574 Status = ScsiDiskDetectMedia (ScsiDiskDevice, FALSE, &MediaChange);\r
575 if (EFI_ERROR (Status)) {\r
576 Status = EFI_DEVICE_ERROR;\r
577 goto Done;\r
578 }\r
579\r
580 if (MediaChange) {\r
581 gBS->ReinstallProtocolInterface (\r
582 ScsiDiskDevice->Handle,\r
583 &gEfiBlockIoProtocolGuid,\r
584 &ScsiDiskDevice->BlkIo,\r
585 &ScsiDiskDevice->BlkIo\r
586 );\r
587 gBS->ReinstallProtocolInterface (\r
588 ScsiDiskDevice->Handle,\r
589 &gEfiBlockIo2ProtocolGuid,\r
590 &ScsiDiskDevice->BlkIo2,\r
591 &ScsiDiskDevice->BlkIo2\r
592 );\r
593 if (DetermineInstallEraseBlock(ScsiDiskDevice, ScsiDiskDevice->Handle)) {\r
594 gBS->ReinstallProtocolInterface (\r
595 ScsiDiskDevice->Handle,\r
596 &gEfiEraseBlockProtocolGuid,\r
597 &ScsiDiskDevice->EraseBlock,\r
598 &ScsiDiskDevice->EraseBlock\r
599 );\r
600 }\r
601 Status = EFI_MEDIA_CHANGED;\r
602 goto Done;\r
603 }\r
604 }\r
605 //\r
606 // Get the intrinsic block size\r
607 //\r
608 Media = ScsiDiskDevice->BlkIo.Media;\r
609 BlockSize = Media->BlockSize;\r
610\r
611 NumberOfBlocks = BufferSize / BlockSize;\r
612\r
613 if (!(Media->MediaPresent)) {\r
614 Status = EFI_NO_MEDIA;\r
615 goto Done;\r
616 }\r
617\r
618 if (MediaId != Media->MediaId) {\r
619 Status = EFI_MEDIA_CHANGED;\r
620 goto Done;\r
621 }\r
622\r
623 if (Buffer == NULL) {\r
624 Status = EFI_INVALID_PARAMETER;\r
625 goto Done;\r
626 }\r
627\r
628 if (BufferSize == 0) {\r
629 Status = EFI_SUCCESS;\r
630 goto Done;\r
631 }\r
632\r
633 if (BufferSize % BlockSize != 0) {\r
634 Status = EFI_BAD_BUFFER_SIZE;\r
635 goto Done;\r
636 }\r
637\r
638 if (Lba > Media->LastBlock) {\r
639 Status = EFI_INVALID_PARAMETER;\r
640 goto Done;\r
641 }\r
642\r
643 if ((Lba + NumberOfBlocks - 1) > Media->LastBlock) {\r
644 Status = EFI_INVALID_PARAMETER;\r
645 goto Done;\r
646 }\r
647\r
648 if ((Media->IoAlign > 1) && (((UINTN) Buffer & (Media->IoAlign - 1)) != 0)) {\r
649 Status = EFI_INVALID_PARAMETER;\r
650 goto Done;\r
651 }\r
652\r
653 //\r
654 // If all the parameters are valid, then perform read sectors command\r
655 // to transfer data from device to host.\r
656 //\r
657 Status = ScsiDiskReadSectors (ScsiDiskDevice, Buffer, Lba, NumberOfBlocks);\r
658\r
659Done:\r
660 gBS->RestoreTPL (OldTpl);\r
661 return Status;\r
662}\r
663\r
664/**\r
665 The function is to Write Block to SCSI Disk.\r
666\r
667 @param This The pointer of EFI_BLOCK_IO_PROTOCOL\r
668 @param MediaId The Id of Media detected\r
669 @param Lba The logic block address\r
670 @param BufferSize The size of Buffer\r
671 @param Buffer The buffer to fill the read out data\r
672\r
673 @retval EFI_SUCCESS Successfully to read out block.\r
674 @retval EFI_WRITE_PROTECTED The device can not be written to.\r
675 @retval EFI_DEVICE_ERROR Fail to detect media.\r
676 @retval EFI_NO_MEDIA Media is not present.\r
677 @retval EFI_MEDIA_CHNAGED Media has changed.\r
678 @retval EFI_BAD_BUFFER_SIZE The Buffer was not a multiple of the block size of the device.\r
679 @retval EFI_INVALID_PARAMETER Invalid parameter passed in.\r
680\r
681**/\r
682EFI_STATUS\r
683EFIAPI\r
684ScsiDiskWriteBlocks (\r
685 IN EFI_BLOCK_IO_PROTOCOL *This,\r
686 IN UINT32 MediaId,\r
687 IN EFI_LBA Lba,\r
688 IN UINTN BufferSize,\r
689 IN VOID *Buffer\r
690 )\r
691{\r
692 SCSI_DISK_DEV *ScsiDiskDevice;\r
693 EFI_BLOCK_IO_MEDIA *Media;\r
694 EFI_STATUS Status;\r
695 UINTN BlockSize;\r
696 UINTN NumberOfBlocks;\r
697 BOOLEAN MediaChange;\r
698 EFI_TPL OldTpl;\r
699\r
700 MediaChange = FALSE;\r
701 OldTpl = gBS->RaiseTPL (TPL_CALLBACK);\r
702 ScsiDiskDevice = SCSI_DISK_DEV_FROM_BLKIO (This);\r
703\r
704 if (!IS_DEVICE_FIXED(ScsiDiskDevice)) {\r
705\r
706 Status = ScsiDiskDetectMedia (ScsiDiskDevice, FALSE, &MediaChange);\r
707 if (EFI_ERROR (Status)) {\r
708 Status = EFI_DEVICE_ERROR;\r
709 goto Done;\r
710 }\r
711\r
712 if (MediaChange) {\r
713 gBS->ReinstallProtocolInterface (\r
714 ScsiDiskDevice->Handle,\r
715 &gEfiBlockIoProtocolGuid,\r
716 &ScsiDiskDevice->BlkIo,\r
717 &ScsiDiskDevice->BlkIo\r
718 );\r
719 gBS->ReinstallProtocolInterface (\r
720 ScsiDiskDevice->Handle,\r
721 &gEfiBlockIo2ProtocolGuid,\r
722 &ScsiDiskDevice->BlkIo2,\r
723 &ScsiDiskDevice->BlkIo2\r
724 );\r
725 if (DetermineInstallEraseBlock(ScsiDiskDevice, ScsiDiskDevice->Handle)) {\r
726 gBS->ReinstallProtocolInterface (\r
727 ScsiDiskDevice->Handle,\r
728 &gEfiEraseBlockProtocolGuid,\r
729 &ScsiDiskDevice->EraseBlock,\r
730 &ScsiDiskDevice->EraseBlock\r
731 );\r
732 }\r
733 Status = EFI_MEDIA_CHANGED;\r
734 goto Done;\r
735 }\r
736 }\r
737 //\r
738 // Get the intrinsic block size\r
739 //\r
740 Media = ScsiDiskDevice->BlkIo.Media;\r
741 BlockSize = Media->BlockSize;\r
742\r
743 NumberOfBlocks = BufferSize / BlockSize;\r
744\r
745 if (!(Media->MediaPresent)) {\r
746 Status = EFI_NO_MEDIA;\r
747 goto Done;\r
748 }\r
749\r
750 if (MediaId != Media->MediaId) {\r
751 Status = EFI_MEDIA_CHANGED;\r
752 goto Done;\r
753 }\r
754\r
755 if (Media->ReadOnly) {\r
756 Status = EFI_WRITE_PROTECTED;\r
757 goto Done;\r
758 }\r
759\r
760 if (BufferSize == 0) {\r
761 Status = EFI_SUCCESS;\r
762 goto Done;\r
763 }\r
764\r
765 if (Buffer == NULL) {\r
766 Status = EFI_INVALID_PARAMETER;\r
767 goto Done;\r
768 }\r
769\r
770 if (BufferSize % BlockSize != 0) {\r
771 Status = EFI_BAD_BUFFER_SIZE;\r
772 goto Done;\r
773 }\r
774\r
775 if (Lba > Media->LastBlock) {\r
776 Status = EFI_INVALID_PARAMETER;\r
777 goto Done;\r
778 }\r
779\r
780 if ((Lba + NumberOfBlocks - 1) > Media->LastBlock) {\r
781 Status = EFI_INVALID_PARAMETER;\r
782 goto Done;\r
783 }\r
784\r
785 if ((Media->IoAlign > 1) && (((UINTN) Buffer & (Media->IoAlign - 1)) != 0)) {\r
786 Status = EFI_INVALID_PARAMETER;\r
787 goto Done;\r
788 }\r
789 //\r
790 // if all the parameters are valid, then perform read sectors command\r
791 // to transfer data from device to host.\r
792 //\r
793 Status = ScsiDiskWriteSectors (ScsiDiskDevice, Buffer, Lba, NumberOfBlocks);\r
794\r
795Done:\r
796 gBS->RestoreTPL (OldTpl);\r
797 return Status;\r
798}\r
799\r
800/**\r
801 Flush Block to Disk.\r
802\r
803 EFI_SUCCESS is returned directly.\r
804\r
805 @param This The pointer of EFI_BLOCK_IO_PROTOCOL\r
806\r
807 @retval EFI_SUCCESS All outstanding data was written to the device\r
808\r
809**/\r
810EFI_STATUS\r
811EFIAPI\r
812ScsiDiskFlushBlocks (\r
813 IN EFI_BLOCK_IO_PROTOCOL *This\r
814 )\r
815{\r
816 //\r
817 // return directly\r
818 //\r
819 return EFI_SUCCESS;\r
820}\r
821\r
822\r
823/**\r
824 Reset SCSI Disk.\r
825\r
826 @param This The pointer of EFI_BLOCK_IO2_PROTOCOL.\r
827 @param ExtendedVerification The flag about if extend verificate.\r
828\r
829 @retval EFI_SUCCESS The device was reset.\r
830 @retval EFI_DEVICE_ERROR The device is not functioning properly and could\r
831 not be reset.\r
832 @return EFI_STATUS is returned from EFI_SCSI_IO_PROTOCOL.ResetDevice().\r
833\r
834**/\r
835EFI_STATUS\r
836EFIAPI\r
837ScsiDiskResetEx (\r
838 IN EFI_BLOCK_IO2_PROTOCOL *This,\r
839 IN BOOLEAN ExtendedVerification\r
840 )\r
841{\r
842 EFI_TPL OldTpl;\r
843 SCSI_DISK_DEV *ScsiDiskDevice;\r
844 EFI_STATUS Status;\r
845\r
846 OldTpl = gBS->RaiseTPL (TPL_CALLBACK);\r
847\r
848 ScsiDiskDevice = SCSI_DISK_DEV_FROM_BLKIO2 (This);\r
849\r
850 Status = ScsiDiskDevice->ScsiIo->ResetDevice (ScsiDiskDevice->ScsiIo);\r
851\r
852 if (EFI_ERROR (Status)) {\r
853 if (Status == EFI_UNSUPPORTED) {\r
854 Status = EFI_SUCCESS;\r
855 } else {\r
856 Status = EFI_DEVICE_ERROR;\r
857 goto Done;\r
858 }\r
859 }\r
860\r
861 if (!ExtendedVerification) {\r
862 goto Done;\r
863 }\r
864\r
865 Status = ScsiDiskDevice->ScsiIo->ResetBus (ScsiDiskDevice->ScsiIo);\r
866\r
867 if (EFI_ERROR (Status)) {\r
868 Status = EFI_DEVICE_ERROR;\r
869 goto Done;\r
870 }\r
871\r
872Done:\r
873 gBS->RestoreTPL (OldTpl);\r
874 return Status;\r
875}\r
876\r
877/**\r
878 The function is to Read Block from SCSI Disk.\r
879\r
880 @param This The pointer of EFI_BLOCK_IO_PROTOCOL.\r
881 @param MediaId The Id of Media detected.\r
882 @param Lba The logic block address.\r
883 @param Token A pointer to the token associated with the transaction.\r
884 @param BufferSize The size of Buffer.\r
885 @param Buffer The buffer to fill the read out data.\r
886\r
887 @retval EFI_SUCCESS The read request was queued if Token-> Event is\r
888 not NULL. The data was read correctly from the\r
889 device if theToken-> Event is NULL.\r
890 @retval EFI_DEVICE_ERROR The device reported an error while attempting\r
891 to perform the read operation.\r
892 @retval EFI_NO_MEDIA There is no media in the device.\r
893 @retval EFI_MEDIA_CHANGED The MediaId is not for the current media.\r
894 @retval EFI_BAD_BUFFER_SIZE The BufferSize parameter is not a multiple of\r
895 the intrinsic block size of the device.\r
896 @retval EFI_INVALID_PARAMETER The read request contains LBAs that are not\r
897 valid, or the buffer is not on proper\r
898 alignment.\r
899 @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a\r
900 lack of resources.\r
901\r
902**/\r
903EFI_STATUS\r
904EFIAPI\r
905ScsiDiskReadBlocksEx (\r
906 IN EFI_BLOCK_IO2_PROTOCOL *This,\r
907 IN UINT32 MediaId,\r
908 IN EFI_LBA Lba,\r
909 IN OUT EFI_BLOCK_IO2_TOKEN *Token,\r
910 IN UINTN BufferSize,\r
911 OUT VOID *Buffer\r
912 )\r
913{\r
914 SCSI_DISK_DEV *ScsiDiskDevice;\r
915 EFI_BLOCK_IO_MEDIA *Media;\r
916 EFI_STATUS Status;\r
917 UINTN BlockSize;\r
918 UINTN NumberOfBlocks;\r
919 BOOLEAN MediaChange;\r
920 EFI_TPL OldTpl;\r
921\r
922 MediaChange = FALSE;\r
923 OldTpl = gBS->RaiseTPL (TPL_CALLBACK);\r
924 ScsiDiskDevice = SCSI_DISK_DEV_FROM_BLKIO2 (This);\r
925\r
926 if (!IS_DEVICE_FIXED(ScsiDiskDevice)) {\r
927\r
928 Status = ScsiDiskDetectMedia (ScsiDiskDevice, FALSE, &MediaChange);\r
929 if (EFI_ERROR (Status)) {\r
930 Status = EFI_DEVICE_ERROR;\r
931 goto Done;\r
932 }\r
933\r
934 if (MediaChange) {\r
935 gBS->ReinstallProtocolInterface (\r
936 ScsiDiskDevice->Handle,\r
937 &gEfiBlockIoProtocolGuid,\r
938 &ScsiDiskDevice->BlkIo,\r
939 &ScsiDiskDevice->BlkIo\r
940 );\r
941 gBS->ReinstallProtocolInterface (\r
942 ScsiDiskDevice->Handle,\r
943 &gEfiBlockIo2ProtocolGuid,\r
944 &ScsiDiskDevice->BlkIo2,\r
945 &ScsiDiskDevice->BlkIo2\r
946 );\r
947 if (DetermineInstallEraseBlock(ScsiDiskDevice, ScsiDiskDevice->Handle)) {\r
948 gBS->ReinstallProtocolInterface (\r
949 ScsiDiskDevice->Handle,\r
950 &gEfiEraseBlockProtocolGuid,\r
951 &ScsiDiskDevice->EraseBlock,\r
952 &ScsiDiskDevice->EraseBlock\r
953 );\r
954 }\r
955 Status = EFI_MEDIA_CHANGED;\r
956 goto Done;\r
957 }\r
958 }\r
959 //\r
960 // Get the intrinsic block size\r
961 //\r
962 Media = ScsiDiskDevice->BlkIo2.Media;\r
963 BlockSize = Media->BlockSize;\r
964\r
965 NumberOfBlocks = BufferSize / BlockSize;\r
966\r
967 if (!(Media->MediaPresent)) {\r
968 Status = EFI_NO_MEDIA;\r
969 goto Done;\r
970 }\r
971\r
972 if (MediaId != Media->MediaId) {\r
973 Status = EFI_MEDIA_CHANGED;\r
974 goto Done;\r
975 }\r
976\r
977 if (Buffer == NULL) {\r
978 Status = EFI_INVALID_PARAMETER;\r
979 goto Done;\r
980 }\r
981\r
982 if (BufferSize == 0) {\r
983 if ((Token != NULL) && (Token->Event != NULL)) {\r
984 Token->TransactionStatus = EFI_SUCCESS;\r
985 gBS->SignalEvent (Token->Event);\r
986 }\r
987\r
988 Status = EFI_SUCCESS;\r
989 goto Done;\r
990 }\r
991\r
992 if (BufferSize % BlockSize != 0) {\r
993 Status = EFI_BAD_BUFFER_SIZE;\r
994 goto Done;\r
995 }\r
996\r
997 if (Lba > Media->LastBlock) {\r
998 Status = EFI_INVALID_PARAMETER;\r
999 goto Done;\r
1000 }\r
1001\r
1002 if ((Lba + NumberOfBlocks - 1) > Media->LastBlock) {\r
1003 Status = EFI_INVALID_PARAMETER;\r
1004 goto Done;\r
1005 }\r
1006\r
1007 if ((Media->IoAlign > 1) && (((UINTN) Buffer & (Media->IoAlign - 1)) != 0)) {\r
1008 Status = EFI_INVALID_PARAMETER;\r
1009 goto Done;\r
1010 }\r
1011\r
1012 //\r
1013 // If all the parameters are valid, then perform read sectors command\r
1014 // to transfer data from device to host.\r
1015 //\r
1016 if ((Token != NULL) && (Token->Event != NULL)) {\r
1017 Token->TransactionStatus = EFI_SUCCESS;\r
1018 Status = ScsiDiskAsyncReadSectors (\r
1019 ScsiDiskDevice,\r
1020 Buffer,\r
1021 Lba,\r
1022 NumberOfBlocks,\r
1023 Token\r
1024 );\r
1025 } else {\r
1026 Status = ScsiDiskReadSectors (\r
1027 ScsiDiskDevice,\r
1028 Buffer,\r
1029 Lba,\r
1030 NumberOfBlocks\r
1031 );\r
1032 }\r
1033\r
1034Done:\r
1035 gBS->RestoreTPL (OldTpl);\r
1036 return Status;\r
1037}\r
1038\r
1039/**\r
1040 The function is to Write Block to SCSI Disk.\r
1041\r
1042 @param This The pointer of EFI_BLOCK_IO_PROTOCOL.\r
1043 @param MediaId The Id of Media detected.\r
1044 @param Lba The logic block address.\r
1045 @param Token A pointer to the token associated with the transaction.\r
1046 @param BufferSize The size of Buffer.\r
1047 @param Buffer The buffer to fill the read out data.\r
1048\r
1049 @retval EFI_SUCCESS The data were written correctly to the device.\r
1050 @retval EFI_WRITE_PROTECTED The device cannot be written to.\r
1051 @retval EFI_NO_MEDIA There is no media in the device.\r
1052 @retval EFI_MEDIA_CHANGED The MediaId is not for the current media.\r
1053 @retval EFI_DEVICE_ERROR The device reported an error while attempting\r
1054 to perform the write operation.\r
1055 @retval EFI_BAD_BUFFER_SIZE The BufferSize parameter is not a multiple of\r
1056 the intrinsic block size of the device.\r
1057 @retval EFI_INVALID_PARAMETER The write request contains LBAs that are not\r
1058 valid, or the buffer is not on proper\r
1059 alignment.\r
1060\r
1061**/\r
1062EFI_STATUS\r
1063EFIAPI\r
1064ScsiDiskWriteBlocksEx (\r
1065 IN EFI_BLOCK_IO2_PROTOCOL *This,\r
1066 IN UINT32 MediaId,\r
1067 IN EFI_LBA Lba,\r
1068 IN OUT EFI_BLOCK_IO2_TOKEN *Token,\r
1069 IN UINTN BufferSize,\r
1070 IN VOID *Buffer\r
1071 )\r
1072{\r
1073 SCSI_DISK_DEV *ScsiDiskDevice;\r
1074 EFI_BLOCK_IO_MEDIA *Media;\r
1075 EFI_STATUS Status;\r
1076 UINTN BlockSize;\r
1077 UINTN NumberOfBlocks;\r
1078 BOOLEAN MediaChange;\r
1079 EFI_TPL OldTpl;\r
1080\r
1081 MediaChange = FALSE;\r
1082 OldTpl = gBS->RaiseTPL (TPL_CALLBACK);\r
1083 ScsiDiskDevice = SCSI_DISK_DEV_FROM_BLKIO2 (This);\r
1084\r
1085 if (!IS_DEVICE_FIXED(ScsiDiskDevice)) {\r
1086\r
1087 Status = ScsiDiskDetectMedia (ScsiDiskDevice, FALSE, &MediaChange);\r
1088 if (EFI_ERROR (Status)) {\r
1089 Status = EFI_DEVICE_ERROR;\r
1090 goto Done;\r
1091 }\r
1092\r
1093 if (MediaChange) {\r
1094 gBS->ReinstallProtocolInterface (\r
1095 ScsiDiskDevice->Handle,\r
1096 &gEfiBlockIoProtocolGuid,\r
1097 &ScsiDiskDevice->BlkIo,\r
1098 &ScsiDiskDevice->BlkIo\r
1099 );\r
1100 gBS->ReinstallProtocolInterface (\r
1101 ScsiDiskDevice->Handle,\r
1102 &gEfiBlockIo2ProtocolGuid,\r
1103 &ScsiDiskDevice->BlkIo2,\r
1104 &ScsiDiskDevice->BlkIo2\r
1105 );\r
1106 if (DetermineInstallEraseBlock(ScsiDiskDevice, ScsiDiskDevice->Handle)) {\r
1107 gBS->ReinstallProtocolInterface (\r
1108 ScsiDiskDevice->Handle,\r
1109 &gEfiEraseBlockProtocolGuid,\r
1110 &ScsiDiskDevice->EraseBlock,\r
1111 &ScsiDiskDevice->EraseBlock\r
1112 );\r
1113 }\r
1114 Status = EFI_MEDIA_CHANGED;\r
1115 goto Done;\r
1116 }\r
1117 }\r
1118 //\r
1119 // Get the intrinsic block size\r
1120 //\r
1121 Media = ScsiDiskDevice->BlkIo2.Media;\r
1122 BlockSize = Media->BlockSize;\r
1123\r
1124 NumberOfBlocks = BufferSize / BlockSize;\r
1125\r
1126 if (!(Media->MediaPresent)) {\r
1127 Status = EFI_NO_MEDIA;\r
1128 goto Done;\r
1129 }\r
1130\r
1131 if (MediaId != Media->MediaId) {\r
1132 Status = EFI_MEDIA_CHANGED;\r
1133 goto Done;\r
1134 }\r
1135\r
1136 if (Media->ReadOnly) {\r
1137 Status = EFI_WRITE_PROTECTED;\r
1138 goto Done;\r
1139 }\r
1140\r
1141 if (BufferSize == 0) {\r
1142 if ((Token != NULL) && (Token->Event != NULL)) {\r
1143 Token->TransactionStatus = EFI_SUCCESS;\r
1144 gBS->SignalEvent (Token->Event);\r
1145 }\r
1146\r
1147 Status = EFI_SUCCESS;\r
1148 goto Done;\r
1149 }\r
1150\r
1151 if (Buffer == NULL) {\r
1152 Status = EFI_INVALID_PARAMETER;\r
1153 goto Done;\r
1154 }\r
1155\r
1156 if (BufferSize % BlockSize != 0) {\r
1157 Status = EFI_BAD_BUFFER_SIZE;\r
1158 goto Done;\r
1159 }\r
1160\r
1161 if (Lba > Media->LastBlock) {\r
1162 Status = EFI_INVALID_PARAMETER;\r
1163 goto Done;\r
1164 }\r
1165\r
1166 if ((Lba + NumberOfBlocks - 1) > Media->LastBlock) {\r
1167 Status = EFI_INVALID_PARAMETER;\r
1168 goto Done;\r
1169 }\r
1170\r
1171 if ((Media->IoAlign > 1) && (((UINTN) Buffer & (Media->IoAlign - 1)) != 0)) {\r
1172 Status = EFI_INVALID_PARAMETER;\r
1173 goto Done;\r
1174 }\r
1175\r
1176 //\r
1177 // if all the parameters are valid, then perform write sectors command\r
1178 // to transfer data from device to host.\r
1179 //\r
1180 if ((Token != NULL) && (Token->Event != NULL)) {\r
1181 Token->TransactionStatus = EFI_SUCCESS;\r
1182 Status = ScsiDiskAsyncWriteSectors (\r
1183 ScsiDiskDevice,\r
1184 Buffer,\r
1185 Lba,\r
1186 NumberOfBlocks,\r
1187 Token\r
1188 );\r
1189 } else {\r
1190 Status = ScsiDiskWriteSectors (\r
1191 ScsiDiskDevice,\r
1192 Buffer,\r
1193 Lba,\r
1194 NumberOfBlocks\r
1195 );\r
1196 }\r
1197\r
1198Done:\r
1199 gBS->RestoreTPL (OldTpl);\r
1200 return Status;\r
1201}\r
1202\r
1203/**\r
1204 Flush the Block Device.\r
1205\r
1206 @param This Indicates a pointer to the calling context.\r
1207 @param Token A pointer to the token associated with the transaction.\r
1208\r
1209 @retval EFI_SUCCESS All outstanding data was written to the device.\r
1210 @retval EFI_DEVICE_ERROR The device reported an error while attempting to\r
1211 write data.\r
1212 @retval EFI_WRITE_PROTECTED The device cannot be written to.\r
1213 @retval EFI_NO_MEDIA There is no media in the device.\r
1214 @retval EFI_MEDIA_CHANGED The MediaId is not for the current media.\r
1215\r
1216**/\r
1217EFI_STATUS\r
1218EFIAPI\r
1219ScsiDiskFlushBlocksEx (\r
1220 IN EFI_BLOCK_IO2_PROTOCOL *This,\r
1221 IN OUT EFI_BLOCK_IO2_TOKEN *Token\r
1222 )\r
1223{\r
1224 SCSI_DISK_DEV *ScsiDiskDevice;\r
1225 EFI_BLOCK_IO_MEDIA *Media;\r
1226 EFI_STATUS Status;\r
1227 BOOLEAN MediaChange;\r
1228 EFI_TPL OldTpl;\r
1229\r
1230 MediaChange = FALSE;\r
1231 OldTpl = gBS->RaiseTPL (TPL_CALLBACK);\r
1232 ScsiDiskDevice = SCSI_DISK_DEV_FROM_BLKIO2 (This);\r
1233\r
1234 if (!IS_DEVICE_FIXED(ScsiDiskDevice)) {\r
1235\r
1236 Status = ScsiDiskDetectMedia (ScsiDiskDevice, FALSE, &MediaChange);\r
1237 if (EFI_ERROR (Status)) {\r
1238 Status = EFI_DEVICE_ERROR;\r
1239 goto Done;\r
1240 }\r
1241\r
1242 if (MediaChange) {\r
1243 gBS->ReinstallProtocolInterface (\r
1244 ScsiDiskDevice->Handle,\r
1245 &gEfiBlockIoProtocolGuid,\r
1246 &ScsiDiskDevice->BlkIo,\r
1247 &ScsiDiskDevice->BlkIo\r
1248 );\r
1249 gBS->ReinstallProtocolInterface (\r
1250 ScsiDiskDevice->Handle,\r
1251 &gEfiBlockIo2ProtocolGuid,\r
1252 &ScsiDiskDevice->BlkIo2,\r
1253 &ScsiDiskDevice->BlkIo2\r
1254 );\r
1255 if (DetermineInstallEraseBlock(ScsiDiskDevice, ScsiDiskDevice->Handle)) {\r
1256 gBS->ReinstallProtocolInterface (\r
1257 ScsiDiskDevice->Handle,\r
1258 &gEfiEraseBlockProtocolGuid,\r
1259 &ScsiDiskDevice->EraseBlock,\r
1260 &ScsiDiskDevice->EraseBlock\r
1261 );\r
1262 }\r
1263 Status = EFI_MEDIA_CHANGED;\r
1264 goto Done;\r
1265 }\r
1266 }\r
1267\r
1268 Media = ScsiDiskDevice->BlkIo2.Media;\r
1269\r
1270 if (!(Media->MediaPresent)) {\r
1271 Status = EFI_NO_MEDIA;\r
1272 goto Done;\r
1273 }\r
1274\r
1275 if (Media->ReadOnly) {\r
1276 Status = EFI_WRITE_PROTECTED;\r
1277 goto Done;\r
1278 }\r
1279\r
1280 //\r
1281 // Wait for the BlockIo2 requests queue to become empty\r
1282 //\r
1283 while (!IsListEmpty (&ScsiDiskDevice->AsyncTaskQueue));\r
1284\r
1285 Status = EFI_SUCCESS;\r
1286\r
1287 //\r
1288 // Signal caller event\r
1289 //\r
1290 if ((Token != NULL) && (Token->Event != NULL)) {\r
1291 Token->TransactionStatus = EFI_SUCCESS;\r
1292 gBS->SignalEvent (Token->Event);\r
1293 }\r
1294\r
1295Done:\r
1296 gBS->RestoreTPL (OldTpl);\r
1297 return Status;\r
1298}\r
1299\r
1300\r
1301/**\r
1302 Internal helper notify function which process the result of an asynchronous\r
1303 SCSI UNMAP Command and signal the event passed from EraseBlocks.\r
1304\r
1305 @param Event The instance of EFI_EVENT.\r
1306 @param Context The parameter passed in.\r
1307\r
1308**/\r
1309VOID\r
1310EFIAPI\r
1311ScsiDiskAsyncUnmapNotify (\r
1312 IN EFI_EVENT Event,\r
1313 IN VOID *Context\r
1314 )\r
1315{\r
1316 SCSI_ERASEBLK_REQUEST *EraseBlkReq;\r
1317 EFI_SCSI_IO_SCSI_REQUEST_PACKET *CommandPacket;\r
1318 EFI_ERASE_BLOCK_TOKEN *Token;\r
1319 EFI_STATUS Status;\r
1320\r
1321 gBS->CloseEvent (Event);\r
1322\r
1323 EraseBlkReq = (SCSI_ERASEBLK_REQUEST *) Context;\r
1324 CommandPacket = &EraseBlkReq->CommandPacket;\r
1325 Token = EraseBlkReq->Token;\r
1326 Token->TransactionStatus = EFI_SUCCESS;\r
1327\r
1328 Status = CheckHostAdapterStatus (CommandPacket->HostAdapterStatus);\r
1329 if (EFI_ERROR(Status)) {\r
1330 DEBUG ((\r
1331 EFI_D_ERROR,\r
1332 "ScsiDiskAsyncUnmapNotify: Host adapter indicating error status 0x%x.\n",\r
1333 CommandPacket->HostAdapterStatus\r
1334 ));\r
1335\r
1336 Token->TransactionStatus = Status;\r
1337 goto Done;\r
1338 }\r
1339\r
1340 Status = CheckTargetStatus (CommandPacket->TargetStatus);\r
1341 if (EFI_ERROR(Status)) {\r
1342 DEBUG ((\r
1343 EFI_D_ERROR,\r
1344 "ScsiDiskAsyncUnmapNotify: Target indicating error status 0x%x.\n",\r
1345 CommandPacket->HostAdapterStatus\r
1346 ));\r
1347\r
1348 Token->TransactionStatus = Status;\r
1349 goto Done;\r
1350 }\r
1351\r
1352Done:\r
1353 RemoveEntryList (&EraseBlkReq->Link);\r
1354 FreePool (CommandPacket->OutDataBuffer);\r
1355 FreePool (EraseBlkReq->CommandPacket.Cdb);\r
1356 FreePool (EraseBlkReq);\r
1357\r
1358 gBS->SignalEvent (Token->Event);\r
1359}\r
1360\r
1361/**\r
1362 Require the device server to cause one or more LBAs to be unmapped.\r
1363\r
1364 @param ScsiDiskDevice The pointer of ScsiDiskDevice.\r
1365 @param Lba The start block number.\r
1366 @param Blocks Total block number to be unmapped.\r
1367 @param Token The pointer to the token associated with the\r
1368 non-blocking erase block request.\r
1369\r
1370 @retval EFI_SUCCESS Target blocks have been successfully unmapped.\r
1371 @retval EFI_DEVICE_ERROR Fail to unmap the target blocks.\r
1372\r
1373**/\r
1374EFI_STATUS\r
1375ScsiDiskUnmap (\r
1376 IN SCSI_DISK_DEV *ScsiDiskDevice,\r
1377 IN UINT64 Lba,\r
1378 IN UINTN Blocks,\r
1379 IN EFI_ERASE_BLOCK_TOKEN *Token OPTIONAL\r
1380 )\r
1381{\r
1382 EFI_SCSI_IO_PROTOCOL *ScsiIo;\r
1383 SCSI_ERASEBLK_REQUEST *EraseBlkReq;\r
1384 EFI_SCSI_IO_SCSI_REQUEST_PACKET *CommandPacket;\r
1385 EFI_SCSI_DISK_UNMAP_BLOCK_DESP *BlkDespPtr;\r
1386 EFI_STATUS Status;\r
1387 EFI_STATUS ReturnStatus;\r
1388 UINT8 *Cdb;\r
1389 UINT32 MaxLbaCnt;\r
1390 UINT32 MaxBlkDespCnt;\r
1391 UINT32 BlkDespCnt;\r
1392 UINT16 UnmapParamListLen;\r
1393 VOID *UnmapParamList;\r
1394 EFI_EVENT AsyncUnmapEvent;\r
1395 EFI_TPL OldTpl;\r
1396\r
1397 ScsiIo = ScsiDiskDevice->ScsiIo;\r
1398 MaxLbaCnt = ScsiDiskDevice->UnmapInfo.MaxLbaCnt;\r
1399 MaxBlkDespCnt = ScsiDiskDevice->UnmapInfo.MaxBlkDespCnt;\r
1400 EraseBlkReq = NULL;\r
1401 UnmapParamList = NULL;\r
1402 AsyncUnmapEvent = NULL;\r
1403 ReturnStatus = EFI_SUCCESS;\r
1404\r
1405 if (Blocks / (UINTN) MaxLbaCnt > MaxBlkDespCnt) {\r
1406 ReturnStatus = EFI_DEVICE_ERROR;\r
1407 goto Done;\r
1408 }\r
1409\r
1410 EraseBlkReq = AllocateZeroPool (sizeof (SCSI_ERASEBLK_REQUEST));\r
1411 if (EraseBlkReq == NULL) {\r
1412 ReturnStatus = EFI_DEVICE_ERROR;\r
1413 goto Done;\r
1414 }\r
1415\r
1416 EraseBlkReq->CommandPacket.Cdb = AllocateZeroPool (0xA);\r
1417 if (EraseBlkReq->CommandPacket.Cdb == NULL) {\r
1418 ReturnStatus = EFI_DEVICE_ERROR;\r
1419 goto Done;\r
1420 }\r
1421\r
1422 BlkDespCnt = (UINT32) ((Blocks - 1) / MaxLbaCnt + 1);\r
1423 UnmapParamListLen = (UINT16) (sizeof (EFI_SCSI_DISK_UNMAP_PARAM_LIST_HEADER)\r
1424 + BlkDespCnt * sizeof (EFI_SCSI_DISK_UNMAP_BLOCK_DESP));\r
1425 UnmapParamList = AllocateZeroPool (UnmapParamListLen);\r
1426 if (UnmapParamList == NULL) {\r
1427 ReturnStatus = EFI_DEVICE_ERROR;\r
1428 goto Done;\r
1429 }\r
1430\r
1431 *((UINT16 *)UnmapParamList) = SwapBytes16 (UnmapParamListLen - 2);\r
1432 *((UINT16 *)UnmapParamList + 1) = SwapBytes16 (UnmapParamListLen - sizeof (EFI_SCSI_DISK_UNMAP_PARAM_LIST_HEADER));\r
1433\r
1434 BlkDespPtr = (EFI_SCSI_DISK_UNMAP_BLOCK_DESP *)((UINT8 *)UnmapParamList + sizeof (EFI_SCSI_DISK_UNMAP_PARAM_LIST_HEADER));\r
1435 while (Blocks > 0) {\r
1436 if (Blocks > MaxLbaCnt) {\r
1437 *(UINT64 *)(&BlkDespPtr->Lba) = SwapBytes64 (Lba);\r
1438 *(UINT32 *)(&BlkDespPtr->BlockNum) = SwapBytes32 (MaxLbaCnt);\r
1439 Blocks -= MaxLbaCnt;\r
1440 Lba += MaxLbaCnt;\r
1441 } else {\r
1442 *(UINT64 *)(&BlkDespPtr->Lba) = SwapBytes64 (Lba);\r
1443 *(UINT32 *)(&BlkDespPtr->BlockNum) = SwapBytes32 ((UINT32) Blocks);\r
1444 Blocks = 0;\r
1445 }\r
1446\r
1447 BlkDespPtr++;\r
1448 }\r
1449\r
1450 CommandPacket = &EraseBlkReq->CommandPacket;\r
1451 CommandPacket->Timeout = SCSI_DISK_TIMEOUT;\r
1452 CommandPacket->OutDataBuffer = UnmapParamList;\r
1453 CommandPacket->OutTransferLength = UnmapParamListLen;\r
1454 CommandPacket->CdbLength = 0xA;\r
1455 CommandPacket->DataDirection = EFI_SCSI_DATA_OUT;\r
1456 //\r
1457 // Fill Cdb for UNMAP Command\r
1458 //\r
1459 Cdb = CommandPacket->Cdb;\r
1460 Cdb[0] = EFI_SCSI_OP_UNMAP;\r
1461 WriteUnaligned16 ((UINT16 *)&Cdb[7], SwapBytes16 (UnmapParamListLen));\r
1462\r
1463 if ((Token != NULL) && (Token->Event != NULL)) {\r
1464 //\r
1465 // Non-blocking UNMAP request\r
1466 //\r
1467 Status = gBS->CreateEvent (\r
1468 EVT_NOTIFY_SIGNAL,\r
1469 TPL_NOTIFY,\r
1470 ScsiDiskAsyncUnmapNotify,\r
1471 EraseBlkReq,\r
1472 &AsyncUnmapEvent\r
1473 );\r
1474 if (EFI_ERROR(Status)) {\r
1475 ReturnStatus = EFI_DEVICE_ERROR;\r
1476 goto Done;\r
1477 }\r
1478\r
1479 OldTpl = gBS->RaiseTPL (TPL_NOTIFY);\r
1480 InsertTailList (&ScsiDiskDevice->AsyncTaskQueue, &EraseBlkReq->Link);\r
1481 gBS->RestoreTPL (OldTpl);\r
1482\r
1483 EraseBlkReq->Token = Token;\r
1484\r
1485 Status = ScsiIo->ExecuteScsiCommand (\r
1486 ScsiIo,\r
1487 CommandPacket,\r
1488 AsyncUnmapEvent\r
1489 );\r
1490 if (EFI_ERROR(Status)) {\r
1491 ReturnStatus = EFI_DEVICE_ERROR;\r
1492\r
1493 OldTpl = gBS->RaiseTPL (TPL_NOTIFY);\r
1494 RemoveEntryList (&EraseBlkReq->Link);\r
1495 gBS->RestoreTPL (OldTpl);\r
1496\r
1497 goto Done;\r
1498 } else {\r
1499 //\r
1500 // Directly return if the non-blocking UNMAP request is queued.\r
1501 //\r
1502 return EFI_SUCCESS;\r
1503 }\r
1504 } else {\r
1505 //\r
1506 // Blocking UNMAP request\r
1507 //\r
1508 Status = ScsiIo->ExecuteScsiCommand (\r
1509 ScsiIo,\r
1510 CommandPacket,\r
1511 NULL\r
1512 );\r
1513 if (EFI_ERROR(Status)) {\r
1514 ReturnStatus = EFI_DEVICE_ERROR;\r
1515 goto Done;\r
1516 }\r
1517 }\r
1518\r
1519 //\r
1520 // Only blocking UNMAP request will reach here.\r
1521 //\r
1522 Status = CheckHostAdapterStatus (CommandPacket->HostAdapterStatus);\r
1523 if (EFI_ERROR(Status)) {\r
1524 DEBUG ((\r
1525 EFI_D_ERROR,\r
1526 "ScsiDiskUnmap: Host adapter indicating error status 0x%x.\n",\r
1527 CommandPacket->HostAdapterStatus\r
1528 ));\r
1529\r
1530 ReturnStatus = EFI_DEVICE_ERROR;\r
1531 goto Done;\r
1532 }\r
1533\r
1534 Status = CheckTargetStatus (CommandPacket->TargetStatus);\r
1535 if (EFI_ERROR(Status)) {\r
1536 DEBUG ((\r
1537 EFI_D_ERROR,\r
1538 "ScsiDiskUnmap: Target indicating error status 0x%x.\n",\r
1539 CommandPacket->HostAdapterStatus\r
1540 ));\r
1541\r
1542 ReturnStatus = EFI_DEVICE_ERROR;\r
1543 goto Done;\r
1544 }\r
1545\r
1546Done:\r
1547 if (EraseBlkReq != NULL) {\r
1548 if (EraseBlkReq->CommandPacket.Cdb != NULL) {\r
1549 FreePool (EraseBlkReq->CommandPacket.Cdb);\r
1550 }\r
1551 FreePool (EraseBlkReq);\r
1552 }\r
1553\r
1554 if (UnmapParamList != NULL) {\r
1555 FreePool (UnmapParamList);\r
1556 }\r
1557\r
1558 if (AsyncUnmapEvent != NULL) {\r
1559 gBS->CloseEvent (AsyncUnmapEvent);\r
1560 }\r
1561\r
1562 return ReturnStatus;\r
1563}\r
1564\r
1565/**\r
1566 Erase a specified number of device blocks.\r
1567\r
1568 @param[in] This Indicates a pointer to the calling context.\r
1569 @param[in] MediaId The media ID that the erase request is for.\r
1570 @param[in] Lba The starting logical block address to be\r
1571 erased. The caller is responsible for erasing\r
1572 only legitimate locations.\r
1573 @param[in, out] Token A pointer to the token associated with the\r
1574 transaction.\r
1575 @param[in] Size The size in bytes to be erased. This must be\r
1576 a multiple of the physical block size of the\r
1577 device.\r
1578\r
1579 @retval EFI_SUCCESS The erase request was queued if Event is not\r
1580 NULL. The data was erased correctly to the\r
1581 device if the Event is NULL.to the device.\r
1582 @retval EFI_WRITE_PROTECTED The device cannot be erased due to write\r
1583 protection.\r
1584 @retval EFI_DEVICE_ERROR The device reported an error while attempting\r
1585 to perform the erase operation.\r
1586 @retval EFI_INVALID_PARAMETER The erase request contains LBAs that are not\r
1587 valid.\r
1588 @retval EFI_NO_MEDIA There is no media in the device.\r
1589 @retval EFI_MEDIA_CHANGED The MediaId is not for the current media.\r
1590\r
1591**/\r
1592EFI_STATUS\r
1593EFIAPI\r
1594ScsiDiskEraseBlocks (\r
1595 IN EFI_ERASE_BLOCK_PROTOCOL *This,\r
1596 IN UINT32 MediaId,\r
1597 IN EFI_LBA Lba,\r
1598 IN OUT EFI_ERASE_BLOCK_TOKEN *Token,\r
1599 IN UINTN Size\r
1600 )\r
1601{\r
1602 SCSI_DISK_DEV *ScsiDiskDevice;\r
1603 EFI_BLOCK_IO_MEDIA *Media;\r
1604 EFI_STATUS Status;\r
1605 UINTN BlockSize;\r
1606 UINTN NumberOfBlocks;\r
1607 BOOLEAN MediaChange;\r
1608 EFI_TPL OldTpl;\r
1609\r
1610 MediaChange = FALSE;\r
1611 OldTpl = gBS->RaiseTPL (TPL_CALLBACK);\r
1612 ScsiDiskDevice = SCSI_DISK_DEV_FROM_ERASEBLK (This);\r
1613\r
1614 if (!IS_DEVICE_FIXED(ScsiDiskDevice)) {\r
1615 Status = ScsiDiskDetectMedia (ScsiDiskDevice, FALSE, &MediaChange);\r
1616 if (EFI_ERROR (Status)) {\r
1617 Status = EFI_DEVICE_ERROR;\r
1618 goto Done;\r
1619 }\r
1620\r
1621 if (MediaChange) {\r
1622 gBS->ReinstallProtocolInterface (\r
1623 ScsiDiskDevice->Handle,\r
1624 &gEfiBlockIoProtocolGuid,\r
1625 &ScsiDiskDevice->BlkIo,\r
1626 &ScsiDiskDevice->BlkIo\r
1627 );\r
1628 gBS->ReinstallProtocolInterface (\r
1629 ScsiDiskDevice->Handle,\r
1630 &gEfiBlockIo2ProtocolGuid,\r
1631 &ScsiDiskDevice->BlkIo2,\r
1632 &ScsiDiskDevice->BlkIo2\r
1633 );\r
1634 if (DetermineInstallEraseBlock(ScsiDiskDevice, ScsiDiskDevice->Handle)) {\r
1635 gBS->ReinstallProtocolInterface (\r
1636 ScsiDiskDevice->Handle,\r
1637 &gEfiEraseBlockProtocolGuid,\r
1638 &ScsiDiskDevice->EraseBlock,\r
1639 &ScsiDiskDevice->EraseBlock\r
1640 );\r
1641 }\r
1642 Status = EFI_MEDIA_CHANGED;\r
1643 goto Done;\r
1644 }\r
1645 }\r
1646 //\r
1647 // Get the intrinsic block size\r
1648 //\r
1649 Media = ScsiDiskDevice->BlkIo.Media;\r
1650\r
1651 if (!(Media->MediaPresent)) {\r
1652 Status = EFI_NO_MEDIA;\r
1653 goto Done;\r
1654 }\r
1655\r
1656 if (MediaId != Media->MediaId) {\r
1657 Status = EFI_MEDIA_CHANGED;\r
1658 goto Done;\r
1659 }\r
1660\r
1661 if (Media->ReadOnly) {\r
1662 Status = EFI_WRITE_PROTECTED;\r
1663 goto Done;\r
1664 }\r
1665\r
1666 if (Size == 0) {\r
1667 if ((Token != NULL) && (Token->Event != NULL)) {\r
1668 Token->TransactionStatus = EFI_SUCCESS;\r
1669 gBS->SignalEvent (Token->Event);\r
1670 }\r
1671 Status = EFI_SUCCESS;\r
1672 goto Done;\r
1673 }\r
1674\r
1675 BlockSize = Media->BlockSize;\r
1676 if ((Size % BlockSize) != 0) {\r
1677 Status = EFI_INVALID_PARAMETER;\r
1678 goto Done;\r
1679 }\r
1680\r
1681 NumberOfBlocks = Size / BlockSize;\r
1682 if ((Lba + NumberOfBlocks - 1) > Media->LastBlock) {\r
1683 Status = EFI_INVALID_PARAMETER;\r
1684 goto Done;\r
1685 }\r
1686\r
1687 if ((Token != NULL) && (Token->Event != NULL)) {\r
1688 Status = ScsiDiskUnmap (ScsiDiskDevice, Lba, NumberOfBlocks, Token);\r
1689 } else {\r
1690 Status = ScsiDiskUnmap (ScsiDiskDevice, Lba, NumberOfBlocks, NULL);\r
1691 }\r
1692\r
1693Done:\r
1694 gBS->RestoreTPL (OldTpl);\r
1695 return Status;\r
1696}\r
1697\r
1698\r
1699/**\r
1700 Detect Device and read out capacity ,if error occurs, parse the sense key.\r
1701\r
1702 @param ScsiDiskDevice The pointer of SCSI_DISK_DEV\r
1703 @param MustReadCapacity The flag about reading device capacity\r
1704 @param MediaChange The pointer of flag indicates if media has changed \r
1705\r
1706 @retval EFI_DEVICE_ERROR Indicates that error occurs\r
1707 @retval EFI_SUCCESS Successfully to detect media\r
1708\r
1709**/\r
1710EFI_STATUS\r
1711ScsiDiskDetectMedia (\r
1712 IN SCSI_DISK_DEV *ScsiDiskDevice,\r
1713 IN BOOLEAN MustReadCapacity,\r
1714 OUT BOOLEAN *MediaChange\r
1715 )\r
1716{\r
1717 EFI_STATUS Status;\r
1718 EFI_SCSI_SENSE_DATA *SenseData;\r
1719 UINTN NumberOfSenseKeys;\r
1720 BOOLEAN NeedRetry;\r
1721 BOOLEAN NeedReadCapacity;\r
1722 UINT8 Retry;\r
1723 UINT8 MaxRetry;\r
1724 EFI_BLOCK_IO_MEDIA OldMedia;\r
1725 UINTN Action;\r
1726 EFI_EVENT TimeoutEvt;\r
1727\r
1728 Status = EFI_SUCCESS;\r
1729 SenseData = NULL;\r
1730 NumberOfSenseKeys = 0;\r
1731 Retry = 0;\r
1732 MaxRetry = 3;\r
1733 Action = ACTION_NO_ACTION;\r
1734 NeedReadCapacity = FALSE;\r
1735 *MediaChange = FALSE;\r
1736 TimeoutEvt = NULL;\r
1737\r
1738 CopyMem (&OldMedia, ScsiDiskDevice->BlkIo.Media, sizeof (OldMedia));\r
1739\r
1740 Status = gBS->CreateEvent (\r
1741 EVT_TIMER,\r
1742 TPL_CALLBACK,\r
1743 NULL,\r
1744 NULL,\r
1745 &TimeoutEvt\r
1746 );\r
1747 if (EFI_ERROR (Status)) {\r
1748 return Status;\r
1749 }\r
1750\r
1751 Status = gBS->SetTimer (TimeoutEvt, TimerRelative, EFI_TIMER_PERIOD_SECONDS(120));\r
1752 if (EFI_ERROR (Status)) {\r
1753 goto EXIT;\r
1754 }\r
1755\r
1756 //\r
1757 // Sending Test_Unit cmd to poll device status.\r
1758 // If the sense data shows the drive is not ready or reset before, we need poll the device status again.\r
1759 // We limit the upper boundary to 120 seconds.\r
1760 //\r
1761 while (EFI_ERROR (gBS->CheckEvent (TimeoutEvt))) {\r
1762 Status = ScsiDiskTestUnitReady (\r
1763 ScsiDiskDevice,\r
1764 &NeedRetry,\r
1765 &SenseData,\r
1766 &NumberOfSenseKeys\r
1767 );\r
1768 if (!EFI_ERROR (Status)) {\r
1769 Status = DetectMediaParsingSenseKeys (\r
1770 ScsiDiskDevice,\r
1771 SenseData,\r
1772 NumberOfSenseKeys,\r
1773 &Action\r
1774 );\r
1775 if (EFI_ERROR (Status)) {\r
1776 goto EXIT;\r
1777 } else if (Action == ACTION_RETRY_COMMAND_LATER) {\r
1778 continue;\r
1779 } else {\r
1780 break;\r
1781 }\r
1782 } else {\r
1783 Retry++;\r
1784 if (!NeedRetry || (Retry >= MaxRetry)) {\r
1785 goto EXIT;\r
1786 }\r
1787 }\r
1788 }\r
1789\r
1790 if (EFI_ERROR (Status)) {\r
1791 goto EXIT;\r
1792 }\r
1793\r
1794 //\r
1795 // ACTION_NO_ACTION: need not read capacity\r
1796 // other action code: need read capacity\r
1797 //\r
1798 if (Action == ACTION_READ_CAPACITY) {\r
1799 NeedReadCapacity = TRUE;\r
1800 }\r
1801\r
1802 //\r
1803 // either NeedReadCapacity is TRUE, or MustReadCapacity is TRUE,\r
1804 // retrieve capacity via Read Capacity command\r
1805 //\r
1806 if (NeedReadCapacity || MustReadCapacity) {\r
1807 //\r
1808 // retrieve media information\r
1809 //\r
1810 for (Retry = 0; Retry < MaxRetry; Retry++) {\r
1811 Status = ScsiDiskReadCapacity (\r
1812 ScsiDiskDevice,\r
1813 &NeedRetry,\r
1814 &SenseData,\r
1815 &NumberOfSenseKeys\r
1816 );\r
1817 if (!EFI_ERROR (Status)) {\r
1818 //\r
1819 // analyze sense key to action\r
1820 //\r
1821 Status = DetectMediaParsingSenseKeys (\r
1822 ScsiDiskDevice,\r
1823 SenseData,\r
1824 NumberOfSenseKeys,\r
1825 &Action\r
1826 );\r
1827 if (EFI_ERROR (Status)) {\r
1828 //\r
1829 // if Status is error, it may indicate crisis error,\r
1830 // so return without retry.\r
1831 //\r
1832 goto EXIT;\r
1833 } else if (Action == ACTION_RETRY_COMMAND_LATER) {\r
1834 Retry = 0;\r
1835 continue;\r
1836 } else {\r
1837 break;\r
1838 }\r
1839 } else { \r
1840 Retry++;\r
1841 if (!NeedRetry || (Retry >= MaxRetry)) {\r
1842 goto EXIT;\r
1843 }\r
1844 }\r
1845 }\r
1846\r
1847 if (EFI_ERROR (Status)) {\r
1848 goto EXIT;\r
1849 }\r
1850 }\r
1851\r
1852 if (ScsiDiskDevice->BlkIo.Media->MediaId != OldMedia.MediaId) {\r
1853 //\r
1854 // Media change information got from the device\r
1855 //\r
1856 *MediaChange = TRUE;\r
1857 }\r
1858\r
1859 if (ScsiDiskDevice->BlkIo.Media->ReadOnly != OldMedia.ReadOnly) {\r
1860 *MediaChange = TRUE;\r
1861 ScsiDiskDevice->BlkIo.Media->MediaId += 1;\r
1862 }\r
1863\r
1864 if (ScsiDiskDevice->BlkIo.Media->BlockSize != OldMedia.BlockSize) {\r
1865 *MediaChange = TRUE;\r
1866 ScsiDiskDevice->BlkIo.Media->MediaId += 1;\r
1867 }\r
1868\r
1869 if (ScsiDiskDevice->BlkIo.Media->LastBlock != OldMedia.LastBlock) {\r
1870 *MediaChange = TRUE;\r
1871 ScsiDiskDevice->BlkIo.Media->MediaId += 1;\r
1872 }\r
1873\r
1874 if (ScsiDiskDevice->BlkIo.Media->MediaPresent != OldMedia.MediaPresent) {\r
1875 if (ScsiDiskDevice->BlkIo.Media->MediaPresent) {\r
1876 //\r
1877 // when change from no media to media present, reset the MediaId to 1.\r
1878 //\r
1879 ScsiDiskDevice->BlkIo.Media->MediaId = 1;\r
1880 } else {\r
1881 //\r
1882 // when no media, reset the MediaId to zero.\r
1883 //\r
1884 ScsiDiskDevice->BlkIo.Media->MediaId = 0;\r
1885 }\r
1886\r
1887 *MediaChange = TRUE;\r
1888 }\r
1889\r
1890EXIT:\r
1891 if (TimeoutEvt != NULL) {\r
1892 gBS->CloseEvent (TimeoutEvt);\r
1893 }\r
1894 return Status;\r
1895}\r
1896\r
1897\r
1898/**\r
1899 Send out Inquiry command to Device.\r
1900\r
1901 @param ScsiDiskDevice The pointer of SCSI_DISK_DEV\r
1902 @param NeedRetry Indicates if needs try again when error happens\r
1903\r
1904 @retval EFI_DEVICE_ERROR Indicates that error occurs\r
1905 @retval EFI_SUCCESS Successfully to detect media\r
1906\r
1907**/\r
1908EFI_STATUS\r
1909ScsiDiskInquiryDevice (\r
1910 IN OUT SCSI_DISK_DEV *ScsiDiskDevice,\r
1911 OUT BOOLEAN *NeedRetry\r
1912 )\r
1913{\r
1914 UINT32 InquiryDataLength;\r
1915 UINT8 SenseDataLength;\r
1916 UINT8 HostAdapterStatus;\r
1917 UINT8 TargetStatus;\r
1918 EFI_SCSI_SENSE_DATA *SenseDataArray;\r
1919 UINTN NumberOfSenseKeys;\r
1920 EFI_STATUS Status;\r
1921 UINT8 MaxRetry;\r
1922 UINT8 Index;\r
1923 EFI_SCSI_SUPPORTED_VPD_PAGES_VPD_PAGE *SupportedVpdPages;\r
1924 EFI_SCSI_BLOCK_LIMITS_VPD_PAGE *BlockLimits;\r
1925 UINTN PageLength;\r
1926\r
1927 InquiryDataLength = sizeof (EFI_SCSI_INQUIRY_DATA);\r
1928 SenseDataLength = 0;\r
1929\r
1930 Status = ScsiInquiryCommand (\r
1931 ScsiDiskDevice->ScsiIo,\r
1932 SCSI_DISK_TIMEOUT,\r
1933 NULL,\r
1934 &SenseDataLength,\r
1935 &HostAdapterStatus,\r
1936 &TargetStatus,\r
1937 (VOID *) &(ScsiDiskDevice->InquiryData),\r
1938 &InquiryDataLength,\r
1939 FALSE\r
1940 );\r
1941 //\r
1942 // no need to check HostAdapterStatus and TargetStatus\r
1943 //\r
1944 if ((Status == EFI_SUCCESS) || (Status == EFI_WARN_BUFFER_TOO_SMALL)) {\r
1945 ParseInquiryData (ScsiDiskDevice);\r
1946\r
1947 if (ScsiDiskDevice->DeviceType == EFI_SCSI_TYPE_DISK) {\r
1948 //\r
1949 // Check whether the device supports Block Limits VPD page (0xB0)\r
1950 //\r
1951 SupportedVpdPages = AllocateAlignedBuffer (ScsiDiskDevice, sizeof (EFI_SCSI_SUPPORTED_VPD_PAGES_VPD_PAGE));\r
1952 if (SupportedVpdPages == NULL) {\r
1953 *NeedRetry = FALSE;\r
1954 return EFI_DEVICE_ERROR;\r
1955 }\r
1956 ZeroMem (SupportedVpdPages, sizeof (EFI_SCSI_SUPPORTED_VPD_PAGES_VPD_PAGE));\r
1957 InquiryDataLength = sizeof (EFI_SCSI_SUPPORTED_VPD_PAGES_VPD_PAGE);\r
1958 SenseDataLength = 0;\r
1959 Status = ScsiInquiryCommandEx (\r
1960 ScsiDiskDevice->ScsiIo,\r
1961 SCSI_DISK_TIMEOUT,\r
1962 NULL,\r
1963 &SenseDataLength,\r
1964 &HostAdapterStatus,\r
1965 &TargetStatus,\r
1966 (VOID *) SupportedVpdPages,\r
1967 &InquiryDataLength,\r
1968 TRUE,\r
1969 EFI_SCSI_PAGE_CODE_SUPPORTED_VPD\r
1970 );\r
1971 if (!EFI_ERROR (Status)) {\r
1972 PageLength = (SupportedVpdPages->PageLength2 << 8)\r
1973 | SupportedVpdPages->PageLength1;\r
1974\r
1975 //\r
1976 // Sanity checks for coping with broken devices\r
1977 //\r
1978 if (PageLength > sizeof SupportedVpdPages->SupportedVpdPageList) {\r
1979 DEBUG ((EFI_D_WARN,\r
1980 "%a: invalid PageLength (%u) in Supported VPD Pages page\n",\r
1981 __FUNCTION__, (UINT32)PageLength));\r
1982 PageLength = 0;\r
1983 }\r
1984\r
1985 if ((PageLength > 0) &&\r
1986 (SupportedVpdPages->SupportedVpdPageList[0] !=\r
1987 EFI_SCSI_PAGE_CODE_SUPPORTED_VPD)) {\r
1988 DEBUG ((EFI_D_WARN,\r
1989 "%a: Supported VPD Pages page doesn't start with code 0x%02x\n",\r
1990 __FUNCTION__, EFI_SCSI_PAGE_CODE_SUPPORTED_VPD));\r
1991 PageLength = 0;\r
1992 }\r
1993\r
1994 //\r
1995 // Locate the code for the Block Limits VPD page\r
1996 //\r
1997 for (Index = 0; Index < PageLength; Index++) {\r
1998 //\r
1999 // Sanity check\r
2000 //\r
2001 if ((Index > 0) &&\r
2002 (SupportedVpdPages->SupportedVpdPageList[Index] <=\r
2003 SupportedVpdPages->SupportedVpdPageList[Index - 1])) {\r
2004 DEBUG ((EFI_D_WARN,\r
2005 "%a: non-ascending code in Supported VPD Pages page @ %u\n",\r
2006 __FUNCTION__, Index));\r
2007 Index = 0;\r
2008 PageLength = 0;\r
2009 break;\r
2010 }\r
2011\r
2012 if (SupportedVpdPages->SupportedVpdPageList[Index] == EFI_SCSI_PAGE_CODE_BLOCK_LIMITS_VPD) {\r
2013 break;\r
2014 }\r
2015 }\r
2016\r
2017 //\r
2018 // Query the Block Limits VPD page\r
2019 //\r
2020 if (Index < PageLength) {\r
2021 BlockLimits = AllocateAlignedBuffer (ScsiDiskDevice, sizeof (EFI_SCSI_BLOCK_LIMITS_VPD_PAGE));\r
2022 if (BlockLimits == NULL) {\r
2023 FreeAlignedBuffer (SupportedVpdPages, sizeof (EFI_SCSI_SUPPORTED_VPD_PAGES_VPD_PAGE));\r
2024 *NeedRetry = FALSE;\r
2025 return EFI_DEVICE_ERROR;\r
2026 }\r
2027 ZeroMem (BlockLimits, sizeof (EFI_SCSI_BLOCK_LIMITS_VPD_PAGE));\r
2028 InquiryDataLength = sizeof (EFI_SCSI_BLOCK_LIMITS_VPD_PAGE);\r
2029 SenseDataLength = 0;\r
2030 Status = ScsiInquiryCommandEx (\r
2031 ScsiDiskDevice->ScsiIo,\r
2032 SCSI_DISK_TIMEOUT,\r
2033 NULL,\r
2034 &SenseDataLength,\r
2035 &HostAdapterStatus,\r
2036 &TargetStatus,\r
2037 (VOID *) BlockLimits,\r
2038 &InquiryDataLength,\r
2039 TRUE,\r
2040 EFI_SCSI_PAGE_CODE_BLOCK_LIMITS_VPD\r
2041 );\r
2042 if (!EFI_ERROR (Status)) {\r
2043 ScsiDiskDevice->BlkIo.Media->OptimalTransferLengthGranularity = \r
2044 (BlockLimits->OptimalTransferLengthGranularity2 << 8) |\r
2045 BlockLimits->OptimalTransferLengthGranularity1;\r
2046\r
2047 ScsiDiskDevice->UnmapInfo.MaxLbaCnt =\r
2048 (BlockLimits->MaximumUnmapLbaCount4 << 24) |\r
2049 (BlockLimits->MaximumUnmapLbaCount3 << 16) |\r
2050 (BlockLimits->MaximumUnmapLbaCount2 << 8) |\r
2051 BlockLimits->MaximumUnmapLbaCount1;\r
2052 ScsiDiskDevice->UnmapInfo.MaxBlkDespCnt =\r
2053 (BlockLimits->MaximumUnmapBlockDescriptorCount4 << 24) |\r
2054 (BlockLimits->MaximumUnmapBlockDescriptorCount3 << 16) |\r
2055 (BlockLimits->MaximumUnmapBlockDescriptorCount2 << 8) |\r
2056 BlockLimits->MaximumUnmapBlockDescriptorCount1;\r
2057 ScsiDiskDevice->EraseBlock.EraseLengthGranularity =\r
2058 (BlockLimits->OptimalUnmapGranularity4 << 24) |\r
2059 (BlockLimits->OptimalUnmapGranularity3 << 16) |\r
2060 (BlockLimits->OptimalUnmapGranularity2 << 8) |\r
2061 BlockLimits->OptimalUnmapGranularity1;\r
2062 if (BlockLimits->UnmapGranularityAlignmentValid != 0) {\r
2063 ScsiDiskDevice->UnmapInfo.GranularityAlignment =\r
2064 (BlockLimits->UnmapGranularityAlignment4 << 24) |\r
2065 (BlockLimits->UnmapGranularityAlignment3 << 16) |\r
2066 (BlockLimits->UnmapGranularityAlignment2 << 8) |\r
2067 BlockLimits->UnmapGranularityAlignment1;\r
2068 }\r
2069\r
2070 if (ScsiDiskDevice->EraseBlock.EraseLengthGranularity == 0) {\r
2071 //\r
2072 // A value of 0 indicates that the optimal unmap granularity is\r
2073 // not reported.\r
2074 //\r
2075 ScsiDiskDevice->EraseBlock.EraseLengthGranularity = 1;\r
2076 }\r
2077\r
2078 ScsiDiskDevice->BlockLimitsVpdSupported = TRUE;\r
2079 }\r
2080\r
2081 FreeAlignedBuffer (BlockLimits, sizeof (EFI_SCSI_BLOCK_LIMITS_VPD_PAGE));\r
2082 }\r
2083 }\r
2084\r
2085 FreeAlignedBuffer (SupportedVpdPages, sizeof (EFI_SCSI_SUPPORTED_VPD_PAGES_VPD_PAGE));\r
2086 }\r
2087 }\r
2088\r
2089 if (!EFI_ERROR (Status)) {\r
2090 return EFI_SUCCESS;\r
2091\r
2092 } else if (Status == EFI_NOT_READY) {\r
2093 *NeedRetry = TRUE;\r
2094 return EFI_DEVICE_ERROR;\r
2095 \r
2096 } else if ((Status == EFI_INVALID_PARAMETER) || (Status == EFI_UNSUPPORTED)) {\r
2097 *NeedRetry = FALSE;\r
2098 return EFI_DEVICE_ERROR;\r
2099 }\r
2100 //\r
2101 // go ahead to check HostAdapterStatus and TargetStatus\r
2102 // (EFI_TIMEOUT, EFI_DEVICE_ERROR)\r
2103 //\r
2104\r
2105 Status = CheckHostAdapterStatus (HostAdapterStatus);\r
2106 if ((Status == EFI_TIMEOUT) || (Status == EFI_NOT_READY)) {\r
2107 *NeedRetry = TRUE;\r
2108 return EFI_DEVICE_ERROR;\r
2109 } else if (Status == EFI_DEVICE_ERROR) {\r
2110 //\r
2111 // reset the scsi channel\r
2112 //\r
2113 ScsiDiskDevice->ScsiIo->ResetBus (ScsiDiskDevice->ScsiIo);\r
2114 *NeedRetry = FALSE;\r
2115 return EFI_DEVICE_ERROR;\r
2116 }\r
2117\r
2118 Status = CheckTargetStatus (TargetStatus);\r
2119 if (Status == EFI_NOT_READY) {\r
2120 //\r
2121 // reset the scsi device\r
2122 //\r
2123 ScsiDiskDevice->ScsiIo->ResetDevice (ScsiDiskDevice->ScsiIo);\r
2124 *NeedRetry = TRUE;\r
2125 return EFI_DEVICE_ERROR;\r
2126\r
2127 } else if (Status == EFI_DEVICE_ERROR) {\r
2128 *NeedRetry = FALSE;\r
2129 return EFI_DEVICE_ERROR;\r
2130 }\r
2131 \r
2132 //\r
2133 // if goes here, meant ScsiInquiryCommand() failed.\r
2134 // if ScsiDiskRequestSenseKeys() succeeds at last,\r
2135 // better retry ScsiInquiryCommand(). (by setting *NeedRetry = TRUE)\r
2136 //\r
2137 MaxRetry = 3;\r
2138 for (Index = 0; Index < MaxRetry; Index++) {\r
2139 Status = ScsiDiskRequestSenseKeys (\r
2140 ScsiDiskDevice,\r
2141 NeedRetry,\r
2142 &SenseDataArray,\r
2143 &NumberOfSenseKeys,\r
2144 TRUE\r
2145 );\r
2146 if (!EFI_ERROR (Status)) {\r
2147 *NeedRetry = TRUE;\r
2148 return EFI_DEVICE_ERROR;\r
2149 }\r
2150\r
2151 if (!*NeedRetry) {\r
2152 return EFI_DEVICE_ERROR;\r
2153 }\r
2154 }\r
2155 //\r
2156 // ScsiDiskRequestSenseKeys() failed after several rounds of retry.\r
2157 // set *NeedRetry = FALSE to avoid the outside caller try again.\r
2158 //\r
2159 *NeedRetry = FALSE;\r
2160 return EFI_DEVICE_ERROR;\r
2161}\r
2162\r
2163/**\r
2164 To test device.\r
2165\r
2166 When Test Unit Ready command succeeds, retrieve Sense Keys via Request Sense;\r
2167 When Test Unit Ready command encounters any error caused by host adapter or\r
2168 target, return error without retrieving Sense Keys.\r
2169\r
2170 @param ScsiDiskDevice The pointer of SCSI_DISK_DEV\r
2171 @param NeedRetry The pointer of flag indicates try again\r
2172 @param SenseDataArray The pointer of an array of sense data\r
2173 @param NumberOfSenseKeys The pointer of the number of sense data array\r
2174\r
2175 @retval EFI_DEVICE_ERROR Indicates that error occurs\r
2176 @retval EFI_SUCCESS Successfully to test unit\r
2177\r
2178**/\r
2179EFI_STATUS\r
2180ScsiDiskTestUnitReady (\r
2181 IN SCSI_DISK_DEV *ScsiDiskDevice,\r
2182 OUT BOOLEAN *NeedRetry,\r
2183 OUT EFI_SCSI_SENSE_DATA **SenseDataArray,\r
2184 OUT UINTN *NumberOfSenseKeys\r
2185 )\r
2186{\r
2187 EFI_STATUS Status;\r
2188 UINT8 SenseDataLength;\r
2189 UINT8 HostAdapterStatus;\r
2190 UINT8 TargetStatus;\r
2191 UINT8 Index;\r
2192 UINT8 MaxRetry;\r
2193\r
2194 SenseDataLength = (UINT8) (ScsiDiskDevice->SenseDataNumber * sizeof (EFI_SCSI_SENSE_DATA));\r
2195 *NumberOfSenseKeys = 0;\r
2196\r
2197 //\r
2198 // Parameter 3 and 4: do not require sense data, retrieve it when needed.\r
2199 //\r
2200 Status = ScsiTestUnitReadyCommand (\r
2201 ScsiDiskDevice->ScsiIo,\r
2202 SCSI_DISK_TIMEOUT,\r
2203 ScsiDiskDevice->SenseData,\r
2204 &SenseDataLength,\r
2205 &HostAdapterStatus,\r
2206 &TargetStatus\r
2207 );\r
2208 //\r
2209 // no need to check HostAdapterStatus and TargetStatus\r
2210 //\r
2211 if (Status == EFI_NOT_READY) {\r
2212 *NeedRetry = TRUE;\r
2213 return EFI_DEVICE_ERROR;\r
2214\r
2215 } else if ((Status == EFI_INVALID_PARAMETER) || (Status == EFI_UNSUPPORTED)) {\r
2216 *NeedRetry = FALSE;\r
2217 return EFI_DEVICE_ERROR;\r
2218 }\r
2219 //\r
2220 // go ahead to check HostAdapterStatus and TargetStatus(in case of EFI_DEVICE_ERROR)\r
2221 //\r
2222\r
2223 Status = CheckHostAdapterStatus (HostAdapterStatus);\r
2224 if ((Status == EFI_TIMEOUT) || (Status == EFI_NOT_READY)) {\r
2225 *NeedRetry = TRUE;\r
2226 return EFI_DEVICE_ERROR;\r
2227\r
2228 } else if (Status == EFI_DEVICE_ERROR) {\r
2229 //\r
2230 // reset the scsi channel\r
2231 //\r
2232 ScsiDiskDevice->ScsiIo->ResetBus (ScsiDiskDevice->ScsiIo);\r
2233 *NeedRetry = FALSE;\r
2234 return EFI_DEVICE_ERROR;\r
2235 }\r
2236\r
2237 Status = CheckTargetStatus (TargetStatus);\r
2238 if (Status == EFI_NOT_READY) {\r
2239 //\r
2240 // reset the scsi device\r
2241 //\r
2242 ScsiDiskDevice->ScsiIo->ResetDevice (ScsiDiskDevice->ScsiIo);\r
2243 *NeedRetry = TRUE;\r
2244 return EFI_DEVICE_ERROR;\r
2245\r
2246 } else if (Status == EFI_DEVICE_ERROR) {\r
2247 *NeedRetry = FALSE;\r
2248 return EFI_DEVICE_ERROR;\r
2249 }\r
2250\r
2251 if (SenseDataLength != 0) {\r
2252 *NumberOfSenseKeys = SenseDataLength / sizeof (EFI_SCSI_SENSE_DATA);\r
2253 *SenseDataArray = ScsiDiskDevice->SenseData;\r
2254 return EFI_SUCCESS;\r
2255 }\r
2256\r
2257 MaxRetry = 3;\r
2258 for (Index = 0; Index < MaxRetry; Index++) {\r
2259 Status = ScsiDiskRequestSenseKeys (\r
2260 ScsiDiskDevice,\r
2261 NeedRetry,\r
2262 SenseDataArray,\r
2263 NumberOfSenseKeys,\r
2264 FALSE\r
2265 );\r
2266 if (!EFI_ERROR (Status)) {\r
2267 return EFI_SUCCESS;\r
2268 }\r
2269\r
2270 if (!*NeedRetry) {\r
2271 return EFI_DEVICE_ERROR;\r
2272 }\r
2273 }\r
2274 //\r
2275 // ScsiDiskRequestSenseKeys() failed after several rounds of retry.\r
2276 // set *NeedRetry = FALSE to avoid the outside caller try again.\r
2277 //\r
2278 *NeedRetry = FALSE;\r
2279 return EFI_DEVICE_ERROR;\r
2280}\r
2281\r
2282/**\r
2283 Parsing Sense Keys which got from request sense command.\r
2284\r
2285 @param ScsiDiskDevice The pointer of SCSI_DISK_DEV\r
2286 @param SenseData The pointer of EFI_SCSI_SENSE_DATA\r
2287 @param NumberOfSenseKeys The number of sense key \r
2288 @param Action The pointer of action which indicates what is need to do next\r
2289\r
2290 @retval EFI_DEVICE_ERROR Indicates that error occurs\r
2291 @retval EFI_SUCCESS Successfully to complete the parsing\r
2292\r
2293**/\r
2294EFI_STATUS\r
2295DetectMediaParsingSenseKeys (\r
2296 OUT SCSI_DISK_DEV *ScsiDiskDevice,\r
2297 IN EFI_SCSI_SENSE_DATA *SenseData,\r
2298 IN UINTN NumberOfSenseKeys,\r
2299 OUT UINTN *Action\r
2300 )\r
2301{\r
2302 BOOLEAN RetryLater;\r
2303\r
2304 //\r
2305 // Default is to read capacity, unless..\r
2306 //\r
2307 *Action = ACTION_READ_CAPACITY;\r
2308\r
2309 if (NumberOfSenseKeys == 0) {\r
2310 if (ScsiDiskDevice->BlkIo.Media->MediaPresent == TRUE) {\r
2311 *Action = ACTION_NO_ACTION;\r
2312 }\r
2313 return EFI_SUCCESS;\r
2314 }\r
2315\r
2316 if (!ScsiDiskHaveSenseKey (SenseData, NumberOfSenseKeys)) {\r
2317 //\r
2318 // No Sense Key returned from last submitted command\r
2319 //\r
2320 if (ScsiDiskDevice->BlkIo.Media->MediaPresent == TRUE) {\r
2321 *Action = ACTION_NO_ACTION;\r
2322 }\r
2323 return EFI_SUCCESS;\r
2324 }\r
2325\r
2326 if (ScsiDiskIsNoMedia (SenseData, NumberOfSenseKeys)) {\r
2327 ScsiDiskDevice->BlkIo.Media->MediaPresent = FALSE;\r
2328 ScsiDiskDevice->BlkIo.Media->LastBlock = 0;\r
2329 *Action = ACTION_NO_ACTION;\r
2330 DEBUG ((EFI_D_VERBOSE, "ScsiDisk: ScsiDiskIsNoMedia\n"));\r
2331 return EFI_SUCCESS;\r
2332 }\r
2333\r
2334 if (ScsiDiskIsMediaChange (SenseData, NumberOfSenseKeys)) {\r
2335 ScsiDiskDevice->BlkIo.Media->MediaId++;\r
2336 DEBUG ((EFI_D_VERBOSE, "ScsiDisk: ScsiDiskIsMediaChange!\n"));\r
2337 return EFI_SUCCESS;\r
2338 }\r
2339\r
2340 if (ScsiDiskIsResetBefore (SenseData, NumberOfSenseKeys)) {\r
2341 *Action = ACTION_RETRY_COMMAND_LATER;\r
2342 DEBUG ((EFI_D_VERBOSE, "ScsiDisk: ScsiDiskIsResetBefore!\n"));\r
2343 return EFI_SUCCESS;\r
2344 }\r
2345\r
2346 if (ScsiDiskIsMediaError (SenseData, NumberOfSenseKeys)) {\r
2347 DEBUG ((EFI_D_VERBOSE, "ScsiDisk: ScsiDiskIsMediaError\n"));\r
2348 *Action = ACTION_RETRY_WITH_BACKOFF_ALGO;\r
2349 return EFI_DEVICE_ERROR;\r
2350 }\r
2351\r
2352 if (ScsiDiskIsHardwareError (SenseData, NumberOfSenseKeys)) {\r
2353 DEBUG ((EFI_D_VERBOSE, "ScsiDisk: ScsiDiskIsHardwareError\n"));\r
2354 *Action = ACTION_RETRY_WITH_BACKOFF_ALGO;\r
2355 return EFI_DEVICE_ERROR;\r
2356 }\r
2357\r
2358 if (!ScsiDiskIsDriveReady (SenseData, NumberOfSenseKeys, &RetryLater)) {\r
2359 if (RetryLater) {\r
2360 *Action = ACTION_RETRY_COMMAND_LATER;\r
2361 DEBUG ((EFI_D_VERBOSE, "ScsiDisk: ScsiDiskDriveNotReady!\n"));\r
2362 return EFI_SUCCESS;\r
2363 }\r
2364 *Action = ACTION_NO_ACTION;\r
2365 return EFI_DEVICE_ERROR;\r
2366 }\r
2367\r
2368 *Action = ACTION_RETRY_WITH_BACKOFF_ALGO;\r
2369 DEBUG ((EFI_D_VERBOSE, "ScsiDisk: Sense Key = 0x%x ASC = 0x%x!\n", SenseData->Sense_Key, SenseData->Addnl_Sense_Code));\r
2370 return EFI_SUCCESS;\r
2371}\r
2372\r
2373\r
2374/**\r
2375 Send read capacity command to device and get the device parameter.\r
2376\r
2377 @param ScsiDiskDevice The pointer of SCSI_DISK_DEV\r
2378 @param NeedRetry The pointer of flag indicates if need a retry\r
2379 @param SenseDataArray The pointer of an array of sense data\r
2380 @param NumberOfSenseKeys The number of sense key\r
2381\r
2382 @retval EFI_DEVICE_ERROR Indicates that error occurs\r
2383 @retval EFI_SUCCESS Successfully to read capacity or sense data is received.\r
2384\r
2385**/\r
2386EFI_STATUS\r
2387ScsiDiskReadCapacity (\r
2388 IN OUT SCSI_DISK_DEV *ScsiDiskDevice,\r
2389 OUT BOOLEAN *NeedRetry,\r
2390 OUT EFI_SCSI_SENSE_DATA **SenseDataArray,\r
2391 OUT UINTN *NumberOfSenseKeys\r
2392 )\r
2393{\r
2394 UINT8 HostAdapterStatus;\r
2395 UINT8 TargetStatus;\r
2396 EFI_STATUS CommandStatus;\r
2397 EFI_STATUS Status;\r
2398 UINT8 Index;\r
2399 UINT8 MaxRetry;\r
2400 UINT8 SenseDataLength;\r
2401 UINT32 DataLength10;\r
2402 UINT32 DataLength16;\r
2403 EFI_SCSI_DISK_CAPACITY_DATA *CapacityData10;\r
2404 EFI_SCSI_DISK_CAPACITY_DATA16 *CapacityData16;\r
2405\r
2406 CapacityData10 = AllocateAlignedBuffer (ScsiDiskDevice, sizeof (EFI_SCSI_DISK_CAPACITY_DATA));\r
2407 if (CapacityData10 == NULL) {\r
2408 *NeedRetry = FALSE;\r
2409 return EFI_DEVICE_ERROR;\r
2410 }\r
2411 CapacityData16 = AllocateAlignedBuffer (ScsiDiskDevice, sizeof (EFI_SCSI_DISK_CAPACITY_DATA16));\r
2412 if (CapacityData16 == NULL) {\r
2413 FreeAlignedBuffer (CapacityData10, sizeof (EFI_SCSI_DISK_CAPACITY_DATA));\r
2414 *NeedRetry = FALSE;\r
2415 return EFI_DEVICE_ERROR;\r
2416 }\r
2417\r
2418 SenseDataLength = 0;\r
2419 DataLength10 = sizeof (EFI_SCSI_DISK_CAPACITY_DATA);\r
2420 DataLength16 = sizeof (EFI_SCSI_DISK_CAPACITY_DATA16);\r
2421 ZeroMem (CapacityData10, sizeof (EFI_SCSI_DISK_CAPACITY_DATA));\r
2422 ZeroMem (CapacityData16, sizeof (EFI_SCSI_DISK_CAPACITY_DATA16));\r
2423\r
2424 *NumberOfSenseKeys = 0;\r
2425 *NeedRetry = FALSE;\r
2426\r
2427 //\r
2428 // submit Read Capacity(10) Command. If it returns capacity of FFFFFFFFh, \r
2429 // 16 byte command should be used to access large hard disk >2TB\r
2430 //\r
2431 CommandStatus = ScsiReadCapacityCommand (\r
2432 ScsiDiskDevice->ScsiIo,\r
2433 SCSI_DISK_TIMEOUT,\r
2434 NULL,\r
2435 &SenseDataLength,\r
2436 &HostAdapterStatus,\r
2437 &TargetStatus,\r
2438 (VOID *) CapacityData10,\r
2439 &DataLength10,\r
2440 FALSE\r
2441 );\r
2442\r
2443 ScsiDiskDevice->Cdb16Byte = FALSE;\r
2444 if ((!EFI_ERROR (CommandStatus)) && (CapacityData10->LastLba3 == 0xff) && (CapacityData10->LastLba2 == 0xff) &&\r
2445 (CapacityData10->LastLba1 == 0xff) && (CapacityData10->LastLba0 == 0xff)) {\r
2446 //\r
2447 // use Read Capacity (16), Read (16) and Write (16) next when hard disk size > 2TB\r
2448 //\r
2449 ScsiDiskDevice->Cdb16Byte = TRUE;\r
2450 //\r
2451 // submit Read Capacity(16) Command to get parameter LogicalBlocksPerPhysicalBlock\r
2452 // and LowestAlignedLba\r
2453 //\r
2454 CommandStatus = ScsiReadCapacity16Command (\r
2455 ScsiDiskDevice->ScsiIo,\r
2456 SCSI_DISK_TIMEOUT,\r
2457 NULL,\r
2458 &SenseDataLength,\r
2459 &HostAdapterStatus,\r
2460 &TargetStatus,\r
2461 (VOID *) CapacityData16,\r
2462 &DataLength16,\r
2463 FALSE\r
2464 );\r
2465 }\r
2466\r
2467 //\r
2468 // no need to check HostAdapterStatus and TargetStatus\r
2469 //\r
2470 if (CommandStatus == EFI_SUCCESS) {\r
2471 GetMediaInfo (ScsiDiskDevice, CapacityData10, CapacityData16);\r
2472 FreeAlignedBuffer (CapacityData10, sizeof (EFI_SCSI_DISK_CAPACITY_DATA));\r
2473 FreeAlignedBuffer (CapacityData16, sizeof (EFI_SCSI_DISK_CAPACITY_DATA16));\r
2474 return EFI_SUCCESS;\r
2475 }\r
2476\r
2477 FreeAlignedBuffer (CapacityData10, sizeof (EFI_SCSI_DISK_CAPACITY_DATA));\r
2478 FreeAlignedBuffer (CapacityData16, sizeof (EFI_SCSI_DISK_CAPACITY_DATA16));\r
2479\r
2480 if (CommandStatus == EFI_NOT_READY) {\r
2481 *NeedRetry = TRUE;\r
2482 return EFI_DEVICE_ERROR;\r
2483 } else if ((CommandStatus == EFI_INVALID_PARAMETER) || (CommandStatus == EFI_UNSUPPORTED)) {\r
2484 *NeedRetry = FALSE;\r
2485 return EFI_DEVICE_ERROR;\r
2486 }\r
2487\r
2488 //\r
2489 // go ahead to check HostAdapterStatus and TargetStatus\r
2490 // (EFI_TIMEOUT, EFI_DEVICE_ERROR, EFI_WARN_BUFFER_TOO_SMALL)\r
2491 //\r
2492 \r
2493 Status = CheckHostAdapterStatus (HostAdapterStatus);\r
2494 if ((Status == EFI_TIMEOUT) || (Status == EFI_NOT_READY)) {\r
2495 *NeedRetry = TRUE;\r
2496 return EFI_DEVICE_ERROR;\r
2497 \r
2498 } else if (Status == EFI_DEVICE_ERROR) {\r
2499 //\r
2500 // reset the scsi channel\r
2501 //\r
2502 ScsiDiskDevice->ScsiIo->ResetBus (ScsiDiskDevice->ScsiIo);\r
2503 *NeedRetry = FALSE;\r
2504 return EFI_DEVICE_ERROR;\r
2505 }\r
2506\r
2507 Status = CheckTargetStatus (TargetStatus);\r
2508 if (Status == EFI_NOT_READY) {\r
2509 //\r
2510 // reset the scsi device\r
2511 //\r
2512 ScsiDiskDevice->ScsiIo->ResetDevice (ScsiDiskDevice->ScsiIo);\r
2513 *NeedRetry = TRUE;\r
2514 return EFI_DEVICE_ERROR;\r
2515\r
2516 } else if (Status == EFI_DEVICE_ERROR) {\r
2517 *NeedRetry = FALSE;\r
2518 return EFI_DEVICE_ERROR;\r
2519 }\r
2520 \r
2521 //\r
2522 // if goes here, meant ScsiReadCapacityCommand() failed.\r
2523 // if ScsiDiskRequestSenseKeys() succeeds at last,\r
2524 // better retry ScsiReadCapacityCommand(). (by setting *NeedRetry = TRUE)\r
2525 //\r
2526 MaxRetry = 3;\r
2527 for (Index = 0; Index < MaxRetry; Index++) {\r
2528\r
2529 Status = ScsiDiskRequestSenseKeys (\r
2530 ScsiDiskDevice,\r
2531 NeedRetry,\r
2532 SenseDataArray,\r
2533 NumberOfSenseKeys,\r
2534 TRUE\r
2535 );\r
2536 if (!EFI_ERROR (Status)) {\r
2537 return EFI_SUCCESS;\r
2538 }\r
2539\r
2540 if (!*NeedRetry) {\r
2541 return EFI_DEVICE_ERROR;\r
2542 }\r
2543 }\r
2544 //\r
2545 // ScsiDiskRequestSenseKeys() failed after several rounds of retry.\r
2546 // set *NeedRetry = FALSE to avoid the outside caller try again.\r
2547 //\r
2548 *NeedRetry = FALSE;\r
2549 return EFI_DEVICE_ERROR;\r
2550}\r
2551\r
2552/**\r
2553 Check the HostAdapter status and re-interpret it in EFI_STATUS.\r
2554\r
2555 @param HostAdapterStatus Host Adapter status\r
2556\r
2557 @retval EFI_SUCCESS Host adapter is OK.\r
2558 @retval EFI_TIMEOUT Timeout.\r
2559 @retval EFI_NOT_READY Adapter NOT ready.\r
2560 @retval EFI_DEVICE_ERROR Adapter device error.\r
2561\r
2562**/\r
2563EFI_STATUS\r
2564CheckHostAdapterStatus (\r
2565 IN UINT8 HostAdapterStatus\r
2566 )\r
2567{\r
2568 switch (HostAdapterStatus) {\r
2569 case EFI_EXT_SCSI_STATUS_HOST_ADAPTER_OK:\r
2570 return EFI_SUCCESS;\r
2571\r
2572 case EFI_EXT_SCSI_STATUS_HOST_ADAPTER_SELECTION_TIMEOUT:\r
2573 case EFI_EXT_SCSI_STATUS_HOST_ADAPTER_TIMEOUT:\r
2574 case EFI_EXT_SCSI_STATUS_HOST_ADAPTER_TIMEOUT_COMMAND:\r
2575 return EFI_TIMEOUT;\r
2576\r
2577 case EFI_EXT_SCSI_STATUS_HOST_ADAPTER_MESSAGE_REJECT:\r
2578 case EFI_EXT_SCSI_STATUS_HOST_ADAPTER_PARITY_ERROR:\r
2579 case EFI_EXT_SCSI_STATUS_HOST_ADAPTER_REQUEST_SENSE_FAILED:\r
2580 case EFI_EXT_SCSI_STATUS_HOST_ADAPTER_DATA_OVERRUN_UNDERRUN:\r
2581 case EFI_EXT_SCSI_STATUS_HOST_ADAPTER_BUS_RESET:\r
2582 return EFI_NOT_READY;\r
2583\r
2584 case EFI_EXT_SCSI_STATUS_HOST_ADAPTER_BUS_FREE:\r
2585 case EFI_EXT_SCSI_STATUS_HOST_ADAPTER_PHASE_ERROR:\r
2586 return EFI_DEVICE_ERROR;\r
2587\r
2588 default:\r
2589 return EFI_SUCCESS;\r
2590 }\r
2591}\r
2592\r
2593\r
2594/**\r
2595 Check the target status and re-interpret it in EFI_STATUS.\r
2596\r
2597 @param TargetStatus Target status\r
2598\r
2599 @retval EFI_NOT_READY Device is NOT ready.\r
2600 @retval EFI_DEVICE_ERROR \r
2601 @retval EFI_SUCCESS\r
2602\r
2603**/\r
2604EFI_STATUS\r
2605CheckTargetStatus (\r
2606 IN UINT8 TargetStatus\r
2607 )\r
2608{\r
2609 switch (TargetStatus) {\r
2610 case EFI_EXT_SCSI_STATUS_TARGET_GOOD:\r
2611 case EFI_EXT_SCSI_STATUS_TARGET_CHECK_CONDITION:\r
2612 case EFI_EXT_SCSI_STATUS_TARGET_CONDITION_MET:\r
2613 return EFI_SUCCESS;\r
2614\r
2615 case EFI_EXT_SCSI_STATUS_TARGET_INTERMEDIATE:\r
2616 case EFI_EXT_SCSI_STATUS_TARGET_INTERMEDIATE_CONDITION_MET:\r
2617 case EFI_EXT_SCSI_STATUS_TARGET_BUSY:\r
2618 case EFI_EXT_SCSI_STATUS_TARGET_TASK_SET_FULL:\r
2619 return EFI_NOT_READY;\r
2620\r
2621 case EFI_EXT_SCSI_STATUS_TARGET_RESERVATION_CONFLICT:\r
2622 return EFI_DEVICE_ERROR;\r
2623\r
2624 default:\r
2625 return EFI_SUCCESS;\r
2626 }\r
2627}\r
2628\r
2629\r
2630/**\r
2631 Retrieve all sense keys from the device.\r
2632\r
2633 When encountering error during the process, if retrieve sense keys before\r
2634 error encountered, it returns the sense keys with return status set to EFI_SUCCESS,\r
2635 and NeedRetry set to FALSE; otherwize, return the proper return status.\r
2636\r
2637 @param ScsiDiskDevice The pointer of SCSI_DISK_DEV\r
2638 @param NeedRetry The pointer of flag indicates if need a retry\r
2639 @param SenseDataArray The pointer of an array of sense data\r
2640 @param NumberOfSenseKeys The number of sense key\r
2641 @param AskResetIfError The flag indicates if need reset when error occurs\r
2642\r
2643 @retval EFI_DEVICE_ERROR Indicates that error occurs\r
2644 @retval EFI_SUCCESS Successfully to request sense key\r
2645\r
2646**/\r
2647EFI_STATUS\r
2648ScsiDiskRequestSenseKeys (\r
2649 IN OUT SCSI_DISK_DEV *ScsiDiskDevice,\r
2650 OUT BOOLEAN *NeedRetry,\r
2651 OUT EFI_SCSI_SENSE_DATA **SenseDataArray,\r
2652 OUT UINTN *NumberOfSenseKeys,\r
2653 IN BOOLEAN AskResetIfError\r
2654 )\r
2655{\r
2656 EFI_SCSI_SENSE_DATA *PtrSenseData;\r
2657 UINT8 SenseDataLength;\r
2658 BOOLEAN SenseReq;\r
2659 EFI_STATUS Status;\r
2660 EFI_STATUS FallStatus;\r
2661 UINT8 HostAdapterStatus;\r
2662 UINT8 TargetStatus;\r
2663\r
2664 FallStatus = EFI_SUCCESS;\r
2665 SenseDataLength = (UINT8) sizeof (EFI_SCSI_SENSE_DATA);\r
2666\r
2667 ZeroMem (\r
2668 ScsiDiskDevice->SenseData,\r
2669 sizeof (EFI_SCSI_SENSE_DATA) * (ScsiDiskDevice->SenseDataNumber)\r
2670 );\r
2671\r
2672 *NumberOfSenseKeys = 0;\r
2673 *SenseDataArray = ScsiDiskDevice->SenseData;\r
2674 Status = EFI_SUCCESS;\r
2675 PtrSenseData = AllocateAlignedBuffer (ScsiDiskDevice, sizeof (EFI_SCSI_SENSE_DATA));\r
2676 if (PtrSenseData == NULL) {\r
2677 return EFI_DEVICE_ERROR;\r
2678 }\r
2679\r
2680 for (SenseReq = TRUE; SenseReq;) {\r
2681 ZeroMem (PtrSenseData, sizeof (EFI_SCSI_SENSE_DATA));\r
2682 Status = ScsiRequestSenseCommand (\r
2683 ScsiDiskDevice->ScsiIo,\r
2684 SCSI_DISK_TIMEOUT,\r
2685 PtrSenseData,\r
2686 &SenseDataLength,\r
2687 &HostAdapterStatus,\r
2688 &TargetStatus\r
2689 );\r
2690 if ((Status == EFI_SUCCESS) || (Status == EFI_WARN_BUFFER_TOO_SMALL)) {\r
2691 FallStatus = EFI_SUCCESS;\r
2692 \r
2693 } else if ((Status == EFI_TIMEOUT) || (Status == EFI_NOT_READY)) {\r
2694 *NeedRetry = TRUE;\r
2695 FallStatus = EFI_DEVICE_ERROR;\r
2696 \r
2697 } else if ((Status == EFI_INVALID_PARAMETER) || (Status == EFI_UNSUPPORTED)) {\r
2698 *NeedRetry = FALSE;\r
2699 FallStatus = EFI_DEVICE_ERROR;\r
2700 \r
2701 } else if (Status == EFI_DEVICE_ERROR) {\r
2702 if (AskResetIfError) {\r
2703 ScsiDiskDevice->ScsiIo->ResetDevice (ScsiDiskDevice->ScsiIo);\r
2704 }\r
2705 \r
2706 FallStatus = EFI_DEVICE_ERROR;\r
2707 }\r
2708\r
2709 if (EFI_ERROR (FallStatus)) {\r
2710 if (*NumberOfSenseKeys != 0) {\r
2711 *NeedRetry = FALSE;\r
2712 Status = EFI_SUCCESS;\r
2713 goto EXIT;\r
2714 } else {\r
2715 Status = EFI_DEVICE_ERROR;\r
2716 goto EXIT;\r
2717 }\r
2718 }\r
2719\r
2720 CopyMem (ScsiDiskDevice->SenseData + *NumberOfSenseKeys, PtrSenseData, SenseDataLength);\r
2721 (*NumberOfSenseKeys) += 1;\r
2722\r
2723 //\r
2724 // no more sense key or number of sense keys exceeds predefined,\r
2725 // skip the loop.\r
2726 //\r
2727 if ((PtrSenseData->Sense_Key == EFI_SCSI_SK_NO_SENSE) || \r
2728 (*NumberOfSenseKeys == ScsiDiskDevice->SenseDataNumber)) {\r
2729 SenseReq = FALSE;\r
2730 }\r
2731 }\r
2732\r
2733EXIT:\r
2734 FreeAlignedBuffer (PtrSenseData, sizeof (EFI_SCSI_SENSE_DATA));\r
2735 return Status;\r
2736}\r
2737\r
2738\r
2739/**\r
2740 Get information from media read capacity command.\r
2741\r
2742 @param ScsiDiskDevice The pointer of SCSI_DISK_DEV\r
2743 @param Capacity10 The pointer of EFI_SCSI_DISK_CAPACITY_DATA\r
2744 @param Capacity16 The pointer of EFI_SCSI_DISK_CAPACITY_DATA16\r
2745\r
2746**/\r
2747VOID\r
2748GetMediaInfo (\r
2749 IN OUT SCSI_DISK_DEV *ScsiDiskDevice,\r
2750 IN EFI_SCSI_DISK_CAPACITY_DATA *Capacity10,\r
2751 IN EFI_SCSI_DISK_CAPACITY_DATA16 *Capacity16\r
2752 )\r
2753{\r
2754 UINT8 *Ptr;\r
2755\r
2756 if (!ScsiDiskDevice->Cdb16Byte) {\r
2757 ScsiDiskDevice->BlkIo.Media->LastBlock = ((UINT32) Capacity10->LastLba3 << 24) |\r
2758 (Capacity10->LastLba2 << 16) |\r
2759 (Capacity10->LastLba1 << 8) |\r
2760 Capacity10->LastLba0;\r
2761 \r
2762 ScsiDiskDevice->BlkIo.Media->BlockSize = (Capacity10->BlockSize3 << 24) |\r
2763 (Capacity10->BlockSize2 << 16) | \r
2764 (Capacity10->BlockSize1 << 8) |\r
2765 Capacity10->BlockSize0;\r
2766 ScsiDiskDevice->BlkIo.Media->LowestAlignedLba = 0;\r
2767 ScsiDiskDevice->BlkIo.Media->LogicalBlocksPerPhysicalBlock = 0;\r
2768 if (!ScsiDiskDevice->BlockLimitsVpdSupported) {\r
2769 ScsiDiskDevice->UnmapInfo.MaxLbaCnt = (UINT32) ScsiDiskDevice->BlkIo.Media->LastBlock;\r
2770 }\r
2771 } else {\r
2772 Ptr = (UINT8*)&ScsiDiskDevice->BlkIo.Media->LastBlock;\r
2773 *Ptr++ = Capacity16->LastLba0;\r
2774 *Ptr++ = Capacity16->LastLba1;\r
2775 *Ptr++ = Capacity16->LastLba2;\r
2776 *Ptr++ = Capacity16->LastLba3;\r
2777 *Ptr++ = Capacity16->LastLba4;\r
2778 *Ptr++ = Capacity16->LastLba5;\r
2779 *Ptr++ = Capacity16->LastLba6;\r
2780 *Ptr = Capacity16->LastLba7;\r
2781\r
2782 ScsiDiskDevice->BlkIo.Media->BlockSize = (Capacity16->BlockSize3 << 24) |\r
2783 (Capacity16->BlockSize2 << 16) | \r
2784 (Capacity16->BlockSize1 << 8) |\r
2785 Capacity16->BlockSize0;\r
2786\r
2787 ScsiDiskDevice->BlkIo.Media->LowestAlignedLba = (Capacity16->LowestAlignLogic2 << 8) |\r
2788 Capacity16->LowestAlignLogic1;\r
2789 ScsiDiskDevice->BlkIo.Media->LogicalBlocksPerPhysicalBlock = (1 << Capacity16->LogicPerPhysical);\r
2790 if (!ScsiDiskDevice->BlockLimitsVpdSupported) {\r
2791 if (ScsiDiskDevice->BlkIo.Media->LastBlock > (UINT32) -1) {\r
2792 ScsiDiskDevice->UnmapInfo.MaxLbaCnt = (UINT32) -1;\r
2793 } else {\r
2794 ScsiDiskDevice->UnmapInfo.MaxLbaCnt = (UINT32) ScsiDiskDevice->BlkIo.Media->LastBlock;\r
2795 }\r
2796 }\r
2797 }\r
2798\r
2799 ScsiDiskDevice->BlkIo.Media->MediaPresent = TRUE;\r
2800}\r
2801\r
2802/**\r
2803 Parse Inquiry data.\r
2804\r
2805 @param ScsiDiskDevice The pointer of SCSI_DISK_DEV\r
2806\r
2807**/\r
2808VOID\r
2809ParseInquiryData (\r
2810 IN OUT SCSI_DISK_DEV *ScsiDiskDevice\r
2811 )\r
2812{\r
2813 ScsiDiskDevice->FixedDevice = (BOOLEAN) ((ScsiDiskDevice->InquiryData.Rmb == 1) ? 0 : 1);\r
2814 ScsiDiskDevice->BlkIoMedia.RemovableMedia = (BOOLEAN) (!ScsiDiskDevice->FixedDevice);\r
2815}\r
2816\r
2817/**\r
2818 Read sector from SCSI Disk.\r
2819\r
2820 @param ScsiDiskDevice The pointer of SCSI_DISK_DEV\r
2821 @param Buffer The buffer to fill in the read out data\r
2822 @param Lba Logic block address\r
2823 @param NumberOfBlocks The number of blocks to read\r
2824\r
2825 @retval EFI_DEVICE_ERROR Indicates a device error.\r
2826 @retval EFI_SUCCESS Operation is successful.\r
2827\r
2828**/\r
2829EFI_STATUS\r
2830ScsiDiskReadSectors (\r
2831 IN SCSI_DISK_DEV *ScsiDiskDevice,\r
2832 OUT VOID *Buffer,\r
2833 IN EFI_LBA Lba,\r
2834 IN UINTN NumberOfBlocks\r
2835 )\r
2836{\r
2837 UINTN BlocksRemaining;\r
2838 UINT8 *PtrBuffer;\r
2839 UINT32 BlockSize;\r
2840 UINT32 ByteCount;\r
2841 UINT32 MaxBlock;\r
2842 UINT32 SectorCount;\r
2843 UINT32 NextSectorCount;\r
2844 UINT64 Timeout;\r
2845 EFI_STATUS Status;\r
2846 UINT8 Index;\r
2847 UINT8 MaxRetry;\r
2848 BOOLEAN NeedRetry;\r
2849\r
2850 Status = EFI_SUCCESS;\r
2851\r
2852 BlocksRemaining = NumberOfBlocks;\r
2853 BlockSize = ScsiDiskDevice->BlkIo.Media->BlockSize;\r
2854 \r
2855 //\r
2856 // limit the data bytes that can be transferred by one Read(10) or Read(16) Command\r
2857 //\r
2858 if (!ScsiDiskDevice->Cdb16Byte) {\r
2859 MaxBlock = 0xFFFF;\r
2860 } else {\r
2861 MaxBlock = 0xFFFFFFFF;\r
2862 }\r
2863\r
2864 PtrBuffer = Buffer;\r
2865\r
2866 while (BlocksRemaining > 0) {\r
2867\r
2868 if (BlocksRemaining <= MaxBlock) {\r
2869 if (!ScsiDiskDevice->Cdb16Byte) {\r
2870 SectorCount = (UINT16) BlocksRemaining;\r
2871 } else {\r
2872 SectorCount = (UINT32) BlocksRemaining;\r
2873 }\r
2874 } else {\r
2875 SectorCount = MaxBlock;\r
2876 }\r
2877\r
2878 ByteCount = SectorCount * BlockSize;\r
2879 //\r
2880 // |------------------------|-----------------|------------------|-----------------|\r
2881 // | ATA Transfer Mode | Transfer Rate | SCSI Interface | Transfer Rate |\r
2882 // |------------------------|-----------------|------------------|-----------------|\r
2883 // | PIO Mode 0 | 3.3Mbytes/sec | SCSI-1 | 5Mbytes/sec |\r
2884 // |------------------------|-----------------|------------------|-----------------|\r
2885 // | PIO Mode 1 | 5.2Mbytes/sec | Fast SCSI | 10Mbytes/sec |\r
2886 // |------------------------|-----------------|------------------|-----------------|\r
2887 // | PIO Mode 2 | 8.3Mbytes/sec | Fast-Wide SCSI | 20Mbytes/sec |\r
2888 // |------------------------|-----------------|------------------|-----------------|\r
2889 // | PIO Mode 3 | 11.1Mbytes/sec | Ultra SCSI | 20Mbytes/sec |\r
2890 // |------------------------|-----------------|------------------|-----------------|\r
2891 // | PIO Mode 4 | 16.6Mbytes/sec | Ultra Wide SCSI | 40Mbytes/sec |\r
2892 // |------------------------|-----------------|------------------|-----------------|\r
2893 // | Single-word DMA Mode 0 | 2.1Mbytes/sec | Ultra2 SCSI | 40Mbytes/sec |\r
2894 // |------------------------|-----------------|------------------|-----------------|\r
2895 // | Single-word DMA Mode 1 | 4.2Mbytes/sec | Ultra2 Wide SCSI | 80Mbytes/sec |\r
2896 // |------------------------|-----------------|------------------|-----------------|\r
2897 // | Single-word DMA Mode 2 | 8.4Mbytes/sec | Ultra3 SCSI | 160Mbytes/sec |\r
2898 // |------------------------|-----------------|------------------|-----------------|\r
2899 // | Multi-word DMA Mode 0 | 4.2Mbytes/sec | Ultra-320 SCSI | 320Mbytes/sec |\r
2900 // |------------------------|-----------------|------------------|-----------------|\r
2901 // | Multi-word DMA Mode 1 | 13.3Mbytes/sec | Ultra-640 SCSI | 640Mbytes/sec |\r
2902 // |------------------------|-----------------|------------------|-----------------|\r
2903 //\r
2904 // As ScsiDisk and ScsiBus driver are used to manage SCSI or ATAPI devices, we have to use\r
2905 // the lowest transfer rate to calculate the possible maximum timeout value for each operation.\r
2906 // From the above table, we could know 2.1Mbytes per second is lowest one.\r
2907 // The timout value is rounded up to nearest integar and here an additional 30s is added\r
2908 // to follow ATA spec in which it mentioned that the device may take up to 30s to respond\r
2909 // commands in the Standby/Idle mode.\r
2910 //\r
2911 Timeout = EFI_TIMER_PERIOD_SECONDS (ByteCount / 2100000 + 31);\r
2912\r
2913 MaxRetry = 2;\r
2914 for (Index = 0; Index < MaxRetry; Index++) {\r
2915 if (!ScsiDiskDevice->Cdb16Byte) {\r
2916 Status = ScsiDiskRead10 (\r
2917 ScsiDiskDevice,\r
2918 &NeedRetry,\r
2919 Timeout,\r
2920 PtrBuffer,\r
2921 &ByteCount,\r
2922 (UINT32) Lba,\r
2923 SectorCount\r
2924 );\r
2925 } else {\r
2926 Status = ScsiDiskRead16 (\r
2927 ScsiDiskDevice,\r
2928 &NeedRetry,\r
2929 Timeout,\r
2930 PtrBuffer,\r
2931 &ByteCount,\r
2932 Lba,\r
2933 SectorCount\r
2934 );\r
2935 }\r
2936 if (!EFI_ERROR (Status)) {\r
2937 break;\r
2938 }\r
2939\r
2940 if (!NeedRetry) {\r
2941 return EFI_DEVICE_ERROR;\r
2942 }\r
2943\r
2944 //\r
2945 // We need to retry. However, if ScsiDiskRead10() or ScsiDiskRead16() has\r
2946 // lowered ByteCount on output, we must make sure that we lower\r
2947 // SectorCount accordingly. SectorCount will be encoded in the CDB, and\r
2948 // it is invalid to request more sectors in the CDB than the entire\r
2949 // transfer (ie. ByteCount) can carry.\r
2950 //\r
2951 // In addition, ByteCount is only expected to go down, or stay unchaged.\r
2952 // Therefore we don't need to update Timeout: the original timeout should\r
2953 // accommodate shorter transfers too.\r
2954 //\r
2955 NextSectorCount = ByteCount / BlockSize;\r
2956 if (NextSectorCount < SectorCount) {\r
2957 SectorCount = NextSectorCount;\r
2958 //\r
2959 // Account for any rounding down.\r
2960 //\r
2961 ByteCount = SectorCount * BlockSize;\r
2962 }\r
2963 }\r
2964\r
2965 if ((Index == MaxRetry) && (Status != EFI_SUCCESS)) {\r
2966 return EFI_DEVICE_ERROR;\r
2967 }\r
2968\r
2969 //\r
2970 // actual transferred sectors\r
2971 //\r
2972 SectorCount = ByteCount / BlockSize;\r
2973\r
2974 Lba += SectorCount;\r
2975 PtrBuffer = PtrBuffer + SectorCount * BlockSize;\r
2976 BlocksRemaining -= SectorCount;\r
2977 }\r
2978\r
2979 return EFI_SUCCESS;\r
2980}\r
2981\r
2982/**\r
2983 Write sector to SCSI Disk.\r
2984\r
2985 @param ScsiDiskDevice The pointer of SCSI_DISK_DEV\r
2986 @param Buffer The buffer of data to be written into SCSI Disk\r
2987 @param Lba Logic block address\r
2988 @param NumberOfBlocks The number of blocks to read\r
2989\r
2990 @retval EFI_DEVICE_ERROR Indicates a device error.\r
2991 @retval EFI_SUCCESS Operation is successful.\r
2992\r
2993**/\r
2994EFI_STATUS\r
2995ScsiDiskWriteSectors (\r
2996 IN SCSI_DISK_DEV *ScsiDiskDevice,\r
2997 IN VOID *Buffer,\r
2998 IN EFI_LBA Lba,\r
2999 IN UINTN NumberOfBlocks\r
3000 )\r
3001{\r
3002 UINTN BlocksRemaining;\r
3003 UINT8 *PtrBuffer;\r
3004 UINT32 BlockSize;\r
3005 UINT32 ByteCount;\r
3006 UINT32 MaxBlock;\r
3007 UINT32 SectorCount;\r
3008 UINT32 NextSectorCount;\r
3009 UINT64 Timeout;\r
3010 EFI_STATUS Status;\r
3011 UINT8 Index;\r
3012 UINT8 MaxRetry;\r
3013 BOOLEAN NeedRetry;\r
3014\r
3015 Status = EFI_SUCCESS;\r
3016\r
3017 BlocksRemaining = NumberOfBlocks;\r
3018 BlockSize = ScsiDiskDevice->BlkIo.Media->BlockSize;\r
3019\r
3020 //\r
3021 // limit the data bytes that can be transferred by one Read(10) or Read(16) Command\r
3022 //\r
3023 if (!ScsiDiskDevice->Cdb16Byte) {\r
3024 MaxBlock = 0xFFFF;\r
3025 } else {\r
3026 MaxBlock = 0xFFFFFFFF;\r
3027 }\r
3028\r
3029 PtrBuffer = Buffer;\r
3030\r
3031 while (BlocksRemaining > 0) {\r
3032\r
3033 if (BlocksRemaining <= MaxBlock) {\r
3034 if (!ScsiDiskDevice->Cdb16Byte) {\r
3035 SectorCount = (UINT16) BlocksRemaining;\r
3036 } else {\r
3037 SectorCount = (UINT32) BlocksRemaining;\r
3038 }\r
3039 } else {\r
3040 SectorCount = MaxBlock;\r
3041 }\r
3042\r
3043 ByteCount = SectorCount * BlockSize;\r
3044 //\r
3045 // |------------------------|-----------------|------------------|-----------------|\r
3046 // | ATA Transfer Mode | Transfer Rate | SCSI Interface | Transfer Rate |\r
3047 // |------------------------|-----------------|------------------|-----------------|\r
3048 // | PIO Mode 0 | 3.3Mbytes/sec | SCSI-1 | 5Mbytes/sec |\r
3049 // |------------------------|-----------------|------------------|-----------------|\r
3050 // | PIO Mode 1 | 5.2Mbytes/sec | Fast SCSI | 10Mbytes/sec |\r
3051 // |------------------------|-----------------|------------------|-----------------|\r
3052 // | PIO Mode 2 | 8.3Mbytes/sec | Fast-Wide SCSI | 20Mbytes/sec |\r
3053 // |------------------------|-----------------|------------------|-----------------|\r
3054 // | PIO Mode 3 | 11.1Mbytes/sec | Ultra SCSI | 20Mbytes/sec |\r
3055 // |------------------------|-----------------|------------------|-----------------|\r
3056 // | PIO Mode 4 | 16.6Mbytes/sec | Ultra Wide SCSI | 40Mbytes/sec |\r
3057 // |------------------------|-----------------|------------------|-----------------|\r
3058 // | Single-word DMA Mode 0 | 2.1Mbytes/sec | Ultra2 SCSI | 40Mbytes/sec |\r
3059 // |------------------------|-----------------|------------------|-----------------|\r
3060 // | Single-word DMA Mode 1 | 4.2Mbytes/sec | Ultra2 Wide SCSI | 80Mbytes/sec |\r
3061 // |------------------------|-----------------|------------------|-----------------|\r
3062 // | Single-word DMA Mode 2 | 8.4Mbytes/sec | Ultra3 SCSI | 160Mbytes/sec |\r
3063 // |------------------------|-----------------|------------------|-----------------|\r
3064 // | Multi-word DMA Mode 0 | 4.2Mbytes/sec | Ultra-320 SCSI | 320Mbytes/sec |\r
3065 // |------------------------|-----------------|------------------|-----------------|\r
3066 // | Multi-word DMA Mode 1 | 13.3Mbytes/sec | Ultra-640 SCSI | 640Mbytes/sec |\r
3067 // |------------------------|-----------------|------------------|-----------------|\r
3068 //\r
3069 // As ScsiDisk and ScsiBus driver are used to manage SCSI or ATAPI devices, we have to use\r
3070 // the lowest transfer rate to calculate the possible maximum timeout value for each operation.\r
3071 // From the above table, we could know 2.1Mbytes per second is lowest one.\r
3072 // The timout value is rounded up to nearest integar and here an additional 30s is added\r
3073 // to follow ATA spec in which it mentioned that the device may take up to 30s to respond\r
3074 // commands in the Standby/Idle mode.\r
3075 //\r
3076 Timeout = EFI_TIMER_PERIOD_SECONDS (ByteCount / 2100000 + 31);\r
3077 MaxRetry = 2;\r
3078 for (Index = 0; Index < MaxRetry; Index++) {\r
3079 if (!ScsiDiskDevice->Cdb16Byte) {\r
3080 Status = ScsiDiskWrite10 (\r
3081 ScsiDiskDevice,\r
3082 &NeedRetry,\r
3083 Timeout,\r
3084 PtrBuffer,\r
3085 &ByteCount,\r
3086 (UINT32) Lba,\r
3087 SectorCount\r
3088 );\r
3089 } else {\r
3090 Status = ScsiDiskWrite16 (\r
3091 ScsiDiskDevice,\r
3092 &NeedRetry,\r
3093 Timeout,\r
3094 PtrBuffer,\r
3095 &ByteCount,\r
3096 Lba,\r
3097 SectorCount\r
3098 ); \r
3099 }\r
3100 if (!EFI_ERROR (Status)) {\r
3101 break;\r
3102 }\r
3103\r
3104 if (!NeedRetry) {\r
3105 return EFI_DEVICE_ERROR;\r
3106 }\r
3107\r
3108 //\r
3109 // We need to retry. However, if ScsiDiskWrite10() or ScsiDiskWrite16()\r
3110 // has lowered ByteCount on output, we must make sure that we lower\r
3111 // SectorCount accordingly. SectorCount will be encoded in the CDB, and\r
3112 // it is invalid to request more sectors in the CDB than the entire\r
3113 // transfer (ie. ByteCount) can carry.\r
3114 //\r
3115 // In addition, ByteCount is only expected to go down, or stay unchaged.\r
3116 // Therefore we don't need to update Timeout: the original timeout should\r
3117 // accommodate shorter transfers too.\r
3118 //\r
3119 NextSectorCount = ByteCount / BlockSize;\r
3120 if (NextSectorCount < SectorCount) {\r
3121 SectorCount = NextSectorCount;\r
3122 //\r
3123 // Account for any rounding down.\r
3124 //\r
3125 ByteCount = SectorCount * BlockSize;\r
3126 }\r
3127 }\r
3128\r
3129 if ((Index == MaxRetry) && (Status != EFI_SUCCESS)) {\r
3130 return EFI_DEVICE_ERROR;\r
3131 }\r
3132 //\r
3133 // actual transferred sectors\r
3134 //\r
3135 SectorCount = ByteCount / BlockSize;\r
3136\r
3137 Lba += SectorCount;\r
3138 PtrBuffer = PtrBuffer + SectorCount * BlockSize;\r
3139 BlocksRemaining -= SectorCount;\r
3140 }\r
3141\r
3142 return EFI_SUCCESS;\r
3143}\r
3144\r
3145/**\r
3146 Asynchronously read sector from SCSI Disk.\r
3147\r
3148 @param ScsiDiskDevice The pointer of SCSI_DISK_DEV.\r
3149 @param Buffer The buffer to fill in the read out data.\r
3150 @param Lba Logic block address.\r
3151 @param NumberOfBlocks The number of blocks to read.\r
3152 @param Token A pointer to the token associated with the\r
3153 non-blocking read request.\r
3154\r
3155 @retval EFI_INVALID_PARAMETER Token is NULL or Token->Event is NULL.\r
3156 @retval EFI_DEVICE_ERROR Indicates a device error.\r
3157 @retval EFI_SUCCESS Operation is successful.\r
3158\r
3159**/\r
3160EFI_STATUS\r
3161ScsiDiskAsyncReadSectors (\r
3162 IN SCSI_DISK_DEV *ScsiDiskDevice,\r
3163 OUT VOID *Buffer,\r
3164 IN EFI_LBA Lba,\r
3165 IN UINTN NumberOfBlocks,\r
3166 IN EFI_BLOCK_IO2_TOKEN *Token\r
3167 )\r
3168{\r
3169 UINTN BlocksRemaining;\r
3170 UINT8 *PtrBuffer;\r
3171 UINT32 BlockSize;\r
3172 UINT32 ByteCount;\r
3173 UINT32 MaxBlock;\r
3174 UINT32 SectorCount;\r
3175 UINT64 Timeout;\r
3176 SCSI_BLKIO2_REQUEST *BlkIo2Req;\r
3177 EFI_STATUS Status;\r
3178 EFI_TPL OldTpl;\r
3179\r
3180 if ((Token == NULL) || (Token->Event == NULL)) {\r
3181 return EFI_INVALID_PARAMETER;\r
3182 }\r
3183\r
3184 BlkIo2Req = AllocateZeroPool (sizeof (SCSI_BLKIO2_REQUEST));\r
3185 if (BlkIo2Req == NULL) {\r
3186 return EFI_OUT_OF_RESOURCES;\r
3187 }\r
3188\r
3189 BlkIo2Req->Token = Token;\r
3190\r
3191 OldTpl = gBS->RaiseTPL (TPL_NOTIFY);\r
3192 InsertTailList (&ScsiDiskDevice->AsyncTaskQueue, &BlkIo2Req->Link);\r
3193 gBS->RestoreTPL (OldTpl);\r
3194\r
3195 InitializeListHead (&BlkIo2Req->ScsiRWQueue);\r
3196\r
3197 Status = EFI_SUCCESS;\r
3198\r
3199 BlocksRemaining = NumberOfBlocks;\r
3200 BlockSize = ScsiDiskDevice->BlkIo.Media->BlockSize;\r
3201\r
3202 //\r
3203 // Limit the data bytes that can be transferred by one Read(10) or Read(16)\r
3204 // Command\r
3205 //\r
3206 if (!ScsiDiskDevice->Cdb16Byte) {\r
3207 MaxBlock = 0xFFFF;\r
3208 } else {\r
3209 MaxBlock = 0xFFFFFFFF;\r
3210 }\r
3211\r
3212 PtrBuffer = Buffer;\r
3213\r
3214 while (BlocksRemaining > 0) {\r
3215\r
3216 if (BlocksRemaining <= MaxBlock) {\r
3217 if (!ScsiDiskDevice->Cdb16Byte) {\r
3218 SectorCount = (UINT16) BlocksRemaining;\r
3219 } else {\r
3220 SectorCount = (UINT32) BlocksRemaining;\r
3221 }\r
3222 } else {\r
3223 SectorCount = MaxBlock;\r
3224 }\r
3225\r
3226 ByteCount = SectorCount * BlockSize;\r
3227 //\r
3228 // |------------------------|-----------------|------------------|-----------------|\r
3229 // | ATA Transfer Mode | Transfer Rate | SCSI Interface | Transfer Rate |\r
3230 // |------------------------|-----------------|------------------|-----------------|\r
3231 // | PIO Mode 0 | 3.3Mbytes/sec | SCSI-1 | 5Mbytes/sec |\r
3232 // |------------------------|-----------------|------------------|-----------------|\r
3233 // | PIO Mode 1 | 5.2Mbytes/sec | Fast SCSI | 10Mbytes/sec |\r
3234 // |------------------------|-----------------|------------------|-----------------|\r
3235 // | PIO Mode 2 | 8.3Mbytes/sec | Fast-Wide SCSI | 20Mbytes/sec |\r
3236 // |------------------------|-----------------|------------------|-----------------|\r
3237 // | PIO Mode 3 | 11.1Mbytes/sec | Ultra SCSI | 20Mbytes/sec |\r
3238 // |------------------------|-----------------|------------------|-----------------|\r
3239 // | PIO Mode 4 | 16.6Mbytes/sec | Ultra Wide SCSI | 40Mbytes/sec |\r
3240 // |------------------------|-----------------|------------------|-----------------|\r
3241 // | Single-word DMA Mode 0 | 2.1Mbytes/sec | Ultra2 SCSI | 40Mbytes/sec |\r
3242 // |------------------------|-----------------|------------------|-----------------|\r
3243 // | Single-word DMA Mode 1 | 4.2Mbytes/sec | Ultra2 Wide SCSI | 80Mbytes/sec |\r
3244 // |------------------------|-----------------|------------------|-----------------|\r
3245 // | Single-word DMA Mode 2 | 8.4Mbytes/sec | Ultra3 SCSI | 160Mbytes/sec |\r
3246 // |------------------------|-----------------|------------------|-----------------|\r
3247 // | Multi-word DMA Mode 0 | 4.2Mbytes/sec | Ultra-320 SCSI | 320Mbytes/sec |\r
3248 // |------------------------|-----------------|------------------|-----------------|\r
3249 // | Multi-word DMA Mode 1 | 13.3Mbytes/sec | Ultra-640 SCSI | 640Mbytes/sec |\r
3250 // |------------------------|-----------------|------------------|-----------------|\r
3251 //\r
3252 // As ScsiDisk and ScsiBus driver are used to manage SCSI or ATAPI devices,\r
3253 // we have to use the lowest transfer rate to calculate the possible\r
3254 // maximum timeout value for each operation.\r
3255 // From the above table, we could know 2.1Mbytes per second is lowest one.\r
3256 // The timout value is rounded up to nearest integar and here an additional\r
3257 // 30s is added to follow ATA spec in which it mentioned that the device\r
3258 // may take up to 30s to respond commands in the Standby/Idle mode.\r
3259 //\r
3260 Timeout = EFI_TIMER_PERIOD_SECONDS (ByteCount / 2100000 + 31);\r
3261\r
3262 if (!ScsiDiskDevice->Cdb16Byte) {\r
3263 Status = ScsiDiskAsyncRead10 (\r
3264 ScsiDiskDevice,\r
3265 Timeout,\r
3266 0,\r
3267 PtrBuffer,\r
3268 ByteCount,\r
3269 (UINT32) Lba,\r
3270 SectorCount,\r
3271 BlkIo2Req,\r
3272 Token\r
3273 );\r
3274 } else {\r
3275 Status = ScsiDiskAsyncRead16 (\r
3276 ScsiDiskDevice,\r
3277 Timeout,\r
3278 0,\r
3279 PtrBuffer,\r
3280 ByteCount,\r
3281 Lba,\r
3282 SectorCount,\r
3283 BlkIo2Req,\r
3284 Token\r
3285 );\r
3286 }\r
3287 if (EFI_ERROR (Status)) {\r
3288 //\r
3289 // Some devices will return EFI_DEVICE_ERROR or EFI_TIMEOUT when the data\r
3290 // length of a SCSI I/O command is too large.\r
3291 // In this case, we retry sending the SCSI command with a data length\r
3292 // half of its previous value.\r
3293 //\r
3294 if ((Status == EFI_DEVICE_ERROR) || (Status == EFI_TIMEOUT)) {\r
3295 if ((MaxBlock > 1) && (SectorCount > 1)) {\r
3296 MaxBlock = MIN (MaxBlock, SectorCount) >> 1;\r
3297 continue;\r
3298 }\r
3299 }\r
3300\r
3301 OldTpl = gBS->RaiseTPL (TPL_NOTIFY);\r
3302 if (IsListEmpty (&BlkIo2Req->ScsiRWQueue)) {\r
3303 //\r
3304 // Free the SCSI_BLKIO2_REQUEST structure only when there is no other\r
3305 // SCSI sub-task running. Otherwise, it will be freed in the callback\r
3306 // function ScsiDiskNotify().\r
3307 //\r
3308 RemoveEntryList (&BlkIo2Req->Link);\r
3309 FreePool (BlkIo2Req);\r
3310 BlkIo2Req = NULL;\r
3311 gBS->RestoreTPL (OldTpl);\r
3312\r
3313 //\r
3314 // It is safe to return error status to the caller, since there is no\r
3315 // previous SCSI sub-task executing.\r
3316 //\r
3317 Status = EFI_DEVICE_ERROR;\r
3318 goto Done;\r
3319 } else {\r
3320 gBS->RestoreTPL (OldTpl);\r
3321\r
3322 //\r
3323 // There are previous SCSI commands still running, EFI_SUCCESS should\r
3324 // be returned to make sure that the caller does not free resources\r
3325 // still using by these SCSI commands.\r
3326 //\r
3327 Status = EFI_SUCCESS;\r
3328 goto Done;\r
3329 }\r
3330 }\r
3331\r
3332 //\r
3333 // Sectors submitted for transfer\r
3334 //\r
3335 SectorCount = ByteCount / BlockSize;\r
3336\r
3337 Lba += SectorCount;\r
3338 PtrBuffer = PtrBuffer + SectorCount * BlockSize;\r
3339 BlocksRemaining -= SectorCount;\r
3340 }\r
3341\r
3342 Status = EFI_SUCCESS;\r
3343\r
3344Done:\r
3345 if (BlkIo2Req != NULL) {\r
3346 BlkIo2Req->LastScsiRW = TRUE;\r
3347\r
3348 OldTpl = gBS->RaiseTPL (TPL_NOTIFY);\r
3349 if (IsListEmpty (&BlkIo2Req->ScsiRWQueue)) {\r
3350 RemoveEntryList (&BlkIo2Req->Link);\r
3351 FreePool (BlkIo2Req);\r
3352 BlkIo2Req = NULL;\r
3353\r
3354 gBS->SignalEvent (Token->Event);\r
3355 }\r
3356 gBS->RestoreTPL (OldTpl);\r
3357 }\r
3358\r
3359 return Status;\r
3360}\r
3361\r
3362/**\r
3363 Asynchronously write sector to SCSI Disk.\r
3364\r
3365 @param ScsiDiskDevice The pointer of SCSI_DISK_DEV.\r
3366 @param Buffer The buffer of data to be written into SCSI Disk.\r
3367 @param Lba Logic block address.\r
3368 @param NumberOfBlocks The number of blocks to read.\r
3369 @param Token A pointer to the token associated with the\r
3370 non-blocking read request.\r
3371\r
3372 @retval EFI_INVALID_PARAMETER Token is NULL or Token->Event is NULL\r
3373 @retval EFI_DEVICE_ERROR Indicates a device error.\r
3374 @retval EFI_SUCCESS Operation is successful.\r
3375\r
3376**/\r
3377EFI_STATUS\r
3378ScsiDiskAsyncWriteSectors (\r
3379 IN SCSI_DISK_DEV *ScsiDiskDevice,\r
3380 IN VOID *Buffer,\r
3381 IN EFI_LBA Lba,\r
3382 IN UINTN NumberOfBlocks,\r
3383 IN EFI_BLOCK_IO2_TOKEN *Token\r
3384 )\r
3385{\r
3386 UINTN BlocksRemaining;\r
3387 UINT8 *PtrBuffer;\r
3388 UINT32 BlockSize;\r
3389 UINT32 ByteCount;\r
3390 UINT32 MaxBlock;\r
3391 UINT32 SectorCount;\r
3392 UINT64 Timeout;\r
3393 SCSI_BLKIO2_REQUEST *BlkIo2Req;\r
3394 EFI_STATUS Status;\r
3395 EFI_TPL OldTpl;\r
3396\r
3397 if ((Token == NULL) || (Token->Event == NULL)) {\r
3398 return EFI_INVALID_PARAMETER;\r
3399 }\r
3400\r
3401 BlkIo2Req = AllocateZeroPool (sizeof (SCSI_BLKIO2_REQUEST));\r
3402 if (BlkIo2Req == NULL) {\r
3403 return EFI_OUT_OF_RESOURCES;\r
3404 }\r
3405\r
3406 BlkIo2Req->Token = Token;\r
3407\r
3408 OldTpl = gBS->RaiseTPL (TPL_NOTIFY);\r
3409 InsertTailList (&ScsiDiskDevice->AsyncTaskQueue, &BlkIo2Req->Link);\r
3410 gBS->RestoreTPL (OldTpl);\r
3411\r
3412 InitializeListHead (&BlkIo2Req->ScsiRWQueue);\r
3413\r
3414 Status = EFI_SUCCESS;\r
3415\r
3416 BlocksRemaining = NumberOfBlocks;\r
3417 BlockSize = ScsiDiskDevice->BlkIo.Media->BlockSize;\r
3418\r
3419 //\r
3420 // Limit the data bytes that can be transferred by one Read(10) or Read(16)\r
3421 // Command\r
3422 //\r
3423 if (!ScsiDiskDevice->Cdb16Byte) {\r
3424 MaxBlock = 0xFFFF;\r
3425 } else {\r
3426 MaxBlock = 0xFFFFFFFF;\r
3427 }\r
3428\r
3429 PtrBuffer = Buffer;\r
3430\r
3431 while (BlocksRemaining > 0) {\r
3432\r
3433 if (BlocksRemaining <= MaxBlock) {\r
3434 if (!ScsiDiskDevice->Cdb16Byte) {\r
3435 SectorCount = (UINT16) BlocksRemaining;\r
3436 } else {\r
3437 SectorCount = (UINT32) BlocksRemaining;\r
3438 }\r
3439 } else {\r
3440 SectorCount = MaxBlock;\r
3441 }\r
3442\r
3443 ByteCount = SectorCount * BlockSize;\r
3444 //\r
3445 // |------------------------|-----------------|------------------|-----------------|\r
3446 // | ATA Transfer Mode | Transfer Rate | SCSI Interface | Transfer Rate |\r
3447 // |------------------------|-----------------|------------------|-----------------|\r
3448 // | PIO Mode 0 | 3.3Mbytes/sec | SCSI-1 | 5Mbytes/sec |\r
3449 // |------------------------|-----------------|------------------|-----------------|\r
3450 // | PIO Mode 1 | 5.2Mbytes/sec | Fast SCSI | 10Mbytes/sec |\r
3451 // |------------------------|-----------------|------------------|-----------------|\r
3452 // | PIO Mode 2 | 8.3Mbytes/sec | Fast-Wide SCSI | 20Mbytes/sec |\r
3453 // |------------------------|-----------------|------------------|-----------------|\r
3454 // | PIO Mode 3 | 11.1Mbytes/sec | Ultra SCSI | 20Mbytes/sec |\r
3455 // |------------------------|-----------------|------------------|-----------------|\r
3456 // | PIO Mode 4 | 16.6Mbytes/sec | Ultra Wide SCSI | 40Mbytes/sec |\r
3457 // |------------------------|-----------------|------------------|-----------------|\r
3458 // | Single-word DMA Mode 0 | 2.1Mbytes/sec | Ultra2 SCSI | 40Mbytes/sec |\r
3459 // |------------------------|-----------------|------------------|-----------------|\r
3460 // | Single-word DMA Mode 1 | 4.2Mbytes/sec | Ultra2 Wide SCSI | 80Mbytes/sec |\r
3461 // |------------------------|-----------------|------------------|-----------------|\r
3462 // | Single-word DMA Mode 2 | 8.4Mbytes/sec | Ultra3 SCSI | 160Mbytes/sec |\r
3463 // |------------------------|-----------------|------------------|-----------------|\r
3464 // | Multi-word DMA Mode 0 | 4.2Mbytes/sec | Ultra-320 SCSI | 320Mbytes/sec |\r
3465 // |------------------------|-----------------|------------------|-----------------|\r
3466 // | Multi-word DMA Mode 1 | 13.3Mbytes/sec | Ultra-640 SCSI | 640Mbytes/sec |\r
3467 // |------------------------|-----------------|------------------|-----------------|\r
3468 //\r
3469 // As ScsiDisk and ScsiBus driver are used to manage SCSI or ATAPI devices,\r
3470 // we have to use the lowest transfer rate to calculate the possible\r
3471 // maximum timeout value for each operation.\r
3472 // From the above table, we could know 2.1Mbytes per second is lowest one.\r
3473 // The timout value is rounded up to nearest integar and here an additional\r
3474 // 30s is added to follow ATA spec in which it mentioned that the device\r
3475 // may take up to 30s to respond commands in the Standby/Idle mode.\r
3476 //\r
3477 Timeout = EFI_TIMER_PERIOD_SECONDS (ByteCount / 2100000 + 31);\r
3478\r
3479 if (!ScsiDiskDevice->Cdb16Byte) {\r
3480 Status = ScsiDiskAsyncWrite10 (\r
3481 ScsiDiskDevice,\r
3482 Timeout,\r
3483 0,\r
3484 PtrBuffer,\r
3485 ByteCount,\r
3486 (UINT32) Lba,\r
3487 SectorCount,\r
3488 BlkIo2Req,\r
3489 Token\r
3490 );\r
3491 } else {\r
3492 Status = ScsiDiskAsyncWrite16 (\r
3493 ScsiDiskDevice,\r
3494 Timeout,\r
3495 0,\r
3496 PtrBuffer,\r
3497 ByteCount,\r
3498 Lba,\r
3499 SectorCount,\r
3500 BlkIo2Req,\r
3501 Token\r
3502 );\r
3503 }\r
3504 if (EFI_ERROR (Status)) {\r
3505 //\r
3506 // Some devices will return EFI_DEVICE_ERROR or EFI_TIMEOUT when the data\r
3507 // length of a SCSI I/O command is too large.\r
3508 // In this case, we retry sending the SCSI command with a data length\r
3509 // half of its previous value.\r
3510 //\r
3511 if ((Status == EFI_DEVICE_ERROR) || (Status == EFI_TIMEOUT)) {\r
3512 if ((MaxBlock > 1) && (SectorCount > 1)) {\r
3513 MaxBlock = MIN (MaxBlock, SectorCount) >> 1;\r
3514 continue;\r
3515 }\r
3516 }\r
3517\r
3518 OldTpl = gBS->RaiseTPL (TPL_NOTIFY);\r
3519 if (IsListEmpty (&BlkIo2Req->ScsiRWQueue)) {\r
3520 //\r
3521 // Free the SCSI_BLKIO2_REQUEST structure only when there is no other\r
3522 // SCSI sub-task running. Otherwise, it will be freed in the callback\r
3523 // function ScsiDiskNotify().\r
3524 //\r
3525 RemoveEntryList (&BlkIo2Req->Link);\r
3526 FreePool (BlkIo2Req);\r
3527 BlkIo2Req = NULL;\r
3528 gBS->RestoreTPL (OldTpl);\r
3529\r
3530 //\r
3531 // It is safe to return error status to the caller, since there is no\r
3532 // previous SCSI sub-task executing.\r
3533 //\r
3534 Status = EFI_DEVICE_ERROR;\r
3535 goto Done;\r
3536 } else {\r
3537 gBS->RestoreTPL (OldTpl);\r
3538\r
3539 //\r
3540 // There are previous SCSI commands still running, EFI_SUCCESS should\r
3541 // be returned to make sure that the caller does not free resources\r
3542 // still using by these SCSI commands.\r
3543 //\r
3544 Status = EFI_SUCCESS;\r
3545 goto Done;\r
3546 }\r
3547 }\r
3548\r
3549 //\r
3550 // Sectors submitted for transfer\r
3551 //\r
3552 SectorCount = ByteCount / BlockSize;\r
3553\r
3554 Lba += SectorCount;\r
3555 PtrBuffer = PtrBuffer + SectorCount * BlockSize;\r
3556 BlocksRemaining -= SectorCount;\r
3557 }\r
3558\r
3559 Status = EFI_SUCCESS;\r
3560\r
3561Done:\r
3562 if (BlkIo2Req != NULL) {\r
3563 BlkIo2Req->LastScsiRW = TRUE;\r
3564\r
3565 OldTpl = gBS->RaiseTPL (TPL_NOTIFY);\r
3566 if (IsListEmpty (&BlkIo2Req->ScsiRWQueue)) {\r
3567 RemoveEntryList (&BlkIo2Req->Link);\r
3568 FreePool (BlkIo2Req);\r
3569 BlkIo2Req = NULL;\r
3570\r
3571 gBS->SignalEvent (Token->Event);\r
3572 }\r
3573 gBS->RestoreTPL (OldTpl);\r
3574 }\r
3575\r
3576 return Status;\r
3577}\r
3578\r
3579\r
3580/**\r
3581 Submit Read(10) command.\r
3582\r
3583 @param ScsiDiskDevice The pointer of ScsiDiskDevice\r
3584 @param NeedRetry The pointer of flag indicates if needs retry if error happens\r
3585 @param Timeout The time to complete the command\r
3586 @param DataBuffer The buffer to fill with the read out data\r
3587 @param DataLength The length of buffer\r
3588 @param StartLba The start logic block address\r
3589 @param SectorCount The number of blocks to read\r
3590\r
3591 @return EFI_STATUS is returned by calling ScsiRead10Command().\r
3592**/\r
3593EFI_STATUS\r
3594ScsiDiskRead10 (\r
3595 IN SCSI_DISK_DEV *ScsiDiskDevice,\r
3596 OUT BOOLEAN *NeedRetry,\r
3597 IN UINT64 Timeout,\r
3598 OUT UINT8 *DataBuffer,\r
3599 IN OUT UINT32 *DataLength,\r
3600 IN UINT32 StartLba,\r
3601 IN UINT32 SectorCount\r
3602 )\r
3603{\r
3604 UINT8 SenseDataLength;\r
3605 EFI_STATUS Status;\r
3606 EFI_STATUS ReturnStatus;\r
3607 UINT8 HostAdapterStatus;\r
3608 UINT8 TargetStatus;\r
3609 UINTN Action;\r
3610\r
3611 //\r
3612 // Implement a backoff algorithem to resolve some compatibility issues that\r
3613 // some SCSI targets or ATAPI devices couldn't correctly response reading/writing\r
3614 // big data in a single operation.\r
3615 // This algorithem will at first try to execute original request. If the request fails\r
3616 // with media error sense data or else, it will reduce the transfer length to half and\r
3617 // try again till the operation succeeds or fails with one sector transfer length.\r
3618 //\r
3619BackOff:\r
3620 *NeedRetry = FALSE;\r
3621 Action = ACTION_NO_ACTION;\r
3622 SenseDataLength = (UINT8) (ScsiDiskDevice->SenseDataNumber * sizeof (EFI_SCSI_SENSE_DATA));\r
3623 ReturnStatus = ScsiRead10Command (\r
3624 ScsiDiskDevice->ScsiIo,\r
3625 Timeout,\r
3626 ScsiDiskDevice->SenseData,\r
3627 &SenseDataLength,\r
3628 &HostAdapterStatus,\r
3629 &TargetStatus,\r
3630 DataBuffer,\r
3631 DataLength,\r
3632 StartLba,\r
3633 SectorCount\r
3634 );\r
3635\r
3636 if (ReturnStatus == EFI_NOT_READY || ReturnStatus == EFI_BAD_BUFFER_SIZE) {\r
3637 *NeedRetry = TRUE;\r
3638 return EFI_DEVICE_ERROR;\r
3639 } else if ((ReturnStatus == EFI_INVALID_PARAMETER) || (ReturnStatus == EFI_UNSUPPORTED)) {\r
3640 *NeedRetry = FALSE;\r
3641 return ReturnStatus;\r
3642 }\r
3643\r
3644 //\r
3645 // go ahead to check HostAdapterStatus and TargetStatus\r
3646 // (EFI_TIMEOUT, EFI_DEVICE_ERROR, EFI_WARN_BUFFER_TOO_SMALL)\r
3647 //\r
3648 Status = CheckHostAdapterStatus (HostAdapterStatus);\r
3649 if ((Status == EFI_TIMEOUT) || (Status == EFI_NOT_READY)) {\r
3650 *NeedRetry = TRUE;\r
3651 return EFI_DEVICE_ERROR;\r
3652 } else if (Status == EFI_DEVICE_ERROR) {\r
3653 //\r
3654 // reset the scsi channel\r
3655 //\r
3656 ScsiDiskDevice->ScsiIo->ResetBus (ScsiDiskDevice->ScsiIo);\r
3657 *NeedRetry = FALSE;\r
3658 return EFI_DEVICE_ERROR;\r
3659 }\r
3660\r
3661 Status = CheckTargetStatus (TargetStatus);\r
3662 if (Status == EFI_NOT_READY) {\r
3663 //\r
3664 // reset the scsi device\r
3665 //\r
3666 ScsiDiskDevice->ScsiIo->ResetDevice (ScsiDiskDevice->ScsiIo);\r
3667 *NeedRetry = TRUE;\r
3668 return EFI_DEVICE_ERROR;\r
3669 } else if (Status == EFI_DEVICE_ERROR) {\r
3670 *NeedRetry = FALSE;\r
3671 return EFI_DEVICE_ERROR;\r
3672 }\r
3673\r
3674 if ((TargetStatus == EFI_EXT_SCSI_STATUS_TARGET_CHECK_CONDITION) || (EFI_ERROR (ReturnStatus))) {\r
3675 DEBUG ((EFI_D_ERROR, "ScsiDiskRead10: Check Condition happened!\n"));\r
3676 Status = DetectMediaParsingSenseKeys (ScsiDiskDevice, ScsiDiskDevice->SenseData, SenseDataLength / sizeof (EFI_SCSI_SENSE_DATA), &Action);\r
3677 if (Action == ACTION_RETRY_COMMAND_LATER) {\r
3678 *NeedRetry = TRUE;\r
3679 return EFI_DEVICE_ERROR;\r
3680 } else if (Action == ACTION_RETRY_WITH_BACKOFF_ALGO) {\r
3681 if (SectorCount <= 1) {\r
3682 //\r
3683 // Jump out if the operation still fails with one sector transfer length.\r
3684 //\r
3685 *NeedRetry = FALSE;\r
3686 return EFI_DEVICE_ERROR;\r
3687 }\r
3688 //\r
3689 // Try again with half length if the sense data shows we need to retry.\r
3690 //\r
3691 SectorCount >>= 1;\r
3692 *DataLength = SectorCount * ScsiDiskDevice->BlkIo.Media->BlockSize;\r
3693 goto BackOff;\r
3694 } else {\r
3695 *NeedRetry = FALSE;\r
3696 return EFI_DEVICE_ERROR;\r
3697 }\r
3698 }\r
3699\r
3700 return ReturnStatus;\r
3701}\r
3702\r
3703\r
3704/**\r
3705 Submit Write(10) Command.\r
3706\r
3707 @param ScsiDiskDevice The pointer of ScsiDiskDevice\r
3708 @param NeedRetry The pointer of flag indicates if needs retry if error happens\r
3709 @param Timeout The time to complete the command\r
3710 @param DataBuffer The buffer to fill with the read out data\r
3711 @param DataLength The length of buffer\r
3712 @param StartLba The start logic block address\r
3713 @param SectorCount The number of blocks to write\r
3714\r
3715 @return EFI_STATUS is returned by calling ScsiWrite10Command().\r
3716\r
3717**/\r
3718EFI_STATUS\r
3719ScsiDiskWrite10 (\r
3720 IN SCSI_DISK_DEV *ScsiDiskDevice,\r
3721 OUT BOOLEAN *NeedRetry,\r
3722 IN UINT64 Timeout,\r
3723 IN UINT8 *DataBuffer,\r
3724 IN OUT UINT32 *DataLength,\r
3725 IN UINT32 StartLba,\r
3726 IN UINT32 SectorCount\r
3727 )\r
3728{\r
3729 EFI_STATUS Status;\r
3730 EFI_STATUS ReturnStatus;\r
3731 UINT8 SenseDataLength;\r
3732 UINT8 HostAdapterStatus;\r
3733 UINT8 TargetStatus;\r
3734 UINTN Action;\r
3735\r
3736 //\r
3737 // Implement a backoff algorithem to resolve some compatibility issues that\r
3738 // some SCSI targets or ATAPI devices couldn't correctly response reading/writing\r
3739 // big data in a single operation.\r
3740 // This algorithem will at first try to execute original request. If the request fails\r
3741 // with media error sense data or else, it will reduce the transfer length to half and\r
3742 // try again till the operation succeeds or fails with one sector transfer length.\r
3743 //\r
3744BackOff:\r
3745 *NeedRetry = FALSE;\r
3746 Action = ACTION_NO_ACTION;\r
3747 SenseDataLength = (UINT8) (ScsiDiskDevice->SenseDataNumber * sizeof (EFI_SCSI_SENSE_DATA));\r
3748 ReturnStatus = ScsiWrite10Command (\r
3749 ScsiDiskDevice->ScsiIo,\r
3750 Timeout,\r
3751 ScsiDiskDevice->SenseData,\r
3752 &SenseDataLength,\r
3753 &HostAdapterStatus,\r
3754 &TargetStatus,\r
3755 DataBuffer,\r
3756 DataLength,\r
3757 StartLba,\r
3758 SectorCount\r
3759 );\r
3760 if (ReturnStatus == EFI_NOT_READY || ReturnStatus == EFI_BAD_BUFFER_SIZE) {\r
3761 *NeedRetry = TRUE;\r
3762 return EFI_DEVICE_ERROR;\r
3763 } else if ((ReturnStatus == EFI_INVALID_PARAMETER) || (ReturnStatus == EFI_UNSUPPORTED)) {\r
3764 *NeedRetry = FALSE;\r
3765 return ReturnStatus;\r
3766 }\r
3767\r
3768 //\r
3769 // go ahead to check HostAdapterStatus and TargetStatus\r
3770 // (EFI_TIMEOUT, EFI_DEVICE_ERROR, EFI_WARN_BUFFER_TOO_SMALL)\r
3771 //\r
3772 Status = CheckHostAdapterStatus (HostAdapterStatus);\r
3773 if ((Status == EFI_TIMEOUT) || (Status == EFI_NOT_READY)) {\r
3774 *NeedRetry = TRUE;\r
3775 return EFI_DEVICE_ERROR;\r
3776 } else if (Status == EFI_DEVICE_ERROR) {\r
3777 //\r
3778 // reset the scsi channel\r
3779 //\r
3780 ScsiDiskDevice->ScsiIo->ResetBus (ScsiDiskDevice->ScsiIo);\r
3781 *NeedRetry = FALSE;\r
3782 return EFI_DEVICE_ERROR;\r
3783 }\r
3784\r
3785 Status = CheckTargetStatus (TargetStatus);\r
3786 if (Status == EFI_NOT_READY) {\r
3787 //\r
3788 // reset the scsi device\r
3789 //\r
3790 ScsiDiskDevice->ScsiIo->ResetDevice (ScsiDiskDevice->ScsiIo);\r
3791 *NeedRetry = TRUE;\r
3792 return EFI_DEVICE_ERROR;\r
3793 } else if (Status == EFI_DEVICE_ERROR) {\r
3794 *NeedRetry = FALSE;\r
3795 return EFI_DEVICE_ERROR;\r
3796 }\r
3797\r
3798 if ((TargetStatus == EFI_EXT_SCSI_STATUS_TARGET_CHECK_CONDITION) || (EFI_ERROR (ReturnStatus))) {\r
3799 DEBUG ((EFI_D_ERROR, "ScsiDiskWrite10: Check Condition happened!\n"));\r
3800 Status = DetectMediaParsingSenseKeys (ScsiDiskDevice, ScsiDiskDevice->SenseData, SenseDataLength / sizeof (EFI_SCSI_SENSE_DATA), &Action);\r
3801 if (Action == ACTION_RETRY_COMMAND_LATER) {\r
3802 *NeedRetry = TRUE;\r
3803 return EFI_DEVICE_ERROR;\r
3804 } else if (Action == ACTION_RETRY_WITH_BACKOFF_ALGO) {\r
3805 if (SectorCount <= 1) {\r
3806 //\r
3807 // Jump out if the operation still fails with one sector transfer length.\r
3808 //\r
3809 *NeedRetry = FALSE;\r
3810 return EFI_DEVICE_ERROR;\r
3811 }\r
3812 //\r
3813 // Try again with half length if the sense data shows we need to retry.\r
3814 //\r
3815 SectorCount >>= 1;\r
3816 *DataLength = SectorCount * ScsiDiskDevice->BlkIo.Media->BlockSize;\r
3817 goto BackOff;\r
3818 } else {\r
3819 *NeedRetry = FALSE;\r
3820 return EFI_DEVICE_ERROR;\r
3821 }\r
3822 }\r
3823\r
3824 return ReturnStatus;\r
3825}\r
3826\r
3827\r
3828/**\r
3829 Submit Read(16) command.\r
3830\r
3831 @param ScsiDiskDevice The pointer of ScsiDiskDevice\r
3832 @param NeedRetry The pointer of flag indicates if needs retry if error happens\r
3833 @param Timeout The time to complete the command\r
3834 @param DataBuffer The buffer to fill with the read out data\r
3835 @param DataLength The length of buffer\r
3836 @param StartLba The start logic block address\r
3837 @param SectorCount The number of blocks to read\r
3838\r
3839 @return EFI_STATUS is returned by calling ScsiRead16Command().\r
3840**/\r
3841EFI_STATUS\r
3842ScsiDiskRead16 (\r
3843 IN SCSI_DISK_DEV *ScsiDiskDevice,\r
3844 OUT BOOLEAN *NeedRetry,\r
3845 IN UINT64 Timeout,\r
3846 OUT UINT8 *DataBuffer,\r
3847 IN OUT UINT32 *DataLength,\r
3848 IN UINT64 StartLba,\r
3849 IN UINT32 SectorCount\r
3850 )\r
3851{\r
3852 UINT8 SenseDataLength;\r
3853 EFI_STATUS Status;\r
3854 EFI_STATUS ReturnStatus;\r
3855 UINT8 HostAdapterStatus;\r
3856 UINT8 TargetStatus;\r
3857 UINTN Action;\r
3858\r
3859 //\r
3860 // Implement a backoff algorithem to resolve some compatibility issues that\r
3861 // some SCSI targets or ATAPI devices couldn't correctly response reading/writing\r
3862 // big data in a single operation.\r
3863 // This algorithem will at first try to execute original request. If the request fails\r
3864 // with media error sense data or else, it will reduce the transfer length to half and\r
3865 // try again till the operation succeeds or fails with one sector transfer length.\r
3866 //\r
3867BackOff:\r
3868 *NeedRetry = FALSE;\r
3869 Action = ACTION_NO_ACTION;\r
3870 SenseDataLength = (UINT8) (ScsiDiskDevice->SenseDataNumber * sizeof (EFI_SCSI_SENSE_DATA));\r
3871 ReturnStatus = ScsiRead16Command (\r
3872 ScsiDiskDevice->ScsiIo,\r
3873 Timeout,\r
3874 ScsiDiskDevice->SenseData,\r
3875 &SenseDataLength,\r
3876 &HostAdapterStatus,\r
3877 &TargetStatus,\r
3878 DataBuffer,\r
3879 DataLength,\r
3880 StartLba,\r
3881 SectorCount\r
3882 );\r
3883 if (ReturnStatus == EFI_NOT_READY || ReturnStatus == EFI_BAD_BUFFER_SIZE) {\r
3884 *NeedRetry = TRUE;\r
3885 return EFI_DEVICE_ERROR;\r
3886 } else if ((ReturnStatus == EFI_INVALID_PARAMETER) || (ReturnStatus == EFI_UNSUPPORTED)) {\r
3887 *NeedRetry = FALSE;\r
3888 return ReturnStatus;\r
3889 }\r
3890\r
3891 //\r
3892 // go ahead to check HostAdapterStatus and TargetStatus\r
3893 // (EFI_TIMEOUT, EFI_DEVICE_ERROR, EFI_WARN_BUFFER_TOO_SMALL)\r
3894 //\r
3895 Status = CheckHostAdapterStatus (HostAdapterStatus);\r
3896 if ((Status == EFI_TIMEOUT) || (Status == EFI_NOT_READY)) {\r
3897 *NeedRetry = TRUE;\r
3898 return EFI_DEVICE_ERROR;\r
3899 } else if (Status == EFI_DEVICE_ERROR) {\r
3900 //\r
3901 // reset the scsi channel\r
3902 //\r
3903 ScsiDiskDevice->ScsiIo->ResetBus (ScsiDiskDevice->ScsiIo);\r
3904 *NeedRetry = FALSE;\r
3905 return EFI_DEVICE_ERROR;\r
3906 }\r
3907\r
3908 Status = CheckTargetStatus (TargetStatus);\r
3909 if (Status == EFI_NOT_READY) {\r
3910 //\r
3911 // reset the scsi device\r
3912 //\r
3913 ScsiDiskDevice->ScsiIo->ResetDevice (ScsiDiskDevice->ScsiIo);\r
3914 *NeedRetry = TRUE;\r
3915 return EFI_DEVICE_ERROR;\r
3916 } else if (Status == EFI_DEVICE_ERROR) {\r
3917 *NeedRetry = FALSE;\r
3918 return EFI_DEVICE_ERROR;\r
3919 }\r
3920\r
3921 if ((TargetStatus == EFI_EXT_SCSI_STATUS_TARGET_CHECK_CONDITION) || (EFI_ERROR (ReturnStatus))) {\r
3922 DEBUG ((EFI_D_ERROR, "ScsiDiskRead16: Check Condition happened!\n"));\r
3923 Status = DetectMediaParsingSenseKeys (ScsiDiskDevice, ScsiDiskDevice->SenseData, SenseDataLength / sizeof (EFI_SCSI_SENSE_DATA), &Action);\r
3924 if (Action == ACTION_RETRY_COMMAND_LATER) {\r
3925 *NeedRetry = TRUE;\r
3926 return EFI_DEVICE_ERROR;\r
3927 } else if (Action == ACTION_RETRY_WITH_BACKOFF_ALGO) {\r
3928 if (SectorCount <= 1) {\r
3929 //\r
3930 // Jump out if the operation still fails with one sector transfer length.\r
3931 //\r
3932 *NeedRetry = FALSE;\r
3933 return EFI_DEVICE_ERROR;\r
3934 }\r
3935 //\r
3936 // Try again with half length if the sense data shows we need to retry.\r
3937 //\r
3938 SectorCount >>= 1;\r
3939 *DataLength = SectorCount * ScsiDiskDevice->BlkIo.Media->BlockSize;\r
3940 goto BackOff;\r
3941 } else {\r
3942 *NeedRetry = FALSE;\r
3943 return EFI_DEVICE_ERROR;\r
3944 }\r
3945 }\r
3946\r
3947 return ReturnStatus;\r
3948}\r
3949\r
3950\r
3951/**\r
3952 Submit Write(16) Command.\r
3953\r
3954 @param ScsiDiskDevice The pointer of ScsiDiskDevice\r
3955 @param NeedRetry The pointer of flag indicates if needs retry if error happens\r
3956 @param Timeout The time to complete the command\r
3957 @param DataBuffer The buffer to fill with the read out data\r
3958 @param DataLength The length of buffer\r
3959 @param StartLba The start logic block address\r
3960 @param SectorCount The number of blocks to write\r
3961\r
3962 @return EFI_STATUS is returned by calling ScsiWrite16Command().\r
3963\r
3964**/\r
3965EFI_STATUS\r
3966ScsiDiskWrite16 (\r
3967 IN SCSI_DISK_DEV *ScsiDiskDevice,\r
3968 OUT BOOLEAN *NeedRetry,\r
3969 IN UINT64 Timeout,\r
3970 IN UINT8 *DataBuffer,\r
3971 IN OUT UINT32 *DataLength,\r
3972 IN UINT64 StartLba,\r
3973 IN UINT32 SectorCount\r
3974 )\r
3975{\r
3976 EFI_STATUS Status;\r
3977 EFI_STATUS ReturnStatus;\r
3978 UINT8 SenseDataLength;\r
3979 UINT8 HostAdapterStatus;\r
3980 UINT8 TargetStatus;\r
3981 UINTN Action;\r
3982\r
3983 //\r
3984 // Implement a backoff algorithem to resolve some compatibility issues that\r
3985 // some SCSI targets or ATAPI devices couldn't correctly response reading/writing\r
3986 // big data in a single operation.\r
3987 // This algorithem will at first try to execute original request. If the request fails\r
3988 // with media error sense data or else, it will reduce the transfer length to half and\r
3989 // try again till the operation succeeds or fails with one sector transfer length.\r
3990 //\r
3991BackOff:\r
3992 *NeedRetry = FALSE;\r
3993 Action = ACTION_NO_ACTION;\r
3994 SenseDataLength = (UINT8) (ScsiDiskDevice->SenseDataNumber * sizeof (EFI_SCSI_SENSE_DATA));\r
3995 ReturnStatus = ScsiWrite16Command (\r
3996 ScsiDiskDevice->ScsiIo,\r
3997 Timeout,\r
3998 ScsiDiskDevice->SenseData,\r
3999 &SenseDataLength,\r
4000 &HostAdapterStatus,\r
4001 &TargetStatus,\r
4002 DataBuffer,\r
4003 DataLength,\r
4004 StartLba,\r
4005 SectorCount\r
4006 );\r
4007 if (ReturnStatus == EFI_NOT_READY || ReturnStatus == EFI_BAD_BUFFER_SIZE) {\r
4008 *NeedRetry = TRUE;\r
4009 return EFI_DEVICE_ERROR;\r
4010 } else if ((ReturnStatus == EFI_INVALID_PARAMETER) || (ReturnStatus == EFI_UNSUPPORTED)) {\r
4011 *NeedRetry = FALSE;\r
4012 return ReturnStatus;\r
4013 }\r
4014\r
4015 //\r
4016 // go ahead to check HostAdapterStatus and TargetStatus\r
4017 // (EFI_TIMEOUT, EFI_DEVICE_ERROR, EFI_WARN_BUFFER_TOO_SMALL)\r
4018 //\r
4019 Status = CheckHostAdapterStatus (HostAdapterStatus);\r
4020 if ((Status == EFI_TIMEOUT) || (Status == EFI_NOT_READY)) {\r
4021 *NeedRetry = TRUE;\r
4022 return EFI_DEVICE_ERROR;\r
4023 } else if (Status == EFI_DEVICE_ERROR) {\r
4024 //\r
4025 // reset the scsi channel\r
4026 //\r
4027 ScsiDiskDevice->ScsiIo->ResetBus (ScsiDiskDevice->ScsiIo);\r
4028 *NeedRetry = FALSE;\r
4029 return EFI_DEVICE_ERROR;\r
4030 }\r
4031\r
4032 Status = CheckTargetStatus (TargetStatus);\r
4033 if (Status == EFI_NOT_READY) {\r
4034 //\r
4035 // reset the scsi device\r
4036 //\r
4037 ScsiDiskDevice->ScsiIo->ResetDevice (ScsiDiskDevice->ScsiIo);\r
4038 *NeedRetry = TRUE;\r
4039 return EFI_DEVICE_ERROR;\r
4040 } else if (Status == EFI_DEVICE_ERROR) {\r
4041 *NeedRetry = FALSE;\r
4042 return EFI_DEVICE_ERROR;\r
4043 }\r
4044\r
4045 if ((TargetStatus == EFI_EXT_SCSI_STATUS_TARGET_CHECK_CONDITION) || (EFI_ERROR (ReturnStatus))) {\r
4046 DEBUG ((EFI_D_ERROR, "ScsiDiskWrite16: Check Condition happened!\n"));\r
4047 Status = DetectMediaParsingSenseKeys (ScsiDiskDevice, ScsiDiskDevice->SenseData, SenseDataLength / sizeof (EFI_SCSI_SENSE_DATA), &Action);\r
4048 if (Action == ACTION_RETRY_COMMAND_LATER) {\r
4049 *NeedRetry = TRUE;\r
4050 return EFI_DEVICE_ERROR;\r
4051 } else if (Action == ACTION_RETRY_WITH_BACKOFF_ALGO) {\r
4052 if (SectorCount <= 1) {\r
4053 //\r
4054 // Jump out if the operation still fails with one sector transfer length.\r
4055 //\r
4056 *NeedRetry = FALSE;\r
4057 return EFI_DEVICE_ERROR;\r
4058 }\r
4059 //\r
4060 // Try again with half length if the sense data shows we need to retry.\r
4061 //\r
4062 SectorCount >>= 1;\r
4063 *DataLength = SectorCount * ScsiDiskDevice->BlkIo.Media->BlockSize;\r
4064 goto BackOff;\r
4065 } else {\r
4066 *NeedRetry = FALSE;\r
4067 return EFI_DEVICE_ERROR;\r
4068 }\r
4069 }\r
4070\r
4071 return ReturnStatus;\r
4072}\r
4073\r
4074\r
4075/**\r
4076 Internal helper notify function in which determine whether retry of a SCSI\r
4077 Read/Write command is needed and signal the event passed from Block I/O(2) if\r
4078 the SCSI I/O operation completes.\r
4079\r
4080 @param Event The instance of EFI_EVENT.\r
4081 @param Context The parameter passed in.\r
4082\r
4083**/\r
4084VOID\r
4085EFIAPI\r
4086ScsiDiskNotify (\r
4087 IN EFI_EVENT Event,\r
4088 IN VOID *Context\r
4089 )\r
4090{\r
4091 EFI_STATUS Status;\r
4092 SCSI_ASYNC_RW_REQUEST *Request;\r
4093 SCSI_DISK_DEV *ScsiDiskDevice;\r
4094 EFI_BLOCK_IO2_TOKEN *Token;\r
4095 UINTN Action;\r
4096 UINT32 OldDataLength;\r
4097 UINT32 OldSectorCount;\r
4098 UINT8 MaxRetry;\r
4099\r
4100 gBS->CloseEvent (Event);\r
4101\r
4102 Request = (SCSI_ASYNC_RW_REQUEST *) Context;\r
4103 ScsiDiskDevice = Request->ScsiDiskDevice;\r
4104 Token = Request->BlkIo2Req->Token;\r
4105 OldDataLength = Request->DataLength;\r
4106 OldSectorCount = Request->SectorCount;\r
4107 MaxRetry = 2;\r
4108\r
4109 //\r
4110 // If previous sub-tasks already fails, no need to process this sub-task.\r
4111 //\r
4112 if (Token->TransactionStatus != EFI_SUCCESS) {\r
4113 goto Exit;\r
4114 }\r
4115\r
4116 //\r
4117 // Check HostAdapterStatus and TargetStatus\r
4118 // (EFI_TIMEOUT, EFI_DEVICE_ERROR, EFI_WARN_BUFFER_TOO_SMALL)\r
4119 //\r
4120 Status = CheckHostAdapterStatus (Request->HostAdapterStatus);\r
4121 if ((Status == EFI_TIMEOUT) || (Status == EFI_NOT_READY)) {\r
4122 if (++Request->TimesRetry > MaxRetry) {\r
4123 Token->TransactionStatus = EFI_DEVICE_ERROR;\r
4124 goto Exit;\r
4125 } else {\r
4126 goto Retry;\r
4127 }\r
4128 } else if (Status == EFI_DEVICE_ERROR) {\r
4129 //\r
4130 // reset the scsi channel\r
4131 //\r
4132 ScsiDiskDevice->ScsiIo->ResetBus (ScsiDiskDevice->ScsiIo);\r
4133 Token->TransactionStatus = EFI_DEVICE_ERROR;\r
4134 goto Exit;\r
4135 }\r
4136\r
4137 Status = CheckTargetStatus (Request->TargetStatus);\r
4138 if (Status == EFI_NOT_READY) {\r
4139 //\r
4140 // reset the scsi device\r
4141 //\r
4142 ScsiDiskDevice->ScsiIo->ResetDevice (ScsiDiskDevice->ScsiIo);\r
4143 if (++Request->TimesRetry > MaxRetry) {\r
4144 Token->TransactionStatus = EFI_DEVICE_ERROR;\r
4145 goto Exit;\r
4146 } else {\r
4147 goto Retry;\r
4148 }\r
4149 } else if (Status == EFI_DEVICE_ERROR) {\r
4150 Token->TransactionStatus = EFI_DEVICE_ERROR;\r
4151 goto Exit;\r
4152 }\r
4153\r
4154 if (Request->TargetStatus == EFI_EXT_SCSI_STATUS_TARGET_CHECK_CONDITION) {\r
4155 DEBUG ((EFI_D_ERROR, "ScsiDiskNotify: Check Condition happened!\n"));\r
4156\r
4157 Status = DetectMediaParsingSenseKeys (\r
4158 ScsiDiskDevice,\r
4159 Request->SenseData,\r
4160 Request->SenseDataLength / sizeof (EFI_SCSI_SENSE_DATA),\r
4161 &Action\r
4162 );\r
4163 if (Action == ACTION_RETRY_COMMAND_LATER) {\r
4164 if (++Request->TimesRetry > MaxRetry) {\r
4165 Token->TransactionStatus = EFI_DEVICE_ERROR;\r
4166 goto Exit;\r
4167 } else {\r
4168 goto Retry;\r
4169 }\r
4170 } else if (Action == ACTION_RETRY_WITH_BACKOFF_ALGO) {\r
4171 if (Request->SectorCount <= 1) {\r
4172 //\r
4173 // Jump out if the operation still fails with one sector transfer\r
4174 // length.\r
4175 //\r
4176 Token->TransactionStatus = EFI_DEVICE_ERROR;\r
4177 goto Exit;\r
4178 }\r
4179 //\r
4180 // Try again with two half length request if the sense data shows we need\r
4181 // to retry.\r
4182 //\r
4183 Request->SectorCount >>= 1;\r
4184 Request->DataLength = Request->SectorCount * ScsiDiskDevice->BlkIo.Media->BlockSize;\r
4185 Request->TimesRetry = 0;\r
4186\r
4187 goto Retry;\r
4188 } else {\r
4189 Token->TransactionStatus = EFI_DEVICE_ERROR;\r
4190 goto Exit;\r
4191 }\r
4192 }\r
4193\r
4194 //\r
4195 // This sub-task succeeds, no need to retry.\r
4196 //\r
4197 goto Exit;\r
4198\r
4199Retry:\r
4200 if (Request->InBuffer != NULL) {\r
4201 //\r
4202 // SCSI read command\r
4203 //\r
4204 if (!ScsiDiskDevice->Cdb16Byte) {\r
4205 Status = ScsiDiskAsyncRead10 (\r
4206 ScsiDiskDevice,\r
4207 Request->Timeout,\r
4208 Request->TimesRetry,\r
4209 Request->InBuffer,\r
4210 Request->DataLength,\r
4211 (UINT32) Request->StartLba,\r
4212 Request->SectorCount,\r
4213 Request->BlkIo2Req,\r
4214 Token\r
4215 );\r
4216 } else {\r
4217 Status = ScsiDiskAsyncRead16 (\r
4218 ScsiDiskDevice,\r
4219 Request->Timeout,\r
4220 Request->TimesRetry,\r
4221 Request->InBuffer,\r
4222 Request->DataLength,\r
4223 Request->StartLba,\r
4224 Request->SectorCount,\r
4225 Request->BlkIo2Req,\r
4226 Token\r
4227 );\r
4228 }\r
4229\r
4230 if (EFI_ERROR (Status)) {\r
4231 Token->TransactionStatus = EFI_DEVICE_ERROR;\r
4232 goto Exit;\r
4233 } else if (OldSectorCount != Request->SectorCount) {\r
4234 //\r
4235 // Original sub-task will be split into two new sub-tasks with smaller\r
4236 // DataLength\r
4237 //\r
4238 if (!ScsiDiskDevice->Cdb16Byte) {\r
4239 Status = ScsiDiskAsyncRead10 (\r
4240 ScsiDiskDevice,\r
4241 Request->Timeout,\r
4242 0,\r
4243 Request->InBuffer + Request->SectorCount * ScsiDiskDevice->BlkIo.Media->BlockSize,\r
4244 OldDataLength - Request->DataLength,\r
4245 (UINT32) Request->StartLba + Request->SectorCount,\r
4246 OldSectorCount - Request->SectorCount,\r
4247 Request->BlkIo2Req,\r
4248 Token\r
4249 );\r
4250 } else {\r
4251 Status = ScsiDiskAsyncRead16 (\r
4252 ScsiDiskDevice,\r
4253 Request->Timeout,\r
4254 0,\r
4255 Request->InBuffer + Request->SectorCount * ScsiDiskDevice->BlkIo.Media->BlockSize,\r
4256 OldDataLength - Request->DataLength,\r
4257 Request->StartLba + Request->SectorCount,\r
4258 OldSectorCount - Request->SectorCount,\r
4259 Request->BlkIo2Req,\r
4260 Token\r
4261 );\r
4262 }\r
4263 if (EFI_ERROR (Status)) {\r
4264 Token->TransactionStatus = EFI_DEVICE_ERROR;\r
4265 goto Exit;\r
4266 }\r
4267 }\r
4268 } else {\r
4269 //\r
4270 // SCSI write command\r
4271 //\r
4272 if (!ScsiDiskDevice->Cdb16Byte) {\r
4273 Status = ScsiDiskAsyncWrite10 (\r
4274 ScsiDiskDevice,\r
4275 Request->Timeout,\r
4276 Request->TimesRetry,\r
4277 Request->OutBuffer,\r
4278 Request->DataLength,\r
4279 (UINT32) Request->StartLba,\r
4280 Request->SectorCount,\r
4281 Request->BlkIo2Req,\r
4282 Token\r
4283 );\r
4284 } else {\r
4285 Status = ScsiDiskAsyncWrite16 (\r
4286 ScsiDiskDevice,\r
4287 Request->Timeout,\r
4288 Request->TimesRetry,\r
4289 Request->OutBuffer,\r
4290 Request->DataLength,\r
4291 Request->StartLba,\r
4292 Request->SectorCount,\r
4293 Request->BlkIo2Req,\r
4294 Token\r
4295 );\r
4296 }\r
4297\r
4298 if (EFI_ERROR (Status)) {\r
4299 Token->TransactionStatus = EFI_DEVICE_ERROR;\r
4300 goto Exit;\r
4301 } else if (OldSectorCount != Request->SectorCount) {\r
4302 //\r
4303 // Original sub-task will be split into two new sub-tasks with smaller\r
4304 // DataLength\r
4305 //\r
4306 if (!ScsiDiskDevice->Cdb16Byte) {\r
4307 Status = ScsiDiskAsyncWrite10 (\r
4308 ScsiDiskDevice,\r
4309 Request->Timeout,\r
4310 0,\r
4311 Request->OutBuffer + Request->SectorCount * ScsiDiskDevice->BlkIo.Media->BlockSize,\r
4312 OldDataLength - Request->DataLength,\r
4313 (UINT32) Request->StartLba + Request->SectorCount,\r
4314 OldSectorCount - Request->SectorCount,\r
4315 Request->BlkIo2Req,\r
4316 Token\r
4317 );\r
4318 } else {\r
4319 Status = ScsiDiskAsyncWrite16 (\r
4320 ScsiDiskDevice,\r
4321 Request->Timeout,\r
4322 0,\r
4323 Request->OutBuffer + Request->SectorCount * ScsiDiskDevice->BlkIo.Media->BlockSize,\r
4324 OldDataLength - Request->DataLength,\r
4325 Request->StartLba + Request->SectorCount,\r
4326 OldSectorCount - Request->SectorCount,\r
4327 Request->BlkIo2Req,\r
4328 Token\r
4329 );\r
4330 }\r
4331 if (EFI_ERROR (Status)) {\r
4332 Token->TransactionStatus = EFI_DEVICE_ERROR;\r
4333 goto Exit;\r
4334 }\r
4335 }\r
4336 }\r
4337\r
4338Exit:\r
4339 RemoveEntryList (&Request->Link);\r
4340 if ((IsListEmpty (&Request->BlkIo2Req->ScsiRWQueue)) &&\r
4341 (Request->BlkIo2Req->LastScsiRW)) {\r
4342 //\r
4343 // The last SCSI R/W command of a BlockIo2 request completes\r
4344 //\r
4345 RemoveEntryList (&Request->BlkIo2Req->Link);\r
4346 FreePool (Request->BlkIo2Req); // Should be freed only once\r
4347 gBS->SignalEvent (Token->Event);\r
4348 }\r
4349\r
4350 FreePool (Request->SenseData);\r
4351 FreePool (Request);\r
4352}\r
4353\r
4354\r
4355/**\r
4356 Submit Async Read(10) command.\r
4357\r
4358 @param ScsiDiskDevice The pointer of ScsiDiskDevice.\r
4359 @param Timeout The time to complete the command.\r
4360 @param TimesRetry The number of times the command has been retried.\r
4361 @param DataBuffer The buffer to fill with the read out data.\r
4362 @param DataLength The length of buffer.\r
4363 @param StartLba The start logic block address.\r
4364 @param SectorCount The number of blocks to read.\r
4365 @param BlkIo2Req The upstream BlockIo2 request.\r
4366 @param Token The pointer to the token associated with the\r
4367 non-blocking read request.\r
4368\r
4369 @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a\r
4370 lack of resources.\r
4371 @return others Status returned by calling\r
4372 ScsiRead10CommandEx().\r
4373\r
4374**/\r
4375EFI_STATUS\r
4376ScsiDiskAsyncRead10 (\r
4377 IN SCSI_DISK_DEV *ScsiDiskDevice,\r
4378 IN UINT64 Timeout,\r
4379 IN UINT8 TimesRetry,\r
4380 OUT UINT8 *DataBuffer,\r
4381 IN UINT32 DataLength,\r
4382 IN UINT32 StartLba,\r
4383 IN UINT32 SectorCount,\r
4384 IN OUT SCSI_BLKIO2_REQUEST *BlkIo2Req,\r
4385 IN EFI_BLOCK_IO2_TOKEN *Token\r
4386 )\r
4387{\r
4388 EFI_STATUS Status;\r
4389 SCSI_ASYNC_RW_REQUEST *Request;\r
4390 EFI_EVENT AsyncIoEvent;\r
4391 EFI_TPL OldTpl;\r
4392\r
4393 AsyncIoEvent = NULL;\r
4394\r
4395 Request = AllocateZeroPool (sizeof (SCSI_ASYNC_RW_REQUEST));\r
4396 if (Request == NULL) {\r
4397 return EFI_OUT_OF_RESOURCES;\r
4398 }\r
4399\r
4400 OldTpl = gBS->RaiseTPL (TPL_NOTIFY);\r
4401 InsertTailList (&BlkIo2Req->ScsiRWQueue, &Request->Link);\r
4402 gBS->RestoreTPL (OldTpl);\r
4403\r
4404 Request->SenseDataLength = (UINT8) (6 * sizeof (EFI_SCSI_SENSE_DATA));\r
4405 Request->SenseData = AllocateZeroPool (Request->SenseDataLength);\r
4406 if (Request->SenseData == NULL) {\r
4407 Status = EFI_OUT_OF_RESOURCES;\r
4408 goto ErrorExit;\r
4409 }\r
4410\r
4411 Request->ScsiDiskDevice = ScsiDiskDevice;\r
4412 Request->Timeout = Timeout;\r
4413 Request->TimesRetry = TimesRetry;\r
4414 Request->InBuffer = DataBuffer;\r
4415 Request->DataLength = DataLength;\r
4416 Request->StartLba = StartLba;\r
4417 Request->SectorCount = SectorCount;\r
4418 Request->BlkIo2Req = BlkIo2Req;\r
4419\r
4420 //\r
4421 // Create Event\r
4422 //\r
4423 Status = gBS->CreateEvent (\r
4424 EVT_NOTIFY_SIGNAL,\r
4425 TPL_NOTIFY,\r
4426 ScsiDiskNotify,\r
4427 Request,\r
4428 &AsyncIoEvent\r
4429 );\r
4430 if (EFI_ERROR(Status)) {\r
4431 goto ErrorExit;\r
4432 }\r
4433\r
4434 Status = ScsiRead10CommandEx (\r
4435 ScsiDiskDevice->ScsiIo,\r
4436 Request->Timeout,\r
4437 Request->SenseData,\r
4438 &Request->SenseDataLength,\r
4439 &Request->HostAdapterStatus,\r
4440 &Request->TargetStatus,\r
4441 Request->InBuffer,\r
4442 &Request->DataLength,\r
4443 (UINT32) Request->StartLba,\r
4444 Request->SectorCount,\r
4445 AsyncIoEvent\r
4446 );\r
4447 if (EFI_ERROR(Status)) {\r
4448 goto ErrorExit;\r
4449 }\r
4450\r
4451 return EFI_SUCCESS;\r
4452\r
4453ErrorExit:\r
4454 if (AsyncIoEvent != NULL) {\r
4455 gBS->CloseEvent (AsyncIoEvent);\r
4456 }\r
4457\r
4458 if (Request != NULL) {\r
4459 if (Request->SenseData != NULL) {\r
4460 FreePool (Request->SenseData);\r
4461 }\r
4462\r
4463 OldTpl = gBS->RaiseTPL (TPL_NOTIFY);\r
4464 RemoveEntryList (&Request->Link);\r
4465 gBS->RestoreTPL (OldTpl);\r
4466\r
4467 FreePool (Request);\r
4468 }\r
4469\r
4470 return Status;\r
4471}\r
4472\r
4473\r
4474/**\r
4475 Submit Async Write(10) command.\r
4476\r
4477 @param ScsiDiskDevice The pointer of ScsiDiskDevice.\r
4478 @param Timeout The time to complete the command.\r
4479 @param TimesRetry The number of times the command has been retried.\r
4480 @param DataBuffer The buffer contains the data to write.\r
4481 @param DataLength The length of buffer.\r
4482 @param StartLba The start logic block address.\r
4483 @param SectorCount The number of blocks to write.\r
4484 @param BlkIo2Req The upstream BlockIo2 request.\r
4485 @param Token The pointer to the token associated with the\r
4486 non-blocking read request.\r
4487\r
4488 @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a\r
4489 lack of resources.\r
4490 @return others Status returned by calling\r
4491 ScsiWrite10CommandEx().\r
4492\r
4493**/\r
4494EFI_STATUS\r
4495ScsiDiskAsyncWrite10 (\r
4496 IN SCSI_DISK_DEV *ScsiDiskDevice,\r
4497 IN UINT64 Timeout,\r
4498 IN UINT8 TimesRetry,\r
4499 IN UINT8 *DataBuffer,\r
4500 IN UINT32 DataLength,\r
4501 IN UINT32 StartLba,\r
4502 IN UINT32 SectorCount,\r
4503 IN OUT SCSI_BLKIO2_REQUEST *BlkIo2Req,\r
4504 IN EFI_BLOCK_IO2_TOKEN *Token\r
4505 )\r
4506{\r
4507 EFI_STATUS Status;\r
4508 SCSI_ASYNC_RW_REQUEST *Request;\r
4509 EFI_EVENT AsyncIoEvent;\r
4510 EFI_TPL OldTpl;\r
4511\r
4512 AsyncIoEvent = NULL;\r
4513\r
4514 Request = AllocateZeroPool (sizeof (SCSI_ASYNC_RW_REQUEST));\r
4515 if (Request == NULL) {\r
4516 return EFI_OUT_OF_RESOURCES;\r
4517 }\r
4518\r
4519 OldTpl = gBS->RaiseTPL (TPL_NOTIFY);\r
4520 InsertTailList (&BlkIo2Req->ScsiRWQueue, &Request->Link);\r
4521 gBS->RestoreTPL (OldTpl);\r
4522\r
4523 Request->SenseDataLength = (UINT8) (6 * sizeof (EFI_SCSI_SENSE_DATA));\r
4524 Request->SenseData = AllocateZeroPool (Request->SenseDataLength);\r
4525 if (Request->SenseData == NULL) {\r
4526 Status = EFI_OUT_OF_RESOURCES;\r
4527 goto ErrorExit;\r
4528 }\r
4529\r
4530 Request->ScsiDiskDevice = ScsiDiskDevice;\r
4531 Request->Timeout = Timeout;\r
4532 Request->TimesRetry = TimesRetry;\r
4533 Request->OutBuffer = DataBuffer;\r
4534 Request->DataLength = DataLength;\r
4535 Request->StartLba = StartLba;\r
4536 Request->SectorCount = SectorCount;\r
4537 Request->BlkIo2Req = BlkIo2Req;\r
4538\r
4539 //\r
4540 // Create Event\r
4541 //\r
4542 Status = gBS->CreateEvent (\r
4543 EVT_NOTIFY_SIGNAL,\r
4544 TPL_NOTIFY,\r
4545 ScsiDiskNotify,\r
4546 Request,\r
4547 &AsyncIoEvent\r
4548 );\r
4549 if (EFI_ERROR(Status)) {\r
4550 goto ErrorExit;\r
4551 }\r
4552\r
4553 Status = ScsiWrite10CommandEx (\r
4554 ScsiDiskDevice->ScsiIo,\r
4555 Request->Timeout,\r
4556 Request->SenseData,\r
4557 &Request->SenseDataLength,\r
4558 &Request->HostAdapterStatus,\r
4559 &Request->TargetStatus,\r
4560 Request->OutBuffer,\r
4561 &Request->DataLength,\r
4562 (UINT32) Request->StartLba,\r
4563 Request->SectorCount,\r
4564 AsyncIoEvent\r
4565 );\r
4566 if (EFI_ERROR(Status)) {\r
4567 goto ErrorExit;\r
4568 }\r
4569\r
4570 return EFI_SUCCESS;\r
4571\r
4572ErrorExit:\r
4573 if (AsyncIoEvent != NULL) {\r
4574 gBS->CloseEvent (AsyncIoEvent);\r
4575 }\r
4576\r
4577 if (Request != NULL) {\r
4578 if (Request->SenseData != NULL) {\r
4579 FreePool (Request->SenseData);\r
4580 }\r
4581\r
4582 OldTpl = gBS->RaiseTPL (TPL_NOTIFY);\r
4583 RemoveEntryList (&Request->Link);\r
4584 gBS->RestoreTPL (OldTpl);\r
4585\r
4586 FreePool (Request);\r
4587 }\r
4588\r
4589 return Status;\r
4590}\r
4591\r
4592\r
4593/**\r
4594 Submit Async Read(16) command.\r
4595\r
4596 @param ScsiDiskDevice The pointer of ScsiDiskDevice.\r
4597 @param Timeout The time to complete the command.\r
4598 @param TimesRetry The number of times the command has been retried.\r
4599 @param DataBuffer The buffer to fill with the read out data.\r
4600 @param DataLength The length of buffer.\r
4601 @param StartLba The start logic block address.\r
4602 @param SectorCount The number of blocks to read.\r
4603 @param BlkIo2Req The upstream BlockIo2 request.\r
4604 @param Token The pointer to the token associated with the\r
4605 non-blocking read request.\r
4606\r
4607 @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a\r
4608 lack of resources.\r
4609 @return others Status returned by calling\r
4610 ScsiRead16CommandEx().\r
4611\r
4612**/\r
4613EFI_STATUS\r
4614ScsiDiskAsyncRead16 (\r
4615 IN SCSI_DISK_DEV *ScsiDiskDevice,\r
4616 IN UINT64 Timeout,\r
4617 IN UINT8 TimesRetry,\r
4618 OUT UINT8 *DataBuffer,\r
4619 IN UINT32 DataLength,\r
4620 IN UINT64 StartLba,\r
4621 IN UINT32 SectorCount,\r
4622 IN OUT SCSI_BLKIO2_REQUEST *BlkIo2Req,\r
4623 IN EFI_BLOCK_IO2_TOKEN *Token\r
4624 )\r
4625{\r
4626 EFI_STATUS Status;\r
4627 SCSI_ASYNC_RW_REQUEST *Request;\r
4628 EFI_EVENT AsyncIoEvent;\r
4629 EFI_TPL OldTpl;\r
4630\r
4631 AsyncIoEvent = NULL;\r
4632\r
4633 Request = AllocateZeroPool (sizeof (SCSI_ASYNC_RW_REQUEST));\r
4634 if (Request == NULL) {\r
4635 return EFI_OUT_OF_RESOURCES;\r
4636 }\r
4637\r
4638 OldTpl = gBS->RaiseTPL (TPL_NOTIFY);\r
4639 InsertTailList (&BlkIo2Req->ScsiRWQueue, &Request->Link);\r
4640 gBS->RestoreTPL (OldTpl);\r
4641\r
4642 Request->SenseDataLength = (UINT8) (6 * sizeof (EFI_SCSI_SENSE_DATA));\r
4643 Request->SenseData = AllocateZeroPool (Request->SenseDataLength);\r
4644 if (Request->SenseData == NULL) {\r
4645 Status = EFI_OUT_OF_RESOURCES;\r
4646 goto ErrorExit;\r
4647 }\r
4648\r
4649 Request->ScsiDiskDevice = ScsiDiskDevice;\r
4650 Request->Timeout = Timeout;\r
4651 Request->TimesRetry = TimesRetry;\r
4652 Request->InBuffer = DataBuffer;\r
4653 Request->DataLength = DataLength;\r
4654 Request->StartLba = StartLba;\r
4655 Request->SectorCount = SectorCount;\r
4656 Request->BlkIo2Req = BlkIo2Req;\r
4657\r
4658 //\r
4659 // Create Event\r
4660 //\r
4661 Status = gBS->CreateEvent (\r
4662 EVT_NOTIFY_SIGNAL,\r
4663 TPL_NOTIFY,\r
4664 ScsiDiskNotify,\r
4665 Request,\r
4666 &AsyncIoEvent\r
4667 );\r
4668 if (EFI_ERROR(Status)) {\r
4669 goto ErrorExit;\r
4670 }\r
4671\r
4672 Status = ScsiRead16CommandEx (\r
4673 ScsiDiskDevice->ScsiIo,\r
4674 Request->Timeout,\r
4675 Request->SenseData,\r
4676 &Request->SenseDataLength,\r
4677 &Request->HostAdapterStatus,\r
4678 &Request->TargetStatus,\r
4679 Request->InBuffer,\r
4680 &Request->DataLength,\r
4681 Request->StartLba,\r
4682 Request->SectorCount,\r
4683 AsyncIoEvent\r
4684 );\r
4685 if (EFI_ERROR(Status)) {\r
4686 goto ErrorExit;\r
4687 }\r
4688\r
4689 return EFI_SUCCESS;\r
4690\r
4691ErrorExit:\r
4692 if (AsyncIoEvent != NULL) {\r
4693 gBS->CloseEvent (AsyncIoEvent);\r
4694 }\r
4695\r
4696 if (Request != NULL) {\r
4697 if (Request->SenseData != NULL) {\r
4698 FreePool (Request->SenseData);\r
4699 }\r
4700\r
4701 OldTpl = gBS->RaiseTPL (TPL_NOTIFY);\r
4702 RemoveEntryList (&Request->Link);\r
4703 gBS->RestoreTPL (OldTpl);\r
4704\r
4705 FreePool (Request);\r
4706 }\r
4707\r
4708 return Status;\r
4709}\r
4710\r
4711\r
4712/**\r
4713 Submit Async Write(16) command.\r
4714\r
4715 @param ScsiDiskDevice The pointer of ScsiDiskDevice.\r
4716 @param Timeout The time to complete the command.\r
4717 @param TimesRetry The number of times the command has been retried.\r
4718 @param DataBuffer The buffer contains the data to write.\r
4719 @param DataLength The length of buffer.\r
4720 @param StartLba The start logic block address.\r
4721 @param SectorCount The number of blocks to write.\r
4722 @param BlkIo2Req The upstream BlockIo2 request.\r
4723 @param Token The pointer to the token associated with the\r
4724 non-blocking read request.\r
4725\r
4726 @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a\r
4727 lack of resources.\r
4728 @return others Status returned by calling\r
4729 ScsiWrite16CommandEx().\r
4730\r
4731**/\r
4732EFI_STATUS\r
4733ScsiDiskAsyncWrite16 (\r
4734 IN SCSI_DISK_DEV *ScsiDiskDevice,\r
4735 IN UINT64 Timeout,\r
4736 IN UINT8 TimesRetry,\r
4737 IN UINT8 *DataBuffer,\r
4738 IN UINT32 DataLength,\r
4739 IN UINT64 StartLba,\r
4740 IN UINT32 SectorCount,\r
4741 IN OUT SCSI_BLKIO2_REQUEST *BlkIo2Req,\r
4742 IN EFI_BLOCK_IO2_TOKEN *Token\r
4743 )\r
4744{\r
4745 EFI_STATUS Status;\r
4746 SCSI_ASYNC_RW_REQUEST *Request;\r
4747 EFI_EVENT AsyncIoEvent;\r
4748 EFI_TPL OldTpl;\r
4749\r
4750 AsyncIoEvent = NULL;\r
4751\r
4752 Request = AllocateZeroPool (sizeof (SCSI_ASYNC_RW_REQUEST));\r
4753 if (Request == NULL) {\r
4754 return EFI_OUT_OF_RESOURCES;\r
4755 }\r
4756\r
4757 OldTpl = gBS->RaiseTPL (TPL_NOTIFY);\r
4758 InsertTailList (&BlkIo2Req->ScsiRWQueue, &Request->Link);\r
4759 gBS->RestoreTPL (OldTpl);\r
4760\r
4761 Request->SenseDataLength = (UINT8) (6 * sizeof (EFI_SCSI_SENSE_DATA));\r
4762 Request->SenseData = AllocateZeroPool (Request->SenseDataLength);\r
4763 if (Request->SenseData == NULL) {\r
4764 Status = EFI_OUT_OF_RESOURCES;\r
4765 goto ErrorExit;\r
4766 }\r
4767\r
4768 Request->ScsiDiskDevice = ScsiDiskDevice;\r
4769 Request->Timeout = Timeout;\r
4770 Request->TimesRetry = TimesRetry;\r
4771 Request->OutBuffer = DataBuffer;\r
4772 Request->DataLength = DataLength;\r
4773 Request->StartLba = StartLba;\r
4774 Request->SectorCount = SectorCount;\r
4775 Request->BlkIo2Req = BlkIo2Req;\r
4776\r
4777 //\r
4778 // Create Event\r
4779 //\r
4780 Status = gBS->CreateEvent (\r
4781 EVT_NOTIFY_SIGNAL,\r
4782 TPL_NOTIFY,\r
4783 ScsiDiskNotify,\r
4784 Request,\r
4785 &AsyncIoEvent\r
4786 );\r
4787 if (EFI_ERROR(Status)) {\r
4788 goto ErrorExit;\r
4789 }\r
4790\r
4791 Status = ScsiWrite16CommandEx (\r
4792 ScsiDiskDevice->ScsiIo,\r
4793 Request->Timeout,\r
4794 Request->SenseData,\r
4795 &Request->SenseDataLength,\r
4796 &Request->HostAdapterStatus,\r
4797 &Request->TargetStatus,\r
4798 Request->OutBuffer,\r
4799 &Request->DataLength,\r
4800 Request->StartLba,\r
4801 Request->SectorCount,\r
4802 AsyncIoEvent\r
4803 );\r
4804 if (EFI_ERROR(Status)) {\r
4805 goto ErrorExit;\r
4806 }\r
4807\r
4808 return EFI_SUCCESS;\r
4809\r
4810ErrorExit:\r
4811 if (AsyncIoEvent != NULL) {\r
4812 gBS->CloseEvent (AsyncIoEvent);\r
4813 }\r
4814\r
4815 if (Request != NULL) {\r
4816 if (Request->SenseData != NULL) {\r
4817 FreePool (Request->SenseData);\r
4818 }\r
4819\r
4820 OldTpl = gBS->RaiseTPL (TPL_NOTIFY);\r
4821 RemoveEntryList (&Request->Link);\r
4822 gBS->RestoreTPL (OldTpl);\r
4823\r
4824 FreePool (Request);\r
4825 }\r
4826\r
4827 return Status;\r
4828}\r
4829\r
4830\r
4831/**\r
4832 Check sense key to find if media presents.\r
4833\r
4834 @param SenseData The pointer of EFI_SCSI_SENSE_DATA\r
4835 @param SenseCounts The number of sense key\r
4836\r
4837 @retval TRUE NOT any media\r
4838 @retval FALSE Media presents\r
4839**/\r
4840BOOLEAN\r
4841ScsiDiskIsNoMedia (\r
4842 IN EFI_SCSI_SENSE_DATA *SenseData,\r
4843 IN UINTN SenseCounts\r
4844 )\r
4845{\r
4846 EFI_SCSI_SENSE_DATA *SensePtr;\r
4847 UINTN Index;\r
4848 BOOLEAN IsNoMedia;\r
4849\r
4850 IsNoMedia = FALSE;\r
4851 SensePtr = SenseData;\r
4852\r
4853 for (Index = 0; Index < SenseCounts; Index++) {\r
4854 //\r
4855 // Sense Key is EFI_SCSI_SK_NOT_READY (0x2),\r
4856 // Additional Sense Code is ASC_NO_MEDIA (0x3A)\r
4857 //\r
4858 if ((SensePtr->Sense_Key == EFI_SCSI_SK_NOT_READY) &&\r
4859 (SensePtr->Addnl_Sense_Code == EFI_SCSI_ASC_NO_MEDIA)) {\r
4860 IsNoMedia = TRUE;\r
4861 }\r
4862 SensePtr++;\r
4863 }\r
4864\r
4865 return IsNoMedia;\r
4866}\r
4867\r
4868\r
4869/**\r
4870 Parse sense key.\r
4871\r
4872 @param SenseData The pointer of EFI_SCSI_SENSE_DATA\r
4873 @param SenseCounts The number of sense key\r
4874\r
4875 @retval TRUE Error\r
4876 @retval FALSE NOT error\r
4877\r
4878**/\r
4879BOOLEAN\r
4880ScsiDiskIsMediaError (\r
4881 IN EFI_SCSI_SENSE_DATA *SenseData,\r
4882 IN UINTN SenseCounts\r
4883 )\r
4884{\r
4885 EFI_SCSI_SENSE_DATA *SensePtr;\r
4886 UINTN Index;\r
4887 BOOLEAN IsError;\r
4888\r
4889 IsError = FALSE;\r
4890 SensePtr = SenseData;\r
4891\r
4892 for (Index = 0; Index < SenseCounts; Index++) {\r
4893\r
4894 switch (SensePtr->Sense_Key) {\r
4895\r
4896 case EFI_SCSI_SK_MEDIUM_ERROR:\r
4897 //\r
4898 // Sense Key is EFI_SCSI_SK_MEDIUM_ERROR (0x3)\r
4899 //\r
4900 switch (SensePtr->Addnl_Sense_Code) {\r
4901\r
4902 //\r
4903 // fall through\r
4904 //\r
4905 case EFI_SCSI_ASC_MEDIA_ERR1:\r
4906\r
4907 //\r
4908 // fall through\r
4909 //\r
4910 case EFI_SCSI_ASC_MEDIA_ERR2:\r
4911\r
4912 //\r
4913 // fall through\r
4914 //\r
4915 case EFI_SCSI_ASC_MEDIA_ERR3:\r
4916 case EFI_SCSI_ASC_MEDIA_ERR4:\r
4917 IsError = TRUE;\r
4918 break;\r
4919\r
4920 default:\r
4921 break;\r
4922 }\r
4923\r
4924 break;\r
4925\r
4926 case EFI_SCSI_SK_NOT_READY:\r
4927 //\r
4928 // Sense Key is EFI_SCSI_SK_NOT_READY (0x2)\r
4929 //\r
4930 switch (SensePtr->Addnl_Sense_Code) {\r
4931 //\r
4932 // Additional Sense Code is ASC_MEDIA_UPSIDE_DOWN (0x6)\r
4933 //\r
4934 case EFI_SCSI_ASC_MEDIA_UPSIDE_DOWN:\r
4935 IsError = TRUE;\r
4936 break;\r
4937\r
4938 default:\r
4939 break;\r
4940 }\r
4941 break;\r
4942\r
4943 default:\r
4944 break;\r
4945 }\r
4946\r
4947 SensePtr++;\r
4948 }\r
4949\r
4950 return IsError;\r
4951}\r
4952\r
4953\r
4954/**\r
4955 Check sense key to find if hardware error happens.\r
4956\r
4957 @param SenseData The pointer of EFI_SCSI_SENSE_DATA\r
4958 @param SenseCounts The number of sense key\r
4959\r
4960 @retval TRUE Hardware error exits.\r
4961 @retval FALSE NO error.\r
4962\r
4963**/\r
4964BOOLEAN\r
4965ScsiDiskIsHardwareError (\r
4966 IN EFI_SCSI_SENSE_DATA *SenseData,\r
4967 IN UINTN SenseCounts\r
4968 )\r
4969{\r
4970 EFI_SCSI_SENSE_DATA *SensePtr;\r
4971 UINTN Index;\r
4972 BOOLEAN IsError;\r
4973\r
4974 IsError = FALSE;\r
4975 SensePtr = SenseData;\r
4976\r
4977 for (Index = 0; Index < SenseCounts; Index++) {\r
4978 \r
4979 //\r
4980 // Sense Key is EFI_SCSI_SK_HARDWARE_ERROR (0x4)\r
4981 //\r
4982 if (SensePtr->Sense_Key == EFI_SCSI_SK_HARDWARE_ERROR) {\r
4983 IsError = TRUE;\r
4984 }\r
4985\r
4986 SensePtr++;\r
4987 }\r
4988\r
4989 return IsError;\r
4990}\r
4991\r
4992\r
4993/**\r
4994 Check sense key to find if media has changed.\r
4995\r
4996 @param SenseData The pointer of EFI_SCSI_SENSE_DATA\r
4997 @param SenseCounts The number of sense key\r
4998\r
4999 @retval TRUE Media is changed.\r
5000 @retval FALSE Media is NOT changed.\r
5001**/\r
5002BOOLEAN\r
5003ScsiDiskIsMediaChange (\r
5004 IN EFI_SCSI_SENSE_DATA *SenseData,\r
5005 IN UINTN SenseCounts\r
5006 )\r
5007{\r
5008 EFI_SCSI_SENSE_DATA *SensePtr;\r
5009 UINTN Index;\r
5010 BOOLEAN IsMediaChanged;\r
5011\r
5012 IsMediaChanged = FALSE;\r
5013 SensePtr = SenseData;\r
5014\r
5015 for (Index = 0; Index < SenseCounts; Index++) {\r
5016 //\r
5017 // Sense Key is EFI_SCSI_SK_UNIT_ATTENTION (0x6),\r
5018 // Additional sense code is EFI_SCSI_ASC_MEDIA_CHANGE (0x28)\r
5019 //\r
5020 if ((SensePtr->Sense_Key == EFI_SCSI_SK_UNIT_ATTENTION) &&\r
5021 (SensePtr->Addnl_Sense_Code == EFI_SCSI_ASC_MEDIA_CHANGE)) {\r
5022 IsMediaChanged = TRUE;\r
5023 }\r
5024\r
5025 SensePtr++;\r
5026 }\r
5027\r
5028 return IsMediaChanged;\r
5029}\r
5030\r
5031/**\r
5032 Check sense key to find if reset happens.\r
5033\r
5034 @param SenseData The pointer of EFI_SCSI_SENSE_DATA\r
5035 @param SenseCounts The number of sense key\r
5036\r
5037 @retval TRUE It is reset before.\r
5038 @retval FALSE It is NOT reset before.\r
5039\r
5040**/\r
5041BOOLEAN\r
5042ScsiDiskIsResetBefore (\r
5043 IN EFI_SCSI_SENSE_DATA *SenseData,\r
5044 IN UINTN SenseCounts\r
5045 )\r
5046{\r
5047 EFI_SCSI_SENSE_DATA *SensePtr;\r
5048 UINTN Index;\r
5049 BOOLEAN IsResetBefore;\r
5050\r
5051 IsResetBefore = FALSE;\r
5052 SensePtr = SenseData;\r
5053\r
5054 for (Index = 0; Index < SenseCounts; Index++) {\r
5055 \r
5056 //\r
5057 // Sense Key is EFI_SCSI_SK_UNIT_ATTENTION (0x6)\r
5058 // Additional Sense Code is EFI_SCSI_ASC_RESET (0x29)\r
5059 //\r
5060 if ((SensePtr->Sense_Key == EFI_SCSI_SK_UNIT_ATTENTION) &&\r
5061 (SensePtr->Addnl_Sense_Code == EFI_SCSI_ASC_RESET)) {\r
5062 IsResetBefore = TRUE;\r
5063 }\r
5064\r
5065 SensePtr++;\r
5066 }\r
5067\r
5068 return IsResetBefore;\r
5069}\r
5070\r
5071/**\r
5072 Check sense key to find if the drive is ready.\r
5073\r
5074 @param SenseData The pointer of EFI_SCSI_SENSE_DATA\r
5075 @param SenseCounts The number of sense key\r
5076 @param RetryLater The flag means if need a retry \r
5077\r
5078 @retval TRUE Drive is ready.\r
5079 @retval FALSE Drive is NOT ready.\r
5080\r
5081**/\r
5082BOOLEAN\r
5083ScsiDiskIsDriveReady (\r
5084 IN EFI_SCSI_SENSE_DATA *SenseData,\r
5085 IN UINTN SenseCounts,\r
5086 OUT BOOLEAN *RetryLater\r
5087 )\r
5088{\r
5089 EFI_SCSI_SENSE_DATA *SensePtr;\r
5090 UINTN Index;\r
5091 BOOLEAN IsReady;\r
5092\r
5093 IsReady = TRUE;\r
5094 *RetryLater = FALSE;\r
5095 SensePtr = SenseData;\r
5096\r
5097 for (Index = 0; Index < SenseCounts; Index++) {\r
5098\r
5099 switch (SensePtr->Sense_Key) {\r
5100\r
5101 case EFI_SCSI_SK_NOT_READY:\r
5102 //\r
5103 // Sense Key is EFI_SCSI_SK_NOT_READY (0x2)\r
5104 //\r
5105 switch (SensePtr->Addnl_Sense_Code) {\r
5106 case EFI_SCSI_ASC_NOT_READY:\r
5107 //\r
5108 // Additional Sense Code is EFI_SCSI_ASC_NOT_READY (0x4)\r
5109 //\r
5110 switch (SensePtr->Addnl_Sense_Code_Qualifier) {\r
5111 case EFI_SCSI_ASCQ_IN_PROGRESS:\r
5112 //\r
5113 // Additional Sense Code Qualifier is\r
5114 // EFI_SCSI_ASCQ_IN_PROGRESS (0x1)\r
5115 //\r
5116 IsReady = FALSE;\r
5117 *RetryLater = TRUE;\r
5118 break;\r
5119\r
5120 default:\r
5121 IsReady = FALSE;\r
5122 *RetryLater = FALSE;\r
5123 break;\r
5124 }\r
5125 break;\r
5126\r
5127 default:\r
5128 break;\r
5129 }\r
5130 break;\r
5131\r
5132 default:\r
5133 break;\r
5134 }\r
5135\r
5136 SensePtr++;\r
5137 }\r
5138\r
5139 return IsReady;\r
5140}\r
5141\r
5142/**\r
5143 Check sense key to find if it has sense key.\r
5144\r
5145 @param SenseData - The pointer of EFI_SCSI_SENSE_DATA\r
5146 @param SenseCounts - The number of sense key\r
5147\r
5148 @retval TRUE It has sense key.\r
5149 @retval FALSE It has NOT any sense key.\r
5150\r
5151**/\r
5152BOOLEAN\r
5153ScsiDiskHaveSenseKey (\r
5154 IN EFI_SCSI_SENSE_DATA *SenseData,\r
5155 IN UINTN SenseCounts\r
5156 )\r
5157{\r
5158 EFI_SCSI_SENSE_DATA *SensePtr;\r
5159 UINTN Index;\r
5160 BOOLEAN HaveSenseKey;\r
5161\r
5162 if (SenseCounts == 0) {\r
5163 HaveSenseKey = FALSE;\r
5164 } else {\r
5165 HaveSenseKey = TRUE;\r
5166 }\r
5167\r
5168 SensePtr = SenseData;\r
5169\r
5170 for (Index = 0; Index < SenseCounts; Index++) {\r
5171 \r
5172 //\r
5173 // Sense Key is SK_NO_SENSE (0x0)\r
5174 //\r
5175 if ((SensePtr->Sense_Key == EFI_SCSI_SK_NO_SENSE) &&\r
5176 (Index == 0)) {\r
5177 HaveSenseKey = FALSE;\r
5178 }\r
5179\r
5180 SensePtr++;\r
5181 }\r
5182\r
5183 return HaveSenseKey;\r
5184}\r
5185\r
5186/**\r
5187 Release resource about disk device.\r
5188\r
5189 @param ScsiDiskDevice The pointer of SCSI_DISK_DEV\r
5190\r
5191**/\r
5192VOID\r
5193ReleaseScsiDiskDeviceResources (\r
5194 IN SCSI_DISK_DEV *ScsiDiskDevice\r
5195 )\r
5196{\r
5197 if (ScsiDiskDevice == NULL) {\r
5198 return ;\r
5199 }\r
5200\r
5201 if (ScsiDiskDevice->SenseData != NULL) {\r
5202 FreePool (ScsiDiskDevice->SenseData);\r
5203 ScsiDiskDevice->SenseData = NULL;\r
5204 }\r
5205\r
5206 if (ScsiDiskDevice->ControllerNameTable != NULL) {\r
5207 FreeUnicodeStringTable (ScsiDiskDevice->ControllerNameTable);\r
5208 ScsiDiskDevice->ControllerNameTable = NULL;\r
5209 }\r
5210\r
5211 FreePool (ScsiDiskDevice);\r
5212\r
5213 ScsiDiskDevice = NULL;\r
5214}\r
5215\r
5216/**\r
5217 Determine if Block Io & Block Io2 should be produced.\r
5218 \r
5219\r
5220 @param ChildHandle Child Handle to retrieve Parent information.\r
5221 \r
5222 @retval TRUE Should produce Block Io & Block Io2.\r
5223 @retval FALSE Should not produce Block Io & Block Io2.\r
5224\r
5225**/ \r
5226BOOLEAN\r
5227DetermineInstallBlockIo (\r
5228 IN EFI_HANDLE ChildHandle\r
5229 ) \r
5230{\r
5231 EFI_SCSI_PASS_THRU_PROTOCOL *ScsiPassThru;\r
5232 EFI_EXT_SCSI_PASS_THRU_PROTOCOL *ExtScsiPassThru;\r
5233\r
5234 //\r
5235 // Firstly, check if ExtScsiPassThru Protocol parent handle exists. If existence,\r
5236 // check its attribute, logic or physical.\r
5237 //\r
5238 ExtScsiPassThru = (EFI_EXT_SCSI_PASS_THRU_PROTOCOL *)GetParentProtocol (&gEfiExtScsiPassThruProtocolGuid, ChildHandle);\r
5239 if (ExtScsiPassThru != NULL) {\r
5240 if ((ExtScsiPassThru->Mode->Attributes & EFI_SCSI_PASS_THRU_ATTRIBUTES_LOGICAL) != 0) {\r
5241 return TRUE;\r
5242 }\r
5243 }\r
5244\r
5245 //\r
5246 // Secondly, check if ScsiPassThru Protocol parent handle exists. If existence,\r
5247 // check its attribute, logic or physical.\r
5248 //\r
5249 ScsiPassThru = (EFI_SCSI_PASS_THRU_PROTOCOL *)GetParentProtocol (&gEfiScsiPassThruProtocolGuid, ChildHandle);\r
5250 if (ScsiPassThru != NULL) {\r
5251 if ((ScsiPassThru->Mode->Attributes & EFI_SCSI_PASS_THRU_ATTRIBUTES_LOGICAL) != 0) {\r
5252 return TRUE;\r
5253 }\r
5254 }\r
5255 \r
5256 return FALSE;\r
5257}\r
5258\r
5259/**\r
5260 Search protocol database and check to see if the protocol\r
5261 specified by ProtocolGuid is present on a ControllerHandle and opened by\r
5262 ChildHandle with an attribute of EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER.\r
5263 If the ControllerHandle is found, then the protocol specified by ProtocolGuid\r
5264 will be opened on it. \r
5265 \r
5266\r
5267 @param ProtocolGuid ProtocolGuid pointer.\r
5268 @param ChildHandle Child Handle to retrieve Parent information.\r
5269 \r
5270**/ \r
5271VOID *\r
5272EFIAPI\r
5273GetParentProtocol (\r
5274 IN EFI_GUID *ProtocolGuid,\r
5275 IN EFI_HANDLE ChildHandle\r
5276 ) \r
5277{\r
5278 UINTN Index;\r
5279 UINTN HandleCount;\r
5280 VOID *Interface; \r
5281 EFI_STATUS Status;\r
5282 EFI_HANDLE *HandleBuffer;\r
5283\r
5284 //\r
5285 // Retrieve the list of all handles from the handle database\r
5286 //\r
5287 Status = gBS->LocateHandleBuffer (\r
5288 ByProtocol,\r
5289 ProtocolGuid,\r
5290 NULL,\r
5291 &HandleCount,\r
5292 &HandleBuffer\r
5293 );\r
5294\r
5295 if (EFI_ERROR (Status)) {\r
5296 return NULL;\r
5297 }\r
5298\r
5299 //\r
5300 // Iterate to find who is parent handle that is opened with ProtocolGuid by ChildHandle \r
5301 //\r
5302 for (Index = 0; Index < HandleCount; Index++) {\r
5303 Status = EfiTestChildHandle (HandleBuffer[Index], ChildHandle, ProtocolGuid);\r
5304 if (!EFI_ERROR (Status)) {\r
5305 Status = gBS->HandleProtocol (HandleBuffer[Index], ProtocolGuid, (VOID **)&Interface);\r
5306 if (!EFI_ERROR (Status)) {\r
5307 gBS->FreePool (HandleBuffer);\r
5308 return Interface;\r
5309 }\r
5310 }\r
5311 }\r
5312\r
5313 gBS->FreePool (HandleBuffer);\r
5314 return NULL;\r
5315} \r
5316\r
5317/**\r
5318 Determine if EFI Erase Block Protocol should be produced.\r
5319\r
5320 @param ScsiDiskDevice The pointer of SCSI_DISK_DEV.\r
5321 @param ChildHandle Handle of device.\r
5322\r
5323 @retval TRUE Should produce EFI Erase Block Protocol.\r
5324 @retval FALSE Should not produce EFI Erase Block Protocol.\r
5325\r
5326**/\r
5327BOOLEAN\r
5328DetermineInstallEraseBlock (\r
5329 IN SCSI_DISK_DEV *ScsiDiskDevice,\r
5330 IN EFI_HANDLE ChildHandle\r
5331 )\r
5332{\r
5333 UINT8 HostAdapterStatus;\r
5334 UINT8 TargetStatus;\r
5335 EFI_STATUS CommandStatus;\r
5336 EFI_STATUS Status;\r
5337 BOOLEAN UfsDevice;\r
5338 BOOLEAN RetVal;\r
5339 EFI_DEVICE_PATH_PROTOCOL *DevicePathNode;\r
5340 UINT8 SenseDataLength;\r
5341 UINT32 DataLength16;\r
5342 EFI_SCSI_DISK_CAPACITY_DATA16 *CapacityData16;\r
5343\r
5344 UfsDevice = FALSE;\r
5345 RetVal = TRUE;\r
5346 CapacityData16 = NULL;\r
5347\r
5348 Status = gBS->HandleProtocol (\r
5349 ChildHandle,\r
5350 &gEfiDevicePathProtocolGuid,\r
5351 (VOID **) &DevicePathNode\r
5352 );\r
5353 //\r
5354 // Device Path protocol must be installed on the device handle.\r
5355 //\r
5356 ASSERT_EFI_ERROR (Status);\r
5357\r
5358 while (!IsDevicePathEndType (DevicePathNode)) {\r
5359 //\r
5360 // For now, only support Erase Block Protocol on UFS devices.\r
5361 //\r
5362 if ((DevicePathNode->Type == MESSAGING_DEVICE_PATH) &&\r
5363 (DevicePathNode->SubType == MSG_UFS_DP)) {\r
5364 UfsDevice = TRUE;\r
5365 break;\r
5366 }\r
5367\r
5368 DevicePathNode = NextDevicePathNode (DevicePathNode);\r
5369 }\r
5370 if (!UfsDevice) {\r
5371 RetVal = FALSE;\r
5372 goto Done;\r
5373 }\r
5374\r
5375 //\r
5376 // Check whether the erase functionality is enabled on the UFS device.\r
5377 //\r
5378 CapacityData16 = AllocateAlignedBuffer (ScsiDiskDevice, sizeof (EFI_SCSI_DISK_CAPACITY_DATA16));\r
5379 if (CapacityData16 == NULL) {\r
5380 RetVal = FALSE;\r
5381 goto Done;\r
5382 }\r
5383\r
5384 SenseDataLength = 0;\r
5385 DataLength16 = sizeof (EFI_SCSI_DISK_CAPACITY_DATA16);\r
5386 ZeroMem (CapacityData16, sizeof (EFI_SCSI_DISK_CAPACITY_DATA16));\r
5387\r
5388 CommandStatus = ScsiReadCapacity16Command (\r
5389 ScsiDiskDevice->ScsiIo,\r
5390 SCSI_DISK_TIMEOUT,\r
5391 NULL,\r
5392 &SenseDataLength,\r
5393 &HostAdapterStatus,\r
5394 &TargetStatus,\r
5395 (VOID *) CapacityData16,\r
5396 &DataLength16,\r
5397 FALSE\r
5398 );\r
5399\r
5400 if (CommandStatus == EFI_SUCCESS) {\r
5401 //\r
5402 // Universal Flash Storage (UFS) Version 2.0\r
5403 // Section 11.3.9.2\r
5404 // Bits TPE and TPRZ should both be set to enable the erase feature on UFS.\r
5405 //\r
5406 if (((CapacityData16->LowestAlignLogic2 & BIT7) == 0) ||\r
5407 ((CapacityData16->LowestAlignLogic2 & BIT6) == 0)) {\r
5408 DEBUG ((\r
5409 EFI_D_VERBOSE,\r
5410 "ScsiDisk EraseBlock: Either TPE or TPRZ is not set: 0x%x.\n",\r
5411 CapacityData16->LowestAlignLogic2\r
5412 ));\r
5413\r
5414 RetVal = FALSE;\r
5415 goto Done;\r
5416 }\r
5417 } else {\r
5418 DEBUG ((\r
5419 EFI_D_VERBOSE,\r
5420 "ScsiDisk EraseBlock: ReadCapacity16 failed with status %r.\n",\r
5421 CommandStatus\r
5422 ));\r
5423\r
5424 RetVal = FALSE;\r
5425 goto Done;\r
5426 }\r
5427\r
5428 //\r
5429 // Check whether the UFS device server implements the UNMAP command.\r
5430 //\r
5431 if ((ScsiDiskDevice->UnmapInfo.MaxLbaCnt == 0) ||\r
5432 (ScsiDiskDevice->UnmapInfo.MaxBlkDespCnt == 0)) {\r
5433 DEBUG ((\r
5434 EFI_D_VERBOSE,\r
5435 "ScsiDisk EraseBlock: The device server does not implement the UNMAP command.\n"\r
5436 ));\r
5437\r
5438 RetVal = FALSE;\r
5439 goto Done;\r
5440 }\r
5441\r
5442Done:\r
5443 if (CapacityData16 != NULL) {\r
5444 FreeAlignedBuffer (CapacityData16, sizeof (EFI_SCSI_DISK_CAPACITY_DATA16));\r
5445 }\r
5446\r
5447 return RetVal;\r
5448}\r
5449\r
5450/**\r
5451 Provides inquiry information for the controller type.\r
5452 \r
5453 This function is used by the IDE bus driver to get inquiry data. Data format\r
5454 of Identify data is defined by the Interface GUID.\r
5455\r
5456 @param[in] This Pointer to the EFI_DISK_INFO_PROTOCOL instance.\r
5457 @param[in, out] InquiryData Pointer to a buffer for the inquiry data.\r
5458 @param[in, out] InquiryDataSize Pointer to the value for the inquiry data size.\r
5459\r
5460 @retval EFI_SUCCESS The command was accepted without any errors.\r
5461 @retval EFI_NOT_FOUND Device does not support this data class \r
5462 @retval EFI_DEVICE_ERROR Error reading InquiryData from device \r
5463 @retval EFI_BUFFER_TOO_SMALL InquiryDataSize not big enough \r
5464\r
5465**/\r
5466EFI_STATUS\r
5467EFIAPI\r
5468ScsiDiskInfoInquiry (\r
5469 IN EFI_DISK_INFO_PROTOCOL *This,\r
5470 IN OUT VOID *InquiryData,\r
5471 IN OUT UINT32 *InquiryDataSize\r
5472 )\r
5473{\r
5474 EFI_STATUS Status;\r
5475 SCSI_DISK_DEV *ScsiDiskDevice;\r
5476\r
5477 ScsiDiskDevice = SCSI_DISK_DEV_FROM_DISKINFO (This);\r
5478\r
5479 Status = EFI_BUFFER_TOO_SMALL;\r
5480 if (*InquiryDataSize >= sizeof (ScsiDiskDevice->InquiryData)) {\r
5481 Status = EFI_SUCCESS;\r
5482 CopyMem (InquiryData, &ScsiDiskDevice->InquiryData, sizeof (ScsiDiskDevice->InquiryData));\r
5483 }\r
5484 *InquiryDataSize = sizeof (ScsiDiskDevice->InquiryData);\r
5485 return Status;\r
5486}\r
5487\r
5488\r
5489/**\r
5490 Provides identify information for the controller type.\r
5491\r
5492 This function is used by the IDE bus driver to get identify data. Data format\r
5493 of Identify data is defined by the Interface GUID.\r
5494\r
5495 @param[in] This Pointer to the EFI_DISK_INFO_PROTOCOL \r
5496 instance.\r
5497 @param[in, out] IdentifyData Pointer to a buffer for the identify data.\r
5498 @param[in, out] IdentifyDataSize Pointer to the value for the identify data\r
5499 size.\r
5500\r
5501 @retval EFI_SUCCESS The command was accepted without any errors.\r
5502 @retval EFI_NOT_FOUND Device does not support this data class \r
5503 @retval EFI_DEVICE_ERROR Error reading IdentifyData from device \r
5504 @retval EFI_BUFFER_TOO_SMALL IdentifyDataSize not big enough \r
5505\r
5506**/\r
5507EFI_STATUS\r
5508EFIAPI\r
5509ScsiDiskInfoIdentify (\r
5510 IN EFI_DISK_INFO_PROTOCOL *This,\r
5511 IN OUT VOID *IdentifyData,\r
5512 IN OUT UINT32 *IdentifyDataSize\r
5513 )\r
5514{\r
5515 EFI_STATUS Status;\r
5516 SCSI_DISK_DEV *ScsiDiskDevice;\r
5517\r
5518 if (CompareGuid (&This->Interface, &gEfiDiskInfoScsiInterfaceGuid) || CompareGuid (&This->Interface, &gEfiDiskInfoUfsInterfaceGuid)) {\r
5519 //\r
5520 // Physical SCSI bus does not support this data class. \r
5521 //\r
5522 return EFI_NOT_FOUND;\r
5523 }\r
5524\r
5525 ScsiDiskDevice = SCSI_DISK_DEV_FROM_DISKINFO (This);\r
5526\r
5527 Status = EFI_BUFFER_TOO_SMALL;\r
5528 if (*IdentifyDataSize >= sizeof (ScsiDiskDevice->IdentifyData)) {\r
5529 Status = EFI_SUCCESS;\r
5530 CopyMem (IdentifyData, &ScsiDiskDevice->IdentifyData, sizeof (ScsiDiskDevice->IdentifyData));\r
5531 }\r
5532 *IdentifyDataSize = sizeof (ScsiDiskDevice->IdentifyData);\r
5533 return Status;\r
5534}\r
5535\r
5536/**\r
5537 Provides sense data information for the controller type.\r
5538 \r
5539 This function is used by the IDE bus driver to get sense data. \r
5540 Data format of Sense data is defined by the Interface GUID.\r
5541\r
5542 @param[in] This Pointer to the EFI_DISK_INFO_PROTOCOL instance.\r
5543 @param[in, out] SenseData Pointer to the SenseData.\r
5544 @param[in, out] SenseDataSize Size of SenseData in bytes.\r
5545 @param[out] SenseDataNumber Pointer to the value for the sense data size.\r
5546\r
5547 @retval EFI_SUCCESS The command was accepted without any errors.\r
5548 @retval EFI_NOT_FOUND Device does not support this data class.\r
5549 @retval EFI_DEVICE_ERROR Error reading SenseData from device.\r
5550 @retval EFI_BUFFER_TOO_SMALL SenseDataSize not big enough.\r
5551\r
5552**/\r
5553EFI_STATUS\r
5554EFIAPI\r
5555ScsiDiskInfoSenseData (\r
5556 IN EFI_DISK_INFO_PROTOCOL *This,\r
5557 IN OUT VOID *SenseData,\r
5558 IN OUT UINT32 *SenseDataSize,\r
5559 OUT UINT8 *SenseDataNumber\r
5560 )\r
5561{\r
5562 return EFI_NOT_FOUND;\r
5563}\r
5564\r
5565\r
5566/**\r
5567 This function is used by the IDE bus driver to get controller information.\r
5568\r
5569 @param[in] This Pointer to the EFI_DISK_INFO_PROTOCOL instance. \r
5570 @param[out] IdeChannel Pointer to the Ide Channel number. Primary or secondary.\r
5571 @param[out] IdeDevice Pointer to the Ide Device number. Master or slave.\r
5572\r
5573 @retval EFI_SUCCESS IdeChannel and IdeDevice are valid.\r
5574 @retval EFI_UNSUPPORTED This is not an IDE device.\r
5575\r
5576**/\r
5577EFI_STATUS\r
5578EFIAPI\r
5579ScsiDiskInfoWhichIde (\r
5580 IN EFI_DISK_INFO_PROTOCOL *This,\r
5581 OUT UINT32 *IdeChannel,\r
5582 OUT UINT32 *IdeDevice\r
5583 )\r
5584{\r
5585 SCSI_DISK_DEV *ScsiDiskDevice;\r
5586\r
5587 if (CompareGuid (&This->Interface, &gEfiDiskInfoScsiInterfaceGuid) || CompareGuid (&This->Interface, &gEfiDiskInfoUfsInterfaceGuid)) {\r
5588 //\r
5589 // This is not an IDE physical device.\r
5590 //\r
5591 return EFI_UNSUPPORTED;\r
5592 }\r
5593\r
5594 ScsiDiskDevice = SCSI_DISK_DEV_FROM_DISKINFO (This);\r
5595 *IdeChannel = ScsiDiskDevice->Channel;\r
5596 *IdeDevice = ScsiDiskDevice->Device;\r
5597\r
5598 return EFI_SUCCESS;\r
5599}\r
5600\r
5601\r
5602/**\r
5603 Issues ATA IDENTIFY DEVICE command to identify ATAPI device.\r
5604\r
5605 This function tries to fill 512-byte ATAPI_IDENTIFY_DATA for ATAPI device to\r
5606 implement Identify() interface for DiskInfo protocol. The ATA command is sent\r
5607 via SCSI Request Packet.\r
5608\r
5609 @param ScsiDiskDevice The pointer of SCSI_DISK_DEV\r
5610 \r
5611 @retval EFI_SUCCESS The ATAPI device identify data were retrieved successfully.\r
5612 @retval others Some error occurred during the identification that ATAPI device.\r
5613\r
5614**/ \r
5615EFI_STATUS\r
5616AtapiIdentifyDevice (\r
5617 IN OUT SCSI_DISK_DEV *ScsiDiskDevice\r
5618 )\r
5619{\r
5620 EFI_SCSI_IO_SCSI_REQUEST_PACKET CommandPacket;\r
5621 UINT8 Cdb[6];\r
5622\r
5623 //\r
5624 // Initialize SCSI REQUEST_PACKET and 6-byte Cdb\r
5625 //\r
5626 ZeroMem (&CommandPacket, sizeof (CommandPacket));\r
5627 ZeroMem (Cdb, sizeof (Cdb));\r
5628\r
5629 Cdb[0] = ATA_CMD_IDENTIFY_DEVICE;\r
5630 CommandPacket.Timeout = SCSI_DISK_TIMEOUT;\r
5631 CommandPacket.Cdb = Cdb;\r
5632 CommandPacket.CdbLength = (UINT8) sizeof (Cdb);\r
5633 CommandPacket.InDataBuffer = &ScsiDiskDevice->IdentifyData;\r
5634 CommandPacket.InTransferLength = sizeof (ScsiDiskDevice->IdentifyData);\r
5635\r
5636 return ScsiDiskDevice->ScsiIo->ExecuteScsiCommand (ScsiDiskDevice->ScsiIo, &CommandPacket, NULL);\r
5637}\r
5638\r
5639\r
5640/**\r
5641 Initialize the installation of DiskInfo protocol.\r
5642\r
5643 This function prepares for the installation of DiskInfo protocol on the child handle.\r
5644 By default, it installs DiskInfo protocol with SCSI interface GUID. If it further\r
5645 detects that the physical device is an ATAPI/AHCI device, it then updates interface GUID\r
5646 to be IDE/AHCI interface GUID.\r
5647\r
5648 @param ScsiDiskDevice The pointer of SCSI_DISK_DEV.\r
5649 @param ChildHandle Child handle to install DiskInfo protocol.\r
5650 \r
5651**/ \r
5652VOID\r
5653InitializeInstallDiskInfo (\r
5654 IN SCSI_DISK_DEV *ScsiDiskDevice,\r
5655 IN EFI_HANDLE ChildHandle\r
5656 )\r
5657{\r
5658 EFI_STATUS Status;\r
5659 EFI_DEVICE_PATH_PROTOCOL *DevicePathNode;\r
5660 EFI_DEVICE_PATH_PROTOCOL *ChildDevicePathNode;\r
5661 ATAPI_DEVICE_PATH *AtapiDevicePath;\r
5662 SATA_DEVICE_PATH *SataDevicePath;\r
5663 UINTN IdentifyRetry;\r
5664\r
5665 Status = gBS->HandleProtocol (ChildHandle, &gEfiDevicePathProtocolGuid, (VOID **) &DevicePathNode);\r
5666 //\r
5667 // Device Path protocol must be installed on the device handle. \r
5668 //\r
5669 ASSERT_EFI_ERROR (Status);\r
5670 //\r
5671 // Copy the DiskInfo protocol template.\r
5672 //\r
5673 CopyMem (&ScsiDiskDevice->DiskInfo, &gScsiDiskInfoProtocolTemplate, sizeof (gScsiDiskInfoProtocolTemplate));\r
5674\r
5675 while (!IsDevicePathEnd (DevicePathNode)) {\r
5676 ChildDevicePathNode = NextDevicePathNode (DevicePathNode);\r
5677 if ((DevicePathType (DevicePathNode) == HARDWARE_DEVICE_PATH) &&\r
5678 (DevicePathSubType (DevicePathNode) == HW_PCI_DP) &&\r
5679 (DevicePathType (ChildDevicePathNode) == MESSAGING_DEVICE_PATH) &&\r
5680 ((DevicePathSubType (ChildDevicePathNode) == MSG_ATAPI_DP) ||\r
5681 (DevicePathSubType (ChildDevicePathNode) == MSG_SATA_DP))) {\r
5682\r
5683 IdentifyRetry = 3;\r
5684 do {\r
5685 //\r
5686 // Issue ATA Identify Device Command via SCSI command, which is required to publish DiskInfo protocol\r
5687 // with IDE/AHCI interface GUID.\r
5688 //\r
5689 Status = AtapiIdentifyDevice (ScsiDiskDevice);\r
5690 if (!EFI_ERROR (Status)) {\r
5691 if (DevicePathSubType(ChildDevicePathNode) == MSG_ATAPI_DP) {\r
5692 //\r
5693 // We find the valid ATAPI device path\r
5694 //\r
5695 AtapiDevicePath = (ATAPI_DEVICE_PATH *) ChildDevicePathNode;\r
5696 ScsiDiskDevice->Channel = AtapiDevicePath->PrimarySecondary;\r
5697 ScsiDiskDevice->Device = AtapiDevicePath->SlaveMaster;\r
5698 //\r
5699 // Update the DiskInfo.Interface to IDE interface GUID for the physical ATAPI device. \r
5700 //\r
5701 CopyGuid (&ScsiDiskDevice->DiskInfo.Interface, &gEfiDiskInfoIdeInterfaceGuid);\r
5702 } else {\r
5703 //\r
5704 // We find the valid SATA device path\r
5705 //\r
5706 SataDevicePath = (SATA_DEVICE_PATH *) ChildDevicePathNode;\r
5707 ScsiDiskDevice->Channel = SataDevicePath->HBAPortNumber;\r
5708 ScsiDiskDevice->Device = SataDevicePath->PortMultiplierPortNumber;\r
5709 //\r
5710 // Update the DiskInfo.Interface to AHCI interface GUID for the physical AHCI device. \r
5711 //\r
5712 CopyGuid (&ScsiDiskDevice->DiskInfo.Interface, &gEfiDiskInfoAhciInterfaceGuid);\r
5713 }\r
5714 return;\r
5715 }\r
5716 } while (--IdentifyRetry > 0);\r
5717 } else if ((DevicePathType (ChildDevicePathNode) == MESSAGING_DEVICE_PATH) &&\r
5718 (DevicePathSubType (ChildDevicePathNode) == MSG_UFS_DP)) {\r
5719 CopyGuid (&ScsiDiskDevice->DiskInfo.Interface, &gEfiDiskInfoUfsInterfaceGuid);\r
5720 break;\r
5721 }\r
5722 DevicePathNode = ChildDevicePathNode;\r
5723 }\r
5724\r
5725 return;\r
5726}\r