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