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