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