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