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