3 Copyright (c) 2006, Intel Corporation
4 All rights reserved. This program and the accompanying materials
5 are licensed and made available under the terms and conditions of the BSD License
6 which accompanies this distribution. The full text of the license may be found at
7 http://opensource.org/licenses/bsd-license.php
9 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
10 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
23 // The package level header files this module uses
28 // The protocols, PPI and GUID defintions for this module
30 #include <Protocol/ScsiPassThru.h>
31 #include <Protocol/ScsiIo.h>
32 #include <Protocol/ComponentName.h>
33 #include <Protocol/DriverBinding.h>
34 #include <Protocol/DevicePath.h>
36 // The Library classes this module consumes
38 #include <Library/DebugLib.h>
39 #include <Library/UefiDriverEntryPoint.h>
40 #include <Library/UefiLib.h>
41 #include <Library/BaseMemoryLib.h>
42 #include <Library/ScsiLib.h>
43 #include <Library/UefiBootServicesTableLib.h>
44 #include <Library/DevicePathLib.h>
48 EFI_DRIVER_BINDING_PROTOCOL gSCSIBusDriverBinding
= {
49 SCSIBusDriverBindingSupported
,
50 SCSIBusDriverBindingStart
,
51 SCSIBusDriverBindingStop
,
58 The user Entry Point for module ScsiBus. The user code starts with this function.
60 @param[in] ImageHandle The firmware allocated handle for the EFI image.
61 @param[in] SystemTable A pointer to the EFI System Table.
63 @retval EFI_SUCCESS The entry point is executed successfully.
64 @retval other Some error occurs when executing this entry point.
70 IN EFI_HANDLE ImageHandle
,
71 IN EFI_SYSTEM_TABLE
*SystemTable
77 // Install driver model protocol(s).
79 Status
= EfiLibInstallAllDriverProtocols (
82 &gSCSIBusDriverBinding
,
84 &gScsiBusComponentName
,
88 ASSERT_EFI_ERROR (Status
);
96 SCSIBusDriverBindingSupported (
97 IN EFI_DRIVER_BINDING_PROTOCOL
*This
,
98 IN EFI_HANDLE Controller
,
99 IN EFI_DEVICE_PATH_PROTOCOL
*RemainingDevicePath
110 // TODO: This - add argument and description to function comment
111 // TODO: Controller - add argument and description to function comment
112 // TODO: RemainingDevicePath - add argument and description to function comment
113 // TODO: EFI_UNSUPPORTED - add return value to function comment
114 // TODO: EFI_UNSUPPORTED - add return value to function comment
115 // TODO: EFI_SUCCESS - add return value to function comment
120 // If RemainingDevicePath is not NULL, it should verify that the first device
121 // path node in RemainingDevicePath is an ATAPI Device path node.
123 if (RemainingDevicePath
!= NULL
) {
124 if ((RemainingDevicePath
->Type
!= MESSAGING_DEVICE_PATH
) ||
125 (RemainingDevicePath
->SubType
!= MSG_ATAPI_DP
) ||
126 (DevicePathNodeLength (RemainingDevicePath
) != sizeof(ATAPI_DEVICE_PATH
))) {
127 return EFI_UNSUPPORTED
;
131 // check for the existence of SCSI Pass Thru Protocol
133 Status
= gBS
->OpenProtocol (
135 &gEfiScsiPassThruProtocolGuid
,
137 This
->DriverBindingHandle
,
139 EFI_OPEN_PROTOCOL_TEST_PROTOCOL
141 if (EFI_ERROR (Status
) && (Status
!= EFI_ALREADY_STARTED
)) {
142 return EFI_UNSUPPORTED
;
150 SCSIBusDriverBindingStart (
151 IN EFI_DRIVER_BINDING_PROTOCOL
*This
,
152 IN EFI_HANDLE Controller
,
153 IN EFI_DEVICE_PATH_PROTOCOL
*RemainingDevicePath
164 // TODO: This - add argument and description to function comment
165 // TODO: Controller - add argument and description to function comment
166 // TODO: RemainingDevicePath - add argument and description to function comment
169 EFI_DEVICE_PATH_PROTOCOL
*ParentDevicePath
;
170 EFI_SCSI_PASS_THRU_PROTOCOL
*ScsiPassThru
;
175 BOOLEAN ScanOtherPuns
;
179 Status
= gBS
->OpenProtocol (
181 &gEfiDevicePathProtocolGuid
,
182 (VOID
**) &ParentDevicePath
,
183 This
->DriverBindingHandle
,
185 EFI_OPEN_PROTOCOL_BY_DRIVER
187 if (EFI_ERROR (Status
) && Status
!= EFI_ALREADY_STARTED
) {
192 // Consume SCSI Pass Thru protocol.
194 Status
= gBS
->OpenProtocol (
196 &gEfiScsiPassThruProtocolGuid
,
197 (VOID
**) &ScsiPassThru
,
198 This
->DriverBindingHandle
,
200 EFI_OPEN_PROTOCOL_BY_DRIVER
202 if (EFI_ERROR (Status
) && Status
!= EFI_ALREADY_STARTED
) {
205 &gEfiDevicePathProtocolGuid
,
206 This
->DriverBindingHandle
,
212 if (RemainingDevicePath
== NULL
) {
213 StartPun
= 0xFFFFFFFF;
216 ScsiPassThru
->GetTargetLun (ScsiPassThru
, RemainingDevicePath
, &StartPun
, &StartLun
);
219 for (Pun
= StartPun
, ScanOtherPuns
= TRUE
; ScanOtherPuns
;) {
221 if (StartPun
== 0xFFFFFFFF) {
223 // Remaining Device Path is NULL, scan all the possible Puns in the
226 Status
= ScsiPassThru
->GetNextDevice (ScsiPassThru
, &Pun
, &Lun
);
227 if (EFI_ERROR (Status
)) {
229 // no legal Pun and Lun found any more
235 // Remaining Device Path is not NULL, only scan the specified Pun.
239 ScanOtherPuns
= FALSE
;
243 // Avoid creating handle for the host adapter.
245 if (Pun
== ScsiPassThru
->Mode
->AdapterId
) {
250 // Scan for the scsi device, if it attaches to the scsi bus,
251 // then create handle and install scsi i/o protocol.
253 Status
= ScsiScanCreateDevice (This
, Controller
, Pun
, Lun
, ScsiPassThru
, ParentDevicePath
);
261 SCSIBusDriverBindingStop (
262 IN EFI_DRIVER_BINDING_PROTOCOL
*This
,
263 IN EFI_HANDLE Controller
,
264 IN UINTN NumberOfChildren
,
265 IN EFI_HANDLE
*ChildHandleBuffer
276 // TODO: This - add argument and description to function comment
277 // TODO: Controller - add argument and description to function comment
278 // TODO: NumberOfChildren - add argument and description to function comment
279 // TODO: ChildHandleBuffer - add argument and description to function comment
280 // TODO: EFI_SUCCESS - add return value to function comment
281 // TODO: EFI_DEVICE_ERROR - add return value to function comment
282 // TODO: EFI_SUCCESS - add return value to function comment
285 BOOLEAN AllChildrenStopped
;
287 EFI_SCSI_IO_PROTOCOL
*ScsiIo
;
288 SCSI_IO_DEV
*ScsiIoDevice
;
289 EFI_SCSI_PASS_THRU_PROTOCOL
*ScsiPassThru
;
291 if (NumberOfChildren
== 0) {
293 // Close the bus driver
297 &gEfiScsiPassThruProtocolGuid
,
298 This
->DriverBindingHandle
,
303 &gEfiDevicePathProtocolGuid
,
304 This
->DriverBindingHandle
,
311 AllChildrenStopped
= TRUE
;
313 for (Index
= 0; Index
< NumberOfChildren
; Index
++) {
315 Status
= gBS
->OpenProtocol (
316 ChildHandleBuffer
[Index
],
317 &gEfiScsiIoProtocolGuid
,
319 This
->DriverBindingHandle
,
321 EFI_OPEN_PROTOCOL_GET_PROTOCOL
323 if (EFI_ERROR (Status
)) {
324 AllChildrenStopped
= FALSE
;
328 ScsiIoDevice
= SCSI_IO_DEV_FROM_THIS (ScsiIo
);
330 // Close the child handle
332 Status
= gBS
->CloseProtocol (
334 &gEfiScsiPassThruProtocolGuid
,
335 This
->DriverBindingHandle
,
336 ChildHandleBuffer
[Index
]
339 Status
= gBS
->UninstallMultipleProtocolInterfaces (
340 ChildHandleBuffer
[Index
],
341 &gEfiDevicePathProtocolGuid
,
342 ScsiIoDevice
->DevicePath
,
343 &gEfiScsiIoProtocolGuid
,
344 &ScsiIoDevice
->ScsiIo
,
347 if (EFI_ERROR (Status
)) {
348 AllChildrenStopped
= FALSE
;
351 &gEfiScsiPassThruProtocolGuid
,
352 (VOID
**) &ScsiPassThru
,
353 This
->DriverBindingHandle
,
354 ChildHandleBuffer
[Index
],
355 EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER
358 gBS
->FreePool (ScsiIoDevice
);
362 if (!AllChildrenStopped
) {
363 return EFI_DEVICE_ERROR
;
372 IN EFI_SCSI_IO_PROTOCOL
*This
,
373 OUT UINT8
*DeviceType
378 Retrieves the device type information of the SCSI Controller.
381 This - Protocol instance pointer.
382 DeviceType - A pointer to the device type information
383 retrieved from the SCSI Controller.
386 EFI_SUCCESS - Retrieves the device type information successfully.
387 EFI_INVALID_PARAMETER - The DeviceType is NULL.
390 SCSI_IO_DEV
*ScsiIoDevice
;
392 if (DeviceType
== NULL
) {
393 return EFI_INVALID_PARAMETER
;
396 ScsiIoDevice
= SCSI_IO_DEV_FROM_THIS (This
);
397 *DeviceType
= ScsiIoDevice
->ScsiDeviceType
;
404 ScsiGetDeviceLocation (
405 IN EFI_SCSI_IO_PROTOCOL
*This
,
411 Retrieves the device location in the SCSI channel.
414 This - Protocol instance pointer.
415 Target - A pointer to the Target ID of a SCSI device
417 Lun - A pointer to the LUN of the SCSI device on
421 EFI_SUCCESS - Retrieves the device location successfully.
422 EFI_INVALID_PARAMETER - The Target or Lun is NULL.
425 SCSI_IO_DEV
*ScsiIoDevice
;
427 if (Target
== NULL
|| Lun
== NULL
) {
428 return EFI_INVALID_PARAMETER
;
431 ScsiIoDevice
= SCSI_IO_DEV_FROM_THIS (This
);
433 *Target
= ScsiIoDevice
->Pun
;
434 *Lun
= ScsiIoDevice
->Lun
;
442 IN EFI_SCSI_IO_PROTOCOL
*This
447 Resets the SCSI Bus that the SCSI Controller is attached to.
450 This - Protocol instance pointer.
453 EFI_SUCCESS - The SCSI bus is reset successfully.
454 EFI_DEVICE_ERROR - Errors encountered when resetting the SCSI bus.
455 EFI_UNSUPPORTED - The bus reset operation is not supported by the
456 SCSI Host Controller.
457 EFI_TIMEOUT - A timeout occurred while attempting to reset
461 SCSI_IO_DEV
*ScsiIoDevice
;
463 ScsiIoDevice
= SCSI_IO_DEV_FROM_THIS (This
);
465 return ScsiIoDevice
->ScsiPassThru
->ResetChannel (ScsiIoDevice
->ScsiPassThru
);
472 IN EFI_SCSI_IO_PROTOCOL
*This
477 Resets the SCSI Controller that the device handle specifies.
480 This - Protocol instance pointer.
484 EFI_SUCCESS - Reset the SCSI controller successfully.
485 EFI_DEVICE_ERROR - Errors are encountered when resetting the
487 EFI_UNSUPPORTED - The SCSI bus does not support a device
489 EFI_TIMEOUT - A timeout occurred while attempting to
490 reset the SCSI Controller.
493 SCSI_IO_DEV
*ScsiIoDevice
;
495 ScsiIoDevice
= SCSI_IO_DEV_FROM_THIS (This
);
497 return ScsiIoDevice
->ScsiPassThru
->ResetTarget (
498 ScsiIoDevice
->ScsiPassThru
,
506 ScsiExecuteSCSICommand (
507 IN EFI_SCSI_IO_PROTOCOL
*This
,
508 IN OUT EFI_SCSI_IO_SCSI_REQUEST_PACKET
*Packet
,
509 IN EFI_EVENT Event OPTIONAL
514 Sends a SCSI Request Packet to the SCSI Controller for execution.
517 This - Protocol instance pointer.
518 Packet - The SCSI request packet to send to the SCSI
519 Controller specified by the device handle.
520 Event - If the SCSI bus where the SCSI device is attached
521 does not support non-blocking I/O, then Event is
522 ignored, and blocking I/O is performed.
523 If Event is NULL, then blocking I/O is performed.
524 If Event is not NULL and non-blocking I/O is
525 supported, then non-blocking I/O is performed,
526 and Event will be signaled when the SCSI Request
529 EFI_SUCCESS - The SCSI Request Packet was sent by the host
530 successfully, and TransferLength bytes were
531 transferred to/from DataBuffer.See
532 HostAdapterStatus, TargetStatus,
533 SenseDataLength, and SenseData in that order
534 for additional status information.
535 EFI_WARN_BUFFER_TOO_SMALL - The SCSI Request Packet was executed,
536 but the entire DataBuffer could not be transferred.
537 The actual number of bytes transferred is returned
538 in TransferLength. See HostAdapterStatus,
539 TargetStatus, SenseDataLength, and SenseData in
540 that order for additional status information.
541 EFI_NOT_READY - The SCSI Request Packet could not be sent because
542 there are too many SCSI Command Packets already
543 queued.The caller may retry again later.
544 EFI_DEVICE_ERROR - A device error occurred while attempting to send
545 the SCSI Request Packet. See HostAdapterStatus,
546 TargetStatus, SenseDataLength, and SenseData in
547 that order for additional status information.
548 EFI_INVALID_PARAMETER - The contents of CommandPacket are invalid.
549 The SCSI Request Packet was not sent, so no
550 additional status information is available.
551 EFI_UNSUPPORTED - The command described by the SCSI Request Packet
552 is not supported by the SCSI initiator(i.e., SCSI
553 Host Controller). The SCSI Request Packet was not
554 sent, so no additional status information is
556 EFI_TIMEOUT - A timeout occurred while waiting for the SCSI
557 Request Packet to execute. See HostAdapterStatus,
558 TargetStatus, SenseDataLength, and SenseData in
559 that order for additional status information.
562 SCSI_IO_DEV
*ScsiIoDevice
;
565 EFI_SCSI_PASS_THRU_SCSI_REQUEST_PACKET
*RequestPacket
;
567 if (Packet
== NULL
) {
568 return EFI_INVALID_PARAMETER
;
571 ScsiIoDevice
= SCSI_IO_DEV_FROM_THIS (This
);
573 RequestPacket
= (EFI_SCSI_PASS_THRU_SCSI_REQUEST_PACKET
*) Packet
;
575 Status
= ScsiIoDevice
->ScsiPassThru
->PassThru (
576 ScsiIoDevice
->ScsiPassThru
,
586 ScsiScanCreateDevice (
587 EFI_DRIVER_BINDING_PROTOCOL
*This
,
588 EFI_HANDLE Controller
,
591 EFI_SCSI_PASS_THRU_PROTOCOL
*ScsiPassThru
,
592 EFI_DEVICE_PATH_PROTOCOL
*ParentDevicePath
598 TODO: Add function description
602 This - TODO: add argument description
603 Controller - TODO: add argument description
604 Pun - TODO: add argument description
605 Lun - TODO: add argument description
606 ScsiPassThru - TODO: add argument description
607 ParentDevicePath - TODO: add argument description
611 EFI_SUCCESS - TODO: Add description for return value
612 EFI_OUT_OF_RESOURCES - TODO: Add description for return value
613 EFI_SUCCESS - TODO: Add description for return value
618 SCSI_IO_DEV
*ScsiIoDevice
;
619 EFI_DEVICE_PATH_PROTOCOL
*ScsiDevicePath
;
621 Status
= gBS
->AllocatePool (
623 sizeof (SCSI_IO_DEV
),
624 (VOID
**) &ScsiIoDevice
626 if (EFI_ERROR (Status
)) {
630 ZeroMem (ScsiIoDevice
, sizeof (SCSI_IO_DEV
));
632 ScsiIoDevice
->Signature
= SCSI_IO_DEV_SIGNATURE
;
633 ScsiIoDevice
->ScsiPassThru
= ScsiPassThru
;
634 ScsiIoDevice
->Pun
= Pun
;
635 ScsiIoDevice
->Lun
= Lun
;
637 ScsiIoDevice
->ScsiIo
.GetDeviceType
= ScsiGetDeviceType
;
638 ScsiIoDevice
->ScsiIo
.GetDeviceLocation
= ScsiGetDeviceLocation
;
639 ScsiIoDevice
->ScsiIo
.ResetBus
= ScsiResetBus
;
640 ScsiIoDevice
->ScsiIo
.ResetDevice
= ScsiResetDevice
;
641 ScsiIoDevice
->ScsiIo
.ExecuteSCSICommand
= ScsiExecuteSCSICommand
;
643 if (!DiscoverScsiDevice (ScsiIoDevice
)) {
644 gBS
->FreePool (ScsiIoDevice
);
651 Status
= ScsiIoDevice
->ScsiPassThru
->BuildDevicePath (
652 ScsiIoDevice
->ScsiPassThru
,
657 if (Status
== EFI_OUT_OF_RESOURCES
) {
658 gBS
->FreePool (ScsiIoDevice
);
662 ScsiIoDevice
->DevicePath
= AppendDevicePathNode (
667 // The memory space for ScsiDevicePath is allocated in
668 // ScsiPassThru->BuildDevicePath() function; It is no longer used
669 // after EfiAppendDevicePathNode,so free the memory it occupies.
671 gBS
->FreePool (ScsiDevicePath
);
673 if (ScsiIoDevice
->DevicePath
== NULL
) {
674 gBS
->FreePool (ScsiIoDevice
);
675 return EFI_OUT_OF_RESOURCES
;
678 Status
= gBS
->InstallMultipleProtocolInterfaces (
679 &ScsiIoDevice
->Handle
,
680 &gEfiDevicePathProtocolGuid
,
681 ScsiIoDevice
->DevicePath
,
682 &gEfiScsiIoProtocolGuid
,
683 &ScsiIoDevice
->ScsiIo
,
686 if (EFI_ERROR (Status
)) {
687 gBS
->FreePool (ScsiIoDevice
);
691 &gEfiScsiPassThruProtocolGuid
,
692 (VOID
**) &ScsiPassThru
,
693 This
->DriverBindingHandle
,
694 ScsiIoDevice
->Handle
,
695 EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER
704 SCSI_IO_DEV
*ScsiIoDevice
710 TODO: Add function description
714 ScsiIoDevice - TODO: add argument description
718 TODO: add return values
723 EFI_SCSI_INQUIRY_DATA InquiryData
;
724 UINT32 InquiryDataLength
;
725 EFI_SCSI_SENSE_DATA SenseData
;
726 UINT8 SenseDataLength
;
727 UINT8 HostAdapterStatus
;
730 HostAdapterStatus
= 0;
733 // Using Inquiry command to scan for the device
735 InquiryDataLength
= sizeof (EFI_SCSI_INQUIRY_DATA
);
736 SenseDataLength
= sizeof (EFI_SCSI_SENSE_DATA
);
738 Status
= SubmitInquiryCommand (
739 &ScsiIoDevice
->ScsiIo
,
740 EfiScsiStallSeconds (1),
745 (VOID
*) &InquiryData
,
749 if (EFI_ERROR (Status
)) {
751 // ParseSenseData (&SenseData,SenseDataLength);
756 // Retrieved inquiry data successfully
758 if ((InquiryData
.Peripheral_Qualifier
!= 0) &&
759 (InquiryData
.Peripheral_Qualifier
!= 3)) {
763 if (InquiryData
.Peripheral_Qualifier
== 3) {
764 if (InquiryData
.Peripheral_Type
!= 0x1f) {
769 if ((0x1e >= InquiryData
.Peripheral_Type
) && (InquiryData
.Peripheral_Type
>= 0xa)) {
774 // valid device type and peripheral qualifier combination.
776 ScsiIoDevice
->ScsiDeviceType
= InquiryData
.Peripheral_Type
;
777 ScsiIoDevice
->RemovableDevice
= InquiryData
.RMB
;
778 if (InquiryData
.Version
== 0) {
779 ScsiIoDevice
->ScsiVersion
= 0;
782 // ANSI-approved version
784 ScsiIoDevice
->ScsiVersion
= (UINT8
) (InquiryData
.Version
& 0x03);