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