/*++ Copyright (c) 2002 Microsoft Corporation Module Name: Abstract: The format of the SCSI (port driver) device map is as follows: Scsi Port 0 - KEY Driver - REG_SZ specifying the drive name, e.g., aha154x. Interrupt - REG_DWORD specifying the interrupt vector that the HBA uses. For example, 58. IOAddress - REG_DWORD specifying the IO address the HBA uses; for example, 0xd800. Dma64BidAddresses - REG_DWORD specifying whether the HBA is using 64 bit addresses or not. Should always be 1 if present. PCCARD - REG_DWORD specifying whether this is a PCCARD bus or not. The value will always be 1 if present. SCSI Bus 0 - KEY Initiator Id 7 - KEY Target Id 0 - KEY Logical Unit ID 0 - KEY Identifier - REG_SZ specifying the SCSI Vendor ID from the LUNs inquiry data. InquiryData - REG_BINARY specifies the SCSI Inquiry Data for the LUN. SerialNumber - REG_SZ specifies the SCSI Serial Number for the LUN if present. Type - REG_SZ specifies the SCSI device type for the LUN. Usage: The module exports the following functions: PortOpenMapKey - Opens a handle to the root of the SCSI device map. PortBuildAdapterEntry - Creates an entry for the specified adapter in the SCSI device map. PortBuildBusEntry - Creates an entry for the specified bus in the SCSI device map. PortBuildTargetEntry - Creates an entry for the specified target in the SCSI device map. PortBuildLunEntry - Creates an entry for the specified LUN in the SCSI device map. Author: Matthew D Hendel (math) 18-July-2002 Revision History: --*/ #include "precomp.h" #include // // Defines // #define SCSI_DEVMAP_KEY_NAME \ (L"\\Registry\\Machine\\Hardware\\DeviceMap\\Scsi") #define SCSI_LUN_KEY_NAME\ (L"%s\\Scsi Port %d\\SCSI Bus %d\\Target Id %d\\Logical Unit Id %d") // // Implementation // NTSTATUS PortOpenMapKey( OUT PHANDLE DeviceMapKey ) /*++ Routine Description: Open a handle to the root of the SCSI Device Map. The handle must be closed with ZwClose. Arguments: DeviceMapKey - Supplies a buffer where the device map handle should be stored on success. Return Value: NTSTATUS code. --*/ { UNICODE_STRING name; OBJECT_ATTRIBUTES objectAttributes; HANDLE mapKey; ULONG disposition; NTSTATUS status; ASSERT (DeviceMapKey != NULL); PAGED_CODE(); // // Open the SCSI key in the device map. // RtlInitUnicodeString(&name, SCSI_DEVMAP_KEY_NAME); InitializeObjectAttributes(&objectAttributes, &name, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, (PSECURITY_DESCRIPTOR) NULL); // // Create or open the key. // status = ZwCreateKey(&mapKey, KEY_READ | KEY_WRITE, &objectAttributes, 0, (PUNICODE_STRING) NULL, REG_OPTION_VOLATILE, &disposition); if(NT_SUCCESS(status)) { *DeviceMapKey = mapKey; } else { *DeviceMapKey = NULL; } return status; } NTSTATUS PortMapBuildAdapterEntry( IN HANDLE DeviceMapKey, IN ULONG PortNumber, IN ULONG InterruptLevel, OPTIONAL IN ULONG IoAddress, OPTIONAL IN ULONG Dma64BitAddresses, IN PUNICODE_STRING DriverName, IN PGUID BusType, OPTIONAL OUT PHANDLE AdapterKeyBuffer OPTIONAL ) /*++ Routine Description: Create a device map entry for the SCSI HBA. We also include device map entries for each of the Busses attached to the HBA, and the initiator for each bus. Arguments: DeviceMapKey - Supplies the handle to the device map key. PortNumber - Supplies the port number that this HBA represents. InterruptLevel - Supplies the interrupt level, or 0 for none. IoAddress - Supplies the IoAddress or 0 for none. Dma64BitAddress - DriverName - NULL terminated unicode string that is the driver name. BusType - Bus type that this HBA is on. AdapterKeyBuffer - Return Value: NTSTATUS code. --*/ { NTSTATUS Status; ULONG Temp; HANDLE AdapterKey; PAGED_CODE(); // // String must be NULL terminated. // ASSERT (DriverName->Buffer [DriverName->Length / sizeof (WCHAR)] == UNICODE_NULL); Status = PortCreateKeyEx (DeviceMapKey, REG_OPTION_VOLATILE, &AdapterKey, L"Scsi Port %d", PortNumber); if (!NT_SUCCESS (Status)) { return Status; } // // Add interrupt level if non-zero. // if (InterruptLevel) { Status = PortSetValueKey (AdapterKey, L"Interrupt", REG_DWORD, &InterruptLevel, sizeof (ULONG)); } // // Add IoAddress if non-zero. // if (IoAddress) { Status = PortSetValueKey (AdapterKey, L"IOAddress", REG_DWORD, &IoAddress, sizeof (ULONG)); } // // Add Dma64BitAddresses if non-zero. // if (Dma64BitAddresses) { Temp = 1; Status = PortSetValueKey (AdapterKey, L"Dma64BitAddresses", REG_DWORD, &Temp, sizeof (ULONG)); } // // Add the driver name. // Status = PortSetValueKey (AdapterKey, L"Driver", REG_SZ, DriverName->Buffer, DriverName->Length + sizeof (WCHAR)); // // If this is a PCMCIA card, set the PCCARD flag. // if (BusType != NULL && IsEqualGUID (BusType, &GUID_BUS_TYPE_PCMCIA)) { Temp = 1; Status = PortSetValueKey (AdapterKey, L"PCCARD", REG_DWORD, &Temp, sizeof (ULONG)); } if (AdapterKeyBuffer != NULL) { *AdapterKeyBuffer = AdapterKey; } else { ZwClose (AdapterKey); } return STATUS_SUCCESS; } NTSTATUS PortMapBuildBusEntry( IN HANDLE AdapterKey, IN ULONG BusId, IN ULONG InitiatorId, OUT PHANDLE BusKeyBuffer OPTIONAL ) /*++ Routine Description: Build the BusId device map entry under the adapters device map entry. The bus entry is populated with an entry for the initiator ID only. Arguments: AdapterKey - Handle to the adapter's device map entry. BusId - Supplies the ID of this bus. InitiatorId - Supplies the initiator target ID. BusKeyBuffer _ Supplies an optional pointer to a buffer to receive the opened bus key. Return Value: NTSTATUS code. --*/ { NTSTATUS Status; HANDLE BusKey; ASSERT (BusId <= 255); ASSERT (InitiatorId <= 255); PAGED_CODE(); Status = PortCreateKeyEx (AdapterKey, REG_OPTION_VOLATILE, &BusKey, L"SCSI Bus %d", BusId); if (!NT_SUCCESS (Status)) { return Status; } PortCreateKeyEx (BusKey, REG_OPTION_VOLATILE, NULL, L"Initiator Id %d", InitiatorId); if (BusKeyBuffer) { *BusKeyBuffer = BusKey; } else { ZwClose (BusKey); } return STATUS_SUCCESS; } NTSTATUS PortMapBuildTargetEntry( IN HANDLE BusKey, IN ULONG TargetId, OUT PHANDLE TargetKey OPTIONAL ) { NTSTATUS Status; ASSERT (TargetId <= 255); PAGED_CODE(); Status = PortCreateKeyEx (BusKey, REG_OPTION_VOLATILE, TargetKey, L"Target Id %d", TargetId); return Status; } NTSTATUS PortMapBuildLunEntry( IN HANDLE TargetKey, IN ULONG Lun, IN PINQUIRYDATA InquiryData, IN PANSI_STRING SerialNumber, OPTIONAL PVOID DeviceId, IN ULONG DeviceIdLength, OUT PHANDLE LunKeyBuffer OPTIONAL ) /*++ Routine Description: Create and populate the Logical Unit Device Map Entry with the following information: Identifier - REG_SZ specifying the SCSI Vendor Id from the inquriy data. InquiryData - REG_BINARY specifying the SCSI InquiryData. SerialNumber - REG_SZ specifying the serial number (page 80 of Inquriy VPD). Type - REG_SZ specifying the SCSI device type. DeviceIdentifierPage - REG_BINARY specifying the binary device identifier data (page 83 of VPD). Arguments: TargetKey - Specifies the Target's previously opened key. Lun - Specifies the Logical Unit ID for this LUN. InquiryData - Specifies the binary inquriy data for this LUN. NOTE: Only the first INQUIRYDATABUFFERSIZE bytes of the Inquiry data are used. SerialNumber - Specifies the ANSI Serial Number (page 80) for the LUN. May be NULL if there is no serial number. DeviceId - Specifies the device identifier page (page 83) for the LUN. May be NULL if the device does not support page 83. DeviceIdLength - Specifies the length of the DeviceId parameter. Not used when DeviceId is NULL. LunKeyReturn - Specifies the buffer for key for the logical unit to be copied to. May be NULL if not necessary. Return Value: NTSTATUS code. --*/ { NTSTATUS Status; HANDLE LunKey; PCSCSI_DEVICE_TYPE DeviceEntry; ULONG Length; ASSERT (Lun <= 255); ASSERT (InquiryData != NULL); PAGED_CODE(); Status = PortCreateKeyEx (TargetKey, REG_OPTION_VOLATILE, &LunKey, L"Logical Unit Id %d", Lun); if (!NT_SUCCESS (Status)) { return Status; } // // Write out the INQUIRY DATA in binary form. // PortSetValueKey (LunKey, L"InquiryData", REG_BINARY, InquiryData, INQUIRYDATABUFFERSIZE); // // Write out the SERIAL NUMBER as a string. // if (SerialNumber->Length != 0) { PortSetValueKey (LunKey, L"SerialNumber", PORT_REG_ANSI_STRING, SerialNumber->Buffer, SerialNumber->Length); } // // Write the SCSI VendorId. // PortSetValueKey (LunKey, L"Identifier", PORT_REG_ANSI_STRING, InquiryData->VendorId, sizeof (InquiryData->VendorId)); // // Add the DeviceType entry as a string. // DeviceEntry = PortGetDeviceType (InquiryData->DeviceType); Length = wcslen (DeviceEntry->DeviceMap); PortSetValueKey (LunKey, L"DeviceType", REG_SZ, (PVOID)DeviceEntry->DeviceMap, (Length + 1) * sizeof (WCHAR)); // // Write out the DeviceIdentifierPage if it was given. // if (DeviceId != NULL) { PortSetValueKey (LunKey, L"DeviceIdentifierPage", REG_BINARY, DeviceId, DeviceIdLength); } if (LunKeyBuffer) { *LunKeyBuffer = LunKey; } return STATUS_SUCCESS; } NTSTATUS PortMapDeleteAdapterEntry( IN ULONG PortId ) /*++ Routine Description: Delete the Adapter's SCSI DeviceMap entry from the registry. Arguments: PortId - PortId associated with the adapter. Return Value: NTSTATUS code. --*/ { HANDLE AdapterKey; NTSTATUS Status; OBJECT_ATTRIBUTES ObjectAttributes; UNICODE_STRING KeyName; WCHAR KeyNameBuffer[256]; PAGED_CODE(); swprintf (KeyNameBuffer, L"%s\\Scsi Port %d", SCSI_DEVMAP_KEY_NAME, PortId); RtlInitUnicodeString (&KeyName, KeyNameBuffer); InitializeObjectAttributes (&ObjectAttributes, &KeyName, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL); Status = ZwOpenKey (&AdapterKey, KEY_ALL_ACCESS, &ObjectAttributes); if (!NT_SUCCESS (Status)) { return Status; } ZwDeleteKey (AdapterKey); ZwClose (AdapterKey); return Status; } NTSTATUS PortMapDeleteLunEntry( IN ULONG PortId, IN ULONG BusId, IN ULONG TargetId, IN ULONG Lun ) /*++ Routine Description: Delete the logical unit's SCSI DeviceMap entry from the registry. Arguments: PortId - Port ID associaed with the adapter. BusId - Bus ID / PathId that this logical unit is on. TargetId - Target that this logical unit is on. Lun - Logical unit ID for this LUN. Return Value: NTSTATUS code. --*/ { HANDLE LunKey; NTSTATUS Status; OBJECT_ATTRIBUTES ObjectAttributes; UNICODE_STRING KeyName; WCHAR KeyNameBuffer[256]; PAGED_CODE(); swprintf (KeyNameBuffer, SCSI_LUN_KEY_NAME, SCSI_DEVMAP_KEY_NAME, PortId, BusId, TargetId, Lun); RtlInitUnicodeString (&KeyName, KeyNameBuffer); InitializeObjectAttributes (&ObjectAttributes, &KeyName, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL); Status = ZwOpenKey (&LunKey, KEY_ALL_ACCESS, &ObjectAttributes); if (!NT_SUCCESS (Status)) { return Status; } ZwDeleteKey (LunKey); ZwClose (LunKey); return Status; }