]> git.proxmox.com Git - mirror_edk2.git/blame_incremental - MdeModulePkg/Bus/Scsi/ScsiDiskDxe/ScsiDisk.c
MdeModulePkg/AtaAtapiPassThru: Not set AhciCmdC bit of CommandList when doing transac...
[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 - 2012, 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.Reset = ScsiDiskReset;\r
238 ScsiDiskDevice->BlkIo.ReadBlocks = ScsiDiskReadBlocks;\r
239 ScsiDiskDevice->BlkIo.WriteBlocks = ScsiDiskWriteBlocks;\r
240 ScsiDiskDevice->BlkIo.FlushBlocks = ScsiDiskFlushBlocks;\r
241 ScsiDiskDevice->Handle = Controller;\r
242\r
243 ScsiIo->GetDeviceType (ScsiIo, &(ScsiDiskDevice->DeviceType));\r
244 switch (ScsiDiskDevice->DeviceType) {\r
245 case EFI_SCSI_TYPE_DISK:\r
246 ScsiDiskDevice->BlkIo.Media->BlockSize = 0x200;\r
247 MustReadCapacity = TRUE;\r
248 break;\r
249\r
250 case EFI_SCSI_TYPE_CDROM:\r
251 ScsiDiskDevice->BlkIo.Media->BlockSize = 0x800;\r
252 MustReadCapacity = FALSE;\r
253 break;\r
254 }\r
255 //\r
256 // The Sense Data Array's initial size is 6\r
257 //\r
258 ScsiDiskDevice->SenseDataNumber = 6;\r
259 ScsiDiskDevice->SenseData = (EFI_SCSI_SENSE_DATA *) AllocateZeroPool (\r
260 sizeof (EFI_SCSI_SENSE_DATA) * ScsiDiskDevice->SenseDataNumber\r
261 );\r
262 if (ScsiDiskDevice->SenseData == NULL) {\r
263 gBS->CloseProtocol (\r
264 Controller,\r
265 &gEfiScsiIoProtocolGuid,\r
266 This->DriverBindingHandle,\r
267 Controller\r
268 );\r
269 FreePool (ScsiDiskDevice);\r
270 return EFI_OUT_OF_RESOURCES;\r
271 }\r
272\r
273 //\r
274 // Retrieve device information\r
275 //\r
276 MaxRetry = 2;\r
277 for (Index = 0; Index < MaxRetry; Index++) {\r
278 Status = ScsiDiskInquiryDevice (ScsiDiskDevice, &NeedRetry);\r
279 if (!EFI_ERROR (Status)) {\r
280 break;\r
281 }\r
282\r
283 if (!NeedRetry) {\r
284 FreePool (ScsiDiskDevice->SenseData);\r
285 gBS->CloseProtocol (\r
286 Controller,\r
287 &gEfiScsiIoProtocolGuid,\r
288 This->DriverBindingHandle,\r
289 Controller\r
290 );\r
291 FreePool (ScsiDiskDevice);\r
292 return EFI_DEVICE_ERROR;\r
293 }\r
294 }\r
295 //\r
296 // The second parameter "TRUE" means must\r
297 // retrieve media capacity\r
298 //\r
299 Status = ScsiDiskDetectMedia (ScsiDiskDevice, MustReadCapacity, &Temp);\r
300 if (!EFI_ERROR (Status)) {\r
301 //\r
302 // Determine if Block IO should be produced on this controller handle\r
303 //\r
304 if (DetermineInstallBlockIo(Controller)) {\r
305 InitializeInstallDiskInfo(ScsiDiskDevice, Controller);\r
306 Status = gBS->InstallMultipleProtocolInterfaces (\r
307 &Controller,\r
308 &gEfiBlockIoProtocolGuid,\r
309 &ScsiDiskDevice->BlkIo,\r
310 &gEfiDiskInfoProtocolGuid,\r
311 &ScsiDiskDevice->DiskInfo,\r
312 NULL\r
313 );\r
314 if (!EFI_ERROR(Status)) {\r
315 ScsiDiskDevice->ControllerNameTable = NULL;\r
316 AddUnicodeString2 (\r
317 "eng",\r
318 gScsiDiskComponentName.SupportedLanguages,\r
319 &ScsiDiskDevice->ControllerNameTable,\r
320 L"SCSI Disk Device",\r
321 TRUE\r
322 );\r
323 AddUnicodeString2 (\r
324 "en",\r
325 gScsiDiskComponentName2.SupportedLanguages,\r
326 &ScsiDiskDevice->ControllerNameTable,\r
327 L"SCSI Disk Device",\r
328 FALSE\r
329 );\r
330 return EFI_SUCCESS;\r
331 }\r
332 } \r
333 }\r
334\r
335 gBS->FreePool (ScsiDiskDevice->SenseData);\r
336 gBS->FreePool (ScsiDiskDevice);\r
337 gBS->CloseProtocol (\r
338 Controller,\r
339 &gEfiScsiIoProtocolGuid,\r
340 This->DriverBindingHandle,\r
341 Controller\r
342 );\r
343 return Status;\r
344 \r
345}\r
346\r
347\r
348/**\r
349 Stop this driver on ControllerHandle.\r
350\r
351 This service is called by the EFI boot service DisconnectController().\r
352 In order to make drivers as small as possible, there are a few calling\r
353 restrictions for this service. DisconnectController() must follow these\r
354 calling restrictions. If any other agent wishes to call Stop() it must\r
355 also follow these calling restrictions.\r
356 \r
357 @param This Protocol instance pointer.\r
358 @param ControllerHandle Handle of device to stop driver on\r
359 @param NumberOfChildren Number of Handles in ChildHandleBuffer. If number of\r
360 children is zero stop the entire bus driver.\r
361 @param ChildHandleBuffer List of Child Handles to Stop.\r
362\r
363 @retval EFI_SUCCESS This driver is removed ControllerHandle\r
364 @retval other This driver was not removed from this device\r
365\r
366**/\r
367EFI_STATUS\r
368EFIAPI\r
369ScsiDiskDriverBindingStop (\r
370 IN EFI_DRIVER_BINDING_PROTOCOL *This,\r
371 IN EFI_HANDLE Controller,\r
372 IN UINTN NumberOfChildren,\r
373 IN EFI_HANDLE *ChildHandleBuffer OPTIONAL\r
374 )\r
375{\r
376 EFI_BLOCK_IO_PROTOCOL *BlkIo;\r
377 SCSI_DISK_DEV *ScsiDiskDevice;\r
378 EFI_STATUS Status;\r
379\r
380 Status = gBS->OpenProtocol (\r
381 Controller,\r
382 &gEfiBlockIoProtocolGuid,\r
383 (VOID **) &BlkIo,\r
384 This->DriverBindingHandle,\r
385 Controller,\r
386 EFI_OPEN_PROTOCOL_GET_PROTOCOL\r
387 );\r
388 if (EFI_ERROR (Status)) {\r
389 return Status;\r
390 }\r
391\r
392 ScsiDiskDevice = SCSI_DISK_DEV_FROM_THIS (BlkIo);\r
393 Status = gBS->UninstallMultipleProtocolInterfaces (\r
394 Controller,\r
395 &gEfiBlockIoProtocolGuid,\r
396 &ScsiDiskDevice->BlkIo,\r
397 &gEfiDiskInfoProtocolGuid,\r
398 &ScsiDiskDevice->DiskInfo,\r
399 NULL\r
400 );\r
401 if (!EFI_ERROR (Status)) {\r
402 gBS->CloseProtocol (\r
403 Controller,\r
404 &gEfiScsiIoProtocolGuid,\r
405 This->DriverBindingHandle,\r
406 Controller\r
407 );\r
408\r
409 ReleaseScsiDiskDeviceResources (ScsiDiskDevice);\r
410\r
411 return EFI_SUCCESS;\r
412 }\r
413 //\r
414 // errors met\r
415 //\r
416 return Status;\r
417}\r
418\r
419/**\r
420 Reset SCSI Disk.\r
421\r
422\r
423 @param This The pointer of EFI_BLOCK_IO_PROTOCOL\r
424 @param ExtendedVerification The flag about if extend verificate\r
425\r
426 @retval EFI_SUCCESS The device was reset.\r
427 @retval EFI_DEVICE_ERROR The device is not functioning properly and could\r
428 not be reset.\r
429 @return EFI_STATUS is returned from EFI_SCSI_IO_PROTOCOL.ResetDevice().\r
430\r
431**/\r
432EFI_STATUS\r
433EFIAPI\r
434ScsiDiskReset (\r
435 IN EFI_BLOCK_IO_PROTOCOL *This,\r
436 IN BOOLEAN ExtendedVerification\r
437 )\r
438{\r
439 EFI_TPL OldTpl;\r
440 SCSI_DISK_DEV *ScsiDiskDevice;\r
441 EFI_STATUS Status;\r
442\r
443 OldTpl = gBS->RaiseTPL (TPL_CALLBACK);\r
444\r
445 ScsiDiskDevice = SCSI_DISK_DEV_FROM_THIS (This);\r
446\r
447 Status = ScsiDiskDevice->ScsiIo->ResetDevice (ScsiDiskDevice->ScsiIo);\r
448\r
449 if (EFI_ERROR (Status)) {\r
450 Status = EFI_DEVICE_ERROR;\r
451 goto Done;\r
452 }\r
453\r
454 if (!ExtendedVerification) {\r
455 goto Done;\r
456 }\r
457\r
458 Status = ScsiDiskDevice->ScsiIo->ResetBus (ScsiDiskDevice->ScsiIo);\r
459\r
460 if (EFI_ERROR (Status)) {\r
461 Status = EFI_DEVICE_ERROR;\r
462 goto Done;\r
463 }\r
464\r
465Done:\r
466 gBS->RestoreTPL (OldTpl);\r
467 return Status;\r
468}\r
469\r
470/**\r
471 The function is to Read Block from SCSI Disk.\r
472\r
473 @param This The pointer of EFI_BLOCK_IO_PROTOCOL.\r
474 @param MediaId The Id of Media detected\r
475 @param Lba The logic block address\r
476 @param BufferSize The size of Buffer\r
477 @param Buffer The buffer to fill the read out data\r
478\r
479 @retval EFI_SUCCESS Successfully to read out block.\r
480 @retval EFI_DEVICE_ERROR Fail to detect media.\r
481 @retval EFI_NO_MEDIA Media is not present.\r
482 @retval EFI_MEDIA_CHANGED Media has changed.\r
483 @retval EFI_BAD_BUFFER_SIZE The Buffer was not a multiple of the block size of the device.\r
484 @retval EFI_INVALID_PARAMETER Invalid parameter passed in.\r
485\r
486**/\r
487EFI_STATUS\r
488EFIAPI\r
489ScsiDiskReadBlocks (\r
490 IN EFI_BLOCK_IO_PROTOCOL *This,\r
491 IN UINT32 MediaId,\r
492 IN EFI_LBA Lba,\r
493 IN UINTN BufferSize,\r
494 OUT VOID *Buffer\r
495 )\r
496{\r
497 SCSI_DISK_DEV *ScsiDiskDevice;\r
498 EFI_BLOCK_IO_MEDIA *Media;\r
499 EFI_STATUS Status;\r
500 UINTN BlockSize;\r
501 UINTN NumberOfBlocks;\r
502 BOOLEAN MediaChange;\r
503 EFI_TPL OldTpl;\r
504\r
505 MediaChange = FALSE;\r
506 OldTpl = gBS->RaiseTPL (TPL_CALLBACK);\r
507 ScsiDiskDevice = SCSI_DISK_DEV_FROM_THIS (This);\r
508\r
509 if (!IS_DEVICE_FIXED(ScsiDiskDevice)) {\r
510\r
511 Status = ScsiDiskDetectMedia (ScsiDiskDevice, FALSE, &MediaChange);\r
512 if (EFI_ERROR (Status)) {\r
513 Status = EFI_DEVICE_ERROR;\r
514 goto Done;\r
515 }\r
516\r
517 if (MediaChange) {\r
518 gBS->ReinstallProtocolInterface (\r
519 ScsiDiskDevice->Handle,\r
520 &gEfiBlockIoProtocolGuid,\r
521 &ScsiDiskDevice->BlkIo,\r
522 &ScsiDiskDevice->BlkIo\r
523 );\r
524 Status = EFI_MEDIA_CHANGED;\r
525 goto Done;\r
526 }\r
527 }\r
528 //\r
529 // Get the intrinsic block size\r
530 //\r
531 Media = ScsiDiskDevice->BlkIo.Media;\r
532 BlockSize = Media->BlockSize;\r
533\r
534 NumberOfBlocks = BufferSize / BlockSize;\r
535\r
536 if (!(Media->MediaPresent)) {\r
537 Status = EFI_NO_MEDIA;\r
538 goto Done;\r
539 }\r
540\r
541 if (MediaId != Media->MediaId) {\r
542 Status = EFI_MEDIA_CHANGED;\r
543 goto Done;\r
544 }\r
545\r
546 if (Buffer == NULL) {\r
547 Status = EFI_INVALID_PARAMETER;\r
548 goto Done;\r
549 }\r
550\r
551 if (BufferSize == 0) {\r
552 Status = EFI_SUCCESS;\r
553 goto Done;\r
554 }\r
555\r
556 if (BufferSize % BlockSize != 0) {\r
557 Status = EFI_BAD_BUFFER_SIZE;\r
558 goto Done;\r
559 }\r
560\r
561 if (Lba > Media->LastBlock) {\r
562 Status = EFI_INVALID_PARAMETER;\r
563 goto Done;\r
564 }\r
565\r
566 if ((Lba + NumberOfBlocks - 1) > Media->LastBlock) {\r
567 Status = EFI_INVALID_PARAMETER;\r
568 goto Done;\r
569 }\r
570\r
571 if ((Media->IoAlign > 1) && (((UINTN) Buffer & (Media->IoAlign - 1)) != 0)) {\r
572 Status = EFI_INVALID_PARAMETER;\r
573 goto Done;\r
574 }\r
575\r
576 //\r
577 // If all the parameters are valid, then perform read sectors command\r
578 // to transfer data from device to host.\r
579 //\r
580 Status = ScsiDiskReadSectors (ScsiDiskDevice, Buffer, Lba, NumberOfBlocks);\r
581\r
582Done:\r
583 gBS->RestoreTPL (OldTpl);\r
584 return Status;\r
585}\r
586\r
587/**\r
588 The function is to Write Block to SCSI Disk.\r
589\r
590 @param This The pointer of EFI_BLOCK_IO_PROTOCOL\r
591 @param MediaId The Id of Media detected\r
592 @param Lba The logic block address\r
593 @param BufferSize The size of Buffer\r
594 @param Buffer The buffer to fill the read out data\r
595\r
596 @retval EFI_SUCCESS Successfully to read out block.\r
597 @retval EFI_WRITE_PROTECTED The device can not be written to.\r
598 @retval EFI_DEVICE_ERROR Fail to detect media.\r
599 @retval EFI_NO_MEDIA Media is not present.\r
600 @retval EFI_MEDIA_CHNAGED Media has changed.\r
601 @retval EFI_BAD_BUFFER_SIZE The Buffer was not a multiple of the block size of the device.\r
602 @retval EFI_INVALID_PARAMETER Invalid parameter passed in.\r
603\r
604**/\r
605EFI_STATUS\r
606EFIAPI\r
607ScsiDiskWriteBlocks (\r
608 IN EFI_BLOCK_IO_PROTOCOL *This,\r
609 IN UINT32 MediaId,\r
610 IN EFI_LBA Lba,\r
611 IN UINTN BufferSize,\r
612 IN VOID *Buffer\r
613 )\r
614{\r
615 SCSI_DISK_DEV *ScsiDiskDevice;\r
616 EFI_BLOCK_IO_MEDIA *Media;\r
617 EFI_STATUS Status;\r
618 UINTN BlockSize;\r
619 UINTN NumberOfBlocks;\r
620 BOOLEAN MediaChange;\r
621 EFI_TPL OldTpl;\r
622\r
623 MediaChange = FALSE;\r
624 OldTpl = gBS->RaiseTPL (TPL_CALLBACK);\r
625 ScsiDiskDevice = SCSI_DISK_DEV_FROM_THIS (This);\r
626\r
627 if (!IS_DEVICE_FIXED(ScsiDiskDevice)) {\r
628\r
629 Status = ScsiDiskDetectMedia (ScsiDiskDevice, FALSE, &MediaChange);\r
630 if (EFI_ERROR (Status)) {\r
631 Status = EFI_DEVICE_ERROR;\r
632 goto Done;\r
633 }\r
634\r
635 if (MediaChange) {\r
636 gBS->ReinstallProtocolInterface (\r
637 ScsiDiskDevice->Handle,\r
638 &gEfiBlockIoProtocolGuid,\r
639 &ScsiDiskDevice->BlkIo,\r
640 &ScsiDiskDevice->BlkIo\r
641 );\r
642 Status = EFI_MEDIA_CHANGED;\r
643 goto Done;\r
644 }\r
645 }\r
646 //\r
647 // Get the intrinsic block size\r
648 //\r
649 Media = ScsiDiskDevice->BlkIo.Media;\r
650 BlockSize = Media->BlockSize;\r
651\r
652 NumberOfBlocks = BufferSize / BlockSize;\r
653\r
654 if (!(Media->MediaPresent)) {\r
655 Status = EFI_NO_MEDIA;\r
656 goto Done;\r
657 }\r
658\r
659 if (MediaId != Media->MediaId) {\r
660 Status = EFI_MEDIA_CHANGED;\r
661 goto Done;\r
662 }\r
663\r
664 if (BufferSize == 0) {\r
665 Status = EFI_SUCCESS;\r
666 goto Done;\r
667 }\r
668\r
669 if (Buffer == NULL) {\r
670 Status = EFI_INVALID_PARAMETER;\r
671 goto Done;\r
672 }\r
673\r
674 if (BufferSize % BlockSize != 0) {\r
675 Status = EFI_BAD_BUFFER_SIZE;\r
676 goto Done;\r
677 }\r
678\r
679 if (Lba > Media->LastBlock) {\r
680 Status = EFI_INVALID_PARAMETER;\r
681 goto Done;\r
682 }\r
683\r
684 if ((Lba + NumberOfBlocks - 1) > Media->LastBlock) {\r
685 Status = EFI_INVALID_PARAMETER;\r
686 goto Done;\r
687 }\r
688\r
689 if ((Media->IoAlign > 1) && (((UINTN) Buffer & (Media->IoAlign - 1)) != 0)) {\r
690 Status = EFI_INVALID_PARAMETER;\r
691 goto Done;\r
692 }\r
693 //\r
694 // if all the parameters are valid, then perform read sectors command\r
695 // to transfer data from device to host.\r
696 //\r
697 Status = ScsiDiskWriteSectors (ScsiDiskDevice, Buffer, Lba, NumberOfBlocks);\r
698\r
699Done:\r
700 gBS->RestoreTPL (OldTpl);\r
701 return Status;\r
702}\r
703\r
704/**\r
705 Flush Block to Disk.\r
706\r
707 EFI_SUCCESS is returned directly.\r
708\r
709 @param This The pointer of EFI_BLOCK_IO_PROTOCOL\r
710\r
711 @retval EFI_SUCCESS All outstanding data was written to the device\r
712\r
713**/\r
714EFI_STATUS\r
715EFIAPI\r
716ScsiDiskFlushBlocks (\r
717 IN EFI_BLOCK_IO_PROTOCOL *This\r
718 )\r
719{\r
720 //\r
721 // return directly\r
722 //\r
723 return EFI_SUCCESS;\r
724}\r
725\r
726\r
727/**\r
728 Detect Device and read out capacity ,if error occurs, parse the sense key.\r
729\r
730 @param ScsiDiskDevice The pointer of SCSI_DISK_DEV\r
731 @param MustReadCapacity The flag about reading device capacity\r
732 @param MediaChange The pointer of flag indicates if media has changed \r
733\r
734 @retval EFI_DEVICE_ERROR Indicates that error occurs\r
735 @retval EFI_SUCCESS Successfully to detect media\r
736\r
737**/\r
738EFI_STATUS\r
739ScsiDiskDetectMedia (\r
740 IN SCSI_DISK_DEV *ScsiDiskDevice,\r
741 IN BOOLEAN MustReadCapacity,\r
742 OUT BOOLEAN *MediaChange\r
743 )\r
744{\r
745 EFI_STATUS Status;\r
746 EFI_SCSI_SENSE_DATA *SenseData;\r
747 UINTN NumberOfSenseKeys;\r
748 BOOLEAN NeedRetry;\r
749 BOOLEAN NeedReadCapacity;\r
750 UINT8 Retry;\r
751 UINT8 MaxRetry;\r
752 EFI_BLOCK_IO_MEDIA OldMedia;\r
753 UINTN Action;\r
754 EFI_EVENT TimeoutEvt;\r
755\r
756 Status = EFI_SUCCESS;\r
757 SenseData = NULL;\r
758 NumberOfSenseKeys = 0;\r
759 Retry = 0;\r
760 MaxRetry = 3;\r
761 Action = ACTION_NO_ACTION;\r
762 NeedReadCapacity = FALSE;\r
763 *MediaChange = FALSE;\r
764 TimeoutEvt = NULL;\r
765\r
766 CopyMem (&OldMedia, ScsiDiskDevice->BlkIo.Media, sizeof (OldMedia));\r
767\r
768 Status = gBS->CreateEvent (\r
769 EVT_TIMER,\r
770 TPL_CALLBACK,\r
771 NULL,\r
772 NULL,\r
773 &TimeoutEvt\r
774 );\r
775 if (EFI_ERROR (Status)) {\r
776 return Status;\r
777 }\r
778\r
779 Status = gBS->SetTimer (TimeoutEvt, TimerRelative, EFI_TIMER_PERIOD_SECONDS(120));\r
780 if (EFI_ERROR (Status)) {\r
781 goto EXIT;\r
782 }\r
783\r
784 //\r
785 // Sending Test_Unit cmd to poll device status.\r
786 // If the sense data shows the drive is not ready or reset before, we need poll the device status again.\r
787 // We limit the upper boundary to 120 seconds.\r
788 //\r
789 while (EFI_ERROR (gBS->CheckEvent (TimeoutEvt))) {\r
790 Status = ScsiDiskTestUnitReady (\r
791 ScsiDiskDevice,\r
792 &NeedRetry,\r
793 &SenseData,\r
794 &NumberOfSenseKeys\r
795 );\r
796 if (!EFI_ERROR (Status)) {\r
797 Status = DetectMediaParsingSenseKeys (\r
798 ScsiDiskDevice,\r
799 SenseData,\r
800 NumberOfSenseKeys,\r
801 &Action\r
802 );\r
803 if (EFI_ERROR (Status)) {\r
804 goto EXIT;\r
805 } else if (Action == ACTION_RETRY_COMMAND_LATER) {\r
806 continue;\r
807 } else {\r
808 break;\r
809 }\r
810 } else {\r
811 Retry++;\r
812 if (!NeedRetry || (Retry >= MaxRetry)) {\r
813 goto EXIT;\r
814 }\r
815 }\r
816 }\r
817\r
818 if (EFI_ERROR (Status)) {\r
819 goto EXIT;\r
820 }\r
821\r
822 //\r
823 // ACTION_NO_ACTION: need not read capacity\r
824 // other action code: need read capacity\r
825 //\r
826 if (Action == ACTION_READ_CAPACITY) {\r
827 NeedReadCapacity = TRUE;\r
828 }\r
829\r
830 //\r
831 // either NeedReadCapacity is TRUE, or MustReadCapacity is TRUE,\r
832 // retrieve capacity via Read Capacity command\r
833 //\r
834 if (NeedReadCapacity || MustReadCapacity) {\r
835 //\r
836 // retrieve media information\r
837 //\r
838 for (Retry = 0; Retry < MaxRetry; Retry++) {\r
839 Status = ScsiDiskReadCapacity (\r
840 ScsiDiskDevice,\r
841 &NeedRetry,\r
842 &SenseData,\r
843 &NumberOfSenseKeys\r
844 );\r
845 if (!EFI_ERROR (Status)) {\r
846 //\r
847 // analyze sense key to action\r
848 //\r
849 Status = DetectMediaParsingSenseKeys (\r
850 ScsiDiskDevice,\r
851 SenseData,\r
852 NumberOfSenseKeys,\r
853 &Action\r
854 );\r
855 if (EFI_ERROR (Status)) {\r
856 //\r
857 // if Status is error, it may indicate crisis error,\r
858 // so return without retry.\r
859 //\r
860 goto EXIT;\r
861 } else if (Action == ACTION_RETRY_COMMAND_LATER) {\r
862 Retry = 0;\r
863 continue;\r
864 } else {\r
865 break;\r
866 }\r
867 } else { \r
868 Retry++;\r
869 if (!NeedRetry || (Retry >= MaxRetry)) {\r
870 goto EXIT;\r
871 }\r
872 }\r
873 }\r
874\r
875 if (EFI_ERROR (Status)) {\r
876 goto EXIT;\r
877 }\r
878 }\r
879\r
880 if (ScsiDiskDevice->BlkIo.Media->MediaId != OldMedia.MediaId) {\r
881 //\r
882 // Media change information got from the device\r
883 //\r
884 *MediaChange = TRUE;\r
885 }\r
886\r
887 if (ScsiDiskDevice->BlkIo.Media->ReadOnly != OldMedia.ReadOnly) {\r
888 *MediaChange = TRUE;\r
889 ScsiDiskDevice->BlkIo.Media->MediaId += 1;\r
890 }\r
891\r
892 if (ScsiDiskDevice->BlkIo.Media->BlockSize != OldMedia.BlockSize) {\r
893 *MediaChange = TRUE;\r
894 ScsiDiskDevice->BlkIo.Media->MediaId += 1;\r
895 }\r
896\r
897 if (ScsiDiskDevice->BlkIo.Media->LastBlock != OldMedia.LastBlock) {\r
898 *MediaChange = TRUE;\r
899 ScsiDiskDevice->BlkIo.Media->MediaId += 1;\r
900 }\r
901\r
902 if (ScsiDiskDevice->BlkIo.Media->MediaPresent != OldMedia.MediaPresent) {\r
903 if (ScsiDiskDevice->BlkIo.Media->MediaPresent) {\r
904 //\r
905 // when change from no media to media present, reset the MediaId to 1.\r
906 //\r
907 ScsiDiskDevice->BlkIo.Media->MediaId = 1;\r
908 } else {\r
909 //\r
910 // when no media, reset the MediaId to zero.\r
911 //\r
912 ScsiDiskDevice->BlkIo.Media->MediaId = 0;\r
913 }\r
914\r
915 *MediaChange = TRUE;\r
916 }\r
917\r
918EXIT:\r
919 if (TimeoutEvt != NULL) {\r
920 gBS->CloseEvent (TimeoutEvt);\r
921 }\r
922 return Status;\r
923}\r
924\r
925\r
926/**\r
927 Send out Inquiry command to Device.\r
928\r
929 @param ScsiDiskDevice The pointer of SCSI_DISK_DEV\r
930 @param NeedRetry Indicates if needs try again when error happens\r
931\r
932 @retval EFI_DEVICE_ERROR Indicates that error occurs\r
933 @retval EFI_SUCCESS Successfully to detect media\r
934\r
935**/\r
936EFI_STATUS\r
937ScsiDiskInquiryDevice (\r
938 IN OUT SCSI_DISK_DEV *ScsiDiskDevice,\r
939 OUT BOOLEAN *NeedRetry\r
940 )\r
941{\r
942 UINT32 InquiryDataLength;\r
943 UINT8 SenseDataLength;\r
944 UINT8 HostAdapterStatus;\r
945 UINT8 TargetStatus;\r
946 EFI_SCSI_SENSE_DATA *SenseDataArray;\r
947 UINTN NumberOfSenseKeys;\r
948 EFI_STATUS Status;\r
949 UINT8 MaxRetry;\r
950 UINT8 Index;\r
951 EFI_SCSI_SUPPORTED_VPD_PAGES_VPD_PAGE *SupportedVpdPages;\r
952 EFI_SCSI_BLOCK_LIMITS_VPD_PAGE *BlockLimits;\r
953 UINTN PageLength;\r
954\r
955 InquiryDataLength = sizeof (EFI_SCSI_INQUIRY_DATA);\r
956 SenseDataLength = 0;\r
957\r
958 Status = ScsiInquiryCommand (\r
959 ScsiDiskDevice->ScsiIo,\r
960 EFI_TIMER_PERIOD_SECONDS (1),\r
961 NULL,\r
962 &SenseDataLength,\r
963 &HostAdapterStatus,\r
964 &TargetStatus,\r
965 (VOID *) &(ScsiDiskDevice->InquiryData),\r
966 &InquiryDataLength,\r
967 FALSE\r
968 );\r
969 //\r
970 // no need to check HostAdapterStatus and TargetStatus\r
971 //\r
972 if ((Status == EFI_SUCCESS) || (Status == EFI_WARN_BUFFER_TOO_SMALL)) {\r
973 ParseInquiryData (ScsiDiskDevice);\r
974\r
975 if (ScsiDiskDevice->DeviceType == EFI_SCSI_TYPE_DISK) {\r
976 //\r
977 // Check whether the device supports Block Limits VPD page (0xB0)\r
978 //\r
979 SupportedVpdPages = AllocateAlignedBuffer (ScsiDiskDevice, sizeof (EFI_SCSI_SUPPORTED_VPD_PAGES_VPD_PAGE));\r
980 if (SupportedVpdPages == NULL) {\r
981 *NeedRetry = FALSE;\r
982 return EFI_DEVICE_ERROR;\r
983 }\r
984 ZeroMem (SupportedVpdPages, sizeof (EFI_SCSI_SUPPORTED_VPD_PAGES_VPD_PAGE));\r
985 InquiryDataLength = sizeof (EFI_SCSI_SUPPORTED_VPD_PAGES_VPD_PAGE);\r
986 SenseDataLength = 0;\r
987 Status = ScsiInquiryCommandEx (\r
988 ScsiDiskDevice->ScsiIo,\r
989 EFI_TIMER_PERIOD_SECONDS (1),\r
990 NULL,\r
991 &SenseDataLength,\r
992 &HostAdapterStatus,\r
993 &TargetStatus,\r
994 (VOID *) SupportedVpdPages,\r
995 &InquiryDataLength,\r
996 TRUE,\r
997 EFI_SCSI_PAGE_CODE_SUPPORTED_VPD\r
998 );\r
999 if (!EFI_ERROR (Status)) {\r
1000 PageLength = (SupportedVpdPages->PageLength2 << 8)\r
1001 | SupportedVpdPages->PageLength1;\r
1002 for (Index = 0; Index < PageLength; Index++) {\r
1003 if (SupportedVpdPages->SupportedVpdPageList[Index] == EFI_SCSI_PAGE_CODE_BLOCK_LIMITS_VPD) {\r
1004 break;\r
1005 }\r
1006 }\r
1007\r
1008 //\r
1009 // Query the Block Limits VPD page\r
1010 //\r
1011 if (Index < PageLength) {\r
1012 BlockLimits = AllocateAlignedBuffer (ScsiDiskDevice, sizeof (EFI_SCSI_BLOCK_LIMITS_VPD_PAGE));\r
1013 if (BlockLimits == NULL) {\r
1014 FreeAlignedBuffer (SupportedVpdPages, sizeof (EFI_SCSI_SUPPORTED_VPD_PAGES_VPD_PAGE));\r
1015 *NeedRetry = FALSE;\r
1016 return EFI_DEVICE_ERROR;\r
1017 }\r
1018 ZeroMem (BlockLimits, sizeof (EFI_SCSI_BLOCK_LIMITS_VPD_PAGE));\r
1019 InquiryDataLength = sizeof (EFI_SCSI_BLOCK_LIMITS_VPD_PAGE);\r
1020 SenseDataLength = 0;\r
1021 Status = ScsiInquiryCommandEx (\r
1022 ScsiDiskDevice->ScsiIo,\r
1023 EFI_TIMER_PERIOD_SECONDS (1),\r
1024 NULL,\r
1025 &SenseDataLength,\r
1026 &HostAdapterStatus,\r
1027 &TargetStatus,\r
1028 (VOID *) BlockLimits,\r
1029 &InquiryDataLength,\r
1030 TRUE,\r
1031 EFI_SCSI_PAGE_CODE_BLOCK_LIMITS_VPD\r
1032 );\r
1033 if (!EFI_ERROR (Status)) {\r
1034 ScsiDiskDevice->BlkIo.Media->OptimalTransferLengthGranularity = \r
1035 (BlockLimits->OptimalTransferLengthGranularity2 << 8) |\r
1036 BlockLimits->OptimalTransferLengthGranularity1;\r
1037 }\r
1038\r
1039 FreeAlignedBuffer (BlockLimits, sizeof (EFI_SCSI_BLOCK_LIMITS_VPD_PAGE));\r
1040 }\r
1041 }\r
1042\r
1043 FreeAlignedBuffer (SupportedVpdPages, sizeof (EFI_SCSI_SUPPORTED_VPD_PAGES_VPD_PAGE));\r
1044 }\r
1045 }\r
1046\r
1047 if (!EFI_ERROR (Status)) {\r
1048 return EFI_SUCCESS;\r
1049\r
1050 } else if (Status == EFI_NOT_READY) {\r
1051 *NeedRetry = TRUE;\r
1052 return EFI_DEVICE_ERROR;\r
1053 \r
1054 } else if ((Status == EFI_INVALID_PARAMETER) || (Status == EFI_UNSUPPORTED)) {\r
1055 *NeedRetry = FALSE;\r
1056 return EFI_DEVICE_ERROR;\r
1057 }\r
1058 //\r
1059 // go ahead to check HostAdapterStatus and TargetStatus\r
1060 // (EFI_TIMEOUT, EFI_DEVICE_ERROR)\r
1061 //\r
1062\r
1063 Status = CheckHostAdapterStatus (HostAdapterStatus);\r
1064 if ((Status == EFI_TIMEOUT) || (Status == EFI_NOT_READY)) {\r
1065 *NeedRetry = TRUE;\r
1066 return EFI_DEVICE_ERROR;\r
1067 } else if (Status == EFI_DEVICE_ERROR) {\r
1068 //\r
1069 // reset the scsi channel\r
1070 //\r
1071 ScsiDiskDevice->ScsiIo->ResetBus (ScsiDiskDevice->ScsiIo);\r
1072 *NeedRetry = FALSE;\r
1073 return EFI_DEVICE_ERROR;\r
1074 }\r
1075\r
1076 Status = CheckTargetStatus (TargetStatus);\r
1077 if (Status == EFI_NOT_READY) {\r
1078 //\r
1079 // reset the scsi device\r
1080 //\r
1081 ScsiDiskDevice->ScsiIo->ResetDevice (ScsiDiskDevice->ScsiIo);\r
1082 *NeedRetry = TRUE;\r
1083 return EFI_DEVICE_ERROR;\r
1084\r
1085 } else if (Status == EFI_DEVICE_ERROR) {\r
1086 *NeedRetry = FALSE;\r
1087 return EFI_DEVICE_ERROR;\r
1088 }\r
1089 \r
1090 //\r
1091 // if goes here, meant ScsiInquiryCommand() failed.\r
1092 // if ScsiDiskRequestSenseKeys() succeeds at last,\r
1093 // better retry ScsiInquiryCommand(). (by setting *NeedRetry = TRUE)\r
1094 //\r
1095 MaxRetry = 3;\r
1096 for (Index = 0; Index < MaxRetry; Index++) {\r
1097 Status = ScsiDiskRequestSenseKeys (\r
1098 ScsiDiskDevice,\r
1099 NeedRetry,\r
1100 &SenseDataArray,\r
1101 &NumberOfSenseKeys,\r
1102 TRUE\r
1103 );\r
1104 if (!EFI_ERROR (Status)) {\r
1105 *NeedRetry = TRUE;\r
1106 return EFI_DEVICE_ERROR;\r
1107 }\r
1108\r
1109 if (!*NeedRetry) {\r
1110 return EFI_DEVICE_ERROR;\r
1111 }\r
1112 }\r
1113 //\r
1114 // ScsiDiskRequestSenseKeys() failed after several rounds of retry.\r
1115 // set *NeedRetry = FALSE to avoid the outside caller try again.\r
1116 //\r
1117 *NeedRetry = FALSE;\r
1118 return EFI_DEVICE_ERROR;\r
1119}\r
1120\r
1121/**\r
1122 To test device.\r
1123\r
1124 When Test Unit Ready command succeeds, retrieve Sense Keys via Request Sense;\r
1125 When Test Unit Ready command encounters any error caused by host adapter or\r
1126 target, return error without retrieving Sense Keys.\r
1127\r
1128 @param ScsiDiskDevice The pointer of SCSI_DISK_DEV\r
1129 @param NeedRetry The pointer of flag indicates try again\r
1130 @param SenseDataArray The pointer of an array of sense data\r
1131 @param NumberOfSenseKeys The pointer of the number of sense data array\r
1132\r
1133 @retval EFI_DEVICE_ERROR Indicates that error occurs\r
1134 @retval EFI_SUCCESS Successfully to test unit\r
1135\r
1136**/\r
1137EFI_STATUS\r
1138ScsiDiskTestUnitReady (\r
1139 IN SCSI_DISK_DEV *ScsiDiskDevice,\r
1140 OUT BOOLEAN *NeedRetry,\r
1141 OUT EFI_SCSI_SENSE_DATA **SenseDataArray,\r
1142 OUT UINTN *NumberOfSenseKeys\r
1143 )\r
1144{\r
1145 EFI_STATUS Status;\r
1146 UINT8 SenseDataLength;\r
1147 UINT8 HostAdapterStatus;\r
1148 UINT8 TargetStatus;\r
1149 UINT8 Index;\r
1150 UINT8 MaxRetry;\r
1151\r
1152 SenseDataLength = 0;\r
1153 *NumberOfSenseKeys = 0;\r
1154\r
1155 //\r
1156 // Parameter 3 and 4: do not require sense data, retrieve it when needed.\r
1157 //\r
1158 Status = ScsiTestUnitReadyCommand (\r
1159 ScsiDiskDevice->ScsiIo,\r
1160 EFI_TIMER_PERIOD_SECONDS (1),\r
1161 NULL,\r
1162 &SenseDataLength,\r
1163 &HostAdapterStatus,\r
1164 &TargetStatus\r
1165 );\r
1166 //\r
1167 // no need to check HostAdapterStatus and TargetStatus\r
1168 //\r
1169 if (Status == EFI_NOT_READY) {\r
1170 *NeedRetry = TRUE;\r
1171 return EFI_DEVICE_ERROR;\r
1172\r
1173 } else if ((Status == EFI_INVALID_PARAMETER) || (Status == EFI_UNSUPPORTED)) {\r
1174 *NeedRetry = FALSE;\r
1175 return EFI_DEVICE_ERROR;\r
1176 }\r
1177 //\r
1178 // go ahead to check HostAdapterStatus and TargetStatus(in case of EFI_DEVICE_ERROR)\r
1179 //\r
1180\r
1181 Status = CheckHostAdapterStatus (HostAdapterStatus);\r
1182 if ((Status == EFI_TIMEOUT) || (Status == EFI_NOT_READY)) {\r
1183 *NeedRetry = TRUE;\r
1184 return EFI_DEVICE_ERROR;\r
1185\r
1186 } else if (Status == EFI_DEVICE_ERROR) {\r
1187 //\r
1188 // reset the scsi channel\r
1189 //\r
1190 ScsiDiskDevice->ScsiIo->ResetBus (ScsiDiskDevice->ScsiIo);\r
1191 *NeedRetry = FALSE;\r
1192 return EFI_DEVICE_ERROR;\r
1193 }\r
1194\r
1195 Status = CheckTargetStatus (TargetStatus);\r
1196 if (Status == EFI_NOT_READY) {\r
1197 //\r
1198 // reset the scsi device\r
1199 //\r
1200 ScsiDiskDevice->ScsiIo->ResetDevice (ScsiDiskDevice->ScsiIo);\r
1201 *NeedRetry = TRUE;\r
1202 return EFI_DEVICE_ERROR;\r
1203\r
1204 } else if (Status == EFI_DEVICE_ERROR) {\r
1205 *NeedRetry = FALSE;\r
1206 return EFI_DEVICE_ERROR;\r
1207 }\r
1208\r
1209 MaxRetry = 3;\r
1210 for (Index = 0; Index < MaxRetry; Index++) {\r
1211 Status = ScsiDiskRequestSenseKeys (\r
1212 ScsiDiskDevice,\r
1213 NeedRetry,\r
1214 SenseDataArray,\r
1215 NumberOfSenseKeys,\r
1216 FALSE\r
1217 );\r
1218 if (!EFI_ERROR (Status)) {\r
1219 return EFI_SUCCESS;\r
1220 }\r
1221\r
1222 if (!*NeedRetry) {\r
1223 return EFI_DEVICE_ERROR;\r
1224 }\r
1225 }\r
1226 //\r
1227 // ScsiDiskRequestSenseKeys() failed after several rounds of retry.\r
1228 // set *NeedRetry = FALSE to avoid the outside caller try again.\r
1229 //\r
1230 *NeedRetry = FALSE;\r
1231 return EFI_DEVICE_ERROR;\r
1232}\r
1233\r
1234/**\r
1235 Parsing Sense Keys which got from request sense command.\r
1236\r
1237 @param ScsiDiskDevice The pointer of SCSI_DISK_DEV\r
1238 @param SenseData The pointer of EFI_SCSI_SENSE_DATA\r
1239 @param NumberOfSenseKeys The number of sense key \r
1240 @param Action The pointer of action which indicates what is need to do next\r
1241\r
1242 @retval EFI_DEVICE_ERROR Indicates that error occurs\r
1243 @retval EFI_SUCCESS Successfully to complete the parsing\r
1244\r
1245**/\r
1246EFI_STATUS\r
1247DetectMediaParsingSenseKeys (\r
1248 OUT SCSI_DISK_DEV *ScsiDiskDevice,\r
1249 IN EFI_SCSI_SENSE_DATA *SenseData,\r
1250 IN UINTN NumberOfSenseKeys,\r
1251 OUT UINTN *Action\r
1252 )\r
1253{\r
1254 BOOLEAN RetryLater;\r
1255\r
1256 //\r
1257 // Default is to read capacity, unless..\r
1258 //\r
1259 *Action = ACTION_READ_CAPACITY;\r
1260\r
1261 if (NumberOfSenseKeys == 0) {\r
1262 if (ScsiDiskDevice->BlkIo.Media->MediaPresent == TRUE) {\r
1263 *Action = ACTION_NO_ACTION;\r
1264 }\r
1265 return EFI_SUCCESS;\r
1266 }\r
1267\r
1268 if (!ScsiDiskHaveSenseKey (SenseData, NumberOfSenseKeys)) {\r
1269 //\r
1270 // No Sense Key returned from last submitted command\r
1271 //\r
1272 if (ScsiDiskDevice->BlkIo.Media->MediaPresent == TRUE) {\r
1273 *Action = ACTION_NO_ACTION;\r
1274 }\r
1275 return EFI_SUCCESS;\r
1276 }\r
1277\r
1278 if (ScsiDiskIsNoMedia (SenseData, NumberOfSenseKeys)) {\r
1279 ScsiDiskDevice->BlkIo.Media->MediaPresent = FALSE;\r
1280 ScsiDiskDevice->BlkIo.Media->LastBlock = 0;\r
1281 *Action = ACTION_NO_ACTION;\r
1282 return EFI_SUCCESS;\r
1283 }\r
1284\r
1285 if (ScsiDiskIsMediaChange (SenseData, NumberOfSenseKeys)) {\r
1286 ScsiDiskDevice->BlkIo.Media->MediaId++;\r
1287 return EFI_SUCCESS;\r
1288 }\r
1289\r
1290 if (ScsiDiskIsResetBefore (SenseData, NumberOfSenseKeys)) {\r
1291 *Action = ACTION_RETRY_COMMAND_LATER;\r
1292 return EFI_SUCCESS;\r
1293 }\r
1294\r
1295 if (ScsiDiskIsMediaError (SenseData, NumberOfSenseKeys)) {\r
1296 ScsiDiskDevice->BlkIo.Media->MediaPresent = FALSE;\r
1297 ScsiDiskDevice->BlkIo.Media->LastBlock = 0;\r
1298 *Action = ACTION_NO_ACTION;\r
1299 return EFI_DEVICE_ERROR;\r
1300 }\r
1301\r
1302 if (ScsiDiskIsHardwareError (SenseData, NumberOfSenseKeys)) {\r
1303 *Action = ACTION_NO_ACTION;\r
1304 return EFI_DEVICE_ERROR;\r
1305 }\r
1306\r
1307 if (!ScsiDiskIsDriveReady (SenseData, NumberOfSenseKeys, &RetryLater)) {\r
1308 if (RetryLater) {\r
1309 *Action = ACTION_RETRY_COMMAND_LATER;\r
1310 return EFI_SUCCESS;\r
1311 }\r
1312 *Action = ACTION_NO_ACTION;\r
1313 return EFI_DEVICE_ERROR;\r
1314 }\r
1315\r
1316 return EFI_SUCCESS;\r
1317}\r
1318\r
1319\r
1320/**\r
1321 Send read capacity command to device and get the device parameter.\r
1322\r
1323 @param ScsiDiskDevice The pointer of SCSI_DISK_DEV\r
1324 @param NeedRetry The pointer of flag indicates if need a retry\r
1325 @param SenseDataArray The pointer of an array of sense data\r
1326 @param NumberOfSenseKeys The number of sense key\r
1327\r
1328 @retval EFI_DEVICE_ERROR Indicates that error occurs\r
1329 @retval EFI_SUCCESS Successfully to read capacity or sense data is received.\r
1330\r
1331**/\r
1332EFI_STATUS\r
1333ScsiDiskReadCapacity (\r
1334 IN OUT SCSI_DISK_DEV *ScsiDiskDevice,\r
1335 OUT BOOLEAN *NeedRetry,\r
1336 OUT EFI_SCSI_SENSE_DATA **SenseDataArray,\r
1337 OUT UINTN *NumberOfSenseKeys\r
1338 )\r
1339{\r
1340 UINT8 HostAdapterStatus;\r
1341 UINT8 TargetStatus;\r
1342 EFI_STATUS CommandStatus;\r
1343 EFI_STATUS Status;\r
1344 UINT8 Index;\r
1345 UINT8 MaxRetry;\r
1346 UINT8 SenseDataLength;\r
1347 UINT32 DataLength10;\r
1348 UINT32 DataLength16;\r
1349 EFI_SCSI_DISK_CAPACITY_DATA *CapacityData10;\r
1350 EFI_SCSI_DISK_CAPACITY_DATA16 *CapacityData16;\r
1351\r
1352 CapacityData10 = AllocateAlignedBuffer (ScsiDiskDevice, sizeof (EFI_SCSI_DISK_CAPACITY_DATA));\r
1353 if (CapacityData10 == NULL) {\r
1354 *NeedRetry = FALSE;\r
1355 return EFI_DEVICE_ERROR;\r
1356 }\r
1357 CapacityData16 = AllocateAlignedBuffer (ScsiDiskDevice, sizeof (EFI_SCSI_DISK_CAPACITY_DATA16));\r
1358 if (CapacityData16 == NULL) {\r
1359 FreeAlignedBuffer (CapacityData10, sizeof (EFI_SCSI_DISK_CAPACITY_DATA));\r
1360 *NeedRetry = FALSE;\r
1361 return EFI_DEVICE_ERROR;\r
1362 }\r
1363\r
1364 SenseDataLength = 0;\r
1365 DataLength10 = sizeof (EFI_SCSI_DISK_CAPACITY_DATA);\r
1366 DataLength16 = sizeof (EFI_SCSI_DISK_CAPACITY_DATA16);\r
1367 ZeroMem (CapacityData10, sizeof (EFI_SCSI_DISK_CAPACITY_DATA));\r
1368 ZeroMem (CapacityData16, sizeof (EFI_SCSI_DISK_CAPACITY_DATA16));\r
1369\r
1370 *NumberOfSenseKeys = 0;\r
1371 *NeedRetry = FALSE;\r
1372\r
1373 //\r
1374 // submit Read Capacity(10) Command. If it returns capacity of FFFFFFFFh, \r
1375 // 16 byte command should be used to access large hard disk >2TB\r
1376 //\r
1377 CommandStatus = ScsiReadCapacityCommand (\r
1378 ScsiDiskDevice->ScsiIo,\r
1379 EFI_TIMER_PERIOD_SECONDS(1),\r
1380 NULL,\r
1381 &SenseDataLength,\r
1382 &HostAdapterStatus,\r
1383 &TargetStatus,\r
1384 (VOID *) CapacityData10,\r
1385 &DataLength10,\r
1386 FALSE\r
1387 );\r
1388\r
1389 ScsiDiskDevice->Cdb16Byte = FALSE;\r
1390 if ((!EFI_ERROR (CommandStatus)) && (CapacityData10->LastLba3 == 0xff) && (CapacityData10->LastLba2 == 0xff) &&\r
1391 (CapacityData10->LastLba1 == 0xff) && (CapacityData10->LastLba0 == 0xff)) {\r
1392 //\r
1393 // use Read Capacity (16), Read (16) and Write (16) next when hard disk size > 2TB\r
1394 //\r
1395 ScsiDiskDevice->Cdb16Byte = TRUE;\r
1396 //\r
1397 // submit Read Capacity(16) Command to get parameter LogicalBlocksPerPhysicalBlock\r
1398 // and LowestAlignedLba\r
1399 //\r
1400 CommandStatus = ScsiReadCapacity16Command (\r
1401 ScsiDiskDevice->ScsiIo,\r
1402 EFI_TIMER_PERIOD_SECONDS (1),\r
1403 NULL,\r
1404 &SenseDataLength,\r
1405 &HostAdapterStatus,\r
1406 &TargetStatus,\r
1407 (VOID *) CapacityData16,\r
1408 &DataLength16,\r
1409 FALSE\r
1410 );\r
1411 }\r
1412\r
1413 //\r
1414 // no need to check HostAdapterStatus and TargetStatus\r
1415 //\r
1416 if (CommandStatus == EFI_SUCCESS) {\r
1417 GetMediaInfo (ScsiDiskDevice, CapacityData10, CapacityData16);\r
1418 FreeAlignedBuffer (CapacityData10, sizeof (EFI_SCSI_DISK_CAPACITY_DATA));\r
1419 FreeAlignedBuffer (CapacityData16, sizeof (EFI_SCSI_DISK_CAPACITY_DATA16));\r
1420 return EFI_SUCCESS;\r
1421 }\r
1422\r
1423 FreeAlignedBuffer (CapacityData10, sizeof (EFI_SCSI_DISK_CAPACITY_DATA));\r
1424 FreeAlignedBuffer (CapacityData16, sizeof (EFI_SCSI_DISK_CAPACITY_DATA16));\r
1425\r
1426 if (CommandStatus == EFI_NOT_READY) {\r
1427 *NeedRetry = TRUE;\r
1428 return EFI_DEVICE_ERROR;\r
1429 } else if ((CommandStatus == EFI_INVALID_PARAMETER) || (CommandStatus == EFI_UNSUPPORTED)) {\r
1430 *NeedRetry = FALSE;\r
1431 return EFI_DEVICE_ERROR;\r
1432 }\r
1433\r
1434 //\r
1435 // go ahead to check HostAdapterStatus and TargetStatus\r
1436 // (EFI_TIMEOUT, EFI_DEVICE_ERROR, EFI_WARN_BUFFER_TOO_SMALL)\r
1437 //\r
1438 \r
1439 Status = CheckHostAdapterStatus (HostAdapterStatus);\r
1440 if ((Status == EFI_TIMEOUT) || (Status == EFI_NOT_READY)) {\r
1441 *NeedRetry = TRUE;\r
1442 return EFI_DEVICE_ERROR;\r
1443 \r
1444 } else if (Status == EFI_DEVICE_ERROR) {\r
1445 //\r
1446 // reset the scsi channel\r
1447 //\r
1448 ScsiDiskDevice->ScsiIo->ResetBus (ScsiDiskDevice->ScsiIo);\r
1449 *NeedRetry = FALSE;\r
1450 return EFI_DEVICE_ERROR;\r
1451 }\r
1452\r
1453 Status = CheckTargetStatus (TargetStatus);\r
1454 if (Status == EFI_NOT_READY) {\r
1455 //\r
1456 // reset the scsi device\r
1457 //\r
1458 ScsiDiskDevice->ScsiIo->ResetDevice (ScsiDiskDevice->ScsiIo);\r
1459 *NeedRetry = TRUE;\r
1460 return EFI_DEVICE_ERROR;\r
1461\r
1462 } else if (Status == EFI_DEVICE_ERROR) {\r
1463 *NeedRetry = FALSE;\r
1464 return EFI_DEVICE_ERROR;\r
1465 }\r
1466 \r
1467 //\r
1468 // if goes here, meant ScsiReadCapacityCommand() failed.\r
1469 // if ScsiDiskRequestSenseKeys() succeeds at last,\r
1470 // better retry ScsiReadCapacityCommand(). (by setting *NeedRetry = TRUE)\r
1471 //\r
1472 MaxRetry = 3;\r
1473 for (Index = 0; Index < MaxRetry; Index++) {\r
1474\r
1475 Status = ScsiDiskRequestSenseKeys (\r
1476 ScsiDiskDevice,\r
1477 NeedRetry,\r
1478 SenseDataArray,\r
1479 NumberOfSenseKeys,\r
1480 TRUE\r
1481 );\r
1482 if (!EFI_ERROR (Status)) {\r
1483 return EFI_SUCCESS;\r
1484 }\r
1485\r
1486 if (!*NeedRetry) {\r
1487 return EFI_DEVICE_ERROR;\r
1488 }\r
1489 }\r
1490 //\r
1491 // ScsiDiskRequestSenseKeys() failed after several rounds of retry.\r
1492 // set *NeedRetry = FALSE to avoid the outside caller try again.\r
1493 //\r
1494 *NeedRetry = FALSE;\r
1495 return EFI_DEVICE_ERROR;\r
1496}\r
1497\r
1498/**\r
1499 Check the HostAdapter status and re-interpret it in EFI_STATUS.\r
1500\r
1501 @param HostAdapterStatus Host Adapter status\r
1502\r
1503 @retval EFI_SUCCESS Host adapter is OK.\r
1504 @retval EFI_TIMEOUT Timeout.\r
1505 @retval EFI_NOT_READY Adapter NOT ready.\r
1506 @retval EFI_DEVICE_ERROR Adapter device error.\r
1507\r
1508**/\r
1509EFI_STATUS\r
1510CheckHostAdapterStatus (\r
1511 IN UINT8 HostAdapterStatus\r
1512 )\r
1513{\r
1514 switch (HostAdapterStatus) {\r
1515 case EFI_EXT_SCSI_STATUS_HOST_ADAPTER_OK:\r
1516 return EFI_SUCCESS;\r
1517\r
1518 case EFI_EXT_SCSI_STATUS_HOST_ADAPTER_SELECTION_TIMEOUT:\r
1519 case EFI_EXT_SCSI_STATUS_HOST_ADAPTER_TIMEOUT:\r
1520 case EFI_EXT_SCSI_STATUS_HOST_ADAPTER_TIMEOUT_COMMAND:\r
1521 return EFI_TIMEOUT;\r
1522\r
1523 case EFI_EXT_SCSI_STATUS_HOST_ADAPTER_MESSAGE_REJECT:\r
1524 case EFI_EXT_SCSI_STATUS_HOST_ADAPTER_PARITY_ERROR:\r
1525 case EFI_EXT_SCSI_STATUS_HOST_ADAPTER_REQUEST_SENSE_FAILED:\r
1526 case EFI_EXT_SCSI_STATUS_HOST_ADAPTER_DATA_OVERRUN_UNDERRUN:\r
1527 case EFI_EXT_SCSI_STATUS_HOST_ADAPTER_BUS_RESET:\r
1528 return EFI_NOT_READY;\r
1529\r
1530 case EFI_EXT_SCSI_STATUS_HOST_ADAPTER_BUS_FREE:\r
1531 case EFI_EXT_SCSI_STATUS_HOST_ADAPTER_PHASE_ERROR:\r
1532 return EFI_DEVICE_ERROR;\r
1533\r
1534 default:\r
1535 return EFI_SUCCESS;\r
1536 }\r
1537}\r
1538\r
1539\r
1540/**\r
1541 Check the target status and re-interpret it in EFI_STATUS.\r
1542\r
1543 @param TargetStatus Target status\r
1544\r
1545 @retval EFI_NOT_READY Device is NOT ready.\r
1546 @retval EFI_DEVICE_ERROR \r
1547 @retval EFI_SUCCESS\r
1548\r
1549**/\r
1550EFI_STATUS\r
1551CheckTargetStatus (\r
1552 IN UINT8 TargetStatus\r
1553 )\r
1554{\r
1555 switch (TargetStatus) {\r
1556 case EFI_EXT_SCSI_STATUS_TARGET_GOOD:\r
1557 case EFI_EXT_SCSI_STATUS_TARGET_CHECK_CONDITION:\r
1558 case EFI_EXT_SCSI_STATUS_TARGET_CONDITION_MET:\r
1559 return EFI_SUCCESS;\r
1560\r
1561 case EFI_EXT_SCSI_STATUS_TARGET_INTERMEDIATE:\r
1562 case EFI_EXT_SCSI_STATUS_TARGET_INTERMEDIATE_CONDITION_MET:\r
1563 case EFI_EXT_SCSI_STATUS_TARGET_BUSY:\r
1564 case EFI_EXT_SCSI_STATUS_TARGET_TASK_SET_FULL:\r
1565 return EFI_NOT_READY;\r
1566\r
1567 case EFI_EXT_SCSI_STATUS_TARGET_RESERVATION_CONFLICT:\r
1568 return EFI_DEVICE_ERROR;\r
1569 break;\r
1570\r
1571 default:\r
1572 return EFI_SUCCESS;\r
1573 }\r
1574}\r
1575\r
1576\r
1577/**\r
1578 Retrieve all sense keys from the device.\r
1579\r
1580 When encountering error during the process, if retrieve sense keys before\r
1581 error encountered, it returns the sense keys with return status set to EFI_SUCCESS,\r
1582 and NeedRetry set to FALSE; otherwize, return the proper return status.\r
1583\r
1584 @param ScsiDiskDevice The pointer of SCSI_DISK_DEV\r
1585 @param NeedRetry The pointer of flag indicates if need a retry\r
1586 @param SenseDataArray The pointer of an array of sense data\r
1587 @param NumberOfSenseKeys The number of sense key\r
1588 @param AskResetIfError The flag indicates if need reset when error occurs\r
1589\r
1590 @retval EFI_DEVICE_ERROR Indicates that error occurs\r
1591 @retval EFI_SUCCESS Successfully to request sense key\r
1592\r
1593**/\r
1594EFI_STATUS\r
1595ScsiDiskRequestSenseKeys (\r
1596 IN OUT SCSI_DISK_DEV *ScsiDiskDevice,\r
1597 OUT BOOLEAN *NeedRetry,\r
1598 OUT EFI_SCSI_SENSE_DATA **SenseDataArray,\r
1599 OUT UINTN *NumberOfSenseKeys,\r
1600 IN BOOLEAN AskResetIfError\r
1601 )\r
1602{\r
1603 EFI_SCSI_SENSE_DATA *PtrSenseData;\r
1604 UINT8 SenseDataLength;\r
1605 BOOLEAN SenseReq;\r
1606 EFI_STATUS Status;\r
1607 EFI_STATUS FallStatus;\r
1608 UINT8 HostAdapterStatus;\r
1609 UINT8 TargetStatus;\r
1610\r
1611 FallStatus = EFI_SUCCESS;\r
1612 SenseDataLength = (UINT8) sizeof (EFI_SCSI_SENSE_DATA);\r
1613\r
1614 ZeroMem (\r
1615 ScsiDiskDevice->SenseData,\r
1616 sizeof (EFI_SCSI_SENSE_DATA) * (ScsiDiskDevice->SenseDataNumber)\r
1617 );\r
1618\r
1619 *NumberOfSenseKeys = 0;\r
1620 *SenseDataArray = ScsiDiskDevice->SenseData;\r
1621 PtrSenseData = ScsiDiskDevice->SenseData;\r
1622\r
1623 for (SenseReq = TRUE; SenseReq;) {\r
1624 Status = ScsiRequestSenseCommand (\r
1625 ScsiDiskDevice->ScsiIo,\r
1626 EFI_TIMER_PERIOD_SECONDS (2),\r
1627 PtrSenseData,\r
1628 &SenseDataLength,\r
1629 &HostAdapterStatus,\r
1630 &TargetStatus\r
1631 );\r
1632 if ((Status == EFI_SUCCESS) || (Status == EFI_WARN_BUFFER_TOO_SMALL)) {\r
1633 FallStatus = EFI_SUCCESS;\r
1634 \r
1635 } else if ((Status == EFI_TIMEOUT) || (Status == EFI_NOT_READY)) {\r
1636 *NeedRetry = TRUE;\r
1637 FallStatus = EFI_DEVICE_ERROR;\r
1638 \r
1639 } else if ((Status == EFI_INVALID_PARAMETER) || (Status == EFI_UNSUPPORTED)) {\r
1640 *NeedRetry = FALSE;\r
1641 FallStatus = EFI_DEVICE_ERROR;\r
1642 \r
1643 } else if (Status == EFI_DEVICE_ERROR) {\r
1644 if (AskResetIfError) {\r
1645 ScsiDiskDevice->ScsiIo->ResetDevice (ScsiDiskDevice->ScsiIo);\r
1646 }\r
1647 \r
1648 FallStatus = EFI_DEVICE_ERROR;\r
1649 }\r
1650\r
1651 if (EFI_ERROR (FallStatus)) {\r
1652 if (*NumberOfSenseKeys != 0) {\r
1653 *NeedRetry = FALSE;\r
1654 return EFI_SUCCESS;\r
1655 } else {\r
1656 return EFI_DEVICE_ERROR;\r
1657 }\r
1658 }\r
1659\r
1660 (*NumberOfSenseKeys) += 1;\r
1661\r
1662 //\r
1663 // no more sense key or number of sense keys exceeds predefined,\r
1664 // skip the loop.\r
1665 //\r
1666 if ((PtrSenseData->Sense_Key == EFI_SCSI_SK_NO_SENSE) || \r
1667 (*NumberOfSenseKeys == ScsiDiskDevice->SenseDataNumber)) {\r
1668 SenseReq = FALSE;\r
1669 }\r
1670 PtrSenseData += 1;\r
1671 }\r
1672 return EFI_SUCCESS;\r
1673}\r
1674\r
1675\r
1676/**\r
1677 Get information from media read capacity command.\r
1678\r
1679 @param ScsiDiskDevice The pointer of SCSI_DISK_DEV\r
1680 @param Capacity10 The pointer of EFI_SCSI_DISK_CAPACITY_DATA\r
1681 @param Capacity16 The pointer of EFI_SCSI_DISK_CAPACITY_DATA16\r
1682\r
1683**/\r
1684VOID\r
1685GetMediaInfo (\r
1686 IN OUT SCSI_DISK_DEV *ScsiDiskDevice,\r
1687 IN EFI_SCSI_DISK_CAPACITY_DATA *Capacity10,\r
1688 IN EFI_SCSI_DISK_CAPACITY_DATA16 *Capacity16\r
1689 )\r
1690{\r
1691 UINT8 *Ptr;\r
1692\r
1693 if (!ScsiDiskDevice->Cdb16Byte) {\r
1694 ScsiDiskDevice->BlkIo.Media->LastBlock = (Capacity10->LastLba3 << 24) |\r
1695 (Capacity10->LastLba2 << 16) |\r
1696 (Capacity10->LastLba1 << 8) |\r
1697 Capacity10->LastLba0;\r
1698 \r
1699 ScsiDiskDevice->BlkIo.Media->BlockSize = (Capacity10->BlockSize3 << 24) |\r
1700 (Capacity10->BlockSize2 << 16) | \r
1701 (Capacity10->BlockSize1 << 8) |\r
1702 Capacity10->BlockSize0;\r
1703 ScsiDiskDevice->BlkIo.Media->LowestAlignedLba = 0;\r
1704 ScsiDiskDevice->BlkIo.Media->LogicalBlocksPerPhysicalBlock = 0;\r
1705 } else {\r
1706 Ptr = (UINT8*)&ScsiDiskDevice->BlkIo.Media->LastBlock;\r
1707 *Ptr++ = Capacity16->LastLba0;\r
1708 *Ptr++ = Capacity16->LastLba1;\r
1709 *Ptr++ = Capacity16->LastLba2;\r
1710 *Ptr++ = Capacity16->LastLba3;\r
1711 *Ptr++ = Capacity16->LastLba4;\r
1712 *Ptr++ = Capacity16->LastLba5;\r
1713 *Ptr++ = Capacity16->LastLba6;\r
1714 *Ptr = Capacity16->LastLba7;\r
1715\r
1716 ScsiDiskDevice->BlkIo.Media->BlockSize = (Capacity16->BlockSize3 << 24) |\r
1717 (Capacity16->BlockSize2 << 16) | \r
1718 (Capacity16->BlockSize1 << 8) |\r
1719 Capacity16->BlockSize0;\r
1720\r
1721 ScsiDiskDevice->BlkIo.Media->LowestAlignedLba = (Capacity16->LowestAlignLogic2 << 8) |\r
1722 Capacity16->LowestAlignLogic1;\r
1723 ScsiDiskDevice->BlkIo.Media->LogicalBlocksPerPhysicalBlock = (1 << Capacity16->LogicPerPhysical);\r
1724 }\r
1725\r
1726 ScsiDiskDevice->BlkIo.Media->MediaPresent = TRUE;\r
1727 \r
1728 if (ScsiDiskDevice->DeviceType == EFI_SCSI_TYPE_DISK) {\r
1729 ScsiDiskDevice->BlkIo.Media->BlockSize = 0x200;\r
1730 }\r
1731\r
1732 if (ScsiDiskDevice->DeviceType == EFI_SCSI_TYPE_CDROM) {\r
1733 ScsiDiskDevice->BlkIo.Media->BlockSize = 0x800;\r
1734 }\r
1735}\r
1736\r
1737/**\r
1738 Parse Inquiry data.\r
1739\r
1740 @param ScsiDiskDevice The pointer of SCSI_DISK_DEV\r
1741\r
1742**/\r
1743VOID\r
1744ParseInquiryData (\r
1745 IN OUT SCSI_DISK_DEV *ScsiDiskDevice\r
1746 )\r
1747{\r
1748 ScsiDiskDevice->FixedDevice = (BOOLEAN) ((ScsiDiskDevice->InquiryData.Rmb == 1) ? 0 : 1);\r
1749 ScsiDiskDevice->BlkIoMedia.RemovableMedia = (BOOLEAN) (!ScsiDiskDevice->FixedDevice);\r
1750}\r
1751\r
1752/**\r
1753 Read sector from SCSI Disk.\r
1754\r
1755 @param ScsiDiskDevice The pointer of SCSI_DISK_DEV\r
1756 @param Buffer The buffer to fill in the read out data\r
1757 @param Lba Logic block address\r
1758 @param NumberOfBlocks The number of blocks to read\r
1759\r
1760 @retval EFI_DEVICE_ERROR Indicates a device error.\r
1761 @retval EFI_SUCCESS Operation is successful.\r
1762\r
1763**/\r
1764EFI_STATUS\r
1765ScsiDiskReadSectors (\r
1766 IN SCSI_DISK_DEV *ScsiDiskDevice,\r
1767 OUT VOID *Buffer,\r
1768 IN EFI_LBA Lba,\r
1769 IN UINTN NumberOfBlocks\r
1770 )\r
1771{\r
1772 UINTN BlocksRemaining;\r
1773 UINT8 *PtrBuffer;\r
1774 UINT32 BlockSize;\r
1775 UINT32 ByteCount;\r
1776 UINT32 MaxBlock;\r
1777 UINT32 SectorCount;\r
1778 UINT64 Timeout;\r
1779 EFI_STATUS Status;\r
1780 UINT8 Index;\r
1781 UINT8 MaxRetry;\r
1782 BOOLEAN NeedRetry;\r
1783 EFI_SCSI_SENSE_DATA *SenseData;\r
1784 UINTN NumberOfSenseKeys;\r
1785\r
1786 SenseData = NULL;\r
1787 NumberOfSenseKeys = 0;\r
1788\r
1789 Status = EFI_SUCCESS;\r
1790\r
1791 BlocksRemaining = NumberOfBlocks;\r
1792 BlockSize = ScsiDiskDevice->BlkIo.Media->BlockSize;\r
1793 \r
1794 //\r
1795 // limit the data bytes that can be transferred by one Read(10) or Read(16) Command\r
1796 //\r
1797 if (!ScsiDiskDevice->Cdb16Byte) {\r
1798 MaxBlock = 0xFFFF;\r
1799 } else {\r
1800 MaxBlock = 0xFFFFFFFF;\r
1801 }\r
1802\r
1803 PtrBuffer = Buffer;\r
1804\r
1805 while (BlocksRemaining > 0) {\r
1806\r
1807 if (BlocksRemaining <= MaxBlock) {\r
1808 if (!ScsiDiskDevice->Cdb16Byte) {\r
1809 SectorCount = (UINT16) BlocksRemaining;\r
1810 } else {\r
1811 SectorCount = (UINT32) BlocksRemaining;\r
1812 }\r
1813 } else {\r
1814 SectorCount = MaxBlock;\r
1815 }\r
1816\r
1817 ByteCount = SectorCount * BlockSize;\r
1818 Timeout = EFI_TIMER_PERIOD_SECONDS (2);\r
1819\r
1820 MaxRetry = 2;\r
1821 for (Index = 0; Index < MaxRetry; Index++) {\r
1822 if (!ScsiDiskDevice->Cdb16Byte) {\r
1823 Status = ScsiDiskRead10 (\r
1824 ScsiDiskDevice,\r
1825 &NeedRetry,\r
1826 &SenseData,\r
1827 &NumberOfSenseKeys,\r
1828 Timeout,\r
1829 PtrBuffer,\r
1830 &ByteCount,\r
1831 (UINT32) Lba,\r
1832 SectorCount\r
1833 );\r
1834 } else {\r
1835 Status = ScsiDiskRead16 (\r
1836 ScsiDiskDevice,\r
1837 &NeedRetry,\r
1838 &SenseData,\r
1839 &NumberOfSenseKeys,\r
1840 Timeout,\r
1841 PtrBuffer,\r
1842 &ByteCount,\r
1843 Lba,\r
1844 SectorCount\r
1845 );\r
1846 }\r
1847 if (!EFI_ERROR (Status)) {\r
1848 break;\r
1849 }\r
1850\r
1851 if (!NeedRetry) {\r
1852 return EFI_DEVICE_ERROR;\r
1853 }\r
1854\r
1855 }\r
1856\r
1857 if ((Index == MaxRetry) && (Status != EFI_SUCCESS)) {\r
1858 return EFI_DEVICE_ERROR;\r
1859 }\r
1860\r
1861 //\r
1862 // actual transferred sectors\r
1863 //\r
1864 SectorCount = ByteCount / BlockSize;\r
1865\r
1866 Lba += SectorCount;\r
1867 PtrBuffer = PtrBuffer + SectorCount * BlockSize;\r
1868 BlocksRemaining -= SectorCount;\r
1869 }\r
1870\r
1871 return EFI_SUCCESS;\r
1872}\r
1873\r
1874/**\r
1875 Write sector to SCSI Disk.\r
1876\r
1877 @param ScsiDiskDevice The pointer of SCSI_DISK_DEV\r
1878 @param Buffer The buffer of data to be written into SCSI Disk\r
1879 @param Lba Logic block address\r
1880 @param NumberOfBlocks The number of blocks to read\r
1881\r
1882 @retval EFI_DEVICE_ERROR Indicates a device error.\r
1883 @retval EFI_SUCCESS Operation is successful.\r
1884\r
1885**/\r
1886EFI_STATUS\r
1887ScsiDiskWriteSectors (\r
1888 IN SCSI_DISK_DEV *ScsiDiskDevice,\r
1889 IN VOID *Buffer,\r
1890 IN EFI_LBA Lba,\r
1891 IN UINTN NumberOfBlocks\r
1892 )\r
1893{\r
1894 UINTN BlocksRemaining;\r
1895 UINT8 *PtrBuffer;\r
1896 UINT32 BlockSize;\r
1897 UINT32 ByteCount;\r
1898 UINT32 MaxBlock;\r
1899 UINT32 SectorCount;\r
1900 UINT64 Timeout;\r
1901 EFI_STATUS Status;\r
1902 UINT8 Index;\r
1903 UINT8 MaxRetry;\r
1904 BOOLEAN NeedRetry;\r
1905 EFI_SCSI_SENSE_DATA *SenseData;\r
1906 UINTN NumberOfSenseKeys;\r
1907\r
1908 SenseData = NULL;\r
1909 NumberOfSenseKeys = 0;\r
1910\r
1911 Status = EFI_SUCCESS;\r
1912\r
1913 BlocksRemaining = NumberOfBlocks;\r
1914 BlockSize = ScsiDiskDevice->BlkIo.Media->BlockSize;\r
1915\r
1916 //\r
1917 // limit the data bytes that can be transferred by one Read(10) or Read(16) Command\r
1918 //\r
1919 if (!ScsiDiskDevice->Cdb16Byte) {\r
1920 MaxBlock = 0xFFFF;\r
1921 } else {\r
1922 MaxBlock = 0xFFFFFFFF;\r
1923 }\r
1924\r
1925 PtrBuffer = Buffer;\r
1926\r
1927 while (BlocksRemaining > 0) {\r
1928\r
1929 if (BlocksRemaining <= MaxBlock) {\r
1930 if (!ScsiDiskDevice->Cdb16Byte) {\r
1931 SectorCount = (UINT16) BlocksRemaining;\r
1932 } else {\r
1933 SectorCount = (UINT32) BlocksRemaining;\r
1934 }\r
1935 } else {\r
1936 SectorCount = MaxBlock;\r
1937 }\r
1938\r
1939 ByteCount = SectorCount * BlockSize;\r
1940 Timeout = EFI_TIMER_PERIOD_SECONDS (2);\r
1941 MaxRetry = 2;\r
1942 for (Index = 0; Index < MaxRetry; Index++) {\r
1943 if (!ScsiDiskDevice->Cdb16Byte) {\r
1944 Status = ScsiDiskWrite10 (\r
1945 ScsiDiskDevice,\r
1946 &NeedRetry,\r
1947 &SenseData,\r
1948 &NumberOfSenseKeys,\r
1949 Timeout,\r
1950 PtrBuffer,\r
1951 &ByteCount,\r
1952 (UINT32) Lba,\r
1953 SectorCount\r
1954 );\r
1955 } else {\r
1956 Status = ScsiDiskWrite16 (\r
1957 ScsiDiskDevice,\r
1958 &NeedRetry,\r
1959 &SenseData,\r
1960 &NumberOfSenseKeys,\r
1961 Timeout,\r
1962 PtrBuffer,\r
1963 &ByteCount,\r
1964 Lba,\r
1965 SectorCount\r
1966 ); \r
1967 }\r
1968 if (!EFI_ERROR (Status)) {\r
1969 break;\r
1970 }\r
1971\r
1972 if (!NeedRetry) {\r
1973 return EFI_DEVICE_ERROR;\r
1974 }\r
1975 }\r
1976\r
1977 if ((Index == MaxRetry) && (Status != EFI_SUCCESS)) {\r
1978 return EFI_DEVICE_ERROR;\r
1979 }\r
1980 //\r
1981 // actual transferred sectors\r
1982 //\r
1983 SectorCount = ByteCount / BlockSize;\r
1984\r
1985 Lba += SectorCount;\r
1986 PtrBuffer = PtrBuffer + SectorCount * BlockSize;\r
1987 BlocksRemaining -= SectorCount;\r
1988 }\r
1989\r
1990 return EFI_SUCCESS;\r
1991}\r
1992\r
1993\r
1994/**\r
1995 Submit Read(10) command.\r
1996\r
1997 @param ScsiDiskDevice The pointer of ScsiDiskDevice\r
1998 @param NeedRetry The pointer of flag indicates if needs retry if error happens\r
1999 @param SenseDataArray NOT used yet in this function\r
2000 @param NumberOfSenseKeys The number of sense key\r
2001 @param Timeout The time to complete the command\r
2002 @param DataBuffer The buffer to fill with the read out data\r
2003 @param DataLength The length of buffer\r
2004 @param StartLba The start logic block address\r
2005 @param SectorSize The size of sector\r
2006\r
2007 @return EFI_STATUS is returned by calling ScsiRead10Command().\r
2008**/\r
2009EFI_STATUS\r
2010ScsiDiskRead10 (\r
2011 IN SCSI_DISK_DEV *ScsiDiskDevice,\r
2012 OUT BOOLEAN *NeedRetry,\r
2013 OUT EFI_SCSI_SENSE_DATA **SenseDataArray, OPTIONAL\r
2014 OUT UINTN *NumberOfSenseKeys,\r
2015 IN UINT64 Timeout,\r
2016 OUT UINT8 *DataBuffer,\r
2017 IN OUT UINT32 *DataLength,\r
2018 IN UINT32 StartLba,\r
2019 IN UINT32 SectorSize\r
2020 )\r
2021{\r
2022 UINT8 SenseDataLength;\r
2023 EFI_STATUS Status;\r
2024 UINT8 HostAdapterStatus;\r
2025 UINT8 TargetStatus;\r
2026\r
2027 *NeedRetry = FALSE;\r
2028 *NumberOfSenseKeys = 0;\r
2029 SenseDataLength = 0;\r
2030 Status = ScsiRead10Command (\r
2031 ScsiDiskDevice->ScsiIo,\r
2032 Timeout,\r
2033 NULL,\r
2034 &SenseDataLength,\r
2035 &HostAdapterStatus,\r
2036 &TargetStatus,\r
2037 DataBuffer,\r
2038 DataLength,\r
2039 StartLba,\r
2040 SectorSize\r
2041 );\r
2042 return Status;\r
2043}\r
2044\r
2045\r
2046/**\r
2047 Submit Write(10) Command.\r
2048\r
2049 @param ScsiDiskDevice The pointer of ScsiDiskDevice\r
2050 @param NeedRetry The pointer of flag indicates if needs retry if error happens\r
2051 @param SenseDataArray NOT used yet in this function\r
2052 @param NumberOfSenseKeys The number of sense key\r
2053 @param Timeout The time to complete the command\r
2054 @param DataBuffer The buffer to fill with the read out data\r
2055 @param DataLength The length of buffer\r
2056 @param StartLba The start logic block address\r
2057 @param SectorSize The size of sector\r
2058\r
2059 @return EFI_STATUS is returned by calling ScsiWrite10Command().\r
2060\r
2061**/\r
2062EFI_STATUS\r
2063ScsiDiskWrite10 (\r
2064 IN SCSI_DISK_DEV *ScsiDiskDevice,\r
2065 OUT BOOLEAN *NeedRetry,\r
2066 OUT EFI_SCSI_SENSE_DATA **SenseDataArray, OPTIONAL\r
2067 OUT UINTN *NumberOfSenseKeys,\r
2068 IN UINT64 Timeout,\r
2069 IN UINT8 *DataBuffer,\r
2070 IN OUT UINT32 *DataLength,\r
2071 IN UINT32 StartLba,\r
2072 IN UINT32 SectorSize\r
2073 )\r
2074{\r
2075 EFI_STATUS Status;\r
2076 UINT8 SenseDataLength;\r
2077 UINT8 HostAdapterStatus;\r
2078 UINT8 TargetStatus;\r
2079\r
2080 *NeedRetry = FALSE;\r
2081 *NumberOfSenseKeys = 0;\r
2082 SenseDataLength = 0;\r
2083 Status = ScsiWrite10Command (\r
2084 ScsiDiskDevice->ScsiIo,\r
2085 Timeout,\r
2086 NULL,\r
2087 &SenseDataLength,\r
2088 &HostAdapterStatus,\r
2089 &TargetStatus,\r
2090 DataBuffer,\r
2091 DataLength,\r
2092 StartLba,\r
2093 SectorSize\r
2094 );\r
2095 return Status;\r
2096}\r
2097\r
2098\r
2099/**\r
2100 Submit Read(16) command.\r
2101\r
2102 @param ScsiDiskDevice The pointer of ScsiDiskDevice\r
2103 @param NeedRetry The pointer of flag indicates if needs retry if error happens\r
2104 @param SenseDataArray NOT used yet in this function\r
2105 @param NumberOfSenseKeys The number of sense key\r
2106 @param Timeout The time to complete the command\r
2107 @param DataBuffer The buffer to fill with the read out data\r
2108 @param DataLength The length of buffer\r
2109 @param StartLba The start logic block address\r
2110 @param SectorSize The size of sector\r
2111\r
2112 @return EFI_STATUS is returned by calling ScsiRead10Command().\r
2113**/\r
2114EFI_STATUS\r
2115ScsiDiskRead16 (\r
2116 IN SCSI_DISK_DEV *ScsiDiskDevice,\r
2117 OUT BOOLEAN *NeedRetry,\r
2118 OUT EFI_SCSI_SENSE_DATA **SenseDataArray, OPTIONAL\r
2119 OUT UINTN *NumberOfSenseKeys,\r
2120 IN UINT64 Timeout,\r
2121 OUT UINT8 *DataBuffer,\r
2122 IN OUT UINT32 *DataLength,\r
2123 IN UINT64 StartLba,\r
2124 IN UINT32 SectorSize\r
2125 )\r
2126{\r
2127 UINT8 SenseDataLength;\r
2128 EFI_STATUS Status;\r
2129 UINT8 HostAdapterStatus;\r
2130 UINT8 TargetStatus;\r
2131\r
2132 *NeedRetry = FALSE;\r
2133 *NumberOfSenseKeys = 0;\r
2134 SenseDataLength = 0;\r
2135 Status = ScsiRead16Command (\r
2136 ScsiDiskDevice->ScsiIo,\r
2137 Timeout,\r
2138 NULL,\r
2139 &SenseDataLength,\r
2140 &HostAdapterStatus,\r
2141 &TargetStatus,\r
2142 DataBuffer,\r
2143 DataLength,\r
2144 StartLba,\r
2145 SectorSize\r
2146 );\r
2147 return Status;\r
2148}\r
2149\r
2150\r
2151/**\r
2152 Submit Write(16) Command.\r
2153\r
2154 @param ScsiDiskDevice The pointer of ScsiDiskDevice\r
2155 @param NeedRetry The pointer of flag indicates if needs retry if error happens\r
2156 @param SenseDataArray NOT used yet in this function\r
2157 @param NumberOfSenseKeys The number of sense key\r
2158 @param Timeout The time to complete the command\r
2159 @param DataBuffer The buffer to fill with the read out data\r
2160 @param DataLength The length of buffer\r
2161 @param StartLba The start logic block address\r
2162 @param SectorSize The size of sector\r
2163\r
2164 @return EFI_STATUS is returned by calling ScsiWrite10Command().\r
2165\r
2166**/\r
2167EFI_STATUS\r
2168ScsiDiskWrite16 (\r
2169 IN SCSI_DISK_DEV *ScsiDiskDevice,\r
2170 OUT BOOLEAN *NeedRetry,\r
2171 OUT EFI_SCSI_SENSE_DATA **SenseDataArray, OPTIONAL\r
2172 OUT UINTN *NumberOfSenseKeys,\r
2173 IN UINT64 Timeout,\r
2174 IN UINT8 *DataBuffer,\r
2175 IN OUT UINT32 *DataLength,\r
2176 IN UINT64 StartLba,\r
2177 IN UINT32 SectorSize\r
2178 )\r
2179{\r
2180 EFI_STATUS Status;\r
2181 UINT8 SenseDataLength;\r
2182 UINT8 HostAdapterStatus;\r
2183 UINT8 TargetStatus;\r
2184\r
2185 *NeedRetry = FALSE;\r
2186 *NumberOfSenseKeys = 0;\r
2187 SenseDataLength = 0;\r
2188 Status = ScsiWrite16Command (\r
2189 ScsiDiskDevice->ScsiIo,\r
2190 Timeout,\r
2191 NULL,\r
2192 &SenseDataLength,\r
2193 &HostAdapterStatus,\r
2194 &TargetStatus,\r
2195 DataBuffer,\r
2196 DataLength,\r
2197 StartLba,\r
2198 SectorSize\r
2199 );\r
2200 return Status;\r
2201}\r
2202\r
2203\r
2204/**\r
2205 Check sense key to find if media presents.\r
2206\r
2207 @param SenseData The pointer of EFI_SCSI_SENSE_DATA\r
2208 @param SenseCounts The number of sense key\r
2209\r
2210 @retval TRUE NOT any media\r
2211 @retval FALSE Media presents\r
2212**/\r
2213BOOLEAN\r
2214ScsiDiskIsNoMedia (\r
2215 IN EFI_SCSI_SENSE_DATA *SenseData,\r
2216 IN UINTN SenseCounts\r
2217 )\r
2218{\r
2219 EFI_SCSI_SENSE_DATA *SensePtr;\r
2220 UINTN Index;\r
2221 BOOLEAN IsNoMedia;\r
2222\r
2223 IsNoMedia = FALSE;\r
2224 SensePtr = SenseData;\r
2225\r
2226 for (Index = 0; Index < SenseCounts; Index++) {\r
2227 //\r
2228 // Sense Key is EFI_SCSI_SK_NOT_READY (0x2),\r
2229 // Additional Sense Code is ASC_NO_MEDIA (0x3A)\r
2230 //\r
2231 if ((SensePtr->Sense_Key == EFI_SCSI_SK_NOT_READY) &&\r
2232 (SensePtr->Addnl_Sense_Code == EFI_SCSI_ASC_NO_MEDIA)) {\r
2233 IsNoMedia = TRUE;\r
2234 }\r
2235 SensePtr++;\r
2236 }\r
2237\r
2238 return IsNoMedia;\r
2239}\r
2240\r
2241\r
2242/**\r
2243 Parse sense key.\r
2244\r
2245 @param SenseData The pointer of EFI_SCSI_SENSE_DATA\r
2246 @param SenseCounts The number of sense key\r
2247\r
2248 @retval TRUE Error\r
2249 @retval FALSE NOT error\r
2250\r
2251**/\r
2252BOOLEAN\r
2253ScsiDiskIsMediaError (\r
2254 IN EFI_SCSI_SENSE_DATA *SenseData,\r
2255 IN UINTN SenseCounts\r
2256 )\r
2257{\r
2258 EFI_SCSI_SENSE_DATA *SensePtr;\r
2259 UINTN Index;\r
2260 BOOLEAN IsError;\r
2261\r
2262 IsError = FALSE;\r
2263 SensePtr = SenseData;\r
2264\r
2265 for (Index = 0; Index < SenseCounts; Index++) {\r
2266\r
2267 switch (SensePtr->Sense_Key) {\r
2268\r
2269 case EFI_SCSI_SK_MEDIUM_ERROR:\r
2270 //\r
2271 // Sense Key is EFI_SCSI_SK_MEDIUM_ERROR (0x3)\r
2272 //\r
2273 switch (SensePtr->Addnl_Sense_Code) {\r
2274\r
2275 //\r
2276 // fall through\r
2277 //\r
2278 case EFI_SCSI_ASC_MEDIA_ERR1:\r
2279\r
2280 //\r
2281 // fall through\r
2282 //\r
2283 case EFI_SCSI_ASC_MEDIA_ERR2:\r
2284\r
2285 //\r
2286 // fall through\r
2287 //\r
2288 case EFI_SCSI_ASC_MEDIA_ERR3:\r
2289 case EFI_SCSI_ASC_MEDIA_ERR4:\r
2290 IsError = TRUE;\r
2291 break;\r
2292\r
2293 default:\r
2294 break;\r
2295 }\r
2296\r
2297 break;\r
2298\r
2299 case EFI_SCSI_SK_NOT_READY:\r
2300 //\r
2301 // Sense Key is EFI_SCSI_SK_NOT_READY (0x2)\r
2302 //\r
2303 switch (SensePtr->Addnl_Sense_Code) {\r
2304 //\r
2305 // Additional Sense Code is ASC_MEDIA_UPSIDE_DOWN (0x6)\r
2306 //\r
2307 case EFI_SCSI_ASC_MEDIA_UPSIDE_DOWN:\r
2308 IsError = TRUE;\r
2309 break;\r
2310\r
2311 default:\r
2312 break;\r
2313 }\r
2314 break;\r
2315\r
2316 default:\r
2317 break;\r
2318 }\r
2319\r
2320 SensePtr++;\r
2321 }\r
2322\r
2323 return IsError;\r
2324}\r
2325\r
2326\r
2327/**\r
2328 Check sense key to find if hardware error happens.\r
2329\r
2330 @param SenseData The pointer of EFI_SCSI_SENSE_DATA\r
2331 @param SenseCounts The number of sense key\r
2332\r
2333 @retval TRUE Hardware error exits.\r
2334 @retval FALSE NO error.\r
2335\r
2336**/\r
2337BOOLEAN\r
2338ScsiDiskIsHardwareError (\r
2339 IN EFI_SCSI_SENSE_DATA *SenseData,\r
2340 IN UINTN SenseCounts\r
2341 )\r
2342{\r
2343 EFI_SCSI_SENSE_DATA *SensePtr;\r
2344 UINTN Index;\r
2345 BOOLEAN IsError;\r
2346\r
2347 IsError = FALSE;\r
2348 SensePtr = SenseData;\r
2349\r
2350 for (Index = 0; Index < SenseCounts; Index++) {\r
2351 \r
2352 //\r
2353 // Sense Key is EFI_SCSI_SK_HARDWARE_ERROR (0x4)\r
2354 //\r
2355 if (SensePtr->Sense_Key == EFI_SCSI_SK_HARDWARE_ERROR) {\r
2356 IsError = TRUE;\r
2357 }\r
2358\r
2359 SensePtr++;\r
2360 }\r
2361\r
2362 return IsError;\r
2363}\r
2364\r
2365\r
2366/**\r
2367 Check sense key to find if media has changed.\r
2368\r
2369 @param SenseData The pointer of EFI_SCSI_SENSE_DATA\r
2370 @param SenseCounts The number of sense key\r
2371\r
2372 @retval TRUE Media is changed.\r
2373 @retval FALSE Media is NOT changed.\r
2374**/\r
2375BOOLEAN\r
2376ScsiDiskIsMediaChange (\r
2377 IN EFI_SCSI_SENSE_DATA *SenseData,\r
2378 IN UINTN SenseCounts\r
2379 )\r
2380{\r
2381 EFI_SCSI_SENSE_DATA *SensePtr;\r
2382 UINTN Index;\r
2383 BOOLEAN IsMediaChanged;\r
2384\r
2385 IsMediaChanged = FALSE;\r
2386 SensePtr = SenseData;\r
2387\r
2388 for (Index = 0; Index < SenseCounts; Index++) {\r
2389 //\r
2390 // Sense Key is EFI_SCSI_SK_UNIT_ATTENTION (0x6),\r
2391 // Additional sense code is EFI_SCSI_ASC_MEDIA_CHANGE (0x28)\r
2392 //\r
2393 if ((SensePtr->Sense_Key == EFI_SCSI_SK_UNIT_ATTENTION) &&\r
2394 (SensePtr->Addnl_Sense_Code == EFI_SCSI_ASC_MEDIA_CHANGE)) {\r
2395 IsMediaChanged = TRUE;\r
2396 }\r
2397\r
2398 SensePtr++;\r
2399 }\r
2400\r
2401 return IsMediaChanged;\r
2402}\r
2403\r
2404/**\r
2405 Check sense key to find if reset happens.\r
2406\r
2407 @param SenseData The pointer of EFI_SCSI_SENSE_DATA\r
2408 @param SenseCounts The number of sense key\r
2409\r
2410 @retval TRUE It is reset before.\r
2411 @retval FALSE It is NOT reset before.\r
2412\r
2413**/\r
2414BOOLEAN\r
2415ScsiDiskIsResetBefore (\r
2416 IN EFI_SCSI_SENSE_DATA *SenseData,\r
2417 IN UINTN SenseCounts\r
2418 )\r
2419{\r
2420 EFI_SCSI_SENSE_DATA *SensePtr;\r
2421 UINTN Index;\r
2422 BOOLEAN IsResetBefore;\r
2423\r
2424 IsResetBefore = FALSE;\r
2425 SensePtr = SenseData;\r
2426\r
2427 for (Index = 0; Index < SenseCounts; Index++) {\r
2428 \r
2429 //\r
2430 // Sense Key is EFI_SCSI_SK_UNIT_ATTENTION (0x6)\r
2431 // Additional Sense Code is EFI_SCSI_ASC_RESET (0x29)\r
2432 //\r
2433 if ((SensePtr->Sense_Key == EFI_SCSI_SK_UNIT_ATTENTION) &&\r
2434 (SensePtr->Addnl_Sense_Code == EFI_SCSI_ASC_RESET)) {\r
2435 IsResetBefore = TRUE;\r
2436 }\r
2437\r
2438 SensePtr++;\r
2439 }\r
2440\r
2441 return IsResetBefore;\r
2442}\r
2443\r
2444/**\r
2445 Check sense key to find if the drive is ready.\r
2446\r
2447 @param SenseData The pointer of EFI_SCSI_SENSE_DATA\r
2448 @param SenseCounts The number of sense key\r
2449 @param RetryLater The flag means if need a retry \r
2450\r
2451 @retval TRUE Drive is ready.\r
2452 @retval FALSE Drive is NOT ready.\r
2453\r
2454**/\r
2455BOOLEAN\r
2456ScsiDiskIsDriveReady (\r
2457 IN EFI_SCSI_SENSE_DATA *SenseData,\r
2458 IN UINTN SenseCounts,\r
2459 OUT BOOLEAN *RetryLater\r
2460 )\r
2461{\r
2462 EFI_SCSI_SENSE_DATA *SensePtr;\r
2463 UINTN Index;\r
2464 BOOLEAN IsReady;\r
2465\r
2466 IsReady = TRUE;\r
2467 *RetryLater = FALSE;\r
2468 SensePtr = SenseData;\r
2469\r
2470 for (Index = 0; Index < SenseCounts; Index++) {\r
2471\r
2472 switch (SensePtr->Sense_Key) {\r
2473\r
2474 case EFI_SCSI_SK_NOT_READY:\r
2475 //\r
2476 // Sense Key is EFI_SCSI_SK_NOT_READY (0x2)\r
2477 //\r
2478 switch (SensePtr->Addnl_Sense_Code) {\r
2479 case EFI_SCSI_ASC_NOT_READY:\r
2480 //\r
2481 // Additional Sense Code is EFI_SCSI_ASC_NOT_READY (0x4)\r
2482 //\r
2483 switch (SensePtr->Addnl_Sense_Code_Qualifier) {\r
2484 case EFI_SCSI_ASCQ_IN_PROGRESS:\r
2485 //\r
2486 // Additional Sense Code Qualifier is\r
2487 // EFI_SCSI_ASCQ_IN_PROGRESS (0x1)\r
2488 //\r
2489 IsReady = FALSE;\r
2490 *RetryLater = TRUE;\r
2491 break;\r
2492\r
2493 default:\r
2494 IsReady = FALSE;\r
2495 *RetryLater = FALSE;\r
2496 break;\r
2497 }\r
2498 break;\r
2499\r
2500 default:\r
2501 break;\r
2502 }\r
2503 break;\r
2504\r
2505 default:\r
2506 break;\r
2507 }\r
2508\r
2509 SensePtr++;\r
2510 }\r
2511\r
2512 return IsReady;\r
2513}\r
2514\r
2515/**\r
2516 Check sense key to find if it has sense key.\r
2517\r
2518 @param SenseData - The pointer of EFI_SCSI_SENSE_DATA\r
2519 @param SenseCounts - The number of sense key\r
2520\r
2521 @retval TRUE It has sense key.\r
2522 @retval FALSE It has NOT any sense key.\r
2523\r
2524**/\r
2525BOOLEAN\r
2526ScsiDiskHaveSenseKey (\r
2527 IN EFI_SCSI_SENSE_DATA *SenseData,\r
2528 IN UINTN SenseCounts\r
2529 )\r
2530{\r
2531 EFI_SCSI_SENSE_DATA *SensePtr;\r
2532 UINTN Index;\r
2533 BOOLEAN HaveSenseKey;\r
2534\r
2535 if (SenseCounts == 0) {\r
2536 HaveSenseKey = FALSE;\r
2537 } else {\r
2538 HaveSenseKey = TRUE;\r
2539 }\r
2540\r
2541 SensePtr = SenseData;\r
2542\r
2543 for (Index = 0; Index < SenseCounts; Index++) {\r
2544 \r
2545 //\r
2546 // Sense Key is SK_NO_SENSE (0x0)\r
2547 //\r
2548 if ((SensePtr->Sense_Key == EFI_SCSI_SK_NO_SENSE) &&\r
2549 (Index == 0)) {\r
2550 HaveSenseKey = FALSE;\r
2551 }\r
2552\r
2553 SensePtr++;\r
2554 }\r
2555\r
2556 return HaveSenseKey;\r
2557}\r
2558\r
2559/**\r
2560 Release resource about disk device.\r
2561\r
2562 @param ScsiDiskDevice The pointer of SCSI_DISK_DEV\r
2563\r
2564**/\r
2565VOID\r
2566ReleaseScsiDiskDeviceResources (\r
2567 IN SCSI_DISK_DEV *ScsiDiskDevice\r
2568 )\r
2569{\r
2570 if (ScsiDiskDevice == NULL) {\r
2571 return ;\r
2572 }\r
2573\r
2574 if (ScsiDiskDevice->SenseData != NULL) {\r
2575 FreePool (ScsiDiskDevice->SenseData);\r
2576 ScsiDiskDevice->SenseData = NULL;\r
2577 }\r
2578\r
2579 if (ScsiDiskDevice->ControllerNameTable != NULL) {\r
2580 FreeUnicodeStringTable (ScsiDiskDevice->ControllerNameTable);\r
2581 ScsiDiskDevice->ControllerNameTable = NULL;\r
2582 }\r
2583\r
2584 FreePool (ScsiDiskDevice);\r
2585\r
2586 ScsiDiskDevice = NULL;\r
2587}\r
2588\r
2589/**\r
2590 Determine if Block Io should be produced.\r
2591 \r
2592\r
2593 @param ChildHandle Child Handle to retrieve Parent information.\r
2594 \r
2595 @retval TRUE Should produce Block Io.\r
2596 @retval FALSE Should not produce Block Io.\r
2597\r
2598**/ \r
2599BOOLEAN\r
2600DetermineInstallBlockIo (\r
2601 IN EFI_HANDLE ChildHandle\r
2602 ) \r
2603{\r
2604 EFI_SCSI_PASS_THRU_PROTOCOL *ScsiPassThru;\r
2605 EFI_EXT_SCSI_PASS_THRU_PROTOCOL *ExtScsiPassThru;\r
2606\r
2607 //\r
2608 // Firstly, check if ExtScsiPassThru Protocol parent handle exists. If existence,\r
2609 // check its attribute, logic or physical.\r
2610 //\r
2611 ExtScsiPassThru = (EFI_EXT_SCSI_PASS_THRU_PROTOCOL *)GetParentProtocol (&gEfiExtScsiPassThruProtocolGuid, ChildHandle);\r
2612 if (ExtScsiPassThru != NULL) {\r
2613 if ((ExtScsiPassThru->Mode->Attributes & EFI_SCSI_PASS_THRU_ATTRIBUTES_LOGICAL) != 0) {\r
2614 return TRUE;\r
2615 }\r
2616 }\r
2617\r
2618 //\r
2619 // Secondly, check if ScsiPassThru Protocol parent handle exists. If existence,\r
2620 // check its attribute, logic or physical.\r
2621 //\r
2622 ScsiPassThru = (EFI_SCSI_PASS_THRU_PROTOCOL *)GetParentProtocol (&gEfiScsiPassThruProtocolGuid, ChildHandle);\r
2623 if (ScsiPassThru != NULL) {\r
2624 if ((ScsiPassThru->Mode->Attributes & EFI_SCSI_PASS_THRU_ATTRIBUTES_LOGICAL) != 0) {\r
2625 return TRUE;\r
2626 }\r
2627 }\r
2628 \r
2629 return FALSE;\r
2630}\r
2631\r
2632/**\r
2633 Search protocol database and check to see if the protocol\r
2634 specified by ProtocolGuid is present on a ControllerHandle and opened by\r
2635 ChildHandle with an attribute of EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER.\r
2636 If the ControllerHandle is found, then the protocol specified by ProtocolGuid\r
2637 will be opened on it. \r
2638 \r
2639\r
2640 @param ProtocolGuid ProtocolGuid pointer.\r
2641 @param ChildHandle Child Handle to retrieve Parent information.\r
2642 \r
2643**/ \r
2644VOID *\r
2645EFIAPI\r
2646GetParentProtocol (\r
2647 IN EFI_GUID *ProtocolGuid,\r
2648 IN EFI_HANDLE ChildHandle\r
2649 ) \r
2650{\r
2651 UINTN Index;\r
2652 UINTN HandleCount;\r
2653 VOID *Interface; \r
2654 EFI_STATUS Status;\r
2655 EFI_HANDLE *HandleBuffer;\r
2656\r
2657 //\r
2658 // Retrieve the list of all handles from the handle database\r
2659 //\r
2660 Status = gBS->LocateHandleBuffer (\r
2661 ByProtocol,\r
2662 ProtocolGuid,\r
2663 NULL,\r
2664 &HandleCount,\r
2665 &HandleBuffer\r
2666 );\r
2667\r
2668 if (EFI_ERROR (Status)) {\r
2669 return NULL;\r
2670 }\r
2671\r
2672 //\r
2673 // Iterate to find who is parent handle that is opened with ProtocolGuid by ChildHandle \r
2674 //\r
2675 for (Index = 0; Index < HandleCount; Index++) {\r
2676 Status = EfiTestChildHandle (HandleBuffer[Index], ChildHandle, ProtocolGuid);\r
2677 if (!EFI_ERROR (Status)) {\r
2678 Status = gBS->HandleProtocol (HandleBuffer[Index], ProtocolGuid, (VOID **)&Interface);\r
2679 if (!EFI_ERROR (Status)) {\r
2680 gBS->FreePool (HandleBuffer);\r
2681 return Interface;\r
2682 }\r
2683 }\r
2684 }\r
2685\r
2686 gBS->FreePool (HandleBuffer);\r
2687 return NULL;\r
2688} \r
2689\r
2690/**\r
2691 Provides inquiry information for the controller type.\r
2692 \r
2693 This function is used by the IDE bus driver to get inquiry data. Data format\r
2694 of Identify data is defined by the Interface GUID.\r
2695\r
2696 @param[in] This Pointer to the EFI_DISK_INFO_PROTOCOL instance.\r
2697 @param[in, out] InquiryData Pointer to a buffer for the inquiry data.\r
2698 @param[in, out] InquiryDataSize Pointer to the value for the inquiry data size.\r
2699\r
2700 @retval EFI_SUCCESS The command was accepted without any errors.\r
2701 @retval EFI_NOT_FOUND Device does not support this data class \r
2702 @retval EFI_DEVICE_ERROR Error reading InquiryData from device \r
2703 @retval EFI_BUFFER_TOO_SMALL InquiryDataSize not big enough \r
2704\r
2705**/\r
2706EFI_STATUS\r
2707EFIAPI\r
2708ScsiDiskInfoInquiry (\r
2709 IN EFI_DISK_INFO_PROTOCOL *This,\r
2710 IN OUT VOID *InquiryData,\r
2711 IN OUT UINT32 *InquiryDataSize\r
2712 )\r
2713{\r
2714 EFI_STATUS Status;\r
2715 SCSI_DISK_DEV *ScsiDiskDevice;\r
2716\r
2717 ScsiDiskDevice = SCSI_DISK_DEV_FROM_DISKINFO (This);\r
2718\r
2719 Status = EFI_BUFFER_TOO_SMALL;\r
2720 if (*InquiryDataSize >= sizeof (ScsiDiskDevice->InquiryData)) {\r
2721 Status = EFI_SUCCESS;\r
2722 CopyMem (InquiryData, &ScsiDiskDevice->InquiryData, sizeof (ScsiDiskDevice->InquiryData));\r
2723 }\r
2724 *InquiryDataSize = sizeof (ScsiDiskDevice->InquiryData);\r
2725 return Status;\r
2726}\r
2727\r
2728\r
2729/**\r
2730 Provides identify information for the controller type.\r
2731\r
2732 This function is used by the IDE bus driver to get identify data. Data format\r
2733 of Identify data is defined by the Interface GUID.\r
2734\r
2735 @param[in] This Pointer to the EFI_DISK_INFO_PROTOCOL \r
2736 instance.\r
2737 @param[in, out] IdentifyData Pointer to a buffer for the identify data.\r
2738 @param[in, out] IdentifyDataSize Pointer to the value for the identify data\r
2739 size.\r
2740\r
2741 @retval EFI_SUCCESS The command was accepted without any errors.\r
2742 @retval EFI_NOT_FOUND Device does not support this data class \r
2743 @retval EFI_DEVICE_ERROR Error reading IdentifyData from device \r
2744 @retval EFI_BUFFER_TOO_SMALL IdentifyDataSize not big enough \r
2745\r
2746**/\r
2747EFI_STATUS\r
2748EFIAPI\r
2749ScsiDiskInfoIdentify (\r
2750 IN EFI_DISK_INFO_PROTOCOL *This,\r
2751 IN OUT VOID *IdentifyData,\r
2752 IN OUT UINT32 *IdentifyDataSize\r
2753 )\r
2754{\r
2755 EFI_STATUS Status;\r
2756 SCSI_DISK_DEV *ScsiDiskDevice;\r
2757\r
2758 if (CompareGuid (&This->Interface, &gEfiDiskInfoScsiInterfaceGuid)) {\r
2759 //\r
2760 // Physical SCSI bus does not support this data class. \r
2761 //\r
2762 return EFI_NOT_FOUND;\r
2763 }\r
2764\r
2765 ScsiDiskDevice = SCSI_DISK_DEV_FROM_DISKINFO (This);\r
2766\r
2767 Status = EFI_BUFFER_TOO_SMALL;\r
2768 if (*IdentifyDataSize >= sizeof (ScsiDiskDevice->IdentifyData)) {\r
2769 Status = EFI_SUCCESS;\r
2770 CopyMem (IdentifyData, &ScsiDiskDevice->IdentifyData, sizeof (ScsiDiskDevice->IdentifyData));\r
2771 }\r
2772 *IdentifyDataSize = sizeof (ScsiDiskDevice->IdentifyData);\r
2773 return Status;\r
2774}\r
2775\r
2776/**\r
2777 Provides sense data information for the controller type.\r
2778 \r
2779 This function is used by the IDE bus driver to get sense data. \r
2780 Data format of Sense data is defined by the Interface GUID.\r
2781\r
2782 @param[in] This Pointer to the EFI_DISK_INFO_PROTOCOL instance.\r
2783 @param[in, out] SenseData Pointer to the SenseData.\r
2784 @param[in, out] SenseDataSize Size of SenseData in bytes.\r
2785 @param[out] SenseDataNumber Pointer to the value for the sense data size.\r
2786\r
2787 @retval EFI_SUCCESS The command was accepted without any errors.\r
2788 @retval EFI_NOT_FOUND Device does not support this data class.\r
2789 @retval EFI_DEVICE_ERROR Error reading SenseData from device.\r
2790 @retval EFI_BUFFER_TOO_SMALL SenseDataSize not big enough.\r
2791\r
2792**/\r
2793EFI_STATUS\r
2794EFIAPI\r
2795ScsiDiskInfoSenseData (\r
2796 IN EFI_DISK_INFO_PROTOCOL *This,\r
2797 IN OUT VOID *SenseData,\r
2798 IN OUT UINT32 *SenseDataSize,\r
2799 OUT UINT8 *SenseDataNumber\r
2800 )\r
2801{\r
2802 return EFI_NOT_FOUND;\r
2803}\r
2804\r
2805\r
2806/**\r
2807 This function is used by the IDE bus driver to get controller information.\r
2808\r
2809 @param[in] This Pointer to the EFI_DISK_INFO_PROTOCOL instance. \r
2810 @param[out] IdeChannel Pointer to the Ide Channel number. Primary or secondary.\r
2811 @param[out] IdeDevice Pointer to the Ide Device number. Master or slave.\r
2812\r
2813 @retval EFI_SUCCESS IdeChannel and IdeDevice are valid.\r
2814 @retval EFI_UNSUPPORTED This is not an IDE device.\r
2815\r
2816**/\r
2817EFI_STATUS\r
2818EFIAPI\r
2819ScsiDiskInfoWhichIde (\r
2820 IN EFI_DISK_INFO_PROTOCOL *This,\r
2821 OUT UINT32 *IdeChannel,\r
2822 OUT UINT32 *IdeDevice\r
2823 )\r
2824{\r
2825 SCSI_DISK_DEV *ScsiDiskDevice;\r
2826\r
2827 if (CompareGuid (&This->Interface, &gEfiDiskInfoScsiInterfaceGuid)) {\r
2828 //\r
2829 // This is not an IDE physical device.\r
2830 //\r
2831 return EFI_UNSUPPORTED;\r
2832 }\r
2833\r
2834 ScsiDiskDevice = SCSI_DISK_DEV_FROM_DISKINFO (This);\r
2835 *IdeChannel = ScsiDiskDevice->Channel;\r
2836 *IdeDevice = ScsiDiskDevice->Device;\r
2837\r
2838 return EFI_SUCCESS;\r
2839}\r
2840\r
2841\r
2842/**\r
2843 Issues ATA IDENTIFY DEVICE command to identify ATAPI device.\r
2844\r
2845 This function tries to fill 512-byte ATAPI_IDENTIFY_DATA for ATAPI device to\r
2846 implement Identify() interface for DiskInfo protocol. The ATA command is sent\r
2847 via SCSI Request Packet.\r
2848\r
2849 @param ScsiDiskDevice The pointer of SCSI_DISK_DEV\r
2850 \r
2851 @retval EFI_SUCCESS The ATAPI device identify data were retrieved successfully.\r
2852 @retval others Some error occurred during the identification that ATAPI device.\r
2853\r
2854**/ \r
2855EFI_STATUS\r
2856AtapiIdentifyDevice (\r
2857 IN OUT SCSI_DISK_DEV *ScsiDiskDevice\r
2858 )\r
2859{\r
2860 EFI_SCSI_IO_SCSI_REQUEST_PACKET CommandPacket;\r
2861 UINT8 Cdb[6];\r
2862\r
2863 //\r
2864 // Initialize SCSI REQUEST_PACKET and 6-byte Cdb\r
2865 //\r
2866 ZeroMem (&CommandPacket, sizeof (CommandPacket));\r
2867 ZeroMem (Cdb, sizeof (Cdb));\r
2868\r
2869 Cdb[0] = ATA_CMD_IDENTIFY_DEVICE;\r
2870 CommandPacket.Timeout = EFI_TIMER_PERIOD_SECONDS (1);\r
2871 CommandPacket.Cdb = Cdb;\r
2872 CommandPacket.CdbLength = (UINT8) sizeof (Cdb);\r
2873 CommandPacket.InDataBuffer = &ScsiDiskDevice->IdentifyData;\r
2874 CommandPacket.InTransferLength = sizeof (ScsiDiskDevice->IdentifyData);\r
2875\r
2876 return ScsiDiskDevice->ScsiIo->ExecuteScsiCommand (ScsiDiskDevice->ScsiIo, &CommandPacket, NULL);\r
2877}\r
2878\r
2879\r
2880/**\r
2881 Initialize the installation of DiskInfo protocol.\r
2882\r
2883 This function prepares for the installation of DiskInfo protocol on the child handle.\r
2884 By default, it installs DiskInfo protocol with SCSI interface GUID. If it further\r
2885 detects that the physical device is an ATAPI/AHCI device, it then updates interface GUID\r
2886 to be IDE/AHCI interface GUID.\r
2887\r
2888 @param ScsiDiskDevice The pointer of SCSI_DISK_DEV.\r
2889 @param ChildHandle Child handle to install DiskInfo protocol.\r
2890 \r
2891**/ \r
2892VOID\r
2893InitializeInstallDiskInfo (\r
2894 IN SCSI_DISK_DEV *ScsiDiskDevice,\r
2895 IN EFI_HANDLE ChildHandle\r
2896 )\r
2897{\r
2898 EFI_STATUS Status;\r
2899 EFI_DEVICE_PATH_PROTOCOL *DevicePathNode;\r
2900 EFI_DEVICE_PATH_PROTOCOL *ChildDevicePathNode;\r
2901 ATAPI_DEVICE_PATH *AtapiDevicePath;\r
2902 SATA_DEVICE_PATH *SataDevicePath;\r
2903 UINTN IdentifyRetry;\r
2904\r
2905 Status = gBS->HandleProtocol (ChildHandle, &gEfiDevicePathProtocolGuid, (VOID **) &DevicePathNode);\r
2906 //\r
2907 // Device Path protocol must be installed on the device handle. \r
2908 //\r
2909 ASSERT_EFI_ERROR (Status);\r
2910 //\r
2911 // Copy the DiskInfo protocol template.\r
2912 //\r
2913 CopyMem (&ScsiDiskDevice->DiskInfo, &gScsiDiskInfoProtocolTemplate, sizeof (gScsiDiskInfoProtocolTemplate));\r
2914\r
2915 while (!IsDevicePathEnd (DevicePathNode)) {\r
2916 ChildDevicePathNode = NextDevicePathNode (DevicePathNode);\r
2917 if ((DevicePathType (DevicePathNode) == HARDWARE_DEVICE_PATH) &&\r
2918 (DevicePathSubType (DevicePathNode) == HW_PCI_DP) &&\r
2919 (DevicePathType (ChildDevicePathNode) == MESSAGING_DEVICE_PATH) &&\r
2920 ((DevicePathSubType (ChildDevicePathNode) == MSG_ATAPI_DP) ||\r
2921 (DevicePathSubType (ChildDevicePathNode) == MSG_SATA_DP))) {\r
2922\r
2923 IdentifyRetry = 3;\r
2924 do {\r
2925 //\r
2926 // Issue ATA Identify Device Command via SCSI command, which is required to publish DiskInfo protocol\r
2927 // with IDE/AHCI interface GUID.\r
2928 //\r
2929 Status = AtapiIdentifyDevice (ScsiDiskDevice);\r
2930 if (!EFI_ERROR (Status)) {\r
2931 if (DevicePathSubType(ChildDevicePathNode) == MSG_ATAPI_DP) {\r
2932 //\r
2933 // We find the valid ATAPI device path\r
2934 //\r
2935 AtapiDevicePath = (ATAPI_DEVICE_PATH *) ChildDevicePathNode;\r
2936 ScsiDiskDevice->Channel = AtapiDevicePath->PrimarySecondary;\r
2937 ScsiDiskDevice->Device = AtapiDevicePath->SlaveMaster;\r
2938 //\r
2939 // Update the DiskInfo.Interface to IDE interface GUID for the physical ATAPI device. \r
2940 //\r
2941 CopyGuid (&ScsiDiskDevice->DiskInfo.Interface, &gEfiDiskInfoIdeInterfaceGuid);\r
2942 } else {\r
2943 //\r
2944 // We find the valid SATA device path\r
2945 //\r
2946 SataDevicePath = (SATA_DEVICE_PATH *) ChildDevicePathNode;\r
2947 ScsiDiskDevice->Channel = SataDevicePath->HBAPortNumber;\r
2948 ScsiDiskDevice->Device = SataDevicePath->PortMultiplierPortNumber;\r
2949 //\r
2950 // Update the DiskInfo.Interface to AHCI interface GUID for the physical AHCI device. \r
2951 //\r
2952 CopyGuid (&ScsiDiskDevice->DiskInfo.Interface, &gEfiDiskInfoAhciInterfaceGuid);\r
2953 }\r
2954 return;\r
2955 }\r
2956 } while (--IdentifyRetry > 0);\r
2957 }\r
2958 DevicePathNode = ChildDevicePathNode;\r
2959 }\r
2960\r
2961 return;\r
2962}\r