]> git.proxmox.com Git - mirror_edk2.git/blame - MdeModulePkg/Bus/Scsi/ScsiDiskDxe/ScsiDisk.c
Refine soma code to make code run safely.
[mirror_edk2.git] / MdeModulePkg / Bus / Scsi / ScsiDiskDxe / ScsiDisk.c
CommitLineData
3b2dbece 1/** @file\r
2 SCSI disk driver that layers on every SCSI IO protocol in the system.\r
6ad55b15 3\r
cd5ebaa0
HT
4Copyright (c) 2006 - 2010, Intel Corporation. All rights reserved.<BR>\r
5This program and the accompanying materials\r
3b2dbece 6are licensed and made available under the terms and conditions of the BSD License\r
7which accompanies this distribution. The full text of the license may be found at\r
8http://opensource.org/licenses/bsd-license.php\r
6ad55b15 9\r
3b2dbece 10THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
11WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
6ad55b15 12\r
3b2dbece 13**/\r
ed7748fe 14\r
6ad55b15 15\r
16#include "ScsiDisk.h"\r
17\r
18EFI_DRIVER_BINDING_PROTOCOL gScsiDiskDriverBinding = {\r
19 ScsiDiskDriverBindingSupported,\r
20 ScsiDiskDriverBindingStart,\r
21 ScsiDiskDriverBindingStop,\r
22 0xa,\r
23 NULL,\r
24 NULL\r
25};\r
26\r
d716651f 27EFI_DISK_INFO_PROTOCOL gScsiDiskInfoProtocolTemplate = {\r
28 EFI_DISK_INFO_SCSI_INTERFACE_GUID,\r
29 ScsiDiskInfoInquiry,\r
30 ScsiDiskInfoIdentify,\r
31 ScsiDiskInfoSenseData,\r
32 ScsiDiskInfoWhichIde\r
33};\r
9b38ff34 34\r
6ad55b15 35/**\r
9beb888e 36 The user Entry Point for module ScsiDisk.\r
37\r
38 The user code starts with this function.\r
6ad55b15 39\r
9beb888e 40 @param ImageHandle The firmware allocated handle for the EFI image. \r
41 @param SystemTable A pointer to the EFI System Table.\r
6ad55b15 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
70da5bc2 59 Status = EfiLibInstallDriverBindingComponentName2 (\r
6ad55b15 60 ImageHandle,\r
61 SystemTable,\r
62 &gScsiDiskDriverBinding,\r
63 ImageHandle,\r
64 &gScsiDiskComponentName,\r
70da5bc2 65 &gScsiDiskComponentName2\r
6ad55b15 66 );\r
67 ASSERT_EFI_ERROR (Status);\r
68\r
69\r
70 return Status;\r
71}\r
72\r
9beb888e 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
6ad55b15 92EFI_STATUS\r
93EFIAPI\r
94ScsiDiskDriverBindingSupported (\r
95 IN EFI_DRIVER_BINDING_PROTOCOL *This,\r
96 IN EFI_HANDLE Controller,\r
9beb888e 97 IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL\r
6ad55b15 98 )\r
6ad55b15 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
f36d6e66 126 Controller,\r
127 &gEfiScsiIoProtocolGuid,\r
128 This->DriverBindingHandle,\r
129 Controller\r
130 );\r
6ad55b15 131 return Status;\r
132}\r
133\r
9beb888e 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
6ad55b15 154EFI_STATUS\r
155EFIAPI\r
156ScsiDiskDriverBindingStart (\r
157 IN EFI_DRIVER_BINDING_PROTOCOL *This,\r
158 IN EFI_HANDLE Controller,\r
9beb888e 159 IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL\r
6ad55b15 160 )\r
6ad55b15 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
9b38ff34 170 ScsiDiskDevice = (SCSI_DISK_DEV *) AllocateZeroPool (sizeof (SCSI_DISK_DEV));\r
171 if (ScsiDiskDevice == NULL) {\r
172 return EFI_OUT_OF_RESOURCES;\r
6ad55b15 173 }\r
174\r
6ad55b15 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
9b38ff34 184 FreePool (ScsiDiskDevice);\r
6ad55b15 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
9b38ff34 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
6ad55b15 215 gBS->CloseProtocol (\r
216 Controller,\r
217 &gEfiScsiIoProtocolGuid,\r
218 This->DriverBindingHandle,\r
219 Controller\r
220 );\r
9b38ff34 221 FreePool (ScsiDiskDevice);\r
222 return EFI_OUT_OF_RESOURCES;\r
6ad55b15 223 }\r
224\r
6ad55b15 225 //\r
dfe687ca 226 // Retrieve device information\r
6ad55b15 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
9b38ff34 236 FreePool (ScsiDiskDevice->SenseData);\r
6ad55b15 237 gBS->CloseProtocol (\r
f36d6e66 238 Controller,\r
239 &gEfiScsiIoProtocolGuid,\r
240 This->DriverBindingHandle,\r
241 Controller\r
242 );\r
9b38ff34 243 FreePool (ScsiDiskDevice);\r
6ad55b15 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
d14faa52 253 //\r
254 // Determine if Block IO should be produced on this controller handle\r
255 //\r
256 if (DetermineInstallBlockIo(Controller)) {\r
d716651f 257 InitializeInstallDiskInfo(ScsiDiskDevice, Controller);\r
d14faa52 258 Status = gBS->InstallMultipleProtocolInterfaces (\r
259 &Controller,\r
260 &gEfiBlockIoProtocolGuid,\r
261 &ScsiDiskDevice->BlkIo,\r
d716651f 262 &gEfiDiskInfoProtocolGuid,\r
263 &ScsiDiskDevice->DiskInfo,\r
d14faa52 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
6ad55b15 285 }\r
286\r
d14faa52 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
6ad55b15 297}\r
298\r
9beb888e 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
6ad55b15 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
9beb888e 325 IN EFI_HANDLE *ChildHandleBuffer OPTIONAL\r
6ad55b15 326 )\r
6ad55b15 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
d716651f 345 Status = gBS->UninstallMultipleProtocolInterfaces (\r
6ad55b15 346 Controller,\r
347 &gEfiBlockIoProtocolGuid,\r
d716651f 348 &ScsiDiskDevice->BlkIo,\r
349 &gEfiDiskInfoProtocolGuid,\r
350 &ScsiDiskDevice->DiskInfo,\r
351 NULL\r
6ad55b15 352 );\r
353 if (!EFI_ERROR (Status)) {\r
354 gBS->CloseProtocol (\r
f36d6e66 355 Controller,\r
356 &gEfiScsiIoProtocolGuid,\r
357 This->DriverBindingHandle,\r
358 Controller\r
359 );\r
6ad55b15 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
9beb888e 371/**\r
372 Reset SCSI Disk.\r
373\r
6ad55b15 374\r
9beb888e 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
d716651f 381 @return EFI_STATUS is returned from EFI_SCSI_IO_PROTOCOL.ResetDevice().\r
9beb888e 382\r
383**/\r
6ad55b15 384EFI_STATUS\r
385EFIAPI\r
386ScsiDiskReset (\r
387 IN EFI_BLOCK_IO_PROTOCOL *This,\r
388 IN BOOLEAN ExtendedVerification\r
389 )\r
6ad55b15 390{\r
f36d6e66 391 EFI_TPL OldTpl;\r
6ad55b15 392 SCSI_DISK_DEV *ScsiDiskDevice;\r
393 EFI_STATUS Status;\r
6ad55b15 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
9beb888e 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
6ad55b15 429EFI_STATUS\r
430EFIAPI\r
431ScsiDiskReadBlocks (\r
432 IN EFI_BLOCK_IO_PROTOCOL *This,\r
433 IN UINT32 MediaId,\r
9beb888e 434 IN EFI_LBA Lba,\r
6ad55b15 435 IN UINTN BufferSize,\r
436 OUT VOID *Buffer\r
437 )\r
6ad55b15 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
9beb888e 448 if (Buffer == NULL) {\r
6ad55b15 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
9beb888e 460 if (!IS_DEVICE_FIXED(ScsiDiskDevice)) {\r
6ad55b15 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
9beb888e 500 if (Lba > Media->LastBlock) {\r
6ad55b15 501 Status = EFI_INVALID_PARAMETER;\r
502 goto Done;\r
503 }\r
504\r
9beb888e 505 if ((Lba + NumberOfBlocks - 1) > Media->LastBlock) {\r
6ad55b15 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
f36d6e66 514\r
6ad55b15 515 //\r
f36d6e66 516 // If all the parameters are valid, then perform read sectors command\r
6ad55b15 517 // to transfer data from device to host.\r
518 //\r
9beb888e 519 Status = ScsiDiskReadSectors (ScsiDiskDevice, Buffer, Lba, NumberOfBlocks);\r
6ad55b15 520\r
521Done:\r
522 gBS->RestoreTPL (OldTpl);\r
523 return Status;\r
524}\r
525\r
9beb888e 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
6ad55b15 544EFI_STATUS\r
545EFIAPI\r
546ScsiDiskWriteBlocks (\r
547 IN EFI_BLOCK_IO_PROTOCOL *This,\r
548 IN UINT32 MediaId,\r
9beb888e 549 IN EFI_LBA Lba,\r
6ad55b15 550 IN UINTN BufferSize,\r
551 IN VOID *Buffer\r
552 )\r
6ad55b15 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
9beb888e 563 if (Buffer == NULL) {\r
6ad55b15 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
9beb888e 575 if (!IS_DEVICE_FIXED(ScsiDiskDevice)) {\r
6ad55b15 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
9beb888e 615 if (Lba > Media->LastBlock) {\r
6ad55b15 616 Status = EFI_INVALID_PARAMETER;\r
617 goto Done;\r
618 }\r
619\r
9beb888e 620 if ((Lba + NumberOfBlocks - 1) > Media->LastBlock) {\r
6ad55b15 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
9beb888e 633 Status = ScsiDiskWriteSectors (ScsiDiskDevice, Buffer, Lba, NumberOfBlocks);\r
6ad55b15 634\r
635Done:\r
636 gBS->RestoreTPL (OldTpl);\r
6ad55b15 637 return Status;\r
638}\r
639\r
9beb888e 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
6ad55b15 650EFI_STATUS\r
651EFIAPI\r
652ScsiDiskFlushBlocks (\r
653 IN EFI_BLOCK_IO_PROTOCOL *This\r
654 )\r
6ad55b15 655{\r
656 //\r
657 // return directly\r
658 //\r
659 return EFI_SUCCESS;\r
660}\r
661\r
6ad55b15 662\r
9beb888e 663/**\r
d716651f 664 Detect Device and read out capacity ,if error occurs, parse the sense key.\r
6ad55b15 665\r
9beb888e 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
6ad55b15 669\r
9beb888e 670 @retval EFI_DEVICE_ERROR Indicates that error occurs\r
671 @retval EFI_SUCCESS Successfully to detect media\r
6ad55b15 672\r
9beb888e 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
6ad55b15 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
6ad55b15 698 *MediaChange = FALSE;\r
6ad55b15 699 MaxRetry = 3;\r
f36d6e66 700\r
6ad55b15 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
f36d6e66 739\r
6ad55b15 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
6ad55b15 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
6ad55b15 849\r
9beb888e 850/**\r
851 Send out Inquiry command to Device.\r
6ad55b15 852\r
9beb888e 853 @param ScsiDiskDevice The pointer of SCSI_DISK_DEV\r
854 @param NeedRetry Indicates if needs try again when error happens\r
6ad55b15 855\r
9beb888e 856 @retval EFI_DEVICE_ERROR Indicates that error occurs\r
857 @retval EFI_SUCCESS Successfully to detect media\r
6ad55b15 858\r
9beb888e 859**/\r
860EFI_STATUS\r
861ScsiDiskInquiryDevice (\r
862 IN OUT SCSI_DISK_DEV *ScsiDiskDevice,\r
863 OUT BOOLEAN *NeedRetry\r
864 )\r
6ad55b15 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
d35be2a4 879 Status = ScsiInquiryCommand (\r
6ad55b15 880 ScsiDiskDevice->ScsiIo,\r
e72a3b3e 881 EFI_TIMER_PERIOD_SECONDS (1),\r
6ad55b15 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
6ad55b15 890 //\r
891 // no need to check HostAdapterStatus and TargetStatus\r
892 //\r
f36d6e66 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
6ad55b15 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
f36d6e66 931\r
6ad55b15 932 } else if (Status == EFI_DEVICE_ERROR) {\r
933 *NeedRetry = FALSE;\r
934 return EFI_DEVICE_ERROR;\r
935 }\r
936 \r
937 //\r
b96cd313 938 // if goes here, meant ScsiInquiryCommand() failed.\r
6ad55b15 939 // if ScsiDiskRequestSenseKeys() succeeds at last,\r
b96cd313 940 // better retry ScsiInquiryCommand(). (by setting *NeedRetry = TRUE)\r
6ad55b15 941 //\r
942 MaxRetry = 3;\r
943 for (Index = 0; Index < MaxRetry; Index++) {\r
6ad55b15 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
9beb888e 968/**\r
d716651f 969 To test device.\r
f36d6e66 970\r
971 When Test Unit Ready command succeeds, retrieve Sense Keys via Request Sense;\r
6ad55b15 972 When Test Unit Ready command encounters any error caused by host adapter or\r
973 target, return error without retrieving Sense Keys.\r
f36d6e66 974\r
9beb888e 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
f36d6e66 979\r
9beb888e 980 @retval EFI_DEVICE_ERROR Indicates that error occurs\r
981 @retval EFI_SUCCESS Successfully to test unit\r
f36d6e66 982\r
9beb888e 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
6ad55b15 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
d35be2a4 1005 Status = ScsiTestUnitReadyCommand (\r
6ad55b15 1006 ScsiDiskDevice->ScsiIo,\r
e72a3b3e 1007 EFI_TIMER_PERIOD_SECONDS (1),\r
6ad55b15 1008 NULL,\r
1009 &SenseDataLength,\r
1010 &HostAdapterStatus,\r
1011 &TargetStatus\r
1012 );\r
f36d6e66 1013 //\r
1014 // no need to check HostAdapterStatus and TargetStatus\r
1015 //\r
6ad55b15 1016 if (Status == EFI_NOT_READY) {\r
6ad55b15 1017 *NeedRetry = TRUE;\r
1018 return EFI_DEVICE_ERROR;\r
f36d6e66 1019\r
6ad55b15 1020 } else if ((Status == EFI_INVALID_PARAMETER) || (Status == EFI_UNSUPPORTED)) {\r
6ad55b15 1021 *NeedRetry = FALSE;\r
1022 return EFI_DEVICE_ERROR;\r
1023 }\r
1024 //\r
f36d6e66 1025 // go ahead to check HostAdapterStatus and TargetStatus(in case of EFI_DEVICE_ERROR)\r
6ad55b15 1026 //\r
f36d6e66 1027\r
6ad55b15 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
f36d6e66 1032\r
6ad55b15 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
f36d6e66 1050\r
6ad55b15 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
6ad55b15 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
9beb888e 1081/**\r
f36d6e66 1082 Parsing Sense Keys which got from request sense command.\r
6ad55b15 1083\r
9beb888e 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
6ad55b15 1088\r
9beb888e 1089 @retval EFI_DEVICE_ERROR Indicates that error occurs\r
1090 @retval EFI_SUCCESS Successfully to complete the parsing\r
6ad55b15 1091\r
9beb888e 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
6ad55b15 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
6ad55b15 1155\r
9beb888e 1156/**\r
1157 Send read capacity command to device and get the device parameter.\r
6ad55b15 1158\r
9beb888e 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
6ad55b15 1163\r
9beb888e 1164 @retval EFI_DEVICE_ERROR Indicates that error occurs\r
1165 @retval EFI_SUCCESS Successfully to read capacity\r
6ad55b15 1166\r
9beb888e 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
6ad55b15 1175{\r
b96cd313 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
b96cd313 1183 UINT32 DataLength10;\r
1184 UINT32 DataLength16;\r
1185 EFI_SCSI_DISK_CAPACITY_DATA CapacityData10;\r
1186 EFI_SCSI_DISK_CAPACITY_DATA16 CapacityData16;\r
1187\r
1188\r
1189 SenseDataLength = 0;\r
1190 DataLength10 = sizeof (EFI_SCSI_DISK_CAPACITY_DATA);\r
1191 DataLength16 = sizeof (EFI_SCSI_DISK_CAPACITY_DATA16);\r
1192 ZeroMem (&CapacityData10, sizeof (EFI_SCSI_DISK_CAPACITY_DATA));\r
1193 ZeroMem (&CapacityData16, sizeof (EFI_SCSI_DISK_CAPACITY_DATA16));\r
6ad55b15 1194\r
1195 *NumberOfSenseKeys = 0;\r
1196 *NeedRetry = FALSE;\r
b96cd313 1197\r
f95bc048 1198 //\r
1199 // submit Read Capacity(10) Command. If it returns capacity of FFFFFFFFh, \r
1200 // 16 byte command should be used to access large hard disk >2TB\r
1201 //\r
1202 CommandStatus = ScsiReadCapacityCommand (\r
1203 ScsiDiskDevice->ScsiIo,\r
1204 EFI_TIMER_PERIOD_SECONDS(1),\r
1205 NULL,\r
1206 &SenseDataLength,\r
1207 &HostAdapterStatus,\r
1208 &TargetStatus,\r
1209 (VOID *) &CapacityData10,\r
1210 &DataLength10,\r
1211 FALSE\r
1212 );\r
1213\r
1214 ScsiDiskDevice->Cdb16Byte = FALSE;\r
1215 if ((!EFI_ERROR (CommandStatus)) && (CapacityData10.LastLba3 == 0xff) && (CapacityData10.LastLba2 == 0xff) &&\r
1216 (CapacityData10.LastLba1 == 0xff) && (CapacityData10.LastLba0 == 0xff)) {\r
1217 //\r
1218 // use Read Capacity (16), Read (16) and Write (16) next when hard disk size > 2TB\r
1219 //\r
1220 ScsiDiskDevice->Cdb16Byte = TRUE;\r
b96cd313 1221 //\r
f95bc048 1222 // submit Read Capacity(16) Command to get parameter LogicalBlocksPerPhysicalBlock\r
1223 // and LowestAlignedLba\r
b96cd313 1224 //\r
f95bc048 1225 CommandStatus = ScsiReadCapacity16Command (\r
b96cd313 1226 ScsiDiskDevice->ScsiIo,\r
f95bc048 1227 EFI_TIMER_PERIOD_SECONDS (1),\r
b96cd313 1228 NULL,\r
1229 &SenseDataLength,\r
1230 &HostAdapterStatus,\r
1231 &TargetStatus,\r
f95bc048 1232 (VOID *) &CapacityData16,\r
1233 &DataLength16,\r
b96cd313 1234 FALSE\r
1235 );\r
f95bc048 1236 }\r
1237\r
b96cd313 1238 //\r
6ad55b15 1239 // no need to check HostAdapterStatus and TargetStatus\r
1240 //\r
f36d6e66 1241 if (CommandStatus == EFI_SUCCESS) {\r
b96cd313 1242 GetMediaInfo (ScsiDiskDevice, &CapacityData10,&CapacityData16);\r
f36d6e66 1243 return EFI_SUCCESS;\r
1244 \r
1245 } else if (CommandStatus == EFI_NOT_READY) {\r
1246 *NeedRetry = TRUE;\r
1247 return EFI_DEVICE_ERROR;\r
1248 \r
1249 } else if ((CommandStatus == EFI_INVALID_PARAMETER) || (CommandStatus == EFI_UNSUPPORTED)) {\r
1250 *NeedRetry = FALSE;\r
1251 return EFI_DEVICE_ERROR;\r
1252 }\r
1253 //\r
1254 // go ahead to check HostAdapterStatus and TargetStatus\r
1255 // (EFI_TIMEOUT, EFI_DEVICE_ERROR, EFI_WARN_BUFFER_TOO_SMALL)\r
1256 //\r
1257 \r
1258 Status = CheckHostAdapterStatus (HostAdapterStatus);\r
1259 if ((Status == EFI_TIMEOUT) || (Status == EFI_NOT_READY)) {\r
1260 *NeedRetry = TRUE;\r
1261 return EFI_DEVICE_ERROR;\r
1262 \r
1263 } else if (Status == EFI_DEVICE_ERROR) {\r
6ad55b15 1264 //\r
1265 // reset the scsi channel\r
1266 //\r
1267 ScsiDiskDevice->ScsiIo->ResetBus (ScsiDiskDevice->ScsiIo);\r
1268 *NeedRetry = FALSE;\r
1269 return EFI_DEVICE_ERROR;\r
1270 }\r
1271\r
1272 Status = CheckTargetStatus (TargetStatus);\r
1273 if (Status == EFI_NOT_READY) {\r
1274 //\r
1275 // reset the scsi device\r
1276 //\r
1277 ScsiDiskDevice->ScsiIo->ResetDevice (ScsiDiskDevice->ScsiIo);\r
1278 *NeedRetry = TRUE;\r
1279 return EFI_DEVICE_ERROR;\r
f36d6e66 1280\r
6ad55b15 1281 } else if (Status == EFI_DEVICE_ERROR) {\r
1282 *NeedRetry = FALSE;\r
1283 return EFI_DEVICE_ERROR;\r
1284 }\r
1285 \r
1286 //\r
b96cd313 1287 // if goes here, meant ScsiReadCapacityCommand() failed.\r
6ad55b15 1288 // if ScsiDiskRequestSenseKeys() succeeds at last,\r
b96cd313 1289 // better retry ScsiReadCapacityCommand(). (by setting *NeedRetry = TRUE)\r
6ad55b15 1290 //\r
1291 MaxRetry = 3;\r
1292 for (Index = 0; Index < MaxRetry; Index++) {\r
1293\r
1294 Status = ScsiDiskRequestSenseKeys (\r
1295 ScsiDiskDevice,\r
1296 NeedRetry,\r
1297 SenseDataArray,\r
1298 NumberOfSenseKeys,\r
1299 TRUE\r
1300 );\r
1301 if (!EFI_ERROR (Status)) {\r
1302 *NeedRetry = TRUE;\r
1303 return EFI_DEVICE_ERROR;\r
1304 }\r
1305\r
1306 if (!*NeedRetry) {\r
1307 return EFI_DEVICE_ERROR;\r
1308 }\r
1309 }\r
1310 //\r
1311 // ScsiDiskRequestSenseKeys() failed after several rounds of retry.\r
1312 // set *NeedRetry = FALSE to avoid the outside caller try again.\r
1313 //\r
1314 *NeedRetry = FALSE;\r
1315 return EFI_DEVICE_ERROR;\r
1316}\r
1317\r
9beb888e 1318/**\r
1319 Check the HostAdapter status and re-interpret it in EFI_STATUS.\r
6ad55b15 1320\r
9beb888e 1321 @param HostAdapterStatus Host Adapter status\r
6ad55b15 1322\r
9beb888e 1323 @retval EFI_SUCCESS Host adapter is OK.\r
1324 @retval EFI_TIMEOUT Timeout.\r
1325 @retval EFI_NOT_READY Adapter NOT ready.\r
1326 @retval EFI_DEVICE_ERROR Adapter device error.\r
6ad55b15 1327\r
9beb888e 1328**/\r
1329EFI_STATUS\r
1330CheckHostAdapterStatus (\r
1331 IN UINT8 HostAdapterStatus\r
1332 )\r
6ad55b15 1333{\r
1334 switch (HostAdapterStatus) {\r
f36d6e66 1335 case EFI_EXT_SCSI_STATUS_HOST_ADAPTER_OK:\r
6ad55b15 1336 return EFI_SUCCESS;\r
1337\r
f36d6e66 1338 case EFI_EXT_SCSI_STATUS_HOST_ADAPTER_SELECTION_TIMEOUT:\r
1339 case EFI_EXT_SCSI_STATUS_HOST_ADAPTER_TIMEOUT:\r
1340 case EFI_EXT_SCSI_STATUS_HOST_ADAPTER_TIMEOUT_COMMAND:\r
6ad55b15 1341 return EFI_TIMEOUT;\r
1342\r
f36d6e66 1343 case EFI_EXT_SCSI_STATUS_HOST_ADAPTER_MESSAGE_REJECT:\r
1344 case EFI_EXT_SCSI_STATUS_HOST_ADAPTER_PARITY_ERROR:\r
1345 case EFI_EXT_SCSI_STATUS_HOST_ADAPTER_REQUEST_SENSE_FAILED:\r
1346 case EFI_EXT_SCSI_STATUS_HOST_ADAPTER_DATA_OVERRUN_UNDERRUN:\r
1347 case EFI_EXT_SCSI_STATUS_HOST_ADAPTER_BUS_RESET:\r
6ad55b15 1348 return EFI_NOT_READY;\r
1349\r
f36d6e66 1350 case EFI_EXT_SCSI_STATUS_HOST_ADAPTER_BUS_FREE:\r
1351 case EFI_EXT_SCSI_STATUS_HOST_ADAPTER_PHASE_ERROR:\r
6ad55b15 1352 return EFI_DEVICE_ERROR;\r
1353\r
1354 default:\r
1355 return EFI_SUCCESS;\r
1356 }\r
1357}\r
1358\r
6ad55b15 1359\r
9beb888e 1360/**\r
1361 Check the target status and re-interpret it in EFI_STATUS.\r
6ad55b15 1362\r
9beb888e 1363 @param TargetStatus Target status\r
6ad55b15 1364\r
9beb888e 1365 @retval EFI_NOT_READY Device is NOT ready.\r
1366 @retval EFI_DEVICE_ERROR \r
1367 @retval EFI_SUCCESS\r
6ad55b15 1368\r
9beb888e 1369**/\r
1370EFI_STATUS\r
1371CheckTargetStatus (\r
1372 IN UINT8 TargetStatus\r
1373 )\r
6ad55b15 1374{\r
1375 switch (TargetStatus) {\r
f36d6e66 1376 case EFI_EXT_SCSI_STATUS_TARGET_GOOD:\r
1377 case EFI_EXT_SCSI_STATUS_TARGET_CHECK_CONDITION:\r
1378 case EFI_EXT_SCSI_STATUS_TARGET_CONDITION_MET:\r
6ad55b15 1379 return EFI_SUCCESS;\r
1380\r
f36d6e66 1381 case EFI_EXT_SCSI_STATUS_TARGET_INTERMEDIATE:\r
1382 case EFI_EXT_SCSI_STATUS_TARGET_INTERMEDIATE_CONDITION_MET:\r
1383 case EFI_EXT_SCSI_STATUS_TARGET_BUSY:\r
1384 case EFI_EXT_SCSI_STATUS_TARGET_TASK_SET_FULL:\r
6ad55b15 1385 return EFI_NOT_READY;\r
1386\r
f36d6e66 1387 case EFI_EXT_SCSI_STATUS_TARGET_RESERVATION_CONFLICT:\r
6ad55b15 1388 return EFI_DEVICE_ERROR;\r
1389 break;\r
1390\r
1391 default:\r
1392 return EFI_SUCCESS;\r
1393 }\r
1394}\r
1395\r
f36d6e66 1396\r
9beb888e 1397/**\r
6ad55b15 1398 Retrieve all sense keys from the device.\r
f36d6e66 1399\r
9beb888e 1400 When encountering error during the process, if retrieve sense keys before\r
d716651f 1401 error encountered, it returns the sense keys with return status set to EFI_SUCCESS,\r
9beb888e 1402 and NeedRetry set to FALSE; otherwize, return the proper return status.\r
1403\r
1404 @param ScsiDiskDevice The pointer of SCSI_DISK_DEV\r
1405 @param NeedRetry The pointer of flag indicates if need a retry\r
1406 @param SenseDataArray The pointer of an array of sense data\r
1407 @param NumberOfSenseKeys The number of sense key\r
1408 @param AskResetIfError The flag indicates if need reset when error occurs\r
1409\r
1410 @retval EFI_DEVICE_ERROR Indicates that error occurs\r
1411 @retval EFI_SUCCESS Successfully to request sense key\r
f36d6e66 1412\r
9beb888e 1413**/\r
1414EFI_STATUS\r
1415ScsiDiskRequestSenseKeys (\r
1416 IN OUT SCSI_DISK_DEV *ScsiDiskDevice,\r
1417 OUT BOOLEAN *NeedRetry,\r
1418 OUT EFI_SCSI_SENSE_DATA **SenseDataArray,\r
1419 OUT UINTN *NumberOfSenseKeys,\r
1420 IN BOOLEAN AskResetIfError\r
1421 )\r
6ad55b15 1422{\r
1423 EFI_SCSI_SENSE_DATA *PtrSenseData;\r
1424 UINT8 SenseDataLength;\r
1425 BOOLEAN SenseReq;\r
1426 EFI_STATUS Status;\r
1427 EFI_STATUS FallStatus;\r
1428 UINT8 HostAdapterStatus;\r
1429 UINT8 TargetStatus;\r
1430\r
1431 FallStatus = EFI_SUCCESS;\r
c9325700 1432 SenseDataLength = (UINT8) sizeof (EFI_SCSI_SENSE_DATA);\r
6ad55b15 1433\r
1434 ZeroMem (\r
1435 ScsiDiskDevice->SenseData,\r
1436 sizeof (EFI_SCSI_SENSE_DATA) * (ScsiDiskDevice->SenseDataNumber)\r
1437 );\r
1438\r
1439 *NumberOfSenseKeys = 0;\r
1440 *SenseDataArray = ScsiDiskDevice->SenseData;\r
1441 PtrSenseData = ScsiDiskDevice->SenseData;\r
1442\r
1443 for (SenseReq = TRUE; SenseReq;) {\r
d35be2a4 1444 Status = ScsiRequestSenseCommand (\r
6ad55b15 1445 ScsiDiskDevice->ScsiIo,\r
e72a3b3e 1446 EFI_TIMER_PERIOD_SECONDS (2),\r
6ad55b15 1447 PtrSenseData,\r
1448 &SenseDataLength,\r
1449 &HostAdapterStatus,\r
1450 &TargetStatus\r
1451 );\r
f36d6e66 1452 if ((Status == EFI_SUCCESS) || (Status == EFI_WARN_BUFFER_TOO_SMALL)) {\r
1453 FallStatus = EFI_SUCCESS;\r
1454 \r
1455 } else if ((Status == EFI_TIMEOUT) || (Status == EFI_NOT_READY)) {\r
1456 *NeedRetry = TRUE;\r
1457 FallStatus = EFI_DEVICE_ERROR;\r
1458 \r
1459 } else if ((Status == EFI_INVALID_PARAMETER) || (Status == EFI_UNSUPPORTED)) {\r
1460 *NeedRetry = FALSE;\r
1461 FallStatus = EFI_DEVICE_ERROR;\r
1462 \r
1463 } else if (Status == EFI_DEVICE_ERROR) {\r
1464 if (AskResetIfError) {\r
1465 ScsiDiskDevice->ScsiIo->ResetDevice (ScsiDiskDevice->ScsiIo);\r
1466 }\r
1467 \r
1468 FallStatus = EFI_DEVICE_ERROR;\r
6ad55b15 1469 }\r
1470\r
1471 if (EFI_ERROR (FallStatus)) {\r
1472 if (*NumberOfSenseKeys != 0) {\r
1473 *NeedRetry = FALSE;\r
1474 return EFI_SUCCESS;\r
1475 } else {\r
1476 return EFI_DEVICE_ERROR;\r
1477 }\r
1478 }\r
1479\r
1480 (*NumberOfSenseKeys) += 1;\r
1481\r
1482 //\r
1483 // no more sense key or number of sense keys exceeds predefined,\r
1484 // skip the loop.\r
1485 //\r
1486 if ((PtrSenseData->Sense_Key == EFI_SCSI_SK_NO_SENSE) || \r
1487 (*NumberOfSenseKeys == ScsiDiskDevice->SenseDataNumber)) {\r
1488 SenseReq = FALSE;\r
1489 }\r
6ad55b15 1490 PtrSenseData += 1;\r
6ad55b15 1491 }\r
6ad55b15 1492 return EFI_SUCCESS;\r
1493}\r
1494\r
6ad55b15 1495\r
9beb888e 1496/**\r
1497 Get information from media read capacity command.\r
6ad55b15 1498\r
9beb888e 1499 @param ScsiDiskDevice The pointer of SCSI_DISK_DEV\r
aa75dfec 1500 @param Capacity10 The pointer of EFI_SCSI_DISK_CAPACITY_DATA\r
1501 @param Capacity16 The pointer of EFI_SCSI_DISK_CAPACITY_DATA16\r
6ad55b15 1502\r
9beb888e 1503**/\r
1504VOID\r
1505GetMediaInfo (\r
aa75dfec 1506 IN OUT SCSI_DISK_DEV *ScsiDiskDevice,\r
1507 IN EFI_SCSI_DISK_CAPACITY_DATA *Capacity10,\r
1508 IN EFI_SCSI_DISK_CAPACITY_DATA16 *Capacity16\r
9beb888e 1509 )\r
6ad55b15 1510{\r
b96cd313 1511 UINT8 *Ptr;\r
1512\r
b96cd313 1513 ScsiDiskDevice->BlkIo.Media->LowestAlignedLba = 0;\r
1514 ScsiDiskDevice->BlkIo.Media->LogicalBlocksPerPhysicalBlock = 1;\r
1515 \r
1516\r
f95bc048 1517 if (!ScsiDiskDevice->Cdb16Byte) {\r
b96cd313 1518 ScsiDiskDevice->BlkIo.Media->LastBlock = (Capacity10->LastLba3 << 24) |\r
1519 (Capacity10->LastLba2 << 16) |\r
1520 (Capacity10->LastLba1 << 8) |\r
1521 Capacity10->LastLba0;\r
1522 \r
1523 ScsiDiskDevice->BlkIo.Media->BlockSize = (Capacity10->BlockSize3 << 24) |\r
1524 (Capacity10->BlockSize2 << 16) | \r
1525 (Capacity10->BlockSize1 << 8) |\r
1526 Capacity10->BlockSize0;\r
1527 ScsiDiskDevice->BlkIo.Revision = EFI_BLOCK_IO_PROTOCOL_REVISION; \r
1528 } else {\r
1529\r
1530 Ptr = (UINT8*)&ScsiDiskDevice->BlkIo.Media->LastBlock;\r
1531 *Ptr++ = Capacity16->LastLba0;\r
1532 *Ptr++ = Capacity16->LastLba1;\r
1533 *Ptr++ = Capacity16->LastLba2;\r
1534 *Ptr++ = Capacity16->LastLba3;\r
1535 *Ptr++ = Capacity16->LastLba4;\r
1536 *Ptr++ = Capacity16->LastLba5;\r
1537 *Ptr++ = Capacity16->LastLba6;\r
1538 *Ptr = Capacity16->LastLba7;\r
1539 \r
1540 ScsiDiskDevice->BlkIo.Media->BlockSize = (Capacity16->BlockSize3 << 24) |\r
1541 (Capacity16->BlockSize2 << 16) | \r
1542 (Capacity16->BlockSize1 << 8) |\r
1543 Capacity16->BlockSize0;\r
1544\r
1545 ScsiDiskDevice->BlkIo.Media->LowestAlignedLba = (Capacity16->LowestAlignLogic2 << 8)|(Capacity16->LowestAlignLogic1);\r
1546 ScsiDiskDevice->BlkIo.Media->LogicalBlocksPerPhysicalBlock = Capacity16->LogicPerPhysical;\r
1547 ScsiDiskDevice->BlkIo.Revision = EFI_BLOCK_IO_PROTOCOL_REVISION2; \r
1548 }\r
1549\r
6ad55b15 1550\r
1551 ScsiDiskDevice->BlkIo.Media->MediaPresent = TRUE;\r
b96cd313 1552 \r
6ad55b15 1553 if (ScsiDiskDevice->DeviceType == EFI_SCSI_TYPE_DISK) {\r
1554 ScsiDiskDevice->BlkIo.Media->BlockSize = 0x200;\r
1555 }\r
1556\r
1557 if (ScsiDiskDevice->DeviceType == EFI_SCSI_TYPE_CDROM) {\r
1558 ScsiDiskDevice->BlkIo.Media->BlockSize = 0x800;\r
1559 }\r
1560}\r
1561\r
9beb888e 1562/**\r
1563 Parse Inquiry data.\r
1564\r
1565 @param ScsiDiskDevice The pointer of SCSI_DISK_DEV\r
1566\r
1567**/\r
6ad55b15 1568VOID\r
1569ParseInquiryData (\r
9beb888e 1570 IN OUT SCSI_DISK_DEV *ScsiDiskDevice\r
6ad55b15 1571 )\r
6ad55b15 1572{\r
fbfa4a1d 1573 ScsiDiskDevice->FixedDevice = (BOOLEAN) ((ScsiDiskDevice->InquiryData.Rmb == 1) ? 0 : 1);\r
6ad55b15 1574 ScsiDiskDevice->BlkIoMedia.RemovableMedia = (BOOLEAN) (!ScsiDiskDevice->FixedDevice);\r
1575}\r
1576\r
9beb888e 1577/**\r
1578 Read sector from SCSI Disk.\r
6ad55b15 1579\r
d716651f 1580 @param ScsiDiskDevice The pointer of SCSI_DISK_DEV\r
9beb888e 1581 @param Buffer The buffer to fill in the read out data\r
1582 @param Lba Logic block address\r
1583 @param NumberOfBlocks The number of blocks to read\r
6ad55b15 1584\r
9beb888e 1585 @retval EFI_DEVICE_ERROR Indicates a device error.\r
1586 @retval EFI_SUCCESS Operation is successful.\r
6ad55b15 1587\r
9beb888e 1588**/\r
1589EFI_STATUS\r
1590ScsiDiskReadSectors (\r
1591 IN SCSI_DISK_DEV *ScsiDiskDevice,\r
1592 OUT VOID *Buffer,\r
1593 IN EFI_LBA Lba,\r
1594 IN UINTN NumberOfBlocks\r
1595 )\r
6ad55b15 1596{\r
1597 UINTN BlocksRemaining;\r
6ad55b15 1598 UINT8 *PtrBuffer;\r
1599 UINT32 BlockSize;\r
1600 UINT32 ByteCount;\r
1601 UINT32 MaxBlock;\r
1602 UINT32 SectorCount;\r
1603 UINT64 Timeout;\r
1604 EFI_STATUS Status;\r
1605 UINT8 Index;\r
1606 UINT8 MaxRetry;\r
1607 BOOLEAN NeedRetry;\r
1608 EFI_SCSI_SENSE_DATA *SenseData;\r
1609 UINTN NumberOfSenseKeys;\r
1610\r
1611 SenseData = NULL;\r
1612 NumberOfSenseKeys = 0;\r
1613\r
1614 Status = EFI_SUCCESS;\r
1615\r
1616 BlocksRemaining = NumberOfBlocks;\r
1617 BlockSize = ScsiDiskDevice->BlkIo.Media->BlockSize;\r
a108933e 1618 \r
6ad55b15 1619 //\r
a108933e 1620 // limit the data bytes that can be transferred by one Read(10) or Read(16) Command\r
6ad55b15 1621 //\r
f95bc048 1622 if (!ScsiDiskDevice->Cdb16Byte) {\r
5bf5fb30 1623 MaxBlock = 0xFFFF;\r
a108933e 1624 } else {\r
5bf5fb30 1625 MaxBlock = 0xFFFFFFFF;\r
a108933e 1626 }\r
6ad55b15 1627\r
1628 PtrBuffer = Buffer;\r
6ad55b15 1629\r
1630 while (BlocksRemaining > 0) {\r
1631\r
1632 if (BlocksRemaining <= MaxBlock) {\r
f95bc048 1633 if (!ScsiDiskDevice->Cdb16Byte) {\r
a108933e 1634 SectorCount = (UINT16) BlocksRemaining;\r
1635 } else {\r
1636 SectorCount = (UINT32) BlocksRemaining;\r
1637 }\r
6ad55b15 1638 } else {\r
6ad55b15 1639 SectorCount = MaxBlock;\r
1640 }\r
1641\r
1642 ByteCount = SectorCount * BlockSize;\r
e72a3b3e 1643 Timeout = EFI_TIMER_PERIOD_SECONDS (2);\r
6ad55b15 1644\r
1645 MaxRetry = 2;\r
1646 for (Index = 0; Index < MaxRetry; Index++) {\r
f95bc048 1647 if (!ScsiDiskDevice->Cdb16Byte) {\r
1648 Status = ScsiDiskRead10 (\r
a108933e 1649 ScsiDiskDevice,\r
1650 &NeedRetry,\r
1651 &SenseData,\r
1652 &NumberOfSenseKeys,\r
1653 Timeout,\r
1654 PtrBuffer,\r
1655 &ByteCount,\r
f95bc048 1656 (UINT32) Lba,\r
a108933e 1657 SectorCount\r
1658 );\r
1659 } else {\r
f95bc048 1660 Status = ScsiDiskRead16 (\r
a108933e 1661 ScsiDiskDevice,\r
1662 &NeedRetry,\r
1663 &SenseData,\r
1664 &NumberOfSenseKeys,\r
1665 Timeout,\r
1666 PtrBuffer,\r
1667 &ByteCount,\r
f95bc048 1668 Lba,\r
a108933e 1669 SectorCount\r
1670 );\r
1671 }\r
6ad55b15 1672 if (!EFI_ERROR (Status)) {\r
1673 break;\r
1674 }\r
1675\r
1676 if (!NeedRetry) {\r
1677 return EFI_DEVICE_ERROR;\r
1678 }\r
1679\r
1680 }\r
1681\r
1682 if ((Index == MaxRetry) && (Status != EFI_SUCCESS)) {\r
1683 return EFI_DEVICE_ERROR;\r
1684 }\r
1685\r
1686 //\r
1687 // actual transferred sectors\r
1688 //\r
1689 SectorCount = ByteCount / BlockSize;\r
1690\r
a108933e 1691 Lba += SectorCount;\r
6ad55b15 1692 PtrBuffer = PtrBuffer + SectorCount * BlockSize;\r
1693 BlocksRemaining -= SectorCount;\r
1694 }\r
1695\r
1696 return EFI_SUCCESS;\r
1697}\r
1698\r
9beb888e 1699/**\r
1700 Write sector to SCSI Disk.\r
6ad55b15 1701\r
d716651f 1702 @param ScsiDiskDevice The pointer of SCSI_DISK_DEV\r
9beb888e 1703 @param Buffer The buffer of data to be written into SCSI Disk\r
1704 @param Lba Logic block address\r
1705 @param NumberOfBlocks The number of blocks to read\r
6ad55b15 1706\r
9beb888e 1707 @retval EFI_DEVICE_ERROR Indicates a device error.\r
1708 @retval EFI_SUCCESS Operation is successful.\r
6ad55b15 1709\r
9beb888e 1710**/\r
1711EFI_STATUS\r
1712ScsiDiskWriteSectors (\r
1713 IN SCSI_DISK_DEV *ScsiDiskDevice,\r
1714 IN VOID *Buffer,\r
1715 IN EFI_LBA Lba,\r
1716 IN UINTN NumberOfBlocks\r
1717 )\r
6ad55b15 1718{\r
1719 UINTN BlocksRemaining;\r
6ad55b15 1720 UINT8 *PtrBuffer;\r
1721 UINT32 BlockSize;\r
1722 UINT32 ByteCount;\r
1723 UINT32 MaxBlock;\r
1724 UINT32 SectorCount;\r
1725 UINT64 Timeout;\r
1726 EFI_STATUS Status;\r
1727 UINT8 Index;\r
1728 UINT8 MaxRetry;\r
1729 BOOLEAN NeedRetry;\r
1730 EFI_SCSI_SENSE_DATA *SenseData;\r
1731 UINTN NumberOfSenseKeys;\r
1732\r
1733 SenseData = NULL;\r
1734 NumberOfSenseKeys = 0;\r
1735\r
1736 Status = EFI_SUCCESS;\r
1737\r
1738 BlocksRemaining = NumberOfBlocks;\r
1739 BlockSize = ScsiDiskDevice->BlkIo.Media->BlockSize;\r
a108933e 1740\r
6ad55b15 1741 //\r
a108933e 1742 // limit the data bytes that can be transferred by one Read(10) or Read(16) Command\r
6ad55b15 1743 //\r
f95bc048 1744 if (!ScsiDiskDevice->Cdb16Byte) {\r
5bf5fb30 1745 MaxBlock = 0xFFFF;\r
a108933e 1746 } else {\r
5bf5fb30 1747 MaxBlock = 0xFFFFFFFF;\r
a108933e 1748 }\r
6ad55b15 1749\r
1750 PtrBuffer = Buffer;\r
6ad55b15 1751\r
1752 while (BlocksRemaining > 0) {\r
1753\r
1754 if (BlocksRemaining <= MaxBlock) {\r
f95bc048 1755 if (!ScsiDiskDevice->Cdb16Byte) {\r
a108933e 1756 SectorCount = (UINT16) BlocksRemaining;\r
1757 } else {\r
1758 SectorCount = (UINT32) BlocksRemaining;\r
1759 }\r
6ad55b15 1760 } else {\r
6ad55b15 1761 SectorCount = MaxBlock;\r
1762 }\r
1763\r
1764 ByteCount = SectorCount * BlockSize;\r
e72a3b3e 1765 Timeout = EFI_TIMER_PERIOD_SECONDS (2);\r
6ad55b15 1766 MaxRetry = 2;\r
1767 for (Index = 0; Index < MaxRetry; Index++) {\r
f95bc048 1768 if (!ScsiDiskDevice->Cdb16Byte) {\r
1769 Status = ScsiDiskWrite10 (\r
a108933e 1770 ScsiDiskDevice,\r
1771 &NeedRetry,\r
1772 &SenseData,\r
1773 &NumberOfSenseKeys,\r
1774 Timeout,\r
1775 PtrBuffer,\r
1776 &ByteCount,\r
f95bc048 1777 (UINT32) Lba,\r
a108933e 1778 SectorCount\r
f95bc048 1779 );\r
a108933e 1780 } else {\r
f95bc048 1781 Status = ScsiDiskWrite16 (\r
a108933e 1782 ScsiDiskDevice,\r
1783 &NeedRetry,\r
1784 &SenseData,\r
1785 &NumberOfSenseKeys,\r
1786 Timeout,\r
1787 PtrBuffer,\r
1788 &ByteCount,\r
f95bc048 1789 Lba,\r
a108933e 1790 SectorCount\r
f95bc048 1791 ); \r
a108933e 1792 }\r
6ad55b15 1793 if (!EFI_ERROR (Status)) {\r
1794 break;\r
1795 }\r
1796\r
1797 if (!NeedRetry) {\r
1798 return EFI_DEVICE_ERROR;\r
1799 }\r
1800 }\r
1801\r
1802 if ((Index == MaxRetry) && (Status != EFI_SUCCESS)) {\r
1803 return EFI_DEVICE_ERROR;\r
1804 }\r
1805 //\r
1806 // actual transferred sectors\r
1807 //\r
1808 SectorCount = ByteCount / BlockSize;\r
1809\r
a108933e 1810 Lba += SectorCount;\r
6ad55b15 1811 PtrBuffer = PtrBuffer + SectorCount * BlockSize;\r
1812 BlocksRemaining -= SectorCount;\r
1813 }\r
1814\r
1815 return EFI_SUCCESS;\r
1816}\r
1817\r
9beb888e 1818\r
1819/**\r
a108933e 1820 Submit Read(10) command.\r
9beb888e 1821\r
1822 @param ScsiDiskDevice The pointer of ScsiDiskDevice\r
1823 @param NeedRetry The pointer of flag indicates if needs retry if error happens\r
1824 @param SenseDataArray NOT used yet in this function\r
1825 @param NumberOfSenseKeys The number of sense key\r
1826 @param Timeout The time to complete the command\r
1827 @param DataBuffer The buffer to fill with the read out data\r
1828 @param DataLength The length of buffer\r
1829 @param StartLba The start logic block address\r
1830 @param SectorSize The size of sector\r
1831\r
1832 @return EFI_STATUS is returned by calling ScsiRead10Command().\r
1833**/\r
6ad55b15 1834EFI_STATUS\r
1835ScsiDiskRead10 (\r
9beb888e 1836 IN SCSI_DISK_DEV *ScsiDiskDevice,\r
1837 OUT BOOLEAN *NeedRetry,\r
1838 OUT EFI_SCSI_SENSE_DATA **SenseDataArray, OPTIONAL\r
1839 OUT UINTN *NumberOfSenseKeys,\r
1840 IN UINT64 Timeout,\r
1841 OUT UINT8 *DataBuffer,\r
1842 IN OUT UINT32 *DataLength,\r
1843 IN UINT32 StartLba,\r
1844 IN UINT32 SectorSize\r
6ad55b15 1845 )\r
6ad55b15 1846{\r
1847 UINT8 SenseDataLength;\r
1848 EFI_STATUS Status;\r
1849 UINT8 HostAdapterStatus;\r
1850 UINT8 TargetStatus;\r
1851\r
1852 *NeedRetry = FALSE;\r
1853 *NumberOfSenseKeys = 0;\r
1854 SenseDataLength = 0;\r
d35be2a4 1855 Status = ScsiRead10Command (\r
6ad55b15 1856 ScsiDiskDevice->ScsiIo,\r
1857 Timeout,\r
1858 NULL,\r
1859 &SenseDataLength,\r
1860 &HostAdapterStatus,\r
1861 &TargetStatus,\r
1862 DataBuffer,\r
1863 DataLength,\r
1864 StartLba,\r
1865 SectorSize\r
1866 );\r
1867 return Status;\r
1868}\r
1869\r
6ad55b15 1870\r
9beb888e 1871/**\r
a108933e 1872 Submit Write(10) Command.\r
6ad55b15 1873\r
9beb888e 1874 @param ScsiDiskDevice The pointer of ScsiDiskDevice\r
1875 @param NeedRetry The pointer of flag indicates if needs retry if error happens\r
1876 @param SenseDataArray NOT used yet in this function\r
1877 @param NumberOfSenseKeys The number of sense key\r
1878 @param Timeout The time to complete the command\r
1879 @param DataBuffer The buffer to fill with the read out data\r
1880 @param DataLength The length of buffer\r
1881 @param StartLba The start logic block address\r
1882 @param SectorSize The size of sector\r
6ad55b15 1883\r
9beb888e 1884 @return EFI_STATUS is returned by calling ScsiWrite10Command().\r
6ad55b15 1885\r
9beb888e 1886**/\r
1887EFI_STATUS\r
1888ScsiDiskWrite10 (\r
1889 IN SCSI_DISK_DEV *ScsiDiskDevice,\r
1890 OUT BOOLEAN *NeedRetry,\r
1891 OUT EFI_SCSI_SENSE_DATA **SenseDataArray, OPTIONAL\r
1892 OUT UINTN *NumberOfSenseKeys,\r
1893 IN UINT64 Timeout,\r
1894 IN UINT8 *DataBuffer,\r
1895 IN OUT UINT32 *DataLength,\r
1896 IN UINT32 StartLba,\r
1897 IN UINT32 SectorSize\r
1898 )\r
6ad55b15 1899{\r
1900 EFI_STATUS Status;\r
1901 UINT8 SenseDataLength;\r
1902 UINT8 HostAdapterStatus;\r
1903 UINT8 TargetStatus;\r
1904\r
1905 *NeedRetry = FALSE;\r
1906 *NumberOfSenseKeys = 0;\r
1907 SenseDataLength = 0;\r
d35be2a4 1908 Status = ScsiWrite10Command (\r
6ad55b15 1909 ScsiDiskDevice->ScsiIo,\r
1910 Timeout,\r
1911 NULL,\r
1912 &SenseDataLength,\r
1913 &HostAdapterStatus,\r
1914 &TargetStatus,\r
1915 DataBuffer,\r
1916 DataLength,\r
1917 StartLba,\r
1918 SectorSize\r
1919 );\r
1920 return Status;\r
1921}\r
1922\r
9beb888e 1923\r
a108933e 1924/**\r
1925 Submit Read(16) command.\r
1926\r
1927 @param ScsiDiskDevice The pointer of ScsiDiskDevice\r
1928 @param NeedRetry The pointer of flag indicates if needs retry if error happens\r
1929 @param SenseDataArray NOT used yet in this function\r
1930 @param NumberOfSenseKeys The number of sense key\r
1931 @param Timeout The time to complete the command\r
1932 @param DataBuffer The buffer to fill with the read out data\r
1933 @param DataLength The length of buffer\r
1934 @param StartLba The start logic block address\r
1935 @param SectorSize The size of sector\r
1936\r
1937 @return EFI_STATUS is returned by calling ScsiRead10Command().\r
1938**/\r
1939EFI_STATUS\r
1940ScsiDiskRead16 (\r
1941 IN SCSI_DISK_DEV *ScsiDiskDevice,\r
1942 OUT BOOLEAN *NeedRetry,\r
1943 OUT EFI_SCSI_SENSE_DATA **SenseDataArray, OPTIONAL\r
1944 OUT UINTN *NumberOfSenseKeys,\r
1945 IN UINT64 Timeout,\r
1946 OUT UINT8 *DataBuffer,\r
1947 IN OUT UINT32 *DataLength,\r
1948 IN UINT64 StartLba,\r
1949 IN UINT32 SectorSize\r
1950 )\r
1951{\r
1952 UINT8 SenseDataLength;\r
1953 EFI_STATUS Status;\r
1954 UINT8 HostAdapterStatus;\r
1955 UINT8 TargetStatus;\r
1956\r
1957 *NeedRetry = FALSE;\r
1958 *NumberOfSenseKeys = 0;\r
1959 SenseDataLength = 0;\r
1960 Status = ScsiRead16Command (\r
1961 ScsiDiskDevice->ScsiIo,\r
1962 Timeout,\r
1963 NULL,\r
1964 &SenseDataLength,\r
1965 &HostAdapterStatus,\r
1966 &TargetStatus,\r
1967 DataBuffer,\r
1968 DataLength,\r
1969 StartLba,\r
1970 SectorSize\r
1971 );\r
1972 return Status;\r
1973}\r
1974\r
1975\r
1976/**\r
1977 Submit Write(16) Command.\r
1978\r
1979 @param ScsiDiskDevice The pointer of ScsiDiskDevice\r
1980 @param NeedRetry The pointer of flag indicates if needs retry if error happens\r
1981 @param SenseDataArray NOT used yet in this function\r
1982 @param NumberOfSenseKeys The number of sense key\r
1983 @param Timeout The time to complete the command\r
1984 @param DataBuffer The buffer to fill with the read out data\r
1985 @param DataLength The length of buffer\r
1986 @param StartLba The start logic block address\r
1987 @param SectorSize The size of sector\r
1988\r
1989 @return EFI_STATUS is returned by calling ScsiWrite10Command().\r
1990\r
1991**/\r
1992EFI_STATUS\r
1993ScsiDiskWrite16 (\r
1994 IN SCSI_DISK_DEV *ScsiDiskDevice,\r
1995 OUT BOOLEAN *NeedRetry,\r
1996 OUT EFI_SCSI_SENSE_DATA **SenseDataArray, OPTIONAL\r
1997 OUT UINTN *NumberOfSenseKeys,\r
1998 IN UINT64 Timeout,\r
1999 IN UINT8 *DataBuffer,\r
2000 IN OUT UINT32 *DataLength,\r
2001 IN UINT64 StartLba,\r
2002 IN UINT32 SectorSize\r
2003 )\r
2004{\r
2005 EFI_STATUS Status;\r
2006 UINT8 SenseDataLength;\r
2007 UINT8 HostAdapterStatus;\r
2008 UINT8 TargetStatus;\r
2009\r
2010 *NeedRetry = FALSE;\r
2011 *NumberOfSenseKeys = 0;\r
2012 SenseDataLength = 0;\r
2013 Status = ScsiWrite16Command (\r
2014 ScsiDiskDevice->ScsiIo,\r
2015 Timeout,\r
2016 NULL,\r
2017 &SenseDataLength,\r
2018 &HostAdapterStatus,\r
2019 &TargetStatus,\r
2020 DataBuffer,\r
2021 DataLength,\r
2022 StartLba,\r
2023 SectorSize\r
2024 );\r
2025 return Status;\r
2026}\r
2027\r
2028\r
9beb888e 2029/**\r
2030 Check sense key to find if media presents.\r
2031\r
2032 @param SenseData The pointer of EFI_SCSI_SENSE_DATA\r
2033 @param SenseCounts The number of sense key\r
2034\r
2035 @retval TRUE NOT any media\r
2036 @retval FALSE Media presents\r
2037**/\r
6ad55b15 2038BOOLEAN\r
2039ScsiDiskIsNoMedia (\r
2040 IN EFI_SCSI_SENSE_DATA *SenseData,\r
2041 IN UINTN SenseCounts\r
2042 )\r
6ad55b15 2043{\r
2044 EFI_SCSI_SENSE_DATA *SensePtr;\r
2045 UINTN Index;\r
2046 BOOLEAN IsNoMedia;\r
2047\r
2048 IsNoMedia = FALSE;\r
2049 SensePtr = SenseData;\r
2050\r
2051 for (Index = 0; Index < SenseCounts; Index++) {\r
6ad55b15 2052 //\r
2053 // Sense Key is EFI_SCSI_SK_NOT_READY (0x2),\r
2054 // Additional Sense Code is ASC_NO_MEDIA (0x3A)\r
2055 //\r
2056 if ((SensePtr->Sense_Key == EFI_SCSI_SK_NOT_READY) &&\r
2057 (SensePtr->Addnl_Sense_Code == EFI_SCSI_ASC_NO_MEDIA)) {\r
2058 IsNoMedia = TRUE;\r
2059 }\r
6ad55b15 2060 SensePtr++;\r
2061 }\r
2062\r
2063 return IsNoMedia;\r
2064}\r
2065\r
9beb888e 2066\r
2067/**\r
2068 Parse sense key.\r
2069\r
2070 @param SenseData The pointer of EFI_SCSI_SENSE_DATA\r
2071 @param SenseCounts The number of sense key\r
2072\r
2073 @retval TRUE Error\r
2074 @retval FALSE NOT error\r
2075\r
2076**/\r
6ad55b15 2077BOOLEAN\r
2078ScsiDiskIsMediaError (\r
2079 IN EFI_SCSI_SENSE_DATA *SenseData,\r
2080 IN UINTN SenseCounts\r
2081 )\r
6ad55b15 2082{\r
2083 EFI_SCSI_SENSE_DATA *SensePtr;\r
2084 UINTN Index;\r
2085 BOOLEAN IsError;\r
2086\r
2087 IsError = FALSE;\r
2088 SensePtr = SenseData;\r
2089\r
2090 for (Index = 0; Index < SenseCounts; Index++) {\r
2091\r
2092 switch (SensePtr->Sense_Key) {\r
2093\r
2094 case EFI_SCSI_SK_MEDIUM_ERROR:\r
2095 //\r
2096 // Sense Key is EFI_SCSI_SK_MEDIUM_ERROR (0x3)\r
2097 //\r
2098 switch (SensePtr->Addnl_Sense_Code) {\r
2099\r
2100 //\r
2101 // fall through\r
2102 //\r
2103 case EFI_SCSI_ASC_MEDIA_ERR1:\r
2104\r
2105 //\r
2106 // fall through\r
2107 //\r
2108 case EFI_SCSI_ASC_MEDIA_ERR2:\r
2109\r
2110 //\r
2111 // fall through\r
2112 //\r
2113 case EFI_SCSI_ASC_MEDIA_ERR3:\r
2114 case EFI_SCSI_ASC_MEDIA_ERR4:\r
2115 IsError = TRUE;\r
2116 break;\r
2117\r
2118 default:\r
2119 break;\r
2120 }\r
2121\r
2122 break;\r
2123\r
2124 case EFI_SCSI_SK_NOT_READY:\r
2125 //\r
2126 // Sense Key is EFI_SCSI_SK_NOT_READY (0x2)\r
2127 //\r
2128 switch (SensePtr->Addnl_Sense_Code) {\r
2129 //\r
2130 // Additional Sense Code is ASC_MEDIA_UPSIDE_DOWN (0x6)\r
2131 //\r
2132 case EFI_SCSI_ASC_MEDIA_UPSIDE_DOWN:\r
2133 IsError = TRUE;\r
2134 break;\r
2135\r
2136 default:\r
2137 break;\r
2138 }\r
2139 break;\r
2140\r
2141 default:\r
2142 break;\r
2143 }\r
2144\r
2145 SensePtr++;\r
2146 }\r
2147\r
2148 return IsError;\r
2149}\r
2150\r
9beb888e 2151\r
2152/**\r
2153 Check sense key to find if hardware error happens.\r
2154\r
2155 @param SenseData The pointer of EFI_SCSI_SENSE_DATA\r
2156 @param SenseCounts The number of sense key\r
2157\r
2158 @retval TRUE Hardware error exits.\r
2159 @retval FALSE NO error.\r
2160\r
2161**/\r
6ad55b15 2162BOOLEAN\r
2163ScsiDiskIsHardwareError (\r
2164 IN EFI_SCSI_SENSE_DATA *SenseData,\r
2165 IN UINTN SenseCounts\r
2166 )\r
6ad55b15 2167{\r
2168 EFI_SCSI_SENSE_DATA *SensePtr;\r
2169 UINTN Index;\r
2170 BOOLEAN IsError;\r
2171\r
2172 IsError = FALSE;\r
2173 SensePtr = SenseData;\r
2174\r
2175 for (Index = 0; Index < SenseCounts; Index++) {\r
2176 \r
2177 //\r
2178 // Sense Key is EFI_SCSI_SK_HARDWARE_ERROR (0x4)\r
2179 //\r
2180 if (SensePtr->Sense_Key == EFI_SCSI_SK_HARDWARE_ERROR) {\r
2181 IsError = TRUE;\r
2182 }\r
2183\r
2184 SensePtr++;\r
2185 }\r
2186\r
2187 return IsError;\r
2188}\r
2189\r
9beb888e 2190\r
2191/**\r
2192 Check sense key to find if media has changed.\r
2193\r
2194 @param SenseData The pointer of EFI_SCSI_SENSE_DATA\r
2195 @param SenseCounts The number of sense key\r
2196\r
2197 @retval TRUE Media is changed.\r
d716651f 2198 @retval FALSE Media is NOT changed.\r
9beb888e 2199**/\r
6ad55b15 2200BOOLEAN\r
2201ScsiDiskIsMediaChange (\r
2202 IN EFI_SCSI_SENSE_DATA *SenseData,\r
2203 IN UINTN SenseCounts\r
2204 )\r
6ad55b15 2205{\r
2206 EFI_SCSI_SENSE_DATA *SensePtr;\r
2207 UINTN Index;\r
2208 BOOLEAN IsMediaChanged;\r
2209\r
2210 IsMediaChanged = FALSE;\r
2211 SensePtr = SenseData;\r
2212\r
2213 for (Index = 0; Index < SenseCounts; Index++) {\r
2214 //\r
2215 // Sense Key is EFI_SCSI_SK_UNIT_ATTENTION (0x6),\r
2216 // Additional sense code is EFI_SCSI_ASC_MEDIA_CHANGE (0x28)\r
2217 //\r
2218 if ((SensePtr->Sense_Key == EFI_SCSI_SK_UNIT_ATTENTION) &&\r
2219 (SensePtr->Addnl_Sense_Code == EFI_SCSI_ASC_MEDIA_CHANGE)) {\r
2220 IsMediaChanged = TRUE;\r
2221 }\r
2222\r
2223 SensePtr++;\r
2224 }\r
2225\r
2226 return IsMediaChanged;\r
2227}\r
2228\r
9beb888e 2229/**\r
2230 Check sense key to find if reset happens.\r
2231\r
2232 @param SenseData The pointer of EFI_SCSI_SENSE_DATA\r
2233 @param SenseCounts The number of sense key\r
2234\r
2235 @retval TRUE It is reset before.\r
2236 @retval FALSE It is NOT reset before.\r
2237\r
2238**/\r
6ad55b15 2239BOOLEAN\r
2240ScsiDiskIsResetBefore (\r
2241 IN EFI_SCSI_SENSE_DATA *SenseData,\r
2242 IN UINTN SenseCounts\r
2243 )\r
6ad55b15 2244{\r
2245 EFI_SCSI_SENSE_DATA *SensePtr;\r
2246 UINTN Index;\r
2247 BOOLEAN IsResetBefore;\r
2248\r
2249 IsResetBefore = FALSE;\r
2250 SensePtr = SenseData;\r
2251\r
2252 for (Index = 0; Index < SenseCounts; Index++) {\r
2253 \r
2254 //\r
2255 // Sense Key is EFI_SCSI_SK_UNIT_ATTENTION (0x6)\r
2256 // Additional Sense Code is EFI_SCSI_ASC_RESET (0x29)\r
2257 //\r
2258 if ((SensePtr->Sense_Key == EFI_SCSI_SK_UNIT_ATTENTION) &&\r
2259 (SensePtr->Addnl_Sense_Code == EFI_SCSI_ASC_RESET)) {\r
2260 IsResetBefore = TRUE;\r
2261 }\r
2262\r
2263 SensePtr++;\r
2264 }\r
2265\r
2266 return IsResetBefore;\r
2267}\r
2268\r
9beb888e 2269/**\r
2270 Check sense key to find if the drive is ready.\r
2271\r
2272 @param SenseData The pointer of EFI_SCSI_SENSE_DATA\r
2273 @param SenseCounts The number of sense key\r
2274 @param RetryLater The flag means if need a retry \r
2275\r
2276 @retval TRUE Drive is ready.\r
2277 @retval FALSE Drive is NOT ready.\r
2278\r
2279**/\r
6ad55b15 2280BOOLEAN\r
2281ScsiDiskIsDriveReady (\r
2282 IN EFI_SCSI_SENSE_DATA *SenseData,\r
2283 IN UINTN SenseCounts,\r
2284 OUT BOOLEAN *RetryLater\r
2285 )\r
6ad55b15 2286{\r
2287 EFI_SCSI_SENSE_DATA *SensePtr;\r
2288 UINTN Index;\r
2289 BOOLEAN IsReady;\r
2290\r
2291 IsReady = TRUE;\r
2292 *RetryLater = FALSE;\r
2293 SensePtr = SenseData;\r
2294\r
2295 for (Index = 0; Index < SenseCounts; Index++) {\r
2296\r
2297 switch (SensePtr->Sense_Key) {\r
2298\r
2299 case EFI_SCSI_SK_NOT_READY:\r
2300 //\r
2301 // Sense Key is EFI_SCSI_SK_NOT_READY (0x2)\r
2302 //\r
2303 switch (SensePtr->Addnl_Sense_Code) {\r
2304 case EFI_SCSI_ASC_NOT_READY:\r
2305 //\r
2306 // Additional Sense Code is EFI_SCSI_ASC_NOT_READY (0x4)\r
2307 //\r
2308 switch (SensePtr->Addnl_Sense_Code_Qualifier) {\r
2309 case EFI_SCSI_ASCQ_IN_PROGRESS:\r
2310 //\r
2311 // Additional Sense Code Qualifier is\r
2312 // EFI_SCSI_ASCQ_IN_PROGRESS (0x1)\r
2313 //\r
2314 IsReady = FALSE;\r
2315 *RetryLater = TRUE;\r
2316 break;\r
2317\r
2318 default:\r
2319 IsReady = FALSE;\r
2320 *RetryLater = FALSE;\r
2321 break;\r
2322 }\r
2323 break;\r
2324\r
2325 default:\r
2326 break;\r
2327 }\r
2328 break;\r
2329\r
2330 default:\r
2331 break;\r
2332 }\r
2333\r
2334 SensePtr++;\r
2335 }\r
2336\r
2337 return IsReady;\r
2338}\r
2339\r
9beb888e 2340/**\r
2341 Check sense key to find if it has sense key.\r
2342\r
2343 @param SenseData - The pointer of EFI_SCSI_SENSE_DATA\r
2344 @param SenseCounts - The number of sense key\r
2345\r
2346 @retval TRUE It has sense key.\r
2347 @retval FALSE It has NOT any sense key.\r
2348\r
2349**/\r
6ad55b15 2350BOOLEAN\r
2351ScsiDiskHaveSenseKey (\r
2352 IN EFI_SCSI_SENSE_DATA *SenseData,\r
2353 IN UINTN SenseCounts\r
2354 )\r
6ad55b15 2355{\r
2356 EFI_SCSI_SENSE_DATA *SensePtr;\r
2357 UINTN Index;\r
2358 BOOLEAN HaveSenseKey;\r
2359\r
2360 if (SenseCounts == 0) {\r
2361 HaveSenseKey = FALSE;\r
2362 } else {\r
2363 HaveSenseKey = TRUE;\r
2364 }\r
2365\r
2366 SensePtr = SenseData;\r
2367\r
2368 for (Index = 0; Index < SenseCounts; Index++) {\r
2369 \r
2370 //\r
2371 // Sense Key is SK_NO_SENSE (0x0)\r
2372 //\r
2373 if ((SensePtr->Sense_Key == EFI_SCSI_SK_NO_SENSE) &&\r
2374 (Index == 0)) {\r
2375 HaveSenseKey = FALSE;\r
2376 }\r
2377\r
2378 SensePtr++;\r
2379 }\r
2380\r
2381 return HaveSenseKey;\r
2382}\r
2383\r
9beb888e 2384/**\r
2385 Release resource about disk device.\r
2386\r
2387 @param ScsiDiskDevice The pointer of SCSI_DISK_DEV\r
2388\r
2389**/\r
6ad55b15 2390VOID\r
2391ReleaseScsiDiskDeviceResources (\r
2392 IN SCSI_DISK_DEV *ScsiDiskDevice\r
2393 )\r
6ad55b15 2394{\r
2395 if (ScsiDiskDevice == NULL) {\r
2396 return ;\r
2397 }\r
2398\r
2399 if (ScsiDiskDevice->SenseData != NULL) {\r
9b38ff34 2400 FreePool (ScsiDiskDevice->SenseData);\r
6ad55b15 2401 ScsiDiskDevice->SenseData = NULL;\r
2402 }\r
2403\r
2404 if (ScsiDiskDevice->ControllerNameTable != NULL) {\r
2405 FreeUnicodeStringTable (ScsiDiskDevice->ControllerNameTable);\r
2406 ScsiDiskDevice->ControllerNameTable = NULL;\r
2407 }\r
2408\r
9b38ff34 2409 FreePool (ScsiDiskDevice);\r
6ad55b15 2410\r
2411 ScsiDiskDevice = NULL;\r
2412}\r
d14faa52 2413\r
2414/**\r
2415 Determine if Block Io should be produced.\r
2416 \r
2417\r
d716651f 2418 @param ChildHandle Child Handle to retrieve Parent information.\r
d14faa52 2419 \r
2420 @retval TRUE Should produce Block Io.\r
2421 @retval FALSE Should not produce Block Io.\r
2422\r
2423**/ \r
2424BOOLEAN\r
2425DetermineInstallBlockIo (\r
2426 IN EFI_HANDLE ChildHandle\r
2427 ) \r
2428{\r
2429 EFI_SCSI_PASS_THRU_PROTOCOL *ScsiPassThru;\r
2430 EFI_EXT_SCSI_PASS_THRU_PROTOCOL *ExtScsiPassThru;\r
2431\r
2432 //\r
2433 // Firstly, check if ExtScsiPassThru Protocol parent handle exists. If existence,\r
2434 // check its attribute, logic or physical.\r
2435 //\r
2436 ExtScsiPassThru = (EFI_EXT_SCSI_PASS_THRU_PROTOCOL *)GetParentProtocol (&gEfiExtScsiPassThruProtocolGuid, ChildHandle);\r
2437 if (ExtScsiPassThru != NULL) {\r
2438 if ((ExtScsiPassThru->Mode->Attributes & EFI_SCSI_PASS_THRU_ATTRIBUTES_LOGICAL) != 0) {\r
2439 return TRUE;\r
2440 }\r
2441 }\r
2442\r
2443 //\r
2444 // Secondly, check if ScsiPassThru Protocol parent handle exists. If existence,\r
2445 // check its attribute, logic or physical.\r
2446 //\r
2447 ScsiPassThru = (EFI_SCSI_PASS_THRU_PROTOCOL *)GetParentProtocol (&gEfiScsiPassThruProtocolGuid, ChildHandle);\r
2448 if (ScsiPassThru != NULL) {\r
2449 if ((ScsiPassThru->Mode->Attributes & EFI_SCSI_PASS_THRU_ATTRIBUTES_LOGICAL) != 0) {\r
2450 return TRUE;\r
2451 }\r
2452 }\r
2453 \r
2454 return FALSE;\r
2455}\r
2456\r
2457/**\r
2458 Search protocol database and check to see if the protocol\r
2459 specified by ProtocolGuid is present on a ControllerHandle and opened by\r
2460 ChildHandle with an attribute of EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER.\r
2461 If the ControllerHandle is found, then the protocol specified by ProtocolGuid\r
2462 will be opened on it. \r
2463 \r
2464\r
2465 @param ProtocolGuid ProtocolGuid pointer.\r
2466 @param ChildHandle Child Handle to retrieve Parent information.\r
2467 \r
2468**/ \r
2469VOID *\r
2470EFIAPI\r
2471GetParentProtocol (\r
2472 IN EFI_GUID *ProtocolGuid,\r
2473 IN EFI_HANDLE ChildHandle\r
2474 ) \r
2475{\r
2476 UINTN Index;\r
2477 UINTN HandleCount;\r
2478 VOID *Interface; \r
2479 EFI_STATUS Status;\r
2480 EFI_HANDLE *HandleBuffer;\r
2481\r
2482 //\r
2483 // Retrieve the list of all handles from the handle database\r
2484 //\r
2485 Status = gBS->LocateHandleBuffer (\r
2486 ByProtocol,\r
2487 ProtocolGuid,\r
2488 NULL,\r
2489 &HandleCount,\r
2490 &HandleBuffer\r
2491 );\r
2492\r
2493 if (EFI_ERROR (Status)) {\r
2494 return NULL;\r
2495 }\r
2496\r
2497 //\r
2498 // Iterate to find who is parent handle that is opened with ProtocolGuid by ChildHandle \r
2499 //\r
2500 for (Index = 0; Index < HandleCount; Index++) {\r
2501 Status = EfiTestChildHandle (HandleBuffer[Index], ChildHandle, ProtocolGuid);\r
2502 if (!EFI_ERROR (Status)) {\r
2503 Status = gBS->HandleProtocol (HandleBuffer[Index], ProtocolGuid, (VOID **)&Interface);\r
2504 if (!EFI_ERROR (Status)) {\r
2505 gBS->FreePool (HandleBuffer);\r
2506 return Interface;\r
2507 }\r
2508 }\r
2509 }\r
2510\r
2511 gBS->FreePool (HandleBuffer);\r
2512 return NULL;\r
2513} \r
2514\r
d716651f 2515/**\r
2516 Provides inquiry information for the controller type.\r
2517 \r
2518 This function is used by the IDE bus driver to get inquiry data. Data format\r
2519 of Identify data is defined by the Interface GUID.\r
2520\r
4140a663 2521 @param[in] This Pointer to the EFI_DISK_INFO_PROTOCOL instance.\r
2522 @param[in, out] InquiryData Pointer to a buffer for the inquiry data.\r
2523 @param[in, out] InquiryDataSize Pointer to the value for the inquiry data size.\r
d716651f 2524\r
2525 @retval EFI_SUCCESS The command was accepted without any errors.\r
2526 @retval EFI_NOT_FOUND Device does not support this data class \r
2527 @retval EFI_DEVICE_ERROR Error reading InquiryData from device \r
2528 @retval EFI_BUFFER_TOO_SMALL InquiryDataSize not big enough \r
2529\r
2530**/\r
2531EFI_STATUS\r
2532EFIAPI\r
2533ScsiDiskInfoInquiry (\r
2534 IN EFI_DISK_INFO_PROTOCOL *This,\r
2535 IN OUT VOID *InquiryData,\r
2536 IN OUT UINT32 *InquiryDataSize\r
2537 )\r
2538{\r
2539 EFI_STATUS Status;\r
2540 SCSI_DISK_DEV *ScsiDiskDevice;\r
2541\r
2542 ScsiDiskDevice = SCSI_DISK_DEV_FROM_DISKINFO (This);\r
2543\r
2544 Status = EFI_BUFFER_TOO_SMALL;\r
2545 if (*InquiryDataSize >= sizeof (ScsiDiskDevice->InquiryData)) {\r
2546 Status = EFI_SUCCESS;\r
2547 CopyMem (InquiryData, &ScsiDiskDevice->InquiryData, sizeof (ScsiDiskDevice->InquiryData));\r
2548 }\r
2549 *InquiryDataSize = sizeof (ScsiDiskDevice->InquiryData);\r
2550 return Status;\r
2551}\r
2552\r
2553\r
2554/**\r
2555 Provides identify information for the controller type.\r
2556\r
2557 This function is used by the IDE bus driver to get identify data. Data format\r
2558 of Identify data is defined by the Interface GUID.\r
2559\r
4140a663 2560 @param[in] This Pointer to the EFI_DISK_INFO_PROTOCOL \r
d716651f 2561 instance.\r
4140a663 2562 @param[in, out] IdentifyData Pointer to a buffer for the identify data.\r
2563 @param[in, out] IdentifyDataSize Pointer to the value for the identify data\r
d716651f 2564 size.\r
2565\r
2566 @retval EFI_SUCCESS The command was accepted without any errors.\r
2567 @retval EFI_NOT_FOUND Device does not support this data class \r
2568 @retval EFI_DEVICE_ERROR Error reading IdentifyData from device \r
2569 @retval EFI_BUFFER_TOO_SMALL IdentifyDataSize not big enough \r
2570\r
2571**/\r
2572EFI_STATUS\r
2573EFIAPI\r
2574ScsiDiskInfoIdentify (\r
2575 IN EFI_DISK_INFO_PROTOCOL *This,\r
2576 IN OUT VOID *IdentifyData,\r
2577 IN OUT UINT32 *IdentifyDataSize\r
2578 )\r
2579{\r
2580 EFI_STATUS Status;\r
2581 SCSI_DISK_DEV *ScsiDiskDevice;\r
2582\r
2583 if (CompareGuid (&This->Interface, &gEfiDiskInfoScsiInterfaceGuid)) {\r
2584 //\r
2585 // Physical SCSI bus does not support this data class. \r
2586 //\r
2587 return EFI_NOT_FOUND;\r
2588 }\r
2589\r
2590 ScsiDiskDevice = SCSI_DISK_DEV_FROM_DISKINFO (This);\r
2591\r
2592 Status = EFI_BUFFER_TOO_SMALL;\r
2593 if (*IdentifyDataSize >= sizeof (ScsiDiskDevice->IdentifyData)) {\r
2594 Status = EFI_SUCCESS;\r
2595 CopyMem (IdentifyData, &ScsiDiskDevice->IdentifyData, sizeof (ScsiDiskDevice->IdentifyData));\r
2596 }\r
2597 *IdentifyDataSize = sizeof (ScsiDiskDevice->IdentifyData);\r
2598 return Status;\r
2599}\r
2600\r
2601/**\r
2602 Provides sense data information for the controller type.\r
2603 \r
2604 This function is used by the IDE bus driver to get sense data. \r
2605 Data format of Sense data is defined by the Interface GUID.\r
2606\r
4140a663 2607 @param[in] This Pointer to the EFI_DISK_INFO_PROTOCOL instance.\r
2608 @param[in, out] SenseData Pointer to the SenseData.\r
2609 @param[in, out] SenseDataSize Size of SenseData in bytes.\r
2610 @param[out] SenseDataNumber Pointer to the value for the sense data size.\r
d716651f 2611\r
2612 @retval EFI_SUCCESS The command was accepted without any errors.\r
2613 @retval EFI_NOT_FOUND Device does not support this data class.\r
2614 @retval EFI_DEVICE_ERROR Error reading SenseData from device.\r
2615 @retval EFI_BUFFER_TOO_SMALL SenseDataSize not big enough.\r
2616\r
2617**/\r
2618EFI_STATUS\r
2619EFIAPI\r
2620ScsiDiskInfoSenseData (\r
2621 IN EFI_DISK_INFO_PROTOCOL *This,\r
2622 IN OUT VOID *SenseData,\r
2623 IN OUT UINT32 *SenseDataSize,\r
2624 OUT UINT8 *SenseDataNumber\r
2625 )\r
2626{\r
2627 return EFI_NOT_FOUND;\r
2628}\r
2629\r
2630\r
2631/**\r
2632 This function is used by the IDE bus driver to get controller information.\r
2633\r
2634 @param[in] This Pointer to the EFI_DISK_INFO_PROTOCOL instance. \r
2635 @param[out] IdeChannel Pointer to the Ide Channel number. Primary or secondary.\r
2636 @param[out] IdeDevice Pointer to the Ide Device number. Master or slave.\r
2637\r
2638 @retval EFI_SUCCESS IdeChannel and IdeDevice are valid.\r
2639 @retval EFI_UNSUPPORTED This is not an IDE device.\r
2640\r
2641**/\r
2642EFI_STATUS\r
2643EFIAPI\r
2644ScsiDiskInfoWhichIde (\r
2645 IN EFI_DISK_INFO_PROTOCOL *This,\r
2646 OUT UINT32 *IdeChannel,\r
2647 OUT UINT32 *IdeDevice\r
2648 )\r
2649{\r
2650 SCSI_DISK_DEV *ScsiDiskDevice;\r
2651\r
2652 if (CompareGuid (&This->Interface, &gEfiDiskInfoScsiInterfaceGuid)) {\r
2653 //\r
2654 // This is not an IDE physical device.\r
2655 //\r
2656 return EFI_UNSUPPORTED;\r
2657 }\r
2658\r
2659 ScsiDiskDevice = SCSI_DISK_DEV_FROM_DISKINFO (This);\r
2660 *IdeChannel = ScsiDiskDevice->Channel;\r
2661 *IdeDevice = ScsiDiskDevice->Device;\r
2662\r
2663 return EFI_SUCCESS;\r
2664}\r
2665\r
2666\r
2667/**\r
2668 Issues ATA IDENTIFY DEVICE command to identify ATAPI device.\r
2669\r
2670 This function tries to fill 512-byte ATAPI_IDENTIFY_DATA for ATAPI device to\r
2671 implement Identify() interface for DiskInfo protocol. The ATA command is sent\r
2672 via SCSI Request Packet.\r
2673\r
2674 @param ScsiDiskDevice The pointer of SCSI_DISK_DEV\r
2675 \r
2676 @retval EFI_SUCCESS The ATAPI device identify data were retrieved successfully.\r
2677 @retval others Some error occurred during the identification that ATAPI device.\r
2678\r
2679**/ \r
2680EFI_STATUS\r
2681AtapiIdentifyDevice (\r
2682 IN OUT SCSI_DISK_DEV *ScsiDiskDevice\r
2683 )\r
2684{\r
2685 EFI_SCSI_IO_SCSI_REQUEST_PACKET CommandPacket;\r
2686 UINT8 Cdb[6];\r
2687\r
2688 //\r
2689 // Initialize SCSI REQUEST_PACKET and 6-byte Cdb\r
2690 //\r
2691 ZeroMem (&CommandPacket, sizeof (CommandPacket));\r
2692 ZeroMem (Cdb, sizeof (Cdb));\r
2693\r
2694 Cdb[0] = ATA_CMD_IDENTIFY_DEVICE;\r
2695 CommandPacket.Timeout = EFI_TIMER_PERIOD_SECONDS (1);\r
2696 CommandPacket.Cdb = Cdb;\r
c9325700 2697 CommandPacket.CdbLength = (UINT8) sizeof (Cdb);\r
d716651f 2698 CommandPacket.InDataBuffer = &ScsiDiskDevice->IdentifyData;\r
2699 CommandPacket.InTransferLength = sizeof (ScsiDiskDevice->IdentifyData);\r
2700\r
2701 return ScsiDiskDevice->ScsiIo->ExecuteScsiCommand (ScsiDiskDevice->ScsiIo, &CommandPacket, NULL);\r
2702}\r
2703\r
2704\r
2705/**\r
2706 Initialize the installation of DiskInfo protocol.\r
2707\r
2708 This function prepares for the installation of DiskInfo protocol on the child handle.\r
2709 By default, it installs DiskInfo protocol with SCSI interface GUID. If it further\r
2710 detects that the physical device is an ATAPI/AHCI device, it then updates interface GUID\r
2711 to be IDE/AHCI interface GUID.\r
2712\r
2713 @param ScsiDiskDevice The pointer of SCSI_DISK_DEV.\r
2714 @param ChildHandle Child handle to install DiskInfo protocol.\r
2715 \r
2716**/ \r
2717VOID\r
2718InitializeInstallDiskInfo (\r
2719 IN SCSI_DISK_DEV *ScsiDiskDevice,\r
2720 IN EFI_HANDLE ChildHandle\r
2721 )\r
2722{\r
2723 EFI_STATUS Status;\r
2724 EFI_DEVICE_PATH_PROTOCOL *DevicePathNode;\r
2725 EFI_DEVICE_PATH_PROTOCOL *ChildDevicePathNode;\r
2726 ATAPI_DEVICE_PATH *AtapiDevicePath;\r
2727 SATA_DEVICE_PATH *SataDevicePath;\r
2728 UINTN IdentifyRetry;\r
2729\r
2730 Status = gBS->HandleProtocol (ChildHandle, &gEfiDevicePathProtocolGuid, (VOID **) &DevicePathNode);\r
2731 //\r
2732 // Device Path protocol must be installed on the device handle. \r
2733 //\r
2734 ASSERT_EFI_ERROR (Status);\r
2735 //\r
2736 // Copy the DiskInfo protocol template.\r
2737 //\r
2738 CopyMem (&ScsiDiskDevice->DiskInfo, &gScsiDiskInfoProtocolTemplate, sizeof (gScsiDiskInfoProtocolTemplate));\r
2739\r
2740 while (!IsDevicePathEnd (DevicePathNode)) {\r
2741 ChildDevicePathNode = NextDevicePathNode (DevicePathNode);\r
2742 if ((DevicePathType (DevicePathNode) == HARDWARE_DEVICE_PATH) &&\r
2743 (DevicePathSubType (DevicePathNode) == HW_PCI_DP) &&\r
2744 (DevicePathType (ChildDevicePathNode) == MESSAGING_DEVICE_PATH) &&\r
2745 ((DevicePathSubType (ChildDevicePathNode) == MSG_ATAPI_DP) ||\r
2746 (DevicePathSubType (ChildDevicePathNode) == MSG_SATA_DP))) {\r
2747\r
2748 IdentifyRetry = 3;\r
2749 do {\r
2750 //\r
2751 // Issue ATA Identify Device Command via SCSI command, which is required to publish DiskInfo protocol\r
2752 // with IDE/AHCI interface GUID.\r
2753 //\r
2754 Status = AtapiIdentifyDevice (ScsiDiskDevice);\r
2755 if (!EFI_ERROR (Status)) {\r
2756 if (DevicePathSubType(ChildDevicePathNode) == MSG_ATAPI_DP) {\r
2757 //\r
2758 // We find the valid ATAPI device path\r
2759 //\r
2760 AtapiDevicePath = (ATAPI_DEVICE_PATH *) ChildDevicePathNode;\r
2761 ScsiDiskDevice->Channel = AtapiDevicePath->PrimarySecondary;\r
2762 ScsiDiskDevice->Device = AtapiDevicePath->SlaveMaster;\r
2763 //\r
2764 // Update the DiskInfo.Interface to IDE interface GUID for the physical ATAPI device. \r
2765 //\r
2766 CopyGuid (&ScsiDiskDevice->DiskInfo.Interface, &gEfiDiskInfoIdeInterfaceGuid);\r
2767 } else {\r
2768 //\r
2769 // We find the valid SATA device path\r
2770 //\r
2771 SataDevicePath = (SATA_DEVICE_PATH *) ChildDevicePathNode;\r
2772 ScsiDiskDevice->Channel = SataDevicePath->HBAPortNumber;\r
2773 ScsiDiskDevice->Device = SataDevicePath->PortMultiplierPortNumber;\r
2774 //\r
2775 // Update the DiskInfo.Interface to AHCI interface GUID for the physical AHCI device. \r
2776 //\r
2777 CopyGuid (&ScsiDiskDevice->DiskInfo.Interface, &gEfiDiskInfoAhciInterfaceGuid);\r
2778 }\r
2779 return;\r
2780 }\r
2781 } while (--IdentifyRetry > 0);\r
2782 }\r
2783 DevicePathNode = ChildDevicePathNode;\r
2784 }\r
2785\r
2786 return;\r
2787}\r