]> git.proxmox.com Git - mirror_edk2.git/blame - MdeModulePkg/Bus/Scsi/ScsiDiskDxe/ScsiDisk.c
Fix the bug that necessary call of SetVectorBase() is missing in the SetMode() function.
[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
3b2dbece 4Copyright (c) 2006 - 2008, Intel Corporation. <BR>\r
5All rights reserved. This program and the accompanying materials\r
6are licensed and made available under the terms and conditions of the BSD License\r
7which accompanies this distribution. The full text of the license may be found at\r
8http://opensource.org/licenses/bsd-license.php\r
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
9beb888e 18\r
6ad55b15 19EFI_DRIVER_BINDING_PROTOCOL gScsiDiskDriverBinding = {\r
20 ScsiDiskDriverBindingSupported,\r
21 ScsiDiskDriverBindingStart,\r
22 ScsiDiskDriverBindingStop,\r
23 0xa,\r
24 NULL,\r
25 NULL\r
26};\r
27\r
9b38ff34 28\r
6ad55b15 29/**\r
9beb888e 30 The user Entry Point for module ScsiDisk.\r
31\r
32 The user code starts with this function.\r
6ad55b15 33\r
9beb888e 34 @param ImageHandle The firmware allocated handle for the EFI image. \r
35 @param SystemTable A pointer to the EFI System Table.\r
6ad55b15 36 \r
37 @retval EFI_SUCCESS The entry point is executed successfully.\r
38 @retval other Some error occurs when executing this entry point.\r
39\r
40**/\r
41EFI_STATUS\r
42EFIAPI\r
43InitializeScsiDisk(\r
44 IN EFI_HANDLE ImageHandle,\r
45 IN EFI_SYSTEM_TABLE *SystemTable\r
46 )\r
47{\r
48 EFI_STATUS Status;\r
49\r
50 //\r
51 // Install driver model protocol(s).\r
52 //\r
70da5bc2 53 Status = EfiLibInstallDriverBindingComponentName2 (\r
6ad55b15 54 ImageHandle,\r
55 SystemTable,\r
56 &gScsiDiskDriverBinding,\r
57 ImageHandle,\r
58 &gScsiDiskComponentName,\r
70da5bc2 59 &gScsiDiskComponentName2\r
6ad55b15 60 );\r
61 ASSERT_EFI_ERROR (Status);\r
62\r
63\r
64 return Status;\r
65}\r
66\r
9beb888e 67/**\r
68 Test to see if this driver supports ControllerHandle.\r
69\r
70 This service is called by the EFI boot service ConnectController(). In order\r
71 to make drivers as small as possible, there are a few calling restrictions for\r
72 this service. ConnectController() must follow these calling restrictions.\r
73 If any other agent wishes to call Supported() it must also follow these\r
74 calling restrictions.\r
75\r
76 @param This Protocol instance pointer.\r
77 @param ControllerHandle Handle of device to test\r
78 @param RemainingDevicePath Optional parameter use to pick a specific child\r
79 device to start.\r
80\r
81 @retval EFI_SUCCESS This driver supports this device\r
82 @retval EFI_ALREADY_STARTED This driver is already running on this device\r
83 @retval other This driver does not support this device\r
84\r
85**/\r
6ad55b15 86EFI_STATUS\r
87EFIAPI\r
88ScsiDiskDriverBindingSupported (\r
89 IN EFI_DRIVER_BINDING_PROTOCOL *This,\r
90 IN EFI_HANDLE Controller,\r
9beb888e 91 IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL\r
6ad55b15 92 )\r
6ad55b15 93{\r
94 EFI_STATUS Status;\r
95 EFI_SCSI_IO_PROTOCOL *ScsiIo;\r
96 UINT8 DeviceType;\r
97\r
98 Status = gBS->OpenProtocol (\r
99 Controller,\r
100 &gEfiScsiIoProtocolGuid,\r
101 (VOID **) &ScsiIo,\r
102 This->DriverBindingHandle,\r
103 Controller,\r
104 EFI_OPEN_PROTOCOL_BY_DRIVER\r
105 );\r
106 if (EFI_ERROR (Status)) {\r
107 return Status;\r
108 }\r
109\r
110 Status = ScsiIo->GetDeviceType (ScsiIo, &DeviceType);\r
111 if (!EFI_ERROR (Status)) {\r
112 if ((DeviceType == EFI_SCSI_TYPE_DISK) || (DeviceType == EFI_SCSI_TYPE_CDROM)) {\r
113 Status = EFI_SUCCESS;\r
114 } else {\r
115 Status = EFI_UNSUPPORTED;\r
116 }\r
117 }\r
118\r
119 gBS->CloseProtocol (\r
f36d6e66 120 Controller,\r
121 &gEfiScsiIoProtocolGuid,\r
122 This->DriverBindingHandle,\r
123 Controller\r
124 );\r
6ad55b15 125 return Status;\r
126}\r
127\r
9beb888e 128\r
129/**\r
130 Start this driver on ControllerHandle.\r
131\r
132 This service is called by the EFI boot service ConnectController(). In order\r
133 to make drivers as small as possible, there are a few calling restrictions for\r
134 this service. ConnectController() must follow these calling restrictions. If\r
135 any other agent wishes to call Start() it must also follow these calling\r
136 restrictions.\r
137\r
138 @param This Protocol instance pointer.\r
139 @param ControllerHandle Handle of device to bind driver to\r
140 @param RemainingDevicePath Optional parameter use to pick a specific child\r
141 device to start.\r
142\r
143 @retval EFI_SUCCESS This driver is added to ControllerHandle\r
144 @retval EFI_ALREADY_STARTED This driver is already running on ControllerHandle\r
145 @retval other This driver does not support this device\r
146\r
147**/\r
6ad55b15 148EFI_STATUS\r
149EFIAPI\r
150ScsiDiskDriverBindingStart (\r
151 IN EFI_DRIVER_BINDING_PROTOCOL *This,\r
152 IN EFI_HANDLE Controller,\r
9beb888e 153 IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL\r
6ad55b15 154 )\r
6ad55b15 155{\r
156 EFI_STATUS Status;\r
157 EFI_SCSI_IO_PROTOCOL *ScsiIo;\r
158 SCSI_DISK_DEV *ScsiDiskDevice;\r
159 BOOLEAN Temp;\r
160 UINT8 Index;\r
161 UINT8 MaxRetry;\r
162 BOOLEAN NeedRetry;\r
163\r
9b38ff34 164 ScsiDiskDevice = (SCSI_DISK_DEV *) AllocateZeroPool (sizeof (SCSI_DISK_DEV));\r
165 if (ScsiDiskDevice == NULL) {\r
166 return EFI_OUT_OF_RESOURCES;\r
6ad55b15 167 }\r
168\r
6ad55b15 169 Status = gBS->OpenProtocol (\r
170 Controller,\r
171 &gEfiScsiIoProtocolGuid,\r
172 (VOID **) &ScsiIo,\r
173 This->DriverBindingHandle,\r
174 Controller,\r
175 EFI_OPEN_PROTOCOL_BY_DRIVER\r
176 );\r
177 if (EFI_ERROR (Status)) {\r
9b38ff34 178 FreePool (ScsiDiskDevice);\r
6ad55b15 179 return Status;\r
180 }\r
181\r
182 ScsiDiskDevice->Signature = SCSI_DISK_DEV_SIGNATURE;\r
183 ScsiDiskDevice->ScsiIo = ScsiIo;\r
184 ScsiDiskDevice->BlkIo.Media = &ScsiDiskDevice->BlkIoMedia;\r
185 ScsiDiskDevice->BlkIo.Reset = ScsiDiskReset;\r
186 ScsiDiskDevice->BlkIo.ReadBlocks = ScsiDiskReadBlocks;\r
187 ScsiDiskDevice->BlkIo.WriteBlocks = ScsiDiskWriteBlocks;\r
188 ScsiDiskDevice->BlkIo.FlushBlocks = ScsiDiskFlushBlocks;\r
189 ScsiDiskDevice->Handle = Controller;\r
190\r
191 ScsiIo->GetDeviceType (ScsiIo, &(ScsiDiskDevice->DeviceType));\r
192 switch (ScsiDiskDevice->DeviceType) {\r
193 case EFI_SCSI_TYPE_DISK:\r
194 ScsiDiskDevice->BlkIo.Media->BlockSize = 0x200;\r
195 break;\r
196\r
197 case EFI_SCSI_TYPE_CDROM:\r
198 ScsiDiskDevice->BlkIo.Media->BlockSize = 0x800;\r
199 break;\r
200 }\r
201 //\r
202 // The Sense Data Array's initial size is 6\r
203 //\r
204 ScsiDiskDevice->SenseDataNumber = 6;\r
9b38ff34 205 ScsiDiskDevice->SenseData = (EFI_SCSI_SENSE_DATA *) AllocateZeroPool (\r
206 sizeof (EFI_SCSI_SENSE_DATA) * ScsiDiskDevice->SenseDataNumber\r
207 );\r
208 if (ScsiDiskDevice->SenseData == NULL) {\r
6ad55b15 209 gBS->CloseProtocol (\r
210 Controller,\r
211 &gEfiScsiIoProtocolGuid,\r
212 This->DriverBindingHandle,\r
213 Controller\r
214 );\r
9b38ff34 215 FreePool (ScsiDiskDevice);\r
216 return EFI_OUT_OF_RESOURCES;\r
6ad55b15 217 }\r
218\r
6ad55b15 219 //\r
dfe687ca 220 // Retrieve device information\r
6ad55b15 221 //\r
222 MaxRetry = 2;\r
223 for (Index = 0; Index < MaxRetry; Index++) {\r
224 Status = ScsiDiskInquiryDevice (ScsiDiskDevice, &NeedRetry);\r
225 if (!EFI_ERROR (Status)) {\r
226 break;\r
227 }\r
228\r
229 if (!NeedRetry) {\r
9b38ff34 230 FreePool (ScsiDiskDevice->SenseData);\r
6ad55b15 231 gBS->CloseProtocol (\r
f36d6e66 232 Controller,\r
233 &gEfiScsiIoProtocolGuid,\r
234 This->DriverBindingHandle,\r
235 Controller\r
236 );\r
9b38ff34 237 FreePool (ScsiDiskDevice);\r
6ad55b15 238 return EFI_DEVICE_ERROR;\r
239 }\r
240 }\r
241 //\r
242 // The second parameter "TRUE" means must\r
243 // retrieve media capacity\r
244 //\r
245 Status = ScsiDiskDetectMedia (ScsiDiskDevice, TRUE, &Temp);\r
246 if (!EFI_ERROR (Status)) {\r
247 Status = gBS->InstallMultipleProtocolInterfaces (\r
248 &Controller,\r
249 &gEfiBlockIoProtocolGuid,\r
250 &ScsiDiskDevice->BlkIo,\r
251 NULL\r
252 );\r
253 }\r
254\r
255 if (EFI_ERROR (Status)) {\r
9b38ff34 256 FreePool (ScsiDiskDevice->SenseData);\r
6ad55b15 257 gBS->CloseProtocol (\r
f36d6e66 258 Controller,\r
259 &gEfiScsiIoProtocolGuid,\r
260 This->DriverBindingHandle,\r
261 Controller\r
262 );\r
9b38ff34 263 FreePool (ScsiDiskDevice);\r
6ad55b15 264 return Status;\r
265 }\r
266\r
267 ScsiDiskDevice->ControllerNameTable = NULL;\r
70da5bc2 268 AddUnicodeString2 (\r
6ad55b15 269 "eng",\r
270 gScsiDiskComponentName.SupportedLanguages,\r
271 &ScsiDiskDevice->ControllerNameTable,\r
70da5bc2 272 L"SCSI Disk Device",\r
273 TRUE\r
6ad55b15 274 );\r
70da5bc2 275 AddUnicodeString2 (\r
276 "en",\r
277 gScsiDiskComponentName2.SupportedLanguages,\r
278 &ScsiDiskDevice->ControllerNameTable,\r
279 L"SCSI Disk Device",\r
280 FALSE\r
281 );\r
282\r
6ad55b15 283\r
284 return EFI_SUCCESS;\r
285\r
286}\r
287\r
9beb888e 288\r
289/**\r
290 Stop this driver on ControllerHandle.\r
291\r
292 This service is called by the EFI boot service DisconnectController().\r
293 In order to make drivers as small as possible, there are a few calling\r
294 restrictions for this service. DisconnectController() must follow these\r
295 calling restrictions. If any other agent wishes to call Stop() it must\r
296 also follow these calling restrictions.\r
297 \r
298 @param This Protocol instance pointer.\r
299 @param ControllerHandle Handle of device to stop driver on\r
300 @param NumberOfChildren Number of Handles in ChildHandleBuffer. If number of\r
301 children is zero stop the entire bus driver.\r
302 @param ChildHandleBuffer List of Child Handles to Stop.\r
303\r
304 @retval EFI_SUCCESS This driver is removed ControllerHandle\r
305 @retval other This driver was not removed from this device\r
306\r
307**/\r
6ad55b15 308EFI_STATUS\r
309EFIAPI\r
310ScsiDiskDriverBindingStop (\r
311 IN EFI_DRIVER_BINDING_PROTOCOL *This,\r
312 IN EFI_HANDLE Controller,\r
313 IN UINTN NumberOfChildren,\r
9beb888e 314 IN EFI_HANDLE *ChildHandleBuffer OPTIONAL\r
6ad55b15 315 )\r
6ad55b15 316{\r
317 EFI_BLOCK_IO_PROTOCOL *BlkIo;\r
318 SCSI_DISK_DEV *ScsiDiskDevice;\r
319 EFI_STATUS Status;\r
320\r
321 Status = gBS->OpenProtocol (\r
322 Controller,\r
323 &gEfiBlockIoProtocolGuid,\r
324 (VOID **) &BlkIo,\r
325 This->DriverBindingHandle,\r
326 Controller,\r
327 EFI_OPEN_PROTOCOL_GET_PROTOCOL\r
328 );\r
329 if (EFI_ERROR (Status)) {\r
330 return Status;\r
331 }\r
332\r
333 ScsiDiskDevice = SCSI_DISK_DEV_FROM_THIS (BlkIo);\r
334 Status = gBS->UninstallProtocolInterface (\r
335 Controller,\r
336 &gEfiBlockIoProtocolGuid,\r
337 &ScsiDiskDevice->BlkIo\r
338 );\r
339 if (!EFI_ERROR (Status)) {\r
340 gBS->CloseProtocol (\r
f36d6e66 341 Controller,\r
342 &gEfiScsiIoProtocolGuid,\r
343 This->DriverBindingHandle,\r
344 Controller\r
345 );\r
6ad55b15 346\r
347 ReleaseScsiDiskDeviceResources (ScsiDiskDevice);\r
348\r
349 return EFI_SUCCESS;\r
350 }\r
351 //\r
352 // errors met\r
353 //\r
354 return Status;\r
355}\r
356\r
9beb888e 357/**\r
358 Reset SCSI Disk.\r
359\r
6ad55b15 360\r
9beb888e 361 @param This The pointer of EFI_BLOCK_IO_PROTOCOL\r
362 @param ExtendedVerification The flag about if extend verificate\r
363\r
364 @retval EFI_SUCCESS The device was reset.\r
365 @retval EFI_DEVICE_ERROR The device is not functioning properly and could\r
366 not be reset.\r
367 @return EFI_STATUS is retured from EFI_SCSI_IO_PROTOCOL.ResetDevice().\r
368\r
369**/\r
6ad55b15 370EFI_STATUS\r
371EFIAPI\r
372ScsiDiskReset (\r
373 IN EFI_BLOCK_IO_PROTOCOL *This,\r
374 IN BOOLEAN ExtendedVerification\r
375 )\r
6ad55b15 376{\r
f36d6e66 377 EFI_TPL OldTpl;\r
6ad55b15 378 SCSI_DISK_DEV *ScsiDiskDevice;\r
379 EFI_STATUS Status;\r
6ad55b15 380\r
381 OldTpl = gBS->RaiseTPL (TPL_CALLBACK);\r
382\r
383 ScsiDiskDevice = SCSI_DISK_DEV_FROM_THIS (This);\r
384\r
385 Status = ScsiDiskDevice->ScsiIo->ResetDevice (ScsiDiskDevice->ScsiIo);\r
386\r
387 if (!ExtendedVerification) {\r
388 goto Done;\r
389 }\r
390\r
391 Status = ScsiDiskDevice->ScsiIo->ResetBus (ScsiDiskDevice->ScsiIo);\r
392\r
393Done:\r
394 gBS->RestoreTPL (OldTpl);\r
395 return Status;\r
396}\r
397\r
9beb888e 398/**\r
399 The function is to Read Block from SCSI Disk.\r
400\r
401 @param This The pointer of EFI_BLOCK_IO_PROTOCOL.\r
402 @param MediaId The Id of Media detected\r
403 @param Lba The logic block address\r
404 @param BufferSize The size of Buffer\r
405 @param Buffer The buffer to fill the read out data\r
406\r
407 @retval EFI_SUCCESS Successfully to read out block.\r
408 @retval EFI_DEVICE_ERROR Fail to detect media.\r
409 @retval EFI_NO_MEDIA Media is not present.\r
410 @retval EFI_MEDIA_CHANGED Media has changed.\r
411 @retval EFI_BAD_BUFFER_SIZE The Buffer was not a multiple of the block size of the device.\r
412 @retval EFI_INVALID_PARAMETER Invalid parameter passed in.\r
413\r
414**/\r
6ad55b15 415EFI_STATUS\r
416EFIAPI\r
417ScsiDiskReadBlocks (\r
418 IN EFI_BLOCK_IO_PROTOCOL *This,\r
419 IN UINT32 MediaId,\r
9beb888e 420 IN EFI_LBA Lba,\r
6ad55b15 421 IN UINTN BufferSize,\r
422 OUT VOID *Buffer\r
423 )\r
6ad55b15 424{\r
425 SCSI_DISK_DEV *ScsiDiskDevice;\r
426 EFI_BLOCK_IO_MEDIA *Media;\r
427 EFI_STATUS Status;\r
428 UINTN BlockSize;\r
429 UINTN NumberOfBlocks;\r
430 BOOLEAN MediaChange;\r
431 EFI_TPL OldTpl;\r
432\r
433 MediaChange = FALSE;\r
9beb888e 434 if (Buffer == NULL) {\r
6ad55b15 435 return EFI_INVALID_PARAMETER;\r
436 }\r
437\r
438 if (BufferSize == 0) {\r
439 return EFI_SUCCESS;\r
440 }\r
441\r
442 OldTpl = gBS->RaiseTPL (TPL_CALLBACK);\r
443\r
444 ScsiDiskDevice = SCSI_DISK_DEV_FROM_THIS (This);\r
445\r
9beb888e 446 if (!IS_DEVICE_FIXED(ScsiDiskDevice)) {\r
6ad55b15 447\r
448 Status = ScsiDiskDetectMedia (ScsiDiskDevice, FALSE, &MediaChange);\r
449 if (EFI_ERROR (Status)) {\r
450 Status = EFI_DEVICE_ERROR;\r
451 goto Done;\r
452 }\r
453\r
454 if (MediaChange) {\r
455 gBS->ReinstallProtocolInterface (\r
456 ScsiDiskDevice->Handle,\r
457 &gEfiBlockIoProtocolGuid,\r
458 &ScsiDiskDevice->BlkIo,\r
459 &ScsiDiskDevice->BlkIo\r
460 );\r
461 }\r
462 }\r
463 //\r
464 // Get the intrinsic block size\r
465 //\r
466 Media = ScsiDiskDevice->BlkIo.Media;\r
467 BlockSize = Media->BlockSize;\r
468\r
469 NumberOfBlocks = BufferSize / BlockSize;\r
470\r
471 if (!(Media->MediaPresent)) {\r
472 Status = EFI_NO_MEDIA;\r
473 goto Done;\r
474 }\r
475\r
476 if (MediaId != Media->MediaId) {\r
477 Status = EFI_MEDIA_CHANGED;\r
478 goto Done;\r
479 }\r
480\r
481 if (BufferSize % BlockSize != 0) {\r
482 Status = EFI_BAD_BUFFER_SIZE;\r
483 goto Done;\r
484 }\r
485\r
9beb888e 486 if (Lba > Media->LastBlock) {\r
6ad55b15 487 Status = EFI_INVALID_PARAMETER;\r
488 goto Done;\r
489 }\r
490\r
9beb888e 491 if ((Lba + NumberOfBlocks - 1) > Media->LastBlock) {\r
6ad55b15 492 Status = EFI_INVALID_PARAMETER;\r
493 goto Done;\r
494 }\r
495\r
496 if ((Media->IoAlign > 1) && (((UINTN) Buffer & (Media->IoAlign - 1)) != 0)) {\r
497 Status = EFI_INVALID_PARAMETER;\r
498 goto Done;\r
499 }\r
f36d6e66 500\r
6ad55b15 501 //\r
f36d6e66 502 // If all the parameters are valid, then perform read sectors command\r
6ad55b15 503 // to transfer data from device to host.\r
504 //\r
9beb888e 505 Status = ScsiDiskReadSectors (ScsiDiskDevice, Buffer, Lba, NumberOfBlocks);\r
6ad55b15 506\r
507Done:\r
508 gBS->RestoreTPL (OldTpl);\r
509 return Status;\r
510}\r
511\r
9beb888e 512/**\r
513 The function is to Write Block to SCSI Disk.\r
514\r
515 @param This The pointer of EFI_BLOCK_IO_PROTOCOL\r
516 @param MediaId The Id of Media detected\r
517 @param Lba The logic block address\r
518 @param BufferSize The size of Buffer\r
519 @param Buffer The buffer to fill the read out data\r
520\r
521 @retval EFI_SUCCESS Successfully to read out block.\r
522 @retval EFI_WRITE_PROTECTED The device can not be written to.\r
523 @retval EFI_DEVICE_ERROR Fail to detect media.\r
524 @retval EFI_NO_MEDIA Media is not present.\r
525 @retval EFI_MEDIA_CHNAGED Media has changed.\r
526 @retval EFI_BAD_BUFFER_SIZE The Buffer was not a multiple of the block size of the device.\r
527 @retval EFI_INVALID_PARAMETER Invalid parameter passed in.\r
528\r
529**/\r
6ad55b15 530EFI_STATUS\r
531EFIAPI\r
532ScsiDiskWriteBlocks (\r
533 IN EFI_BLOCK_IO_PROTOCOL *This,\r
534 IN UINT32 MediaId,\r
9beb888e 535 IN EFI_LBA Lba,\r
6ad55b15 536 IN UINTN BufferSize,\r
537 IN VOID *Buffer\r
538 )\r
6ad55b15 539{\r
540 SCSI_DISK_DEV *ScsiDiskDevice;\r
541 EFI_BLOCK_IO_MEDIA *Media;\r
542 EFI_STATUS Status;\r
543 UINTN BlockSize;\r
544 UINTN NumberOfBlocks;\r
545 BOOLEAN MediaChange;\r
546 EFI_TPL OldTpl;\r
547\r
548 MediaChange = FALSE;\r
9beb888e 549 if (Buffer == NULL) {\r
6ad55b15 550 return EFI_INVALID_PARAMETER;\r
551 }\r
552\r
553 if (BufferSize == 0) {\r
554 return EFI_SUCCESS;\r
555 }\r
556\r
557 OldTpl = gBS->RaiseTPL (TPL_CALLBACK);\r
558\r
559 ScsiDiskDevice = SCSI_DISK_DEV_FROM_THIS (This);\r
560\r
9beb888e 561 if (!IS_DEVICE_FIXED(ScsiDiskDevice)) {\r
6ad55b15 562\r
563 Status = ScsiDiskDetectMedia (ScsiDiskDevice, FALSE, &MediaChange);\r
564 if (EFI_ERROR (Status)) {\r
565 Status = EFI_DEVICE_ERROR;\r
566 goto Done;\r
567 }\r
568\r
569 if (MediaChange) {\r
570 gBS->ReinstallProtocolInterface (\r
571 ScsiDiskDevice->Handle,\r
572 &gEfiBlockIoProtocolGuid,\r
573 &ScsiDiskDevice->BlkIo,\r
574 &ScsiDiskDevice->BlkIo\r
575 );\r
576 }\r
577 }\r
578 //\r
579 // Get the intrinsic block size\r
580 //\r
581 Media = ScsiDiskDevice->BlkIo.Media;\r
582 BlockSize = Media->BlockSize;\r
583\r
584 NumberOfBlocks = BufferSize / BlockSize;\r
585\r
586 if (!(Media->MediaPresent)) {\r
587 Status = EFI_NO_MEDIA;\r
588 goto Done;\r
589 }\r
590\r
591 if (MediaId != Media->MediaId) {\r
592 Status = EFI_MEDIA_CHANGED;\r
593 goto Done;\r
594 }\r
595\r
596 if (BufferSize % BlockSize != 0) {\r
597 Status = EFI_BAD_BUFFER_SIZE;\r
598 goto Done;\r
599 }\r
600\r
9beb888e 601 if (Lba > Media->LastBlock) {\r
6ad55b15 602 Status = EFI_INVALID_PARAMETER;\r
603 goto Done;\r
604 }\r
605\r
9beb888e 606 if ((Lba + NumberOfBlocks - 1) > Media->LastBlock) {\r
6ad55b15 607 Status = EFI_INVALID_PARAMETER;\r
608 goto Done;\r
609 }\r
610\r
611 if ((Media->IoAlign > 1) && (((UINTN) Buffer & (Media->IoAlign - 1)) != 0)) {\r
612 Status = EFI_INVALID_PARAMETER;\r
613 goto Done;\r
614 }\r
615 //\r
616 // if all the parameters are valid, then perform read sectors command\r
617 // to transfer data from device to host.\r
618 //\r
9beb888e 619 Status = ScsiDiskWriteSectors (ScsiDiskDevice, Buffer, Lba, NumberOfBlocks);\r
6ad55b15 620\r
621Done:\r
622 gBS->RestoreTPL (OldTpl);\r
6ad55b15 623 return Status;\r
624}\r
625\r
9beb888e 626/**\r
627 Flush Block to Disk.\r
628\r
629 EFI_SUCCESS is returned directly.\r
630\r
631 @param This The pointer of EFI_BLOCK_IO_PROTOCOL\r
632\r
633 @retval EFI_SUCCESS All outstanding data was written to the device\r
634\r
635**/\r
6ad55b15 636EFI_STATUS\r
637EFIAPI\r
638ScsiDiskFlushBlocks (\r
639 IN EFI_BLOCK_IO_PROTOCOL *This\r
640 )\r
6ad55b15 641{\r
642 //\r
643 // return directly\r
644 //\r
645 return EFI_SUCCESS;\r
646}\r
647\r
6ad55b15 648\r
9beb888e 649/**\r
f36d6e66 650 Dectect Device and read out capacity ,if error occurs, parse the sense key.\r
6ad55b15 651\r
9beb888e 652 @param ScsiDiskDevice The pointer of SCSI_DISK_DEV\r
653 @param MustReadCapacity The flag about reading device capacity\r
654 @param MediaChange The pointer of flag indicates if media has changed \r
6ad55b15 655\r
9beb888e 656 @retval EFI_DEVICE_ERROR Indicates that error occurs\r
657 @retval EFI_SUCCESS Successfully to detect media\r
6ad55b15 658\r
9beb888e 659**/\r
660EFI_STATUS\r
661ScsiDiskDetectMedia (\r
662 IN SCSI_DISK_DEV *ScsiDiskDevice,\r
663 IN BOOLEAN MustReadCapacity,\r
664 OUT BOOLEAN *MediaChange\r
665 )\r
6ad55b15 666{\r
667 EFI_STATUS Status;\r
668 EFI_STATUS ReadCapacityStatus;\r
669 EFI_SCSI_SENSE_DATA *SenseData;\r
670 UINTN NumberOfSenseKeys;\r
671 BOOLEAN NeedRetry;\r
672 BOOLEAN NeedReadCapacity;\r
673 UINT8 Index;\r
674 UINT8 MaxRetry;\r
675 EFI_BLOCK_IO_MEDIA OldMedia;\r
676 UINTN Action;\r
677\r
678 Status = EFI_SUCCESS;\r
679 ReadCapacityStatus = EFI_SUCCESS;\r
680 SenseData = NULL;\r
681 NumberOfSenseKeys = 0;\r
682 NeedReadCapacity = FALSE;\r
683 CopyMem (&OldMedia, ScsiDiskDevice->BlkIo.Media, sizeof (OldMedia));\r
6ad55b15 684 *MediaChange = FALSE;\r
6ad55b15 685 MaxRetry = 3;\r
f36d6e66 686\r
6ad55b15 687 for (Index = 0; Index < MaxRetry; Index++) {\r
688 Status = ScsiDiskTestUnitReady (\r
689 ScsiDiskDevice,\r
690 &NeedRetry,\r
691 &SenseData,\r
692 &NumberOfSenseKeys\r
693 );\r
694 if (!EFI_ERROR (Status)) {\r
695 break;\r
696 }\r
697\r
698 if (!NeedRetry) {\r
699 return Status;\r
700 }\r
701 }\r
702\r
703 if ((Index == MaxRetry) && EFI_ERROR (Status)) {\r
704 return EFI_DEVICE_ERROR;\r
705 }\r
706\r
707 Status = DetectMediaParsingSenseKeys (\r
708 ScsiDiskDevice,\r
709 SenseData,\r
710 NumberOfSenseKeys,\r
711 &Action\r
712 );\r
713 if (EFI_ERROR (Status)) {\r
714 return Status;\r
715 }\r
716 //\r
717 // ACTION_NO_ACTION: need not read capacity\r
718 // other action code: need read capacity\r
719 //\r
720 if (Action == ACTION_NO_ACTION) {\r
721 NeedReadCapacity = FALSE;\r
722 } else {\r
723 NeedReadCapacity = TRUE;\r
724 }\r
f36d6e66 725\r
6ad55b15 726 //\r
727 // either NeedReadCapacity is TRUE, or MustReadCapacity is TRUE,\r
728 // retrieve capacity via Read Capacity command\r
729 //\r
730 if (NeedReadCapacity || MustReadCapacity) {\r
6ad55b15 731 //\r
732 // retrieve media information\r
733 //\r
734 MaxRetry = 3;\r
735 for (Index = 0; Index < MaxRetry; Index++) {\r
736\r
737 ReadCapacityStatus = ScsiDiskReadCapacity (\r
738 ScsiDiskDevice,\r
739 &NeedRetry,\r
740 &SenseData,\r
741 &NumberOfSenseKeys\r
742 );\r
743 if (EFI_ERROR (ReadCapacityStatus) && !NeedRetry) {\r
744 return EFI_DEVICE_ERROR;\r
745 }\r
746 //\r
747 // analyze sense key to action\r
748 //\r
749 Status = DetectMediaParsingSenseKeys (\r
750 ScsiDiskDevice,\r
751 SenseData,\r
752 NumberOfSenseKeys,\r
753 &Action\r
754 );\r
755 //\r
756 // if Status is error, it may indicate crisis error,\r
757 // so return without retry.\r
758 //\r
759 if (EFI_ERROR (Status)) {\r
760 return Status;\r
761 }\r
762\r
763 switch (Action) {\r
764 case ACTION_NO_ACTION:\r
765 //\r
766 // no retry\r
767 //\r
768 Index = MaxRetry;\r
769 break;\r
770\r
771 case ACTION_RETRY_COMMAND_LATER:\r
772 //\r
773 // retry the ReadCapacity later and continuously, until the condition\r
774 // no longer emerges.\r
775 // stall time is 100000us, or say 0.1 second.\r
776 //\r
777 gBS->Stall (100000);\r
778 Index = 0;\r
779 break;\r
780\r
781 default:\r
782 //\r
783 // other cases, just retry the command\r
784 //\r
785 break;\r
786 }\r
787 }\r
788\r
789 if ((Index == MaxRetry) && EFI_ERROR (ReadCapacityStatus)) {\r
790 return EFI_DEVICE_ERROR;\r
791 }\r
792 }\r
793\r
794 if (ScsiDiskDevice->BlkIo.Media->MediaId != OldMedia.MediaId) {\r
795 //\r
796 // Media change information got from the device\r
797 //\r
798 *MediaChange = TRUE;\r
799 }\r
800\r
801 if (ScsiDiskDevice->BlkIo.Media->ReadOnly != OldMedia.ReadOnly) {\r
802 *MediaChange = TRUE;\r
803 ScsiDiskDevice->BlkIo.Media->MediaId += 1;\r
804 }\r
805\r
806 if (ScsiDiskDevice->BlkIo.Media->BlockSize != OldMedia.BlockSize) {\r
807 *MediaChange = TRUE;\r
808 ScsiDiskDevice->BlkIo.Media->MediaId += 1;\r
809 }\r
810\r
811 if (ScsiDiskDevice->BlkIo.Media->LastBlock != OldMedia.LastBlock) {\r
812 *MediaChange = TRUE;\r
813 ScsiDiskDevice->BlkIo.Media->MediaId += 1;\r
814 }\r
815\r
816 if (ScsiDiskDevice->BlkIo.Media->MediaPresent != OldMedia.MediaPresent) {\r
817 if (ScsiDiskDevice->BlkIo.Media->MediaPresent) {\r
818 //\r
819 // when change from no media to media present, reset the MediaId to 1.\r
820 //\r
821 ScsiDiskDevice->BlkIo.Media->MediaId = 1;\r
822 } else {\r
823 //\r
824 // when no media, reset the MediaId to zero.\r
825 //\r
826 ScsiDiskDevice->BlkIo.Media->MediaId = 0;\r
827 }\r
828\r
829 *MediaChange = TRUE;\r
830 }\r
831\r
832 return EFI_SUCCESS;\r
833}\r
834\r
6ad55b15 835\r
9beb888e 836/**\r
837 Send out Inquiry command to Device.\r
6ad55b15 838\r
9beb888e 839 @param ScsiDiskDevice The pointer of SCSI_DISK_DEV\r
840 @param NeedRetry Indicates if needs try again when error happens\r
6ad55b15 841\r
9beb888e 842 @retval EFI_DEVICE_ERROR Indicates that error occurs\r
843 @retval EFI_SUCCESS Successfully to detect media\r
6ad55b15 844\r
9beb888e 845**/\r
846EFI_STATUS\r
847ScsiDiskInquiryDevice (\r
848 IN OUT SCSI_DISK_DEV *ScsiDiskDevice,\r
849 OUT BOOLEAN *NeedRetry\r
850 )\r
6ad55b15 851{\r
852 UINT32 InquiryDataLength;\r
853 UINT8 SenseDataLength;\r
854 UINT8 HostAdapterStatus;\r
855 UINT8 TargetStatus;\r
856 EFI_SCSI_SENSE_DATA *SenseDataArray;\r
857 UINTN NumberOfSenseKeys;\r
858 EFI_STATUS Status;\r
859 UINT8 MaxRetry;\r
860 UINT8 Index;\r
861\r
862 InquiryDataLength = sizeof (EFI_SCSI_INQUIRY_DATA);\r
863 SenseDataLength = 0;\r
864\r
d35be2a4 865 Status = ScsiInquiryCommand (\r
6ad55b15 866 ScsiDiskDevice->ScsiIo,\r
e72a3b3e 867 EFI_TIMER_PERIOD_SECONDS (1),\r
6ad55b15 868 NULL,\r
869 &SenseDataLength,\r
870 &HostAdapterStatus,\r
871 &TargetStatus,\r
872 (VOID *) &(ScsiDiskDevice->InquiryData),\r
873 &InquiryDataLength,\r
874 FALSE\r
875 );\r
6ad55b15 876 //\r
877 // no need to check HostAdapterStatus and TargetStatus\r
878 //\r
f36d6e66 879 if ((Status == EFI_SUCCESS) || (Status == EFI_WARN_BUFFER_TOO_SMALL)) {\r
880 ParseInquiryData (ScsiDiskDevice);\r
881 return EFI_SUCCESS;\r
882 \r
883 } else if (Status == EFI_NOT_READY) {\r
884 *NeedRetry = TRUE;\r
885 return EFI_DEVICE_ERROR;\r
886 \r
887 } else if ((Status == EFI_INVALID_PARAMETER) || (Status == EFI_UNSUPPORTED)) {\r
888 *NeedRetry = FALSE;\r
889 return EFI_DEVICE_ERROR;\r
890 }\r
891 //\r
892 // go ahead to check HostAdapterStatus and TargetStatus\r
893 // (EFI_TIMEOUT, EFI_DEVICE_ERROR)\r
894 //\r
895 \r
896 Status = CheckHostAdapterStatus (HostAdapterStatus);\r
897 if ((Status == EFI_TIMEOUT) || (Status == EFI_NOT_READY)) {\r
898 *NeedRetry = TRUE;\r
899 return EFI_DEVICE_ERROR;\r
900 } else if (Status == EFI_DEVICE_ERROR) {\r
901 //\r
902 // reset the scsi channel\r
903 //\r
6ad55b15 904 ScsiDiskDevice->ScsiIo->ResetBus (ScsiDiskDevice->ScsiIo);\r
905 *NeedRetry = FALSE;\r
906 return EFI_DEVICE_ERROR;\r
907 }\r
908\r
909 Status = CheckTargetStatus (TargetStatus);\r
910 if (Status == EFI_NOT_READY) {\r
911 //\r
912 // reset the scsi device\r
913 //\r
914 ScsiDiskDevice->ScsiIo->ResetDevice (ScsiDiskDevice->ScsiIo);\r
915 *NeedRetry = TRUE;\r
916 return EFI_DEVICE_ERROR;\r
f36d6e66 917\r
6ad55b15 918 } else if (Status == EFI_DEVICE_ERROR) {\r
919 *NeedRetry = FALSE;\r
920 return EFI_DEVICE_ERROR;\r
921 }\r
922 \r
923 //\r
b96cd313 924 // if goes here, meant ScsiInquiryCommand() failed.\r
6ad55b15 925 // if ScsiDiskRequestSenseKeys() succeeds at last,\r
b96cd313 926 // better retry ScsiInquiryCommand(). (by setting *NeedRetry = TRUE)\r
6ad55b15 927 //\r
928 MaxRetry = 3;\r
929 for (Index = 0; Index < MaxRetry; Index++) {\r
6ad55b15 930 Status = ScsiDiskRequestSenseKeys (\r
931 ScsiDiskDevice,\r
932 NeedRetry,\r
933 &SenseDataArray,\r
934 &NumberOfSenseKeys,\r
935 TRUE\r
936 );\r
937 if (!EFI_ERROR (Status)) {\r
938 *NeedRetry = TRUE;\r
939 return EFI_DEVICE_ERROR;\r
940 }\r
941\r
942 if (!*NeedRetry) {\r
943 return EFI_DEVICE_ERROR;\r
944 }\r
945 }\r
946 //\r
947 // ScsiDiskRequestSenseKeys() failed after several rounds of retry.\r
948 // set *NeedRetry = FALSE to avoid the outside caller try again.\r
949 //\r
950 *NeedRetry = FALSE;\r
951 return EFI_DEVICE_ERROR;\r
952}\r
953\r
9beb888e 954/**\r
955 To test deivice.\r
f36d6e66 956\r
957 When Test Unit Ready command succeeds, retrieve Sense Keys via Request Sense;\r
6ad55b15 958 When Test Unit Ready command encounters any error caused by host adapter or\r
959 target, return error without retrieving Sense Keys.\r
f36d6e66 960\r
9beb888e 961 @param ScsiDiskDevice The pointer of SCSI_DISK_DEV\r
962 @param NeedRetry The pointer of flag indicates try again\r
963 @param SenseDataArray The pointer of an array of sense data\r
964 @param NumberOfSenseKeys The pointer of the number of sense data array\r
f36d6e66 965\r
9beb888e 966 @retval EFI_DEVICE_ERROR Indicates that error occurs\r
967 @retval EFI_SUCCESS Successfully to test unit\r
f36d6e66 968\r
9beb888e 969**/\r
970EFI_STATUS\r
971ScsiDiskTestUnitReady (\r
972 IN SCSI_DISK_DEV *ScsiDiskDevice,\r
973 OUT BOOLEAN *NeedRetry,\r
974 OUT EFI_SCSI_SENSE_DATA **SenseDataArray,\r
975 OUT UINTN *NumberOfSenseKeys\r
976 )\r
6ad55b15 977{\r
978 EFI_STATUS Status;\r
979 UINT8 SenseDataLength;\r
980 UINT8 HostAdapterStatus;\r
981 UINT8 TargetStatus;\r
982 UINT8 Index;\r
983 UINT8 MaxRetry;\r
984\r
985 SenseDataLength = 0;\r
986 *NumberOfSenseKeys = 0;\r
987\r
988 //\r
989 // Parameter 3 and 4: do not require sense data, retrieve it when needed.\r
990 //\r
d35be2a4 991 Status = ScsiTestUnitReadyCommand (\r
6ad55b15 992 ScsiDiskDevice->ScsiIo,\r
e72a3b3e 993 EFI_TIMER_PERIOD_SECONDS (1),\r
6ad55b15 994 NULL,\r
995 &SenseDataLength,\r
996 &HostAdapterStatus,\r
997 &TargetStatus\r
998 );\r
f36d6e66 999 //\r
1000 // no need to check HostAdapterStatus and TargetStatus\r
1001 //\r
6ad55b15 1002 if (Status == EFI_NOT_READY) {\r
6ad55b15 1003 *NeedRetry = TRUE;\r
1004 return EFI_DEVICE_ERROR;\r
f36d6e66 1005\r
6ad55b15 1006 } else if ((Status == EFI_INVALID_PARAMETER) || (Status == EFI_UNSUPPORTED)) {\r
6ad55b15 1007 *NeedRetry = FALSE;\r
1008 return EFI_DEVICE_ERROR;\r
1009 }\r
1010 //\r
f36d6e66 1011 // go ahead to check HostAdapterStatus and TargetStatus(in case of EFI_DEVICE_ERROR)\r
6ad55b15 1012 //\r
f36d6e66 1013\r
6ad55b15 1014 Status = CheckHostAdapterStatus (HostAdapterStatus);\r
1015 if ((Status == EFI_TIMEOUT) || (Status == EFI_NOT_READY)) {\r
1016 *NeedRetry = TRUE;\r
1017 return EFI_DEVICE_ERROR;\r
f36d6e66 1018\r
6ad55b15 1019 } else if (Status == EFI_DEVICE_ERROR) {\r
1020 //\r
1021 // reset the scsi channel\r
1022 //\r
1023 ScsiDiskDevice->ScsiIo->ResetBus (ScsiDiskDevice->ScsiIo);\r
1024 *NeedRetry = FALSE;\r
1025 return EFI_DEVICE_ERROR;\r
1026 }\r
1027\r
1028 Status = CheckTargetStatus (TargetStatus);\r
1029 if (Status == EFI_NOT_READY) {\r
1030 //\r
1031 // reset the scsi device\r
1032 //\r
1033 ScsiDiskDevice->ScsiIo->ResetDevice (ScsiDiskDevice->ScsiIo);\r
1034 *NeedRetry = TRUE;\r
1035 return EFI_DEVICE_ERROR;\r
f36d6e66 1036\r
6ad55b15 1037 } else if (Status == EFI_DEVICE_ERROR) {\r
1038 *NeedRetry = FALSE;\r
1039 return EFI_DEVICE_ERROR;\r
1040 }\r
1041\r
1042 MaxRetry = 3;\r
1043 for (Index = 0; Index < MaxRetry; Index++) {\r
6ad55b15 1044 Status = ScsiDiskRequestSenseKeys (\r
1045 ScsiDiskDevice,\r
1046 NeedRetry,\r
1047 SenseDataArray,\r
1048 NumberOfSenseKeys,\r
1049 FALSE\r
1050 );\r
1051 if (!EFI_ERROR (Status)) {\r
1052 return EFI_SUCCESS;\r
1053 }\r
1054\r
1055 if (!*NeedRetry) {\r
1056 return EFI_DEVICE_ERROR;\r
1057 }\r
1058 }\r
1059 //\r
1060 // ScsiDiskRequestSenseKeys() failed after several rounds of retry.\r
1061 // set *NeedRetry = FALSE to avoid the outside caller try again.\r
1062 //\r
1063 *NeedRetry = FALSE;\r
1064 return EFI_DEVICE_ERROR;\r
1065}\r
1066\r
9beb888e 1067/**\r
f36d6e66 1068 Parsing Sense Keys which got from request sense command.\r
6ad55b15 1069\r
9beb888e 1070 @param ScsiDiskDevice The pointer of SCSI_DISK_DEV\r
1071 @param SenseData The pointer of EFI_SCSI_SENSE_DATA\r
1072 @param NumberOfSenseKeys The number of sense key \r
1073 @param Action The pointer of action which indicates what is need to do next\r
6ad55b15 1074\r
9beb888e 1075 @retval EFI_DEVICE_ERROR Indicates that error occurs\r
1076 @retval EFI_SUCCESS Successfully to complete the parsing\r
6ad55b15 1077\r
9beb888e 1078**/\r
1079EFI_STATUS\r
1080DetectMediaParsingSenseKeys (\r
1081 OUT SCSI_DISK_DEV *ScsiDiskDevice,\r
1082 IN EFI_SCSI_SENSE_DATA *SenseData,\r
1083 IN UINTN NumberOfSenseKeys,\r
1084 OUT UINTN *Action\r
1085 )\r
6ad55b15 1086{\r
1087 BOOLEAN RetryLater;\r
1088\r
1089 //\r
1090 // Default is to read capacity, unless..\r
1091 //\r
1092 *Action = ACTION_READ_CAPACITY;\r
1093\r
1094 if (NumberOfSenseKeys == 0) {\r
1095 *Action = ACTION_NO_ACTION;\r
1096 return EFI_SUCCESS;\r
1097 }\r
1098\r
1099 if (!ScsiDiskHaveSenseKey (SenseData, NumberOfSenseKeys)) {\r
1100 //\r
1101 // No Sense Key returned from last submitted command\r
1102 //\r
1103 *Action = ACTION_NO_ACTION;\r
1104 return EFI_SUCCESS;\r
1105 }\r
1106\r
1107 if (ScsiDiskIsNoMedia (SenseData, NumberOfSenseKeys)) {\r
1108 ScsiDiskDevice->BlkIo.Media->MediaPresent = FALSE;\r
1109 ScsiDiskDevice->BlkIo.Media->LastBlock = 0;\r
1110 *Action = ACTION_NO_ACTION;\r
1111 return EFI_SUCCESS;\r
1112 }\r
1113\r
1114 if (ScsiDiskIsMediaChange (SenseData, NumberOfSenseKeys)) {\r
1115 ScsiDiskDevice->BlkIo.Media->MediaId++;\r
1116 return EFI_SUCCESS;\r
1117 }\r
1118\r
1119 if (ScsiDiskIsMediaError (SenseData, NumberOfSenseKeys)) {\r
1120 ScsiDiskDevice->BlkIo.Media->MediaPresent = FALSE;\r
1121 ScsiDiskDevice->BlkIo.Media->LastBlock = 0;\r
1122 return EFI_DEVICE_ERROR;\r
1123 }\r
1124\r
1125 if (ScsiDiskIsHardwareError (SenseData, NumberOfSenseKeys)) {\r
1126 return EFI_DEVICE_ERROR;\r
1127 }\r
1128\r
1129 if (!ScsiDiskIsDriveReady (SenseData, NumberOfSenseKeys, &RetryLater)) {\r
1130 if (RetryLater) {\r
1131 *Action = ACTION_RETRY_COMMAND_LATER;\r
1132 return EFI_SUCCESS;\r
1133 }\r
1134\r
1135 return EFI_DEVICE_ERROR;\r
1136 }\r
1137\r
1138 return EFI_SUCCESS;\r
1139}\r
1140\r
6ad55b15 1141\r
9beb888e 1142/**\r
1143 Send read capacity command to device and get the device parameter.\r
6ad55b15 1144\r
9beb888e 1145 @param ScsiDiskDevice The pointer of SCSI_DISK_DEV\r
1146 @param NeedRetry The pointer of flag indicates if need a retry\r
1147 @param SenseDataArray The pointer of an array of sense data\r
1148 @param NumberOfSenseKeys The number of sense key\r
6ad55b15 1149\r
9beb888e 1150 @retval EFI_DEVICE_ERROR Indicates that error occurs\r
1151 @retval EFI_SUCCESS Successfully to read capacity\r
6ad55b15 1152\r
9beb888e 1153**/\r
1154EFI_STATUS\r
1155ScsiDiskReadCapacity (\r
1156 IN OUT SCSI_DISK_DEV *ScsiDiskDevice,\r
1157 OUT BOOLEAN *NeedRetry,\r
1158 OUT EFI_SCSI_SENSE_DATA **SenseDataArray,\r
1159 OUT UINTN *NumberOfSenseKeys\r
1160 )\r
6ad55b15 1161{\r
b96cd313 1162 UINT8 HostAdapterStatus;\r
1163 UINT8 TargetStatus;\r
1164 EFI_STATUS CommandStatus;\r
1165 EFI_STATUS Status;\r
1166 UINT8 Index;\r
1167 UINT8 MaxRetry;\r
1168 UINT8 SenseDataLength;\r
1169 UINT8 ScsiVersion;\r
1170 UINT32 DataLength10;\r
1171 UINT32 DataLength16;\r
1172 EFI_SCSI_DISK_CAPACITY_DATA CapacityData10;\r
1173 EFI_SCSI_DISK_CAPACITY_DATA16 CapacityData16;\r
1174\r
1175\r
1176 SenseDataLength = 0;\r
1177 DataLength10 = sizeof (EFI_SCSI_DISK_CAPACITY_DATA);\r
1178 DataLength16 = sizeof (EFI_SCSI_DISK_CAPACITY_DATA16);\r
1179 ZeroMem (&CapacityData10, sizeof (EFI_SCSI_DISK_CAPACITY_DATA));\r
1180 ZeroMem (&CapacityData16, sizeof (EFI_SCSI_DISK_CAPACITY_DATA16));\r
6ad55b15 1181\r
1182 *NumberOfSenseKeys = 0;\r
1183 *NeedRetry = FALSE;\r
b96cd313 1184 ScsiVersion = (UINT8)(ScsiDiskDevice->InquiryData.Version & 0x03);\r
1185\r
1186 if (ScsiVersion < SCSI_COMMAND_VERSION_3) {\r
1187 //\r
1188 // submit Read Capacity(10) Command. in this call,not request sense data\r
1189 //\r
1190 CommandStatus = ScsiReadCapacityCommand (\r
1191 ScsiDiskDevice->ScsiIo,\r
1192 EFI_TIMER_PERIOD_SECONDS(1),\r
1193 NULL,\r
1194 &SenseDataLength,\r
1195 &HostAdapterStatus,\r
1196 &TargetStatus,\r
1197 (VOID *) &CapacityData10,\r
1198 &DataLength10,\r
1199 FALSE\r
1200 );\r
1201 } else {\r
1202 //\r
1203 // submit Read Capacity(16) Command to get parameter LogicalBlocksPerPhysicalBlock\r
1204 // and LowestAlignedLba\r
1205 //\r
1206 CommandStatus = ScsiReadCapacity16Command (\r
1207 ScsiDiskDevice->ScsiIo,\r
1208 EFI_TIMER_PERIOD_SECONDS (1),\r
1209 NULL,\r
1210 &SenseDataLength,\r
1211 &HostAdapterStatus,\r
1212 &TargetStatus,\r
1213 (VOID *) &CapacityData16,\r
1214 &DataLength16,\r
1215 FALSE\r
1216 );\r
1217 }\r
1218 //\r
6ad55b15 1219 // no need to check HostAdapterStatus and TargetStatus\r
1220 //\r
f36d6e66 1221 if (CommandStatus == EFI_SUCCESS) {\r
b96cd313 1222 GetMediaInfo (ScsiDiskDevice, &CapacityData10,&CapacityData16);\r
f36d6e66 1223 return EFI_SUCCESS;\r
1224 \r
1225 } else if (CommandStatus == EFI_NOT_READY) {\r
1226 *NeedRetry = TRUE;\r
1227 return EFI_DEVICE_ERROR;\r
1228 \r
1229 } else if ((CommandStatus == EFI_INVALID_PARAMETER) || (CommandStatus == EFI_UNSUPPORTED)) {\r
1230 *NeedRetry = FALSE;\r
1231 return EFI_DEVICE_ERROR;\r
1232 }\r
1233 //\r
1234 // go ahead to check HostAdapterStatus and TargetStatus\r
1235 // (EFI_TIMEOUT, EFI_DEVICE_ERROR, EFI_WARN_BUFFER_TOO_SMALL)\r
1236 //\r
1237 \r
1238 Status = CheckHostAdapterStatus (HostAdapterStatus);\r
1239 if ((Status == EFI_TIMEOUT) || (Status == EFI_NOT_READY)) {\r
1240 *NeedRetry = TRUE;\r
1241 return EFI_DEVICE_ERROR;\r
1242 \r
1243 } else if (Status == EFI_DEVICE_ERROR) {\r
6ad55b15 1244 //\r
1245 // reset the scsi channel\r
1246 //\r
1247 ScsiDiskDevice->ScsiIo->ResetBus (ScsiDiskDevice->ScsiIo);\r
1248 *NeedRetry = FALSE;\r
1249 return EFI_DEVICE_ERROR;\r
1250 }\r
1251\r
1252 Status = CheckTargetStatus (TargetStatus);\r
1253 if (Status == EFI_NOT_READY) {\r
1254 //\r
1255 // reset the scsi device\r
1256 //\r
1257 ScsiDiskDevice->ScsiIo->ResetDevice (ScsiDiskDevice->ScsiIo);\r
1258 *NeedRetry = TRUE;\r
1259 return EFI_DEVICE_ERROR;\r
f36d6e66 1260\r
6ad55b15 1261 } else if (Status == EFI_DEVICE_ERROR) {\r
1262 *NeedRetry = FALSE;\r
1263 return EFI_DEVICE_ERROR;\r
1264 }\r
1265 \r
1266 //\r
b96cd313 1267 // if goes here, meant ScsiReadCapacityCommand() failed.\r
6ad55b15 1268 // if ScsiDiskRequestSenseKeys() succeeds at last,\r
b96cd313 1269 // better retry ScsiReadCapacityCommand(). (by setting *NeedRetry = TRUE)\r
6ad55b15 1270 //\r
1271 MaxRetry = 3;\r
1272 for (Index = 0; Index < MaxRetry; Index++) {\r
1273\r
1274 Status = ScsiDiskRequestSenseKeys (\r
1275 ScsiDiskDevice,\r
1276 NeedRetry,\r
1277 SenseDataArray,\r
1278 NumberOfSenseKeys,\r
1279 TRUE\r
1280 );\r
1281 if (!EFI_ERROR (Status)) {\r
1282 *NeedRetry = TRUE;\r
1283 return EFI_DEVICE_ERROR;\r
1284 }\r
1285\r
1286 if (!*NeedRetry) {\r
1287 return EFI_DEVICE_ERROR;\r
1288 }\r
1289 }\r
1290 //\r
1291 // ScsiDiskRequestSenseKeys() failed after several rounds of retry.\r
1292 // set *NeedRetry = FALSE to avoid the outside caller try again.\r
1293 //\r
1294 *NeedRetry = FALSE;\r
1295 return EFI_DEVICE_ERROR;\r
1296}\r
1297\r
9beb888e 1298/**\r
1299 Check the HostAdapter status and re-interpret it in EFI_STATUS.\r
6ad55b15 1300\r
9beb888e 1301 @param HostAdapterStatus Host Adapter status\r
6ad55b15 1302\r
9beb888e 1303 @retval EFI_SUCCESS Host adapter is OK.\r
1304 @retval EFI_TIMEOUT Timeout.\r
1305 @retval EFI_NOT_READY Adapter NOT ready.\r
1306 @retval EFI_DEVICE_ERROR Adapter device error.\r
6ad55b15 1307\r
9beb888e 1308**/\r
1309EFI_STATUS\r
1310CheckHostAdapterStatus (\r
1311 IN UINT8 HostAdapterStatus\r
1312 )\r
6ad55b15 1313{\r
1314 switch (HostAdapterStatus) {\r
f36d6e66 1315 case EFI_EXT_SCSI_STATUS_HOST_ADAPTER_OK:\r
6ad55b15 1316 return EFI_SUCCESS;\r
1317\r
f36d6e66 1318 case EFI_EXT_SCSI_STATUS_HOST_ADAPTER_SELECTION_TIMEOUT:\r
1319 case EFI_EXT_SCSI_STATUS_HOST_ADAPTER_TIMEOUT:\r
1320 case EFI_EXT_SCSI_STATUS_HOST_ADAPTER_TIMEOUT_COMMAND:\r
6ad55b15 1321 return EFI_TIMEOUT;\r
1322\r
f36d6e66 1323 case EFI_EXT_SCSI_STATUS_HOST_ADAPTER_MESSAGE_REJECT:\r
1324 case EFI_EXT_SCSI_STATUS_HOST_ADAPTER_PARITY_ERROR:\r
1325 case EFI_EXT_SCSI_STATUS_HOST_ADAPTER_REQUEST_SENSE_FAILED:\r
1326 case EFI_EXT_SCSI_STATUS_HOST_ADAPTER_DATA_OVERRUN_UNDERRUN:\r
1327 case EFI_EXT_SCSI_STATUS_HOST_ADAPTER_BUS_RESET:\r
6ad55b15 1328 return EFI_NOT_READY;\r
1329\r
f36d6e66 1330 case EFI_EXT_SCSI_STATUS_HOST_ADAPTER_BUS_FREE:\r
1331 case EFI_EXT_SCSI_STATUS_HOST_ADAPTER_PHASE_ERROR:\r
6ad55b15 1332 return EFI_DEVICE_ERROR;\r
1333\r
1334 default:\r
1335 return EFI_SUCCESS;\r
1336 }\r
1337}\r
1338\r
6ad55b15 1339\r
9beb888e 1340/**\r
1341 Check the target status and re-interpret it in EFI_STATUS.\r
6ad55b15 1342\r
9beb888e 1343 @param TargetStatus Target status\r
6ad55b15 1344\r
9beb888e 1345 @retval EFI_NOT_READY Device is NOT ready.\r
1346 @retval EFI_DEVICE_ERROR \r
1347 @retval EFI_SUCCESS\r
6ad55b15 1348\r
9beb888e 1349**/\r
1350EFI_STATUS\r
1351CheckTargetStatus (\r
1352 IN UINT8 TargetStatus\r
1353 )\r
6ad55b15 1354{\r
1355 switch (TargetStatus) {\r
f36d6e66 1356 case EFI_EXT_SCSI_STATUS_TARGET_GOOD:\r
1357 case EFI_EXT_SCSI_STATUS_TARGET_CHECK_CONDITION:\r
1358 case EFI_EXT_SCSI_STATUS_TARGET_CONDITION_MET:\r
6ad55b15 1359 return EFI_SUCCESS;\r
1360\r
f36d6e66 1361 case EFI_EXT_SCSI_STATUS_TARGET_INTERMEDIATE:\r
1362 case EFI_EXT_SCSI_STATUS_TARGET_INTERMEDIATE_CONDITION_MET:\r
1363 case EFI_EXT_SCSI_STATUS_TARGET_BUSY:\r
1364 case EFI_EXT_SCSI_STATUS_TARGET_TASK_SET_FULL:\r
6ad55b15 1365 return EFI_NOT_READY;\r
1366\r
f36d6e66 1367 case EFI_EXT_SCSI_STATUS_TARGET_RESERVATION_CONFLICT:\r
6ad55b15 1368 return EFI_DEVICE_ERROR;\r
1369 break;\r
1370\r
1371 default:\r
1372 return EFI_SUCCESS;\r
1373 }\r
1374}\r
1375\r
f36d6e66 1376\r
9beb888e 1377/**\r
6ad55b15 1378 Retrieve all sense keys from the device.\r
f36d6e66 1379\r
9beb888e 1380 When encountering error during the process, if retrieve sense keys before\r
1381 error encounterred, it returns the sense keys with return status set to EFI_SUCCESS,\r
1382 and NeedRetry set to FALSE; otherwize, return the proper return status.\r
1383\r
1384 @param ScsiDiskDevice The pointer of SCSI_DISK_DEV\r
1385 @param NeedRetry The pointer of flag indicates if need a retry\r
1386 @param SenseDataArray The pointer of an array of sense data\r
1387 @param NumberOfSenseKeys The number of sense key\r
1388 @param AskResetIfError The flag indicates if need reset when error occurs\r
1389\r
1390 @retval EFI_DEVICE_ERROR Indicates that error occurs\r
1391 @retval EFI_SUCCESS Successfully to request sense key\r
f36d6e66 1392\r
9beb888e 1393**/\r
1394EFI_STATUS\r
1395ScsiDiskRequestSenseKeys (\r
1396 IN OUT SCSI_DISK_DEV *ScsiDiskDevice,\r
1397 OUT BOOLEAN *NeedRetry,\r
1398 OUT EFI_SCSI_SENSE_DATA **SenseDataArray,\r
1399 OUT UINTN *NumberOfSenseKeys,\r
1400 IN BOOLEAN AskResetIfError\r
1401 )\r
6ad55b15 1402{\r
1403 EFI_SCSI_SENSE_DATA *PtrSenseData;\r
1404 UINT8 SenseDataLength;\r
1405 BOOLEAN SenseReq;\r
1406 EFI_STATUS Status;\r
1407 EFI_STATUS FallStatus;\r
1408 UINT8 HostAdapterStatus;\r
1409 UINT8 TargetStatus;\r
1410\r
1411 FallStatus = EFI_SUCCESS;\r
1412 SenseDataLength = sizeof (EFI_SCSI_SENSE_DATA);\r
1413\r
1414 ZeroMem (\r
1415 ScsiDiskDevice->SenseData,\r
1416 sizeof (EFI_SCSI_SENSE_DATA) * (ScsiDiskDevice->SenseDataNumber)\r
1417 );\r
1418\r
1419 *NumberOfSenseKeys = 0;\r
1420 *SenseDataArray = ScsiDiskDevice->SenseData;\r
1421 PtrSenseData = ScsiDiskDevice->SenseData;\r
1422\r
1423 for (SenseReq = TRUE; SenseReq;) {\r
d35be2a4 1424 Status = ScsiRequestSenseCommand (\r
6ad55b15 1425 ScsiDiskDevice->ScsiIo,\r
e72a3b3e 1426 EFI_TIMER_PERIOD_SECONDS (2),\r
6ad55b15 1427 PtrSenseData,\r
1428 &SenseDataLength,\r
1429 &HostAdapterStatus,\r
1430 &TargetStatus\r
1431 );\r
f36d6e66 1432 if ((Status == EFI_SUCCESS) || (Status == EFI_WARN_BUFFER_TOO_SMALL)) {\r
1433 FallStatus = EFI_SUCCESS;\r
1434 \r
1435 } else if ((Status == EFI_TIMEOUT) || (Status == EFI_NOT_READY)) {\r
1436 *NeedRetry = TRUE;\r
1437 FallStatus = EFI_DEVICE_ERROR;\r
1438 \r
1439 } else if ((Status == EFI_INVALID_PARAMETER) || (Status == EFI_UNSUPPORTED)) {\r
1440 *NeedRetry = FALSE;\r
1441 FallStatus = EFI_DEVICE_ERROR;\r
1442 \r
1443 } else if (Status == EFI_DEVICE_ERROR) {\r
1444 if (AskResetIfError) {\r
1445 ScsiDiskDevice->ScsiIo->ResetDevice (ScsiDiskDevice->ScsiIo);\r
1446 }\r
1447 \r
1448 FallStatus = EFI_DEVICE_ERROR;\r
6ad55b15 1449 }\r
1450\r
1451 if (EFI_ERROR (FallStatus)) {\r
1452 if (*NumberOfSenseKeys != 0) {\r
1453 *NeedRetry = FALSE;\r
1454 return EFI_SUCCESS;\r
1455 } else {\r
1456 return EFI_DEVICE_ERROR;\r
1457 }\r
1458 }\r
1459\r
1460 (*NumberOfSenseKeys) += 1;\r
1461\r
1462 //\r
1463 // no more sense key or number of sense keys exceeds predefined,\r
1464 // skip the loop.\r
1465 //\r
1466 if ((PtrSenseData->Sense_Key == EFI_SCSI_SK_NO_SENSE) || \r
1467 (*NumberOfSenseKeys == ScsiDiskDevice->SenseDataNumber)) {\r
1468 SenseReq = FALSE;\r
1469 }\r
6ad55b15 1470 PtrSenseData += 1;\r
6ad55b15 1471 }\r
6ad55b15 1472 return EFI_SUCCESS;\r
1473}\r
1474\r
6ad55b15 1475\r
9beb888e 1476/**\r
1477 Get information from media read capacity command.\r
6ad55b15 1478\r
9beb888e 1479 @param ScsiDiskDevice The pointer of SCSI_DISK_DEV\r
1480 @param Capacity The pointer of EFI_SCSI_DISK_CAPACITY_DATA\r
6ad55b15 1481\r
9beb888e 1482**/\r
1483VOID\r
1484GetMediaInfo (\r
1485 IN OUT SCSI_DISK_DEV *ScsiDiskDevice,\r
b96cd313 1486 EFI_SCSI_DISK_CAPACITY_DATA *Capacity10,\r
1487 EFI_SCSI_DISK_CAPACITY_DATA16 *Capacity16\r
9beb888e 1488 )\r
6ad55b15 1489{\r
b96cd313 1490 UINT8 ScsiVersion;\r
1491 UINT8 *Ptr;\r
1492\r
1493 ScsiVersion = (UINT8)(ScsiDiskDevice->InquiryData.Version & 0x03);\r
1494 ScsiDiskDevice->BlkIo.Media->LowestAlignedLba = 0;\r
1495 ScsiDiskDevice->BlkIo.Media->LogicalBlocksPerPhysicalBlock = 1;\r
1496 \r
1497\r
1498 if (ScsiVersion < SCSI_COMMAND_VERSION_3) {\r
1499 ScsiDiskDevice->BlkIo.Media->LastBlock = (Capacity10->LastLba3 << 24) |\r
1500 (Capacity10->LastLba2 << 16) |\r
1501 (Capacity10->LastLba1 << 8) |\r
1502 Capacity10->LastLba0;\r
1503 \r
1504 ScsiDiskDevice->BlkIo.Media->BlockSize = (Capacity10->BlockSize3 << 24) |\r
1505 (Capacity10->BlockSize2 << 16) | \r
1506 (Capacity10->BlockSize1 << 8) |\r
1507 Capacity10->BlockSize0;\r
1508 ScsiDiskDevice->BlkIo.Revision = EFI_BLOCK_IO_PROTOCOL_REVISION; \r
1509 } else {\r
1510\r
1511 Ptr = (UINT8*)&ScsiDiskDevice->BlkIo.Media->LastBlock;\r
1512 *Ptr++ = Capacity16->LastLba0;\r
1513 *Ptr++ = Capacity16->LastLba1;\r
1514 *Ptr++ = Capacity16->LastLba2;\r
1515 *Ptr++ = Capacity16->LastLba3;\r
1516 *Ptr++ = Capacity16->LastLba4;\r
1517 *Ptr++ = Capacity16->LastLba5;\r
1518 *Ptr++ = Capacity16->LastLba6;\r
1519 *Ptr = Capacity16->LastLba7;\r
1520 \r
1521 ScsiDiskDevice->BlkIo.Media->BlockSize = (Capacity16->BlockSize3 << 24) |\r
1522 (Capacity16->BlockSize2 << 16) | \r
1523 (Capacity16->BlockSize1 << 8) |\r
1524 Capacity16->BlockSize0;\r
1525\r
1526 ScsiDiskDevice->BlkIo.Media->LowestAlignedLba = (Capacity16->LowestAlignLogic2 << 8)|(Capacity16->LowestAlignLogic1);\r
1527 ScsiDiskDevice->BlkIo.Media->LogicalBlocksPerPhysicalBlock = Capacity16->LogicPerPhysical;\r
1528 ScsiDiskDevice->BlkIo.Revision = EFI_BLOCK_IO_PROTOCOL_REVISION2; \r
1529 }\r
1530\r
6ad55b15 1531\r
1532 ScsiDiskDevice->BlkIo.Media->MediaPresent = TRUE;\r
b96cd313 1533 \r
6ad55b15 1534 if (ScsiDiskDevice->DeviceType == EFI_SCSI_TYPE_DISK) {\r
1535 ScsiDiskDevice->BlkIo.Media->BlockSize = 0x200;\r
1536 }\r
1537\r
1538 if (ScsiDiskDevice->DeviceType == EFI_SCSI_TYPE_CDROM) {\r
1539 ScsiDiskDevice->BlkIo.Media->BlockSize = 0x800;\r
1540 }\r
1541}\r
1542\r
9beb888e 1543/**\r
1544 Parse Inquiry data.\r
1545\r
1546 @param ScsiDiskDevice The pointer of SCSI_DISK_DEV\r
1547\r
1548**/\r
6ad55b15 1549VOID\r
1550ParseInquiryData (\r
9beb888e 1551 IN OUT SCSI_DISK_DEV *ScsiDiskDevice\r
6ad55b15 1552 )\r
6ad55b15 1553{\r
fbfa4a1d 1554 ScsiDiskDevice->FixedDevice = (BOOLEAN) ((ScsiDiskDevice->InquiryData.Rmb == 1) ? 0 : 1);\r
6ad55b15 1555 ScsiDiskDevice->BlkIoMedia.RemovableMedia = (BOOLEAN) (!ScsiDiskDevice->FixedDevice);\r
1556}\r
1557\r
9beb888e 1558/**\r
1559 Read sector from SCSI Disk.\r
6ad55b15 1560\r
9beb888e 1561 @param ScsiDiskDevice The poiniter of SCSI_DISK_DEV\r
1562 @param Buffer The buffer to fill in the read out data\r
1563 @param Lba Logic block address\r
1564 @param NumberOfBlocks The number of blocks to read\r
6ad55b15 1565\r
9beb888e 1566 @retval EFI_DEVICE_ERROR Indicates a device error.\r
1567 @retval EFI_SUCCESS Operation is successful.\r
6ad55b15 1568\r
9beb888e 1569**/\r
1570EFI_STATUS\r
1571ScsiDiskReadSectors (\r
1572 IN SCSI_DISK_DEV *ScsiDiskDevice,\r
1573 OUT VOID *Buffer,\r
1574 IN EFI_LBA Lba,\r
1575 IN UINTN NumberOfBlocks\r
1576 )\r
6ad55b15 1577{\r
1578 UINTN BlocksRemaining;\r
1579 UINT32 Lba32;\r
1580 UINT8 *PtrBuffer;\r
1581 UINT32 BlockSize;\r
1582 UINT32 ByteCount;\r
1583 UINT32 MaxBlock;\r
1584 UINT32 SectorCount;\r
1585 UINT64 Timeout;\r
1586 EFI_STATUS Status;\r
1587 UINT8 Index;\r
1588 UINT8 MaxRetry;\r
1589 BOOLEAN NeedRetry;\r
1590 EFI_SCSI_SENSE_DATA *SenseData;\r
1591 UINTN NumberOfSenseKeys;\r
1592\r
1593 SenseData = NULL;\r
1594 NumberOfSenseKeys = 0;\r
1595\r
1596 Status = EFI_SUCCESS;\r
1597\r
1598 BlocksRemaining = NumberOfBlocks;\r
1599 BlockSize = ScsiDiskDevice->BlkIo.Media->BlockSize;\r
1600 //\r
1601 // limit the data bytes that can be transferred by one Read(10) Command\r
1602 //\r
1603 MaxBlock = 65536;\r
1604\r
1605 PtrBuffer = Buffer;\r
1606 Lba32 = (UINT32) Lba;\r
1607\r
1608 while (BlocksRemaining > 0) {\r
1609\r
1610 if (BlocksRemaining <= MaxBlock) {\r
1611\r
1612 SectorCount = (UINT16) BlocksRemaining;\r
1613 } else {\r
1614\r
1615 SectorCount = MaxBlock;\r
1616 }\r
1617\r
1618 ByteCount = SectorCount * BlockSize;\r
e72a3b3e 1619 Timeout = EFI_TIMER_PERIOD_SECONDS (2);\r
6ad55b15 1620\r
1621 MaxRetry = 2;\r
1622 for (Index = 0; Index < MaxRetry; Index++) {\r
1623\r
1624 Status = ScsiDiskRead10 (\r
1625 ScsiDiskDevice,\r
1626 &NeedRetry,\r
1627 &SenseData,\r
1628 &NumberOfSenseKeys,\r
1629 Timeout,\r
1630 PtrBuffer,\r
1631 &ByteCount,\r
1632 Lba32,\r
1633 SectorCount\r
1634 );\r
1635 if (!EFI_ERROR (Status)) {\r
1636 break;\r
1637 }\r
1638\r
1639 if (!NeedRetry) {\r
1640 return EFI_DEVICE_ERROR;\r
1641 }\r
1642\r
1643 }\r
1644\r
1645 if ((Index == MaxRetry) && (Status != EFI_SUCCESS)) {\r
1646 return EFI_DEVICE_ERROR;\r
1647 }\r
1648\r
1649 //\r
1650 // actual transferred sectors\r
1651 //\r
1652 SectorCount = ByteCount / BlockSize;\r
1653\r
1654 Lba32 += SectorCount;\r
1655 PtrBuffer = PtrBuffer + SectorCount * BlockSize;\r
1656 BlocksRemaining -= SectorCount;\r
1657 }\r
1658\r
1659 return EFI_SUCCESS;\r
1660}\r
1661\r
9beb888e 1662/**\r
1663 Write sector to SCSI Disk.\r
6ad55b15 1664\r
9beb888e 1665 @param ScsiDiskDevice The poiniter of SCSI_DISK_DEV\r
1666 @param Buffer The buffer of data to be written into SCSI Disk\r
1667 @param Lba Logic block address\r
1668 @param NumberOfBlocks The number of blocks to read\r
6ad55b15 1669\r
9beb888e 1670 @retval EFI_DEVICE_ERROR Indicates a device error.\r
1671 @retval EFI_SUCCESS Operation is successful.\r
6ad55b15 1672\r
9beb888e 1673**/\r
1674EFI_STATUS\r
1675ScsiDiskWriteSectors (\r
1676 IN SCSI_DISK_DEV *ScsiDiskDevice,\r
1677 IN VOID *Buffer,\r
1678 IN EFI_LBA Lba,\r
1679 IN UINTN NumberOfBlocks\r
1680 )\r
6ad55b15 1681{\r
1682 UINTN BlocksRemaining;\r
1683 UINT32 Lba32;\r
1684 UINT8 *PtrBuffer;\r
1685 UINT32 BlockSize;\r
1686 UINT32 ByteCount;\r
1687 UINT32 MaxBlock;\r
1688 UINT32 SectorCount;\r
1689 UINT64 Timeout;\r
1690 EFI_STATUS Status;\r
1691 UINT8 Index;\r
1692 UINT8 MaxRetry;\r
1693 BOOLEAN NeedRetry;\r
1694 EFI_SCSI_SENSE_DATA *SenseData;\r
1695 UINTN NumberOfSenseKeys;\r
1696\r
1697 SenseData = NULL;\r
1698 NumberOfSenseKeys = 0;\r
1699\r
1700 Status = EFI_SUCCESS;\r
1701\r
1702 BlocksRemaining = NumberOfBlocks;\r
1703 BlockSize = ScsiDiskDevice->BlkIo.Media->BlockSize;\r
1704 //\r
1705 // limit the data bytes that can be transferred by one Write(10) Command\r
1706 //\r
1707 MaxBlock = 65536;\r
1708\r
1709 PtrBuffer = Buffer;\r
1710 Lba32 = (UINT32) Lba;\r
1711\r
1712 while (BlocksRemaining > 0) {\r
1713\r
1714 if (BlocksRemaining <= MaxBlock) {\r
1715\r
1716 SectorCount = (UINT16) BlocksRemaining;\r
1717 } else {\r
1718\r
1719 SectorCount = MaxBlock;\r
1720 }\r
1721\r
1722 ByteCount = SectorCount * BlockSize;\r
e72a3b3e 1723 Timeout = EFI_TIMER_PERIOD_SECONDS (2);\r
6ad55b15 1724 MaxRetry = 2;\r
1725 for (Index = 0; Index < MaxRetry; Index++) {\r
1726 Status = ScsiDiskWrite10 (\r
1727 ScsiDiskDevice,\r
1728 &NeedRetry,\r
1729 &SenseData,\r
1730 &NumberOfSenseKeys,\r
1731 Timeout,\r
1732 PtrBuffer,\r
1733 &ByteCount,\r
1734 Lba32,\r
1735 SectorCount\r
1736 );\r
1737 if (!EFI_ERROR (Status)) {\r
1738 break;\r
1739 }\r
1740\r
1741 if (!NeedRetry) {\r
1742 return EFI_DEVICE_ERROR;\r
1743 }\r
1744 }\r
1745\r
1746 if ((Index == MaxRetry) && (Status != EFI_SUCCESS)) {\r
1747 return EFI_DEVICE_ERROR;\r
1748 }\r
1749 //\r
1750 // actual transferred sectors\r
1751 //\r
1752 SectorCount = ByteCount / BlockSize;\r
1753\r
1754 Lba32 += SectorCount;\r
1755 PtrBuffer = PtrBuffer + SectorCount * BlockSize;\r
1756 BlocksRemaining -= SectorCount;\r
1757 }\r
1758\r
1759 return EFI_SUCCESS;\r
1760}\r
1761\r
9beb888e 1762\r
1763/**\r
1764 Sumbmit Read command.\r
1765\r
1766 @param ScsiDiskDevice The pointer of ScsiDiskDevice\r
1767 @param NeedRetry The pointer of flag indicates if needs retry if error happens\r
1768 @param SenseDataArray NOT used yet in this function\r
1769 @param NumberOfSenseKeys The number of sense key\r
1770 @param Timeout The time to complete the command\r
1771 @param DataBuffer The buffer to fill with the read out data\r
1772 @param DataLength The length of buffer\r
1773 @param StartLba The start logic block address\r
1774 @param SectorSize The size of sector\r
1775\r
1776 @return EFI_STATUS is returned by calling ScsiRead10Command().\r
1777**/\r
6ad55b15 1778EFI_STATUS\r
1779ScsiDiskRead10 (\r
9beb888e 1780 IN SCSI_DISK_DEV *ScsiDiskDevice,\r
1781 OUT BOOLEAN *NeedRetry,\r
1782 OUT EFI_SCSI_SENSE_DATA **SenseDataArray, OPTIONAL\r
1783 OUT UINTN *NumberOfSenseKeys,\r
1784 IN UINT64 Timeout,\r
1785 OUT UINT8 *DataBuffer,\r
1786 IN OUT UINT32 *DataLength,\r
1787 IN UINT32 StartLba,\r
1788 IN UINT32 SectorSize\r
6ad55b15 1789 )\r
6ad55b15 1790{\r
1791 UINT8 SenseDataLength;\r
1792 EFI_STATUS Status;\r
1793 UINT8 HostAdapterStatus;\r
1794 UINT8 TargetStatus;\r
1795\r
1796 *NeedRetry = FALSE;\r
1797 *NumberOfSenseKeys = 0;\r
1798 SenseDataLength = 0;\r
d35be2a4 1799 Status = ScsiRead10Command (\r
6ad55b15 1800 ScsiDiskDevice->ScsiIo,\r
1801 Timeout,\r
1802 NULL,\r
1803 &SenseDataLength,\r
1804 &HostAdapterStatus,\r
1805 &TargetStatus,\r
1806 DataBuffer,\r
1807 DataLength,\r
1808 StartLba,\r
1809 SectorSize\r
1810 );\r
1811 return Status;\r
1812}\r
1813\r
6ad55b15 1814\r
9beb888e 1815/**\r
1816 Submit Write Command.\r
6ad55b15 1817\r
9beb888e 1818 @param ScsiDiskDevice The pointer of ScsiDiskDevice\r
1819 @param NeedRetry The pointer of flag indicates if needs retry if error happens\r
1820 @param SenseDataArray NOT used yet in this function\r
1821 @param NumberOfSenseKeys The number of sense key\r
1822 @param Timeout The time to complete the command\r
1823 @param DataBuffer The buffer to fill with the read out data\r
1824 @param DataLength The length of buffer\r
1825 @param StartLba The start logic block address\r
1826 @param SectorSize The size of sector\r
6ad55b15 1827\r
9beb888e 1828 @return EFI_STATUS is returned by calling ScsiWrite10Command().\r
6ad55b15 1829\r
9beb888e 1830**/\r
1831EFI_STATUS\r
1832ScsiDiskWrite10 (\r
1833 IN SCSI_DISK_DEV *ScsiDiskDevice,\r
1834 OUT BOOLEAN *NeedRetry,\r
1835 OUT EFI_SCSI_SENSE_DATA **SenseDataArray, OPTIONAL\r
1836 OUT UINTN *NumberOfSenseKeys,\r
1837 IN UINT64 Timeout,\r
1838 IN UINT8 *DataBuffer,\r
1839 IN OUT UINT32 *DataLength,\r
1840 IN UINT32 StartLba,\r
1841 IN UINT32 SectorSize\r
1842 )\r
6ad55b15 1843{\r
1844 EFI_STATUS Status;\r
1845 UINT8 SenseDataLength;\r
1846 UINT8 HostAdapterStatus;\r
1847 UINT8 TargetStatus;\r
1848\r
1849 *NeedRetry = FALSE;\r
1850 *NumberOfSenseKeys = 0;\r
1851 SenseDataLength = 0;\r
d35be2a4 1852 Status = ScsiWrite10Command (\r
6ad55b15 1853 ScsiDiskDevice->ScsiIo,\r
1854 Timeout,\r
1855 NULL,\r
1856 &SenseDataLength,\r
1857 &HostAdapterStatus,\r
1858 &TargetStatus,\r
1859 DataBuffer,\r
1860 DataLength,\r
1861 StartLba,\r
1862 SectorSize\r
1863 );\r
1864 return Status;\r
1865}\r
1866\r
9beb888e 1867\r
1868/**\r
1869 Check sense key to find if media presents.\r
1870\r
1871 @param SenseData The pointer of EFI_SCSI_SENSE_DATA\r
1872 @param SenseCounts The number of sense key\r
1873\r
1874 @retval TRUE NOT any media\r
1875 @retval FALSE Media presents\r
1876**/\r
6ad55b15 1877BOOLEAN\r
1878ScsiDiskIsNoMedia (\r
1879 IN EFI_SCSI_SENSE_DATA *SenseData,\r
1880 IN UINTN SenseCounts\r
1881 )\r
6ad55b15 1882{\r
1883 EFI_SCSI_SENSE_DATA *SensePtr;\r
1884 UINTN Index;\r
1885 BOOLEAN IsNoMedia;\r
1886\r
1887 IsNoMedia = FALSE;\r
1888 SensePtr = SenseData;\r
1889\r
1890 for (Index = 0; Index < SenseCounts; Index++) {\r
6ad55b15 1891 //\r
1892 // Sense Key is EFI_SCSI_SK_NOT_READY (0x2),\r
1893 // Additional Sense Code is ASC_NO_MEDIA (0x3A)\r
1894 //\r
1895 if ((SensePtr->Sense_Key == EFI_SCSI_SK_NOT_READY) &&\r
1896 (SensePtr->Addnl_Sense_Code == EFI_SCSI_ASC_NO_MEDIA)) {\r
1897 IsNoMedia = TRUE;\r
1898 }\r
6ad55b15 1899 SensePtr++;\r
1900 }\r
1901\r
1902 return IsNoMedia;\r
1903}\r
1904\r
9beb888e 1905\r
1906/**\r
1907 Parse sense key.\r
1908\r
1909 @param SenseData The pointer of EFI_SCSI_SENSE_DATA\r
1910 @param SenseCounts The number of sense key\r
1911\r
1912 @retval TRUE Error\r
1913 @retval FALSE NOT error\r
1914\r
1915**/\r
6ad55b15 1916BOOLEAN\r
1917ScsiDiskIsMediaError (\r
1918 IN EFI_SCSI_SENSE_DATA *SenseData,\r
1919 IN UINTN SenseCounts\r
1920 )\r
6ad55b15 1921{\r
1922 EFI_SCSI_SENSE_DATA *SensePtr;\r
1923 UINTN Index;\r
1924 BOOLEAN IsError;\r
1925\r
1926 IsError = FALSE;\r
1927 SensePtr = SenseData;\r
1928\r
1929 for (Index = 0; Index < SenseCounts; Index++) {\r
1930\r
1931 switch (SensePtr->Sense_Key) {\r
1932\r
1933 case EFI_SCSI_SK_MEDIUM_ERROR:\r
1934 //\r
1935 // Sense Key is EFI_SCSI_SK_MEDIUM_ERROR (0x3)\r
1936 //\r
1937 switch (SensePtr->Addnl_Sense_Code) {\r
1938\r
1939 //\r
1940 // fall through\r
1941 //\r
1942 case EFI_SCSI_ASC_MEDIA_ERR1:\r
1943\r
1944 //\r
1945 // fall through\r
1946 //\r
1947 case EFI_SCSI_ASC_MEDIA_ERR2:\r
1948\r
1949 //\r
1950 // fall through\r
1951 //\r
1952 case EFI_SCSI_ASC_MEDIA_ERR3:\r
1953 case EFI_SCSI_ASC_MEDIA_ERR4:\r
1954 IsError = TRUE;\r
1955 break;\r
1956\r
1957 default:\r
1958 break;\r
1959 }\r
1960\r
1961 break;\r
1962\r
1963 case EFI_SCSI_SK_NOT_READY:\r
1964 //\r
1965 // Sense Key is EFI_SCSI_SK_NOT_READY (0x2)\r
1966 //\r
1967 switch (SensePtr->Addnl_Sense_Code) {\r
1968 //\r
1969 // Additional Sense Code is ASC_MEDIA_UPSIDE_DOWN (0x6)\r
1970 //\r
1971 case EFI_SCSI_ASC_MEDIA_UPSIDE_DOWN:\r
1972 IsError = TRUE;\r
1973 break;\r
1974\r
1975 default:\r
1976 break;\r
1977 }\r
1978 break;\r
1979\r
1980 default:\r
1981 break;\r
1982 }\r
1983\r
1984 SensePtr++;\r
1985 }\r
1986\r
1987 return IsError;\r
1988}\r
1989\r
9beb888e 1990\r
1991/**\r
1992 Check sense key to find if hardware error happens.\r
1993\r
1994 @param SenseData The pointer of EFI_SCSI_SENSE_DATA\r
1995 @param SenseCounts The number of sense key\r
1996\r
1997 @retval TRUE Hardware error exits.\r
1998 @retval FALSE NO error.\r
1999\r
2000**/\r
6ad55b15 2001BOOLEAN\r
2002ScsiDiskIsHardwareError (\r
2003 IN EFI_SCSI_SENSE_DATA *SenseData,\r
2004 IN UINTN SenseCounts\r
2005 )\r
6ad55b15 2006{\r
2007 EFI_SCSI_SENSE_DATA *SensePtr;\r
2008 UINTN Index;\r
2009 BOOLEAN IsError;\r
2010\r
2011 IsError = FALSE;\r
2012 SensePtr = SenseData;\r
2013\r
2014 for (Index = 0; Index < SenseCounts; Index++) {\r
2015 \r
2016 //\r
2017 // Sense Key is EFI_SCSI_SK_HARDWARE_ERROR (0x4)\r
2018 //\r
2019 if (SensePtr->Sense_Key == EFI_SCSI_SK_HARDWARE_ERROR) {\r
2020 IsError = TRUE;\r
2021 }\r
2022\r
2023 SensePtr++;\r
2024 }\r
2025\r
2026 return IsError;\r
2027}\r
2028\r
9beb888e 2029\r
2030/**\r
2031 Check sense key to find if media has changed.\r
2032\r
2033 @param SenseData The pointer of EFI_SCSI_SENSE_DATA\r
2034 @param SenseCounts The number of sense key\r
2035\r
2036 @retval TRUE Media is changed.\r
2037 @retval FALSE Medit is NOT changed.\r
2038**/\r
6ad55b15 2039BOOLEAN\r
2040ScsiDiskIsMediaChange (\r
2041 IN EFI_SCSI_SENSE_DATA *SenseData,\r
2042 IN UINTN SenseCounts\r
2043 )\r
6ad55b15 2044{\r
2045 EFI_SCSI_SENSE_DATA *SensePtr;\r
2046 UINTN Index;\r
2047 BOOLEAN IsMediaChanged;\r
2048\r
2049 IsMediaChanged = FALSE;\r
2050 SensePtr = SenseData;\r
2051\r
2052 for (Index = 0; Index < SenseCounts; Index++) {\r
2053 //\r
2054 // Sense Key is EFI_SCSI_SK_UNIT_ATTENTION (0x6),\r
2055 // Additional sense code is EFI_SCSI_ASC_MEDIA_CHANGE (0x28)\r
2056 //\r
2057 if ((SensePtr->Sense_Key == EFI_SCSI_SK_UNIT_ATTENTION) &&\r
2058 (SensePtr->Addnl_Sense_Code == EFI_SCSI_ASC_MEDIA_CHANGE)) {\r
2059 IsMediaChanged = TRUE;\r
2060 }\r
2061\r
2062 SensePtr++;\r
2063 }\r
2064\r
2065 return IsMediaChanged;\r
2066}\r
2067\r
9beb888e 2068/**\r
2069 Check sense key to find if reset happens.\r
2070\r
2071 @param SenseData The pointer of EFI_SCSI_SENSE_DATA\r
2072 @param SenseCounts The number of sense key\r
2073\r
2074 @retval TRUE It is reset before.\r
2075 @retval FALSE It is NOT reset before.\r
2076\r
2077**/\r
6ad55b15 2078BOOLEAN\r
2079ScsiDiskIsResetBefore (\r
2080 IN EFI_SCSI_SENSE_DATA *SenseData,\r
2081 IN UINTN SenseCounts\r
2082 )\r
6ad55b15 2083{\r
2084 EFI_SCSI_SENSE_DATA *SensePtr;\r
2085 UINTN Index;\r
2086 BOOLEAN IsResetBefore;\r
2087\r
2088 IsResetBefore = FALSE;\r
2089 SensePtr = SenseData;\r
2090\r
2091 for (Index = 0; Index < SenseCounts; Index++) {\r
2092 \r
2093 //\r
2094 // Sense Key is EFI_SCSI_SK_UNIT_ATTENTION (0x6)\r
2095 // Additional Sense Code is EFI_SCSI_ASC_RESET (0x29)\r
2096 //\r
2097 if ((SensePtr->Sense_Key == EFI_SCSI_SK_UNIT_ATTENTION) &&\r
2098 (SensePtr->Addnl_Sense_Code == EFI_SCSI_ASC_RESET)) {\r
2099 IsResetBefore = TRUE;\r
2100 }\r
2101\r
2102 SensePtr++;\r
2103 }\r
2104\r
2105 return IsResetBefore;\r
2106}\r
2107\r
9beb888e 2108/**\r
2109 Check sense key to find if the drive is ready.\r
2110\r
2111 @param SenseData The pointer of EFI_SCSI_SENSE_DATA\r
2112 @param SenseCounts The number of sense key\r
2113 @param RetryLater The flag means if need a retry \r
2114\r
2115 @retval TRUE Drive is ready.\r
2116 @retval FALSE Drive is NOT ready.\r
2117\r
2118**/\r
6ad55b15 2119BOOLEAN\r
2120ScsiDiskIsDriveReady (\r
2121 IN EFI_SCSI_SENSE_DATA *SenseData,\r
2122 IN UINTN SenseCounts,\r
2123 OUT BOOLEAN *RetryLater\r
2124 )\r
6ad55b15 2125{\r
2126 EFI_SCSI_SENSE_DATA *SensePtr;\r
2127 UINTN Index;\r
2128 BOOLEAN IsReady;\r
2129\r
2130 IsReady = TRUE;\r
2131 *RetryLater = FALSE;\r
2132 SensePtr = SenseData;\r
2133\r
2134 for (Index = 0; Index < SenseCounts; Index++) {\r
2135\r
2136 switch (SensePtr->Sense_Key) {\r
2137\r
2138 case EFI_SCSI_SK_NOT_READY:\r
2139 //\r
2140 // Sense Key is EFI_SCSI_SK_NOT_READY (0x2)\r
2141 //\r
2142 switch (SensePtr->Addnl_Sense_Code) {\r
2143 case EFI_SCSI_ASC_NOT_READY:\r
2144 //\r
2145 // Additional Sense Code is EFI_SCSI_ASC_NOT_READY (0x4)\r
2146 //\r
2147 switch (SensePtr->Addnl_Sense_Code_Qualifier) {\r
2148 case EFI_SCSI_ASCQ_IN_PROGRESS:\r
2149 //\r
2150 // Additional Sense Code Qualifier is\r
2151 // EFI_SCSI_ASCQ_IN_PROGRESS (0x1)\r
2152 //\r
2153 IsReady = FALSE;\r
2154 *RetryLater = TRUE;\r
2155 break;\r
2156\r
2157 default:\r
2158 IsReady = FALSE;\r
2159 *RetryLater = FALSE;\r
2160 break;\r
2161 }\r
2162 break;\r
2163\r
2164 default:\r
2165 break;\r
2166 }\r
2167 break;\r
2168\r
2169 default:\r
2170 break;\r
2171 }\r
2172\r
2173 SensePtr++;\r
2174 }\r
2175\r
2176 return IsReady;\r
2177}\r
2178\r
9beb888e 2179/**\r
2180 Check sense key to find if it has sense key.\r
2181\r
2182 @param SenseData - The pointer of EFI_SCSI_SENSE_DATA\r
2183 @param SenseCounts - The number of sense key\r
2184\r
2185 @retval TRUE It has sense key.\r
2186 @retval FALSE It has NOT any sense key.\r
2187\r
2188**/\r
6ad55b15 2189BOOLEAN\r
2190ScsiDiskHaveSenseKey (\r
2191 IN EFI_SCSI_SENSE_DATA *SenseData,\r
2192 IN UINTN SenseCounts\r
2193 )\r
6ad55b15 2194{\r
2195 EFI_SCSI_SENSE_DATA *SensePtr;\r
2196 UINTN Index;\r
2197 BOOLEAN HaveSenseKey;\r
2198\r
2199 if (SenseCounts == 0) {\r
2200 HaveSenseKey = FALSE;\r
2201 } else {\r
2202 HaveSenseKey = TRUE;\r
2203 }\r
2204\r
2205 SensePtr = SenseData;\r
2206\r
2207 for (Index = 0; Index < SenseCounts; Index++) {\r
2208 \r
2209 //\r
2210 // Sense Key is SK_NO_SENSE (0x0)\r
2211 //\r
2212 if ((SensePtr->Sense_Key == EFI_SCSI_SK_NO_SENSE) &&\r
2213 (Index == 0)) {\r
2214 HaveSenseKey = FALSE;\r
2215 }\r
2216\r
2217 SensePtr++;\r
2218 }\r
2219\r
2220 return HaveSenseKey;\r
2221}\r
2222\r
9beb888e 2223/**\r
2224 Release resource about disk device.\r
2225\r
2226 @param ScsiDiskDevice The pointer of SCSI_DISK_DEV\r
2227\r
2228**/\r
6ad55b15 2229VOID\r
2230ReleaseScsiDiskDeviceResources (\r
2231 IN SCSI_DISK_DEV *ScsiDiskDevice\r
2232 )\r
6ad55b15 2233{\r
2234 if (ScsiDiskDevice == NULL) {\r
2235 return ;\r
2236 }\r
2237\r
2238 if (ScsiDiskDevice->SenseData != NULL) {\r
9b38ff34 2239 FreePool (ScsiDiskDevice->SenseData);\r
6ad55b15 2240 ScsiDiskDevice->SenseData = NULL;\r
2241 }\r
2242\r
2243 if (ScsiDiskDevice->ControllerNameTable != NULL) {\r
2244 FreeUnicodeStringTable (ScsiDiskDevice->ControllerNameTable);\r
2245 ScsiDiskDevice->ControllerNameTable = NULL;\r
2246 }\r
2247\r
9b38ff34 2248 FreePool (ScsiDiskDevice);\r
6ad55b15 2249\r
2250 ScsiDiskDevice = NULL;\r
2251}\r