windows-nt/Source/XPSP1/NT/base/ntsetup/syssetup/storinst.c
2020-09-26 16:20:57 +08:00

2692 lines
76 KiB
C
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
Copyright (c) Microsoft Corporation. All rights reserved.
Module Name:
clasinst.c
Abstract:
Routines for the following 'built-in' class installers:
TapeDrive
SCSIAdapter
CdromDrive
Author:
Lonny McMichael 26-February-1996
--*/
#include "setupp.h"
#pragma hdrstop
//
// include common INF strings headerfile.
//
#include <infstr.h>
//
// instantiate device class GUIDs.
//
#include <initguid.h>
#include <devguid.h>
#include <ntddvol.h>
#include <ntddscsi.h> // for StorageCdromQueryCdda()
#include <ntddcdrm.h> // for StorageCdromQueryCdda()
#define _NTSCSI_USER_MODE_ // prevents all the kernel-mode stuff
#include <scsi.h> // for StorageCdromQueryCdda()
ULONG BreakWhenGettingModePage2A = FALSE;
#ifdef UNICODE
#define _UNICODE
#endif
#include <tchar.h>
#define TCHAR_NULL TEXT('\0')
//
// Just to make sure no one is trying to use this obsolete string definition.
//
#ifdef IDS_DEVINSTALL_ERROR
#undef IDS_DEVINSTALL_ERROR
#endif
#if (_X86_)
#define DISABLE_IMAPI 0
#else
#define DISABLE_IMAPI 1
#endif
//
// Define the location of the device settings tree.
//
#define STORAGE_DEVICE_SETTINGS_DATABASE TEXT("Software\\Microsoft\\Windows NT\\CurrentVersion\\Storage\\DeviceSettings\\")
#define REDBOOK_SETTINGS_KEY TEXT("DigitalAudio")
#define REDBOOK_SERVICE_NAME TEXT("redbook")
#define IMAPI_SETTINGS_KEY TEXT("Imapi")
#define IMAPI_ENABLE_VALUE TEXT("EnableImapi")
#define IMAPI_SERVICE_NAME TEXT("imapi")
typedef struct STORAGE_COINSTALLER_CONTEXT {
PWSTR DeviceDescBuffer;
HANDLE DeviceEnumKey;
union {
struct {
BOOLEAN RedbookInstalled;
BOOLEAN ImapiInstalled;
} CdRom;
};
} STORAGE_COINSTALLER_CONTEXT, *PSTORAGE_COINSTALLER_CONTEXT;
typedef struct _PASS_THROUGH_REQUEST {
SCSI_PASS_THROUGH Srb;
SENSE_DATA SenseInfoBuffer;
UCHAR DataBuffer[0];
} PASS_THROUGH_REQUEST, *PPASS_THROUGH_REQUEST;
#define PASS_THROUGH_NOT_READY_RETRY_INTERVAL 100
//
// Some debugging aids for us kernel types
//
#define CHKPRINT 0
#if CHKPRINT
#define ChkPrintEx(_x_) DbgPrint _x_ // use: ChkPrintEx(( "%x", var, ... ));
#define ChkBreak() DbgBreakPoint()
#else
#define ChkPrintEx(_x_)
#define ChkBreak()
#endif
DWORD StorageForceRedbookOnInaccurateDrives = FALSE;
BOOLEAN
OverrideFriendlyNameForTape(
IN HDEVINFO DeviceInfoSet,
IN PSP_DEVINFO_DATA DeviceInfoData OPTIONAL
);
DWORD
RegCopyKey(
HKEY SourceKey,
HKEY DestinationKey
);
BOOLEAN
StorageCopyDeviceSettings(
IN HDEVINFO DeviceInfo,
IN PSP_DEVINFO_DATA DeviceInfoData,
IN HKEY DeviceEnumKey
);
VOID
StorageInstallCdrom(
IN HDEVINFO DeviceInfo,
IN PSP_DEVINFO_DATA DeviceInfoData,
IN HANDLE DeviceEnumKey,
IN BOOLEAN PreInstall
);
BOOLEAN
StorageUpdateRedbookSettings(
IN HDEVINFO DeviceInfo,
IN PSP_DEVINFO_DATA DeviceInfoData,
IN HKEY DeviceEnumKey,
IN PCDVD_CAPABILITIES_PAGE DeviceCapabilities OPTIONAL
);
BOOLEAN
StorageUpdateImapiSettings(
IN HDEVINFO DeviceInfo,
IN PSP_DEVINFO_DATA DeviceInfoData,
IN HKEY DeviceEnumKey,
IN PCDVD_CAPABILITIES_PAGE DeviceCapabilities OPTIONAL
);
DWORD
StorageInstallFilter(
IN HDEVINFO DeviceInfo,
IN PSP_DEVINFO_DATA DeviceInfoData,
IN LPTSTR FilterName,
IN DWORD FilterType
);
DWORD
SetServiceStart(
IN LPCTSTR ServiceName,
IN DWORD StartType,
OUT DWORD *OldStartType
);
DWORD
AddFilterDriver(
IN HDEVINFO DeviceInfo,
IN PSP_DEVINFO_DATA DeviceInfoData,
IN LPTSTR ServiceName,
IN DWORD FilterType
);
VOID
StorageInterpretSenseInfo(
IN PSENSE_DATA SenseData,
IN UCHAR SenseDataSize,
OUT PDWORD ErrorValue, // from WinError.h
OUT PBOOLEAN SuggestRetry OPTIONAL,
OUT PDWORD SuggestRetryDelay OPTIONAL
);
typedef struct _STORAGE_REDBOOK_SETTINGS {
ULONG CDDASupported;
ULONG CDDAAccurate;
ULONG ReadSizesSupported;
} STORAGE_REDBOOK_SETTINGS, *PSTORAGE_REDBOOK_SETTINGS;
#if 0
#define BREAK ASSERT(!"Break")
#else
#define BREAK
#endif
DWORD
TapeClassInstaller(
IN DI_FUNCTION InstallFunction,
IN HDEVINFO DeviceInfoSet,
IN PSP_DEVINFO_DATA DeviceInfoData OPTIONAL
)
/*++
Routine Description:
This routine acts as the class installer for TapeDrive devices. Now that
we've stopped supporting legacy INFs, it presently does nothing! :-)
Arguments:
InstallFunction - Specifies the device installer function code indicating
the action being performed.
DeviceInfoSet - Supplies a handle to the device information set being
acted upon by this install action.
DeviceInfoData - Optionally, supplies the address of a device information
element being acted upon by this install action.
Return Value:
If this function successfully completed the requested action, the return
value is NO_ERROR.
If the default behavior is to be performed for the requested action, the
return value is ERROR_DI_DO_DEFAULT.
If an error occurred while attempting to perform the requested action, a
Win32 error code is returned.
--*/
{
switch(InstallFunction) {
default :
//
// Just do the default action.
//
return ERROR_DI_DO_DEFAULT;
}
}
DWORD
ScsiClassInstaller(
IN DI_FUNCTION InstallFunction,
IN HDEVINFO DeviceInfoSet,
IN PSP_DEVINFO_DATA DeviceInfoData OPTIONAL
)
/*++
Routine Description:
This routine acts as the class installer for SCSIAdapter devices. It
provides special handling for the following DeviceInstaller function codes:
DIF_ALLOW_INSTALL - Check to see if the selected driver node supports NT
Arguments:
InstallFunction - Specifies the device installer function code indicating
the action being performed.
DeviceInfoSet - Supplies a handle to the device information set being
acted upon by this install action.
DeviceInfoData - Optionally, supplies the address of a device information
element being acted upon by this install action.
Return Value:
If this function successfully completed the requested action, the return
value is NO_ERROR.
If the default behavior is to be performed for the requested action, the
return value is ERROR_DI_DO_DEFAULT.
If an error occurred while attempting to perform the requested action, a
Win32 error code is returned.
--*/
{
switch(InstallFunction) {
case DIF_ALLOW_INSTALL :
//
// Check to make sure the selected driver node supports NT.
//
if (DriverNodeSupportsNT(DeviceInfoSet, DeviceInfoData)) {
return NO_ERROR;
} else {
SetupDebugPrint(L"A SCSI driver is not a Win NTdriver.\n");
return ERROR_NON_WINDOWS_NT_DRIVER;
}
default :
//
// Just do the default action.
//
return ERROR_DI_DO_DEFAULT;
}
}
DWORD
HdcClassInstaller(
IN DI_FUNCTION InstallFunction,
IN HDEVINFO DeviceInfoSet,
IN PSP_DEVINFO_DATA DeviceInfoData OPTIONAL
)
/*++
Routine Description:
This routine acts as the class installer for hard disk controllers
(IDE controllers/channels). It provides special handling for the
following DeviceInstaller function codes:
DIF_FIRSTTIMESETUP - Search through all root-enumerated devnodes, looking
for ones being controlled by hdc-class drivers. Add
any such devices found into the supplied device
information set.
Arguments:
InstallFunction - Specifies the device installer function code indicating
the action being performed.
DeviceInfoSet - Supplies a handle to the device information set being
acted upon by this install action.
DeviceInfoData - Optionally, supplies the address of a device information
element being acted upon by this install action.
Return Value:
If this function successfully completed the requested action, the return
value is NO_ERROR.
If the default behavior is to be performed for the requested action, the
return value is ERROR_DI_DO_DEFAULT.
If an error occurred while attempting to perform the requested action, a
Win32 error code is returned.
--*/
{
switch(InstallFunction) {
case DIF_FIRSTTIMESETUP :
//
// BUGBUG (lonnym): handle this!
//
default :
//
// Just do the default action.
//
return ERROR_DI_DO_DEFAULT;
}
}
BOOLEAN
StorageGetCDVDCapabilities(
IN HDEVINFO DeviceInfo,
IN PSP_DEVINFO_DATA DeviceInfoData OPTIONAL,
IN OUT PCDVD_CAPABILITIES_PAGE CapabilitiesPage
)
{
PPASS_THROUGH_REQUEST passThrough;
PCDVD_CAPABILITIES_PAGE modePage;
DWORD allocLength;
DWORD dataLength;
ULONG attempt;
BOOLEAN status = FALSE;
HANDLE deviceHandle;
deviceHandle = INVALID_HANDLE_VALUE;
passThrough = NULL;
modePage = NULL;
ASSERT(CapabilitiesPage != NULL);
if (BreakWhenGettingModePage2A) {
ChkPrintEx(("CDGetCap => entering\n"));
DbgBreakPoint();
}
//
// open a handle to the device, needed to send the ioctls
//
deviceHandle = UtilpGetDeviceHandle(DeviceInfo,
DeviceInfoData,
(LPGUID)&CdRomClassGuid,
GENERIC_READ | GENERIC_WRITE
);
if (deviceHandle == INVALID_HANDLE_VALUE) {
ChkPrintEx(("CDGetCap => cannot get device handle\n"));
goto cleanup;
}
//
// determine size of allocation needed
//
dataLength =
sizeof(MODE_PARAMETER_HEADER10) + // larger of 6/10 byte
sizeof(CDVD_CAPABILITIES_PAGE) + // the actual mode page
8; // extra spooge for drives that ignore DBD
allocLength = sizeof(PASS_THROUGH_REQUEST) + dataLength;
//
// allocate this buffer for the ioctls
//
passThrough = (PPASS_THROUGH_REQUEST)MyMalloc(allocLength);
if (passThrough == NULL) {
ChkPrintEx(("CDGetCap => could not allocate for passThrough\n"));
goto cleanup;
}
ASSERT(dataLength <= 0xff); // one char
//
// send 6-byte, then 10-byte if 6-byte failed.
// then, just parse the information
//
for (attempt = 1; attempt <= 2; attempt++) {
ULONG j;
BOOLEAN retry = TRUE;
DWORD error;
for (j=0; (j < 5) && (retry); j++) {
PSCSI_PASS_THROUGH srb = &passThrough->Srb;
PCDB cdb = (PCDB)(&srb->Cdb[0]);
DWORD bytes;
BOOL b;
retry = FALSE;
ZeroMemory(passThrough, allocLength);
srb->TimeOutValue = 20;
srb->Length = sizeof(SCSI_PASS_THROUGH);
srb->SenseInfoLength = sizeof(SENSE_DATA);
srb->SenseInfoOffset =
FIELD_OFFSET(PASS_THROUGH_REQUEST, SenseInfoBuffer);
srb->DataBufferOffset =
FIELD_OFFSET(PASS_THROUGH_REQUEST, DataBuffer);
srb->DataIn = SCSI_IOCTL_DATA_IN;
srb->DataTransferLength = dataLength;
if ((attempt % 2) == 1) { // settings based on 6-byte request
srb->CdbLength = 6;
cdb->MODE_SENSE.OperationCode = SCSIOP_MODE_SENSE;
cdb->MODE_SENSE.PageCode = MODE_PAGE_CAPABILITIES;
cdb->MODE_SENSE.AllocationLength = (UCHAR)dataLength;
cdb->MODE_SENSE.Dbd = 1;
} else { // settings based on 10 bytes request
srb->CdbLength = 10;
cdb->MODE_SENSE10.OperationCode = SCSIOP_MODE_SENSE10;
cdb->MODE_SENSE10.PageCode = MODE_PAGE_CAPABILITIES;
cdb->MODE_SENSE10.AllocationLength[0] = 0;
cdb->MODE_SENSE10.AllocationLength[1] = (UCHAR)(dataLength & 0xff);
cdb->MODE_SENSE10.Dbd = 1;
}
//
// buffers are all set, send the ioctl
//
b = DeviceIoControl(deviceHandle,
IOCTL_SCSI_PASS_THROUGH,
passThrough,
allocLength,
passThrough,
allocLength,
&bytes,
NULL);
if (!b) {
ChkPrintEx(("CDGetCap => %s byte command failed to be sent to device\n",
((attempt%2) ? "6" : "10")
));
retry = FALSE;
continue; // try the next 'j' loop.
}
//
// now see if we should retry
//
StorageInterpretSenseInfo(&passThrough->SenseInfoBuffer,
SENSE_BUFFER_SIZE,
&error,
&retry,
NULL);
if (error != ERROR_SUCCESS) {
ChkPrintEx(("CDGetCap => %s byte command failed (%x/%x/%x),"
"%s retrying\n",
((attempt%2) ? "6" : "10"),
passThrough->SenseInfoBuffer.SenseKey,
passThrough->SenseInfoBuffer.AdditionalSenseCode,
passThrough->SenseInfoBuffer.AdditionalSenseCodeQualifier,
(retry ? "" : "not")
));
//
// retry will be set to either true or false to
// have this loop (j) re-run or not....
//
continue;
}
//
// else it worked!
//
ASSERT(retry == FALSE);
retry = FALSE;
ASSERT(status == FALSE);
status = TRUE;
}
//
// if unable to retrieve the page, just start the next loop.
//
if (!status) {
continue; // try the next 'attempt' loop.
}
//
// find the mode page data
//
// NOTE: if the drive fails to ignore the DBD bit,
// we still need to install? HCT will catch this,
// but legacy drives need it.
//
(ULONG_PTR)modePage = (ULONG_PTR)passThrough->DataBuffer;
if (attempt == 1) {
PMODE_PARAMETER_HEADER h;
h = (PMODE_PARAMETER_HEADER)passThrough->DataBuffer;
//
// add the size of the header
//
(ULONG_PTR)modePage += sizeof(MODE_PARAMETER_HEADER);
dataLength -= sizeof(MODE_PARAMETER_HEADER);
//
// add the size of the block descriptor, which should
// always be zero, but isn't on some poorly behaved drives
//
if (h->BlockDescriptorLength) {
ASSERT(h->BlockDescriptorLength == 8);
ChkPrintEx(("CDGetCap => %s byte command ignored DBD bit (%x)\n",
((attempt%2) ? "6" : "10"),
h->BlockDescriptorLength
));
(ULONG_PTR)modePage += h->BlockDescriptorLength;
dataLength -= h->BlockDescriptorLength;
}
} else {
PMODE_PARAMETER_HEADER10 h;
h = (PMODE_PARAMETER_HEADER10)passThrough->DataBuffer;
//
// add the size of the header
//
(ULONG_PTR)modePage += sizeof(MODE_PARAMETER_HEADER10);
dataLength -= sizeof(MODE_PARAMETER_HEADER10);
//
// add the size of the block descriptor, which should
// always be zero, but isn't on some poorly behaved drives
//
if ((h->BlockDescriptorLength[0] != 0) ||
(h->BlockDescriptorLength[1] != 0)
) {
ULONG_PTR bdLength = 0;
bdLength += ((h->BlockDescriptorLength[0]) << 8);
bdLength += ((h->BlockDescriptorLength[1]) & 0xff);
ASSERT(bdLength == 8);
ChkPrintEx(("CDGetCap => %s byte command ignored DBD bit (%x)\n",
((attempt%2) ? "6" : "10"),
bdLength
));
(ULONG_PTR)modePage += bdLength;
dataLength -= (DWORD)bdLength;
}
}
//
// now have the pointer to the mode page data and length of usable data
// copy it back to requestor's buffer
//
ChkPrintEx(("CDGetCap => %s byte command succeeded\n",
(attempt%2) ? "6" : "10"));
RtlZeroMemory(CapabilitiesPage, sizeof(CDVD_CAPABILITIES_PAGE));
RtlCopyMemory(CapabilitiesPage,
modePage,
min(dataLength, sizeof(CDVD_CAPABILITIES_PAGE))
);
if (BreakWhenGettingModePage2A) {
ChkPrintEx(("CDGetCap => Capabilities @ %#p\n", CapabilitiesPage));
DbgBreakPoint();
}
goto cleanup; // no need to send another command
}
ChkPrintEx(("CDGetCap => Unable to get drive capabilities via modepage\n"));
cleanup:
if (passThrough) {
MyFree(passThrough);
}
if (deviceHandle != INVALID_HANDLE_VALUE) {
CloseHandle(deviceHandle);
}
return status;
}
BOOLEAN
ScReadRegDword(
IN HANDLE Key,
IN LPTSTR ValueName,
OUT PDWORD Value
)
{
DWORD type;
DWORD size = sizeof(DWORD);
DWORD value;
DWORD result;
result = RegQueryValueEx(Key,
ValueName,
NULL,
&type,
(LPBYTE) &value,
&size);
if(result == ERROR_SUCCESS) {
*Value = value;
return TRUE;
}
return FALSE;
}
VOID
StorageReadRedbookSettings(
IN HANDLE Key,
OUT STORAGE_REDBOOK_SETTINGS *Settings
)
{
STORAGE_REDBOOK_SETTINGS settings;
//
// since this key exists, query for the values
//
DWORD dataType;
DWORD dataSize;
DWORD value;
LONG results;
settings.CDDASupported = FALSE;
settings.CDDAAccurate = FALSE;
settings.ReadSizesSupported = 0;
if(ScReadRegDword(Key, TEXT("CDDASupported"), &value)) {
settings.CDDASupported = value ? 1 : 0;
}
if(ScReadRegDword(Key, TEXT("CDDAAccurate"), &value)) {
settings.CDDAAccurate = value ? 1 : 0;
}
if(ScReadRegDword(Key, TEXT("ReadSizesSupported"), &value)) {
settings.ReadSizesSupported = value;
}
//
// one of the three worked
//
ChkPrintEx(("StorageReadSettings: Query Succeeded:\n"));
ChkPrintEx(("StorageReadSettings: ReadSizeMask (pre): %x\n",
settings.ReadSizesSupported));
ChkPrintEx(("StorageReadSettings: CDDAAccurate (pre): %x\n",
settings.CDDAAccurate));
ChkPrintEx(("StorageReadSettings: CDDASupported (pre): %x\n",
settings.CDDASupported));
//
// interpret the redbook device settings.
//
if (settings.ReadSizesSupported) {
ChkPrintEx(("StorageSeed: Drive supported only some sizes "
" (%#08x)\n", settings.ReadSizesSupported));
settings.CDDASupported = TRUE;
settings.CDDAAccurate = FALSE;
} else if (settings.CDDAAccurate) {
ChkPrintEx(("StorageSeed: Drive is fully accurate\n"));
settings.CDDASupported = TRUE;
settings.ReadSizesSupported = -1;
} else if (settings.CDDASupported) {
ChkPrintEx(("StorageSeed: Drive lies about being accurate\n"));
settings.CDDAAccurate = FALSE;
settings.ReadSizesSupported = -1;
} // values are now interpreted
*Settings = settings;
return;
} // end of successful open of key
DWORD
StorageCoInstaller(
IN DI_FUNCTION InstallFunction,
IN HDEVINFO DeviceInfoSet,
IN PSP_DEVINFO_DATA DeviceInfoData, OPTIONAL
IN OUT PCOINSTALLER_CONTEXT_DATA Context
)
/*++
Routine Description:
This routine acts as a co-installer for storage devices. It is presently
registered (via hivesys.inf) for CDROM, DiskDrive, and TapeDrive classes.
The purpose of this co-installer is to save away the bus-supplied default
DeviceDesc into the device's FriendlyName property. The reason for this
is that the bus can retrieve a very specific description from the device
(e.g., via SCSI inquiry data), yet the driver node we install will often
be something very generic (e.g., "Disk drive").
We want to keep the descriptive name, so that it can be displayed in the
UI (DevMgr, etc.) to allow the user to distinguish between multiple storage
devices of the same class.
A secondary purpose for this co-installer is to seed the ability to play
digital audio for a given device. The reason for this is that many cdroms
that support digital audio do not report this ability, there are some that
claim this ability but cannot actually do it reliably, and some that only
work when reading N sectors at a time. This information is seeded in the
registry, and copied to the enum key. If this information does not exist,
no keys are created, and defaults are used.
Arguments:
InstallFunction - Specifies the device installer function code indicating
the action being performed.
DeviceInfoSet - Supplies a handle to the device information set being
acted upon by this install action.
DeviceInfoData - Optionally, supplies the address of a device information
element being acted upon by this install action.
Context - Supplies the installation context that is per-install
request/per-coinstaller.
Return Value:
If this function successfully completed the requested action (or did
nothing) and wishes for the installation to continue, the return
value is NO_ERROR.
If this function successfully completed the requested action (or did
nothing) and would like to be called back once installation has
completed, the return value is ERROR_DI_POSTPROCESSING_REQUIRED.
If an error occurred while attempting to perform the requested action,
a Win32 error code is returned. Installation will be aborted.
--*/
{
PSTORAGE_COINSTALLER_CONTEXT InstallContext;
PWSTR DeviceDescBuffer = NULL;
DWORD DeviceDescBufferLen, Err;
ULONG ulStatus, ulProblem;
switch(InstallFunction) {
case DIF_INSTALLDEVICE : {
if(Context->PostProcessing) {
//
// We're 'on the way out' of an installation. The context
// PrivateData had better contain the string we stored on
// the way in.
//
InstallContext = Context->PrivateData;
MYASSERT(InstallContext);
//
// We only want to store the FriendlyName property if the
// installation succeeded. We only want to seed redbook values
// if the install succeeded.
//
if(Context->InstallResult == NO_ERROR) {
BOOLEAN OverrideFriendlyName = FALSE;
if (IsEqualGUID(&(DeviceInfoData->ClassGuid),
&GUID_DEVCLASS_TAPEDRIVE)) {
//
// This function checks if we need to use
// the device description, given in INF file,
// in UI such as Device Manager. Returns TRUE
// if INF description is to used. FALSE, otherwise.
//
OverrideFriendlyName = OverrideFriendlyNameForTape(
DeviceInfoSet,
DeviceInfoData);
} else if (IsEqualGUID(&(DeviceInfoData->ClassGuid),
&GUID_DEVCLASS_CDROM)) {
//
// See if we need to install any of the filter drivers
// to enable additional CD-ROM (CD-R, DVD-RAM, etc...)
// features.
//
StorageInstallCdrom(DeviceInfoSet,
DeviceInfoData,
InstallContext,
FALSE);
}
if ((OverrideFriendlyName == FALSE) &&
(InstallContext->DeviceDescBuffer != NULL)) {
//
// If we need not use the INF device description
// write the name, generated from SCSI Inquiry data,
// onto FriendlyName
//
SetupDiSetDeviceRegistryProperty(DeviceInfoSet,
DeviceInfoData,
SPDRP_FRIENDLYNAME,
(PBYTE) InstallContext->DeviceDescBuffer,
(lstrlen(InstallContext->DeviceDescBuffer) + 1) * sizeof(WCHAR));
}
}
//
// Now free our installation context.
//
if ((InstallContext->DeviceEnumKey) != INVALID_HANDLE_VALUE) {
RegCloseKey(InstallContext->DeviceEnumKey);
}
if(InstallContext->DeviceDescBuffer) {
MyFree(InstallContext->DeviceDescBuffer);
}
MyFree(InstallContext);
//
// Propagate the result of the previous installer.
//
return Context->InstallResult;
} else {
//
// We're 'on the way in' for device installation.
// Make sure that whoever called SetupDiCallClassInstaller
// passed in a device information element. (Don't fail the
// call if they didn't--that's the job of the class
// installer/SetupDiInstallDevice.)
//
if(!DeviceInfoData) {
return NO_ERROR;
}
//
// Make sure this isn't a root-enumerated device. The root
// enumerator clearly has nothing interesting to say about
// the device's description beyond what the INF says.
//
if((CM_Get_DevNode_Status(&ulStatus, &ulProblem, DeviceInfoData->DevInst, 0) != CR_SUCCESS) ||
(ulStatus & DN_ROOT_ENUMERATED)) {
return NO_ERROR;
}
//
// Allocate our context.
//
InstallContext = MyMalloc(sizeof(STORAGE_COINSTALLER_CONTEXT));
if(InstallContext == NULL) {
return NO_ERROR;
}
memset(InstallContext, 0, sizeof(STORAGE_COINSTALLER_CONTEXT));
InstallContext->DeviceEnumKey = INVALID_HANDLE_VALUE;
//
// open the device's instance under the enum key
//
InstallContext->DeviceEnumKey = SetupDiCreateDevRegKey(
DeviceInfoSet,
DeviceInfoData,
DICS_FLAG_GLOBAL,
0,
DIREG_DEV,
NULL,
NULL);
if (InstallContext->DeviceEnumKey == INVALID_HANDLE_VALUE) {
ChkPrintEx(("StorageInstallCdrom: Failed to open device "
"registry key\n"));
}
//
// Search the device settings database to see if there are
// any settings provided for this particular device.
//
if (InstallContext->DeviceEnumKey != INVALID_HANDLE_VALUE) {
StorageCopyDeviceSettings(DeviceInfoSet,
DeviceInfoData,
InstallContext->DeviceEnumKey);
}
//
// See if we need to install any of the filter drivers to enable
// additional CD-ROM (CD-R, DVD-RAM, etc...) features.
//
if (IsEqualGUID(&(DeviceInfoData->ClassGuid),
&GUID_DEVCLASS_CDROM)) {
StorageInstallCdrom(DeviceInfoSet,
DeviceInfoData,
InstallContext,
TRUE);
}
//
// See if there is currently a 'FriendlyName' property.
//
if(SetupDiGetDeviceRegistryProperty(DeviceInfoSet,
DeviceInfoData,
SPDRP_FRIENDLYNAME,
NULL,
NULL,
0,
NULL) ||
(GetLastError() == ERROR_INSUFFICIENT_BUFFER)) {
//
// Either we succeeded (which should never happen), or we
// failed with a return value of buffer-too-small,
// indicating that the property already exists. In this
// case, there's nothing for us to do.
//
goto CoPreInstallDone;
}
//
// Attempt to retrieve the DeviceDesc property.
// start out with a buffer size that should always be big enough
//
DeviceDescBufferLen = LINE_LEN * sizeof(WCHAR);
while(TRUE) {
if(!(DeviceDescBuffer = MyMalloc(DeviceDescBufferLen))) {
//
// We failed, but what we're doing is not at all
// critical. Thus, we'll go ahead and let the
// installation proceed. If we're out of memory, it's
// going to fail for a much more important reason
// later anyway.
//
goto CoPreInstallDone;
}
if(SetupDiGetDeviceRegistryProperty(DeviceInfoSet,
DeviceInfoData,
SPDRP_DEVICEDESC,
NULL,
(PBYTE)DeviceDescBuffer,
DeviceDescBufferLen,
&DeviceDescBufferLen)) {
break;
}
Err = GetLastError();
//
// Free our current buffer before examining the
// cause of failure.
//
MyFree(DeviceDescBuffer);
DeviceDescBuffer = NULL;
if(Err != ERROR_INSUFFICIENT_BUFFER) {
//
// The failure was for some other reason than
// buffer-too-small. This means we're not going to
// be able to get the DeviceDesc (most likely because
// the bus driver didn't supply us one. There's
// nothing more we can do.
//
goto CoPreInstallDone;
}
}
CoPreInstallDone:
//
// Save the device description buffer away.
//
InstallContext->DeviceDescBuffer = DeviceDescBuffer;
//
// Store the installer context in the context structure and
// request a post-processing callback.
//
Context->PrivateData = InstallContext;
return ERROR_DI_POSTPROCESSING_REQUIRED;
}
}
default :
//
// We should always be 'on the way in', since we never request
// postprocessing except for DIF_INSTALLDEVICE.
//
MYASSERT(!Context->PostProcessing);
return NO_ERROR;
}
}
DWORD
VolumeClassInstaller(
IN DI_FUNCTION InstallFunction,
IN HDEVINFO DeviceInfoSet,
IN PSP_DEVINFO_DATA DeviceInfoData OPTIONAL
)
/*++
Routine Description:
This routine is the class installer function for storage volumes.
Arguments:
InstallFunction - Supplies the install function.
DeviceInfoSet - Supplies the device info set.
DeviceInfoData - Supplies the device info data.
Return Value:
If this function successfully completed the requested action, the return
value is NO_ERROR.
If the default behavior is to be performed for the requested action, the
return value is ERROR_DI_DO_DEFAULT.
If an error occurred while attempting to perform the requested action, a
Win32 error code is returned.
--*/
{
return ERROR_DI_DO_DEFAULT;
}
BOOLEAN
OverrideFriendlyNameForTape(
IN HDEVINFO DeviceInfoSet,
IN PSP_DEVINFO_DATA DeviceInfoData OPTIONAL
)
/*++
Routine Description:
This routine checks the device description, given in the INF, for
the tape being installed. If the device description is a generic
name (Tape drive), then we use the name generated from Inquiry
data in UI such as Device Manager. If the INF provides a specific
name, then we use that instead.
Arguments:
DeviceInfoSet - Supplies the device info set.
DeviceInfoData - Supplies the device info data.
Return Value:
TRUE : If the device description given in the INF should be
used as FriendlyName
FALSE : If the name generated from Inquiry data should be
used as FriendlyName instead of the name given in INF
*/
{
SP_DRVINFO_DETAIL_DATA drvDetData;
SP_DRVINFO_DATA drvData;
DWORD dwSize;
TCHAR szSection[LINE_LEN];
HINF hInf;
INFCONTEXT infContext;
BOOLEAN OverrideFriendlyName = FALSE;
TCHAR szSectionName[LINE_LEN];
ZeroMemory(&drvData, sizeof(SP_DRVINFO_DATA));
drvData.cbSize = sizeof(SP_DRVINFO_DATA);
if (!SetupDiGetSelectedDriver(DeviceInfoSet, DeviceInfoData, &drvData)) {
return FALSE;
}
ZeroMemory(&drvDetData, sizeof(SP_DRVINFO_DETAIL_DATA));
drvDetData.cbSize = sizeof(SP_DRVINFO_DETAIL_DATA);
if (!SetupDiGetDriverInfoDetail(DeviceInfoSet,
DeviceInfoData,
&drvData,
&drvDetData,
drvDetData.cbSize,
&dwSize) &&
GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
return FALSE;
}
hInf = SetupOpenInfFile(drvDetData.InfFileName,
NULL,
INF_STYLE_WIN4,
NULL);
if (hInf == INVALID_HANDLE_VALUE) {
return FALSE;
}
//
// Get the actual device install section name
//
ZeroMemory(szSectionName, sizeof(szSectionName));
SetupDiGetActualSectionToInstall(hInf,
drvDetData.SectionName,
szSectionName,
sizeof(szSectionName) / sizeof(TCHAR),
NULL,
NULL
);
if (SetupFindFirstLine(hInf, szSectionName,
TEXT("UseInfDeviceDesc"),
&infContext)) {
DWORD UseDeviceDesc = 0;
if ((SetupGetIntField(&infContext, 1, (PINT)&UseDeviceDesc)) &&
(UseDeviceDesc)) {
//
// Delete friendly name if it exists.
// Device Description will be used here on.
//
SetupDiSetDeviceRegistryProperty(DeviceInfoSet,
DeviceInfoData,
SPDRP_FRIENDLYNAME,
NULL,
0);
OverrideFriendlyName = TRUE;
}
}
if (OverrideFriendlyName) {
ChkPrintEx(("Will override friendly name\n"));
} else {
ChkPrintEx(("Will NOT override friendly name\n"));
}
SetupCloseInfFile(hInf);
return OverrideFriendlyName;
}
BOOLEAN
CopyKey(
HKEY SourceKey,
HKEY DestinationKey
)
{
DWORD index = 0;
DWORD numberOfKeys;
DWORD numberOfValues;
DWORD keyNameLength;
DWORD valueNameLength;
DWORD valueLength;
DWORD nameLength;
PTCHAR name = NULL;
PVOID data = NULL;
LONG status = ERROR_SUCCESS;
//
// Determine the largest name and data length of the values in this key.
//
status = RegQueryInfoKey(SourceKey,
NULL,
NULL,
NULL,
&numberOfKeys,
&keyNameLength,
NULL,
&numberOfValues,
&valueNameLength,
&valueLength,
NULL,
NULL);
if(status != ERROR_SUCCESS) {
ChkPrintEx(("Error %d getting info for key %#0x\n", status, SourceKey));
return FALSE;
}
//
// Determine the longer of the two name lengths, then account for the
// short lengths returned by the registry code (it leaves out the
// terminating NUL).
//
nameLength = max(valueNameLength, keyNameLength);
nameLength += 1;
//
// Allocate name and data buffers
//
name = MyMalloc(nameLength * sizeof(TCHAR));
if(name == NULL) {
return FALSE;
}
//
// there may not be any data to buffer.
//
if(valueLength != 0) {
data = MyMalloc(valueLength);
if(data == NULL) {
MyFree(name);
return FALSE;
}
}
//
// Enumerate each value in the SourceKey and copy it to the DestinationKey.
//
for(index = 0;
(index < numberOfValues) && (status != ERROR_NO_MORE_ITEMS);
index++) {
DWORD valueDataLength;
DWORD type;
valueNameLength = nameLength;
valueDataLength = valueLength;
//
// Read the value into the pre-allocated buffers.
//
status = RegEnumValue(SourceKey,
index,
name,
&valueNameLength,
NULL,
&type,
data,
&valueDataLength);
if(status != ERROR_SUCCESS) {
ChkPrintEx(("Error %d reading value %x\n", status, index));
continue;
}
//
// Now set this value in the destination key.
// If this fails there's not much we can do but continue on to the
// next value.
//
status = RegSetValueEx(DestinationKey,
name,
0,
type,
data,
valueDataLength);
}
//
// Free the data buffer.
//
MyFree(data);
data = NULL;
status = ERROR_SUCCESS;
//
// Now enumerate each key in the SourceKey, create the same key in the
// desination key, open a handle to each one and recurse.
//
for(index = 0;
(index < numberOfKeys) && (status != ERROR_NO_MORE_ITEMS);
index++) {
FILETIME lastWriteTime;
HKEY newSourceKey;
HKEY newDestinationKey;
keyNameLength = nameLength;
status = RegEnumKeyEx(SourceKey,
index,
name,
&keyNameLength,
NULL,
NULL,
NULL,
&lastWriteTime);
if(status != ERROR_SUCCESS) {
ChkPrintEx(("Error %d enumerating source key %x\n", status, index));
continue;
}
//
// Open the source subkey.
//
status = RegOpenKeyEx(SourceKey,
name,
0L,
KEY_READ,
&newSourceKey);
if(status != ERROR_SUCCESS) {
ChkPrintEx(("Error %d opening source key %x\n", status, index));
continue;
}
//
// Create the destination subkey.
//
status = RegCreateKeyEx(DestinationKey,
name,
0L,
NULL,
REG_OPTION_NON_VOLATILE,
KEY_WRITE,
NULL,
&newDestinationKey,
NULL);
if(status != ERROR_SUCCESS) {
ChkPrintEx(("Error %d creating dest key %x\n", status, index));
RegCloseKey(newSourceKey);
continue;
}
//
// Recursively copy this key.
//
CopyKey(newSourceKey, newDestinationKey);
RegCloseKey(newSourceKey);
RegCloseKey(newDestinationKey);
}
//
// Now free the name buffer.
//
MyFree(name);
return TRUE;
}
BOOLEAN
StorageCopyDeviceSettings(
IN HDEVINFO DeviceInfo,
IN PSP_DEVINFO_DATA DeviceInfoData,
IN HKEY DeviceEnumKey
)
{
PTCHAR hardwareIdList = NULL;
PTCHAR hardwareId = NULL;
DWORD requiredSize = 0;
HKEY settingsDatabaseKey = INVALID_HANDLE_VALUE;
BOOLEAN settingsCopied = FALSE;
DWORD status;
ASSERT(DeviceInfo != NULL);
ASSERT(DeviceInfoData != NULL);
//
// Open the device settings key.
//
status = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
STORAGE_DEVICE_SETTINGS_DATABASE,
0L,
KEY_READ,
&settingsDatabaseKey);
if(status != ERROR_SUCCESS) {
ChkPrintEx(("StorageCopyDeviceSettings: Error %d opening "
"settings database\n",
status));
return FALSE;
}
//
// get the hardware id's
//
if(SetupDiGetDeviceRegistryProperty(DeviceInfo,
DeviceInfoData,
SPDRP_HARDWAREID,
NULL,
NULL,
0,
&requiredSize) ||
(requiredSize == 0)) {
//
// That's odd.
//
ChkPrintEx(("StorageCopyDeviceSettings: no hardware ids available?\n"));
goto cleanup;
}
//
// requiredSize is bytes, not characters
//
hardwareIdList = MyMalloc(requiredSize);
if (hardwareIdList == NULL) {
ChkPrintEx(("StorageCopyDeviceSettings: Couldn't allocate %d bytes "
"for HWIDs\n", requiredSize));
goto cleanup;
}
if(!SetupDiGetDeviceRegistryProperty(DeviceInfo,
DeviceInfoData,
SPDRP_HARDWAREID,
NULL,
(PBYTE)hardwareIdList,
requiredSize,
NULL)) {
ChkPrintEx(("StorageCopyDeviceSettings: failed to get "
"device's hardware ids %x\n",
GetLastError()));
goto cleanup;
}
//
// Look in the device settings database for a matching hardware ID. When
// we find a match copy the contents of that key under the device's
// devnode key.
//
// The hardware IDs we get back from SetupDi are sorted from most exact
// to least exact so we're guaranteed to find the closest match first.
//
hardwareId = hardwareIdList;
while(hardwareId[0] != TCHAR_NULL) {
HKEY deviceSettingsKey;
LONG openStatus;
//
// Replace slashes with #'s so that it's compatible as a registry
// key name.
//
ReplaceSlashWithHash(hardwareId);
openStatus = RegOpenKeyEx(settingsDatabaseKey,
hardwareId,
0,
KEY_READ,
&deviceSettingsKey);
if (openStatus == ERROR_SUCCESS) {
//StorageReadSettings(specialTargetHandle, &settings);
CopyKey(deviceSettingsKey, DeviceEnumKey);
settingsCopied = TRUE;
RegCloseKey(deviceSettingsKey);
break;
}
// get to next null, for statement will advance past it
while (*hardwareId) {
hardwareId += 1;
}
//
// Skip the nul and go to the next tchar.
//
hardwareId += 1;
RegCloseKey(deviceSettingsKey);
} // end of loop for query'ing each id
cleanup:
ChkPrintEx(("StorageCopyDeviceSettings: Cleaning up...\n"));
if (settingsDatabaseKey != INVALID_HANDLE_VALUE) {
RegCloseKey(settingsDatabaseKey);
}
if (hardwareIdList != NULL) {
MyFree(hardwareIdList);
}
return settingsCopied;
}
VOID
StorageInstallCdrom(
IN HDEVINFO DeviceInfo,
IN PSP_DEVINFO_DATA DeviceInfoData,
IN PSTORAGE_COINSTALLER_CONTEXT InstallContext,
IN BOOLEAN PreInstall
)
{
CDVD_CAPABILITIES_PAGE buffer;
PCDVD_CAPABILITIES_PAGE page = NULL;
BOOLEAN installRedbook = FALSE;
BOOLEAN installImapi = FALSE;
BOOLEAN needRestart = FALSE;
//
// If this is post-installation then get the device capabilities page and
// provide it to the update routines.
//
if(PreInstall == FALSE) {
if(StorageGetCDVDCapabilities(DeviceInfo, DeviceInfoData, &buffer)) {
page = &buffer;
}
}
//
// Check in the registry (or query the device) and determine if we should
// enable the redbook (digital audio playback) driver on this device.
//
// If redbook was already installed the first time through then there's no
// need to do this step
//
if((PreInstall == TRUE) ||
((InstallContext->CdRom.RedbookInstalled == FALSE) && (page != NULL))) {
if ((InstallContext->DeviceEnumKey) != INVALID_HANDLE_VALUE) {
installRedbook = StorageUpdateRedbookSettings(
DeviceInfo,
DeviceInfoData,
InstallContext->DeviceEnumKey,
page);
}
}
//
// Check in the registry (or query the device) and determine if we should
// enable the IMAPI driver on this device.
//
// If imapi was already installed the first time through then there's no
// need to do this step
//
if((PreInstall == TRUE) ||
((InstallContext->CdRom.ImapiInstalled == FALSE) && (page != NULL))) {
if ((InstallContext->DeviceEnumKey) != INVALID_HANDLE_VALUE) {
installImapi = StorageUpdateImapiSettings(DeviceInfo,
DeviceInfoData,
InstallContext->DeviceEnumKey,
page);
}
}
//
// If this is a pre-install pass then we can just add the services. If it's
// not then first check to see that we don't do anything here that we
// already did in the pre-install pass.
//
if(PreInstall) {
//
// Save away what we've done during the pre-install pass.
//
InstallContext->CdRom.RedbookInstalled = installRedbook;
InstallContext->CdRom.ImapiInstalled = installImapi;
}
//
// If we're supposed to enable IMAPI then do so by enabling the IMAPI
// service and including it in the list of lower filters for this device.
//
if(installRedbook) {
ChkPrintEx(("StorageInstallCdrom: Installing Upperfilter: REDBOOK\n"));
StorageInstallFilter(DeviceInfo,
DeviceInfoData,
REDBOOK_SERVICE_NAME,
SPDRP_UPPERFILTERS);
needRestart = TRUE;
}
if(installImapi) {
ChkPrintEx(("StorageInstallCdrom: Installing Lowerfilter: IMAPI\n"));
StorageInstallFilter(DeviceInfo,
DeviceInfoData,
IMAPI_SERVICE_NAME,
SPDRP_LOWERFILTERS);
needRestart = TRUE;
}
if((PreInstall == FALSE) && (needRestart == TRUE)) {
SP_PROPCHANGE_PARAMS propChange;
//
// The device is all set but we to indicate that a property change
// has occurred. Set the propchange_pending flag which should cause
// a DIF_PROPERTYCHANGE command to get sent through, which we'll use
// to restart the device.
//
ChkPrintEx(("StorageInstallCdrom: Calling class installer with DIF_PROPERTYCHANGE\n"));
propChange.ClassInstallHeader.cbSize = sizeof(SP_CLASSINSTALL_HEADER);
propChange.ClassInstallHeader.InstallFunction = DIF_PROPERTYCHANGE;
propChange.StateChange = DICS_PROPCHANGE;
propChange.Scope = DICS_FLAG_GLOBAL;
propChange.HwProfile = 0;
SetupDiSetClassInstallParams(DeviceInfo,
DeviceInfoData,
&propChange.ClassInstallHeader,
sizeof(SP_PROPCHANGE_PARAMS));
SetupDiCallClassInstaller(DIF_PROPERTYCHANGE,
DeviceInfo,
DeviceInfoData);
}
return;
}
BOOLEAN
StorageUpdateRedbookSettings(
IN HDEVINFO DeviceInfo,
IN PSP_DEVINFO_DATA DeviceInfoData,
IN HKEY DeviceEnumKey,
IN PCDVD_CAPABILITIES_PAGE CapabilitiesPage OPTIONAL
)
{
STORAGE_REDBOOK_SETTINGS settings;
HKEY redbookKey;
DWORD setFromDevice = FALSE;
DWORD disposition;
DWORD status;
settings.CDDASupported = FALSE;
settings.CDDAAccurate = FALSE;
settings.ReadSizesSupported = 0;
//
// Open the digital audio subkey of the device's enum key. If the device
// hasn't been started yet then we won't create the key. Otherwise we
// will create it and populate it.
//
if(ARGUMENT_PRESENT(CapabilitiesPage)) {
status = RegCreateKeyEx(DeviceEnumKey,
REDBOOK_SETTINGS_KEY,
0L,
NULL,
REG_OPTION_NON_VOLATILE,
KEY_READ | KEY_WRITE,
NULL,
&redbookKey,
&disposition
);
} else {
status = RegOpenKeyEx(DeviceEnumKey,
REDBOOK_SETTINGS_KEY,
0L,
KEY_READ | KEY_WRITE,
&redbookKey);
disposition = REG_OPENED_EXISTING_KEY;
}
if(status != ERROR_SUCCESS) {
ChkPrintEx(("StorageUpdateRedbookSettings: couldn't open redbook key "
"- %d\n", status));
return FALSE;
}
if(disposition == REG_OPENED_EXISTING_KEY) {
//
// Read the redbook settings out of the registry (if there are any) and
// see if they make any sense.
//
StorageReadRedbookSettings(redbookKey, &settings);
} else {
//
// Since the DigitalAudio key didn't exist nothing could be set. Check
// with the device to see what it supports.
//
MYASSERT(CapabilitiesPage != NULL);
settings.CDDASupported = CapabilitiesPage->CDDA;
settings.CDDAAccurate = CapabilitiesPage->CDDAAccurate;
//
// If the device isn't accurate then we can't be quite sure what the valid
// read sizes are (unless they were listed in the registry, but in that case
// we'd never have been here). Use zero, which is a special value for
// ReadSizesSupported which means "i don't know".
//
if((settings.CDDASupported == TRUE) &&
(settings.CDDAAccurate == TRUE)) {
settings.ReadSizesSupported = -1;
}
setFromDevice = TRUE;
}
//
// Write the updated (or derived) settings to the registry.
//
if (settings.CDDAAccurate) {
ChkPrintEx(("StorageUpdateRedbookSettings: "
"Cdrom fully supports CDDA.\n"));
} else if (settings.ReadSizesSupported) {
ChkPrintEx(("StorageUpdateRedbookSettings: "
"Cdrom only supports some sizes CDDA read.\n"));
ChkPrintEx(("StorageUpdateRedbookSettings: "
"These are in the bitmask: %x.\n",
settings.ReadSizesSupported));
} else if (settings.CDDASupported) {
ChkPrintEx(("StorageUpdateRedbookSettings: "
"Cdrom only supports some sizes CDDA read.\n"));
ChkPrintEx(("StorageUpdateRedbookSettings: "
"There is no data on which sizes (if any) "
"are accurate\n"));
} else {
ChkPrintEx(("StorageUpdateRedbookSettings: "
"Cdrom does not support CDDA at all.\n"));
}
RegSetValueEx(redbookKey,
L"ReadSizesSupported",
0,
REG_DWORD,
(BYTE*)&settings.ReadSizesSupported,
sizeof(DWORD)
);
RegSetValueEx(redbookKey,
L"CDDASupported",
0,
REG_DWORD,
(BYTE*)&settings.CDDASupported,
sizeof(DWORD)
);
RegSetValueEx(redbookKey,
L"CDDAAccurate",
0,
REG_DWORD,
(BYTE*)&settings.CDDAAccurate,
sizeof(DWORD)
);
RegSetValueEx(redbookKey,
L"SettingsFromDevice",
0,
REG_DWORD,
(LPBYTE) &(setFromDevice),
sizeof(DWORD)
);
RegCloseKey(redbookKey);
//
// if CDDA is supported, and one of:
// CDDA is accurate
// We have a mask of accurate settings
// We want to force install of redbook
// is true, then return TRUE.
// else, return FALSE.
//
if((settings.CDDASupported) &&
((settings.CDDAAccurate) ||
(settings.ReadSizesSupported != 0) ||
(StorageForceRedbookOnInaccurateDrives))) {
return TRUE;
} else {
return FALSE;
}
}
BOOLEAN
StorageUpdateImapiSettings(
IN HDEVINFO DeviceInfo,
IN PSP_DEVINFO_DATA DeviceInfoData,
IN HKEY DeviceEnumKey,
IN PCDVD_CAPABILITIES_PAGE CapabilitiesPage OPTIONAL
)
{
HKEY imapiKey;
DWORD disposition;
DWORD status;
//
// must be a DWORD so we can read into it from the registry.
//
DWORD enableImapi = FALSE;
//
// Open the imapi subkey of the device's enum key. If the device has been
// started then we'll create the key if it didn't already exist.
//
if(ARGUMENT_PRESENT(CapabilitiesPage)) {
status = RegCreateKeyEx(DeviceEnumKey,
IMAPI_SETTINGS_KEY,
0L,
NULL,
REG_OPTION_NON_VOLATILE,
KEY_READ | KEY_WRITE,
NULL,
&imapiKey,
&disposition
);
} else {
status = RegOpenKeyEx(DeviceEnumKey,
IMAPI_SETTINGS_KEY,
0L,
KEY_READ | KEY_WRITE,
&imapiKey
);
disposition = REG_OPENED_EXISTING_KEY;
}
if(status != ERROR_SUCCESS) {
ChkPrintEx(("StorageUpdateImapiSettings: couldn't open imapi key "
"- %d\n", status));
return FALSE;
}
if(disposition == REG_OPENED_EXISTING_KEY) {
DWORD type = REG_DWORD;
DWORD dataSize = sizeof(DWORD);
//
// Check to see if the EnableImapi value is set in this key. If it is
// then we'll be wanting to enable the filter driver.
//
status = RegQueryValueEx(imapiKey,
IMAPI_ENABLE_VALUE,
NULL,
&type,
(LPBYTE) &enableImapi,
&dataSize);
if (status == ERROR_SUCCESS) {
if(type != REG_DWORD) {
ChkPrintEx(("StorageUpdateImapiSettings: EnableImapi value is of "
"type %d\n", type));
enableImapi = FALSE;
}
RegCloseKey(imapiKey);
return (BOOLEAN) enableImapi ? TRUE : FALSE;
}
//
// else the key wasn't accessible. fall through to query the drive
//
}
if(ARGUMENT_PRESENT(CapabilitiesPage)) {
//
// query the drive to see if it supports mastering...
//
if((CapabilitiesPage->CDRWrite) || (CapabilitiesPage->CDEWrite)) {
enableImapi = TRUE;
}
}
if (enableImapi && DISABLE_IMAPI) {
ChkPrintEx(("StorageUpdateImapiSettings: Imapi would have "
"been enabled"));
enableImapi = FALSE;
}
if (enableImapi) {
//
// must add registry key listed above that suggests that
// imapi must be enabled by default.
//
status = RegSetValueEx(imapiKey,
IMAPI_ENABLE_VALUE,
0,
REG_DWORD,
(BYTE*)&enableImapi,
sizeof(DWORD)
);
//
// if this failed, then the device driver won't attach itself
// to the stack. in this case, we don't want to enable IMAPI
// after all...
//
if (status != ERROR_SUCCESS) {
enableImapi = FALSE;
}
}
RegCloseKey(imapiKey);
return (BOOLEAN) enableImapi ? TRUE : FALSE;
}
DWORD
StorageInstallFilter(
IN HDEVINFO DeviceInfo,
IN PSP_DEVINFO_DATA DeviceInfoData,
IN LPTSTR FilterName,
IN DWORD FilterType
)
{
DWORD status;
DWORD oldStartType;
//
// Check with the service controller and make sure that the IMAPI service
// is set to start at system time.
//
status = SetServiceStart(FilterName, SERVICE_SYSTEM_START, &oldStartType);
if(status != ERROR_SUCCESS) {
return status;
}
//
// Add the IMAPI filter to the list of lower device filters.
//
status = AddFilterDriver(DeviceInfo,
DeviceInfoData,
FilterName,
FilterType
);
if(status != ERROR_SUCCESS) {
//
// if it failed, and the service was previously disabled,
// re-disable the service.
//
if(oldStartType == SERVICE_DISABLED) {
SetServiceStart(FilterName, SERVICE_DISABLED, &oldStartType);
}
}
return status;
}
DWORD
SetServiceStart(
IN LPCTSTR ServiceName,
IN DWORD StartType,
OUT DWORD *OldStartType
)
{
SC_HANDLE serviceManager;
SC_HANDLE service;
DWORD status;
serviceManager = OpenSCManager(NULL, NULL, GENERIC_READ | GENERIC_WRITE);
if(serviceManager == NULL) {
return GetLastError();
}
service = OpenService(serviceManager,
ServiceName,
SERVICE_CHANGE_CONFIG | SERVICE_QUERY_CONFIG);
if(service == NULL) {
status = GetLastError();
CloseServiceHandle(serviceManager);
return status;
}
{
QUERY_SERVICE_CONFIG configBuffer;
LPQUERY_SERVICE_CONFIG config = &(configBuffer);
DWORD configSize;
BOOLEAN wasStarted;
//
// Retrieve the configuration so we can get the current service
// start value. We unfortuantely need to allocate memory for the
// entire service configuration - the fine QueryServiceConfig API
// doesn't give back partial data.
//
memset(config, 0, sizeof(QUERY_SERVICE_CONFIG));
configSize = sizeof(QUERY_SERVICE_CONFIG);
//
// Determine the number of bytes needed for the configuration.
//
QueryServiceConfig(service, config, 0, &configSize);
status = GetLastError();
if(status != ERROR_INSUFFICIENT_BUFFER) {
CloseServiceHandle(service);
CloseServiceHandle(serviceManager);
return status;
}
//
// Allocate the appropriately sized config buffer.
//
config = MyMalloc(configSize);
if(config == NULL) {
CloseServiceHandle(service);
CloseServiceHandle(serviceManager);
return ERROR_NOT_ENOUGH_MEMORY;
}
if(!QueryServiceConfig(service, config, configSize, &configSize)) {
status = GetLastError();
CloseServiceHandle(service);
CloseServiceHandle(serviceManager);
MyFree(config);
return status;
}
//
// Record what the old start type was so that the caller can disable
// the service again if filter-installation fails.
//
*OldStartType = config->dwStartType;
//
// If the start type doesn't need to be changed then bail out now.
//
if(config->dwStartType == StartType) {
CloseServiceHandle(service);
CloseServiceHandle(serviceManager);
MyFree(config);
return ERROR_SUCCESS;
}
//
// Now write the configuration back to the service.
//
if(ChangeServiceConfig(service,
SERVICE_NO_CHANGE,
StartType,
SERVICE_NO_CHANGE,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL) == FALSE) {
status = GetLastError();
} else {
status = ERROR_SUCCESS;
}
CloseServiceHandle(service);
CloseServiceHandle(serviceManager);
MyFree(config);
}
return status;
}
DWORD
AddFilterDriver(
IN HDEVINFO DeviceInfo,
IN PSP_DEVINFO_DATA DeviceInfoData,
IN LPTSTR ServiceName,
IN DWORD FilterType
)
{
DWORD serviceNameLength = (_tcslen(ServiceName) + 2) * sizeof(TCHAR);
LPTSTR filterList = NULL;
DWORD filterListSize = 0;
DWORD type;
DWORD status;
ASSERT((FilterType == SPDRP_LOWERFILTERS) ||
(FilterType == SPDRP_UPPERFILTERS));
//
// Query to find out the property size. If it comes back zero then
// we'll just try to write the property in there.
//
SetupDiGetDeviceRegistryProperty(DeviceInfo,
DeviceInfoData,
FilterType,
&type,
NULL,
0L,
&filterListSize);
status = GetLastError();
if((status != ERROR_INVALID_DATA) &&
(status != ERROR_INSUFFICIENT_BUFFER)) {
//
// If this succeeded with no buffer provided then there's something
// very odd going on.
//
ChkPrintEx(("Unable to get filter list: %x\n", status));
ASSERT(status != ERROR_SUCCESS);
return status;
}
//
// This error code appears to be returned if the property isn't set in the
// devnode. In that event make sure propertySize is cleared.
//
if(status == ERROR_INVALID_DATA) {
filterListSize = 0;
} else if(type != REG_MULTI_SZ) {
return ERROR_INVALID_DATA;
}
//
// If the property size is zero then there's nothing to query. Likewise,
// if it's equal to the size of two nul characters.
//
if(filterListSize >= (sizeof(TCHAR_NULL) * 2)) {
DWORD tmp;
LPTSTR listEnd;
//
// increase the filter list buffer size so that it can hold our
// addition. Make sure to take into account the extra nul character
// already in the existing list.
//
filterListSize += serviceNameLength - sizeof(TCHAR);
filterList = MyMalloc(filterListSize);
if(filterList == NULL) {
return ERROR_NOT_ENOUGH_MEMORY;
}
memset(filterList, 0, filterListSize);
//
// Query the registry information again.
//
if(!SetupDiGetDeviceRegistryProperty(DeviceInfo,
DeviceInfoData,
FilterType,
&type,
(PBYTE) filterList,
filterListSize,
&tmp)) {
status = GetLastError();
MyFree(filterList);
return status;
}
if(type != REG_MULTI_SZ) {
MyFree(filterList);
return ERROR_INVALID_DATA;
}
//
// Compute the end of the filter list and copy the imapi filters
// there.
//
listEnd = filterList;
listEnd += tmp / sizeof(TCHAR);
listEnd -= 1;
memset(listEnd, 0, serviceNameLength);
memcpy(listEnd, ServiceName, serviceNameLength - sizeof(TCHAR_NULL));
} else {
filterList = MyMalloc(serviceNameLength);
if(filterList == NULL) {
return ERROR_NOT_ENOUGH_MEMORY;
}
memset(filterList, 0, serviceNameLength);
memcpy(filterList, ServiceName, serviceNameLength - sizeof(TCHAR_NULL));
filterListSize = serviceNameLength;
}
if(!SetupDiSetDeviceRegistryProperty(DeviceInfo,
DeviceInfoData,
FilterType,
(PBYTE) filterList,
filterListSize)) {
status = GetLastError();
} else {
status = ERROR_SUCCESS;
}
MyFree(filterList);
return status;
}
/*++
Routine Description:
NOTE: we default to RETRY==TRUE except for known error classes
This is based upon classpnp's InterpretSenseInfo().
Arguments:
Return Value:
--*/
VOID
StorageInterpretSenseInfo(
IN PSENSE_DATA SenseData,
IN UCHAR SenseDataSize,
OUT PDWORD ErrorValue, // from WinError.h
OUT PBOOLEAN SuggestRetry OPTIONAL,
OUT PDWORD SuggestRetryDelay OPTIONAL // in 1/10 second intervals
)
{
DWORD error;
DWORD retryDelay;
BOOLEAN retry;
UCHAR senseKey;
UCHAR asc;
UCHAR ascq;
if (SenseDataSize == 0) {
retry = FALSE;
retryDelay = 0;
error = ERROR_IO_DEVICE;
goto SetAndExit;
}
//
// default to suggesting a retry in 1/10 of a second,
// with a status of ERROR_IO_DEVICE.
//
retry = TRUE;
retryDelay = 1;
error = ERROR_IO_DEVICE;
//
// if we can't even see the sense key, just return.
// can't use bitfields in these macros, so use next field
// instead of RTL_SIZEOF_THROUGH_FIELD
//
if (SenseDataSize < FIELD_OFFSET(SENSE_DATA, Information)) {
goto SetAndExit;
}
senseKey = SenseData->SenseKey;
//
// if the device succeeded the request, return success.
//
if (senseKey == 0) {
retry = FALSE;
retryDelay = 0;
error = ERROR_SUCCESS;
goto SetAndExit;
}
{ // set the size to what's actually useful.
UCHAR validLength;
// figure out what we could have gotten with a large sense buffer
if (SenseDataSize <
RTL_SIZEOF_THROUGH_FIELD(SENSE_DATA, AdditionalSenseLength)) {
validLength = SenseDataSize;
} else {
validLength =
RTL_SIZEOF_THROUGH_FIELD(SENSE_DATA, AdditionalSenseLength);
validLength += SenseData->AdditionalSenseLength;
}
// use the smaller of the two values.
SenseDataSize = min(SenseDataSize, validLength);
}
if (SenseDataSize <
RTL_SIZEOF_THROUGH_FIELD(SENSE_DATA, AdditionalSenseCode)) {
asc = SCSI_ADSENSE_NO_SENSE;
} else {
asc = SenseData->AdditionalSenseCode;
}
if (SenseDataSize <
RTL_SIZEOF_THROUGH_FIELD(SENSE_DATA, AdditionalSenseCodeQualifier)) {
ascq = SCSI_SENSEQ_CAUSE_NOT_REPORTABLE; // 0x00
} else {
ascq = SenseData->AdditionalSenseCodeQualifier;
}
//
// interpret :P
//
switch (senseKey & 0xf) {
case SCSI_SENSE_RECOVERED_ERROR: { // 0x01
if (SenseData->IncorrectLength) {
error = ERROR_INVALID_BLOCK_LENGTH;
} else {
error = ERROR_SUCCESS;
}
retry = FALSE;
break;
} // end SCSI_SENSE_RECOVERED_ERROR
case SCSI_SENSE_NOT_READY: { // 0x02
error = ERROR_NOT_READY;
switch (asc) {
case SCSI_ADSENSE_LUN_NOT_READY: {
switch (ascq) {
case SCSI_SENSEQ_BECOMING_READY:
case SCSI_SENSEQ_OPERATION_IN_PROGRESS: {
retryDelay = PASS_THROUGH_NOT_READY_RETRY_INTERVAL;
break;
}
case SCSI_SENSEQ_CAUSE_NOT_REPORTABLE:
case SCSI_SENSEQ_FORMAT_IN_PROGRESS:
case SCSI_SENSEQ_LONG_WRITE_IN_PROGRESS: {
retry = FALSE;
break;
}
case SCSI_SENSEQ_MANUAL_INTERVENTION_REQUIRED: {
retry = FALSE;
break;
}
} // end switch (senseBuffer->AdditionalSenseCodeQualifier)
break;
}
case SCSI_ADSENSE_NO_MEDIA_IN_DEVICE: {
error = ERROR_NOT_READY;
retry = FALSE;
break;
}
} // end switch (senseBuffer->AdditionalSenseCode)
break;
} // end SCSI_SENSE_NOT_READY
case SCSI_SENSE_MEDIUM_ERROR: { // 0x03
error = ERROR_CRC;
retry = FALSE;
//
// Check if this error is due to unknown format
//
if (asc == SCSI_ADSENSE_INVALID_MEDIA) {
switch (ascq) {
case SCSI_SENSEQ_UNKNOWN_FORMAT: {
error = ERROR_UNRECOGNIZED_MEDIA;
break;
}
case SCSI_SENSEQ_CLEANING_CARTRIDGE_INSTALLED: {
error = ERROR_UNRECOGNIZED_MEDIA;
//error = ERROR_CLEANER_CARTRIDGE_INSTALLED;
break;
}
} // end switch AdditionalSenseCodeQualifier
} // end SCSI_ADSENSE_INVALID_MEDIA
break;
} // end SCSI_SENSE_MEDIUM_ERROR
case SCSI_SENSE_ILLEGAL_REQUEST: { // 0x05
error = ERROR_INVALID_FUNCTION;
retry = FALSE;
switch (asc) {
case SCSI_ADSENSE_ILLEGAL_BLOCK: {
error = ERROR_SECTOR_NOT_FOUND;
break;
}
case SCSI_ADSENSE_INVALID_LUN: {
error = ERROR_FILE_NOT_FOUND;
break;
}
case SCSI_ADSENSE_COPY_PROTECTION_FAILURE: {
error = ERROR_FILE_ENCRYPTED;
//error = ERROR_SPT_LIB_COPY_PROTECTION_FAILURE;
switch (ascq) {
case SCSI_SENSEQ_AUTHENTICATION_FAILURE:
//error = ERROR_SPT_LIB_AUTHENTICATION_FAILURE;
break;
case SCSI_SENSEQ_KEY_NOT_PRESENT:
//error = ERROR_SPT_LIB_KEY_NOT_PRESENT;
break;
case SCSI_SENSEQ_KEY_NOT_ESTABLISHED:
//error = ERROR_SPT_LIB_KEY_NOT_ESTABLISHED;
break;
case SCSI_SENSEQ_READ_OF_SCRAMBLED_SECTOR_WITHOUT_AUTHENTICATION:
//error = ERROR_SPT_LIB_SCRAMBLED_SECTOR;
break;
case SCSI_SENSEQ_MEDIA_CODE_MISMATCHED_TO_LOGICAL_UNIT:
//error = ERROR_SPT_LIB_REGION_MISMATCH;
break;
case SCSI_SENSEQ_LOGICAL_UNIT_RESET_COUNT_ERROR:
//error = ERROR_SPT_LIB_RESETS_EXHAUSTED;
break;
} // end switch of ASCQ for COPY_PROTECTION_FAILURE
break;
}
} // end switch (senseBuffer->AdditionalSenseCode)
break;
} // end SCSI_SENSE_ILLEGAL_REQUEST
case SCSI_SENSE_DATA_PROTECT: { // 0x07
error = ERROR_WRITE_PROTECT;
retry = FALSE;
break;
} // end SCSI_SENSE_DATA_PROTECT
case SCSI_SENSE_BLANK_CHECK: { // 0x08
error = ERROR_NO_DATA_DETECTED;
break;
} // end SCSI_SENSE_BLANK_CHECK
case SCSI_SENSE_NO_SENSE: { // 0x00
if (SenseData->IncorrectLength) {
error = ERROR_INVALID_BLOCK_LENGTH;
retry = FALSE;
} else {
error = ERROR_IO_DEVICE;
}
break;
} // end SCSI_SENSE_NO_SENSE
case SCSI_SENSE_HARDWARE_ERROR: // 0x04
case SCSI_SENSE_UNIT_ATTENTION: // 0x06
case SCSI_SENSE_UNIQUE: // 0x09
case SCSI_SENSE_COPY_ABORTED: // 0x0A
case SCSI_SENSE_ABORTED_COMMAND: // 0x0B
case SCSI_SENSE_EQUAL: // 0x0C
case SCSI_SENSE_VOL_OVERFLOW: // 0x0D
case SCSI_SENSE_MISCOMPARE: // 0x0E
case SCSI_SENSE_RESERVED: // 0x0F
default: {
error = ERROR_IO_DEVICE;
break;
}
} // end switch(SenseKey)
SetAndExit:
if (ARGUMENT_PRESENT(SuggestRetry)) {
*SuggestRetry = retry;
}
if (ARGUMENT_PRESENT(SuggestRetryDelay)) {
*SuggestRetryDelay = retryDelay;
}
*ErrorValue = error;
return;
}