/*++ Copyright (C) Microsoft Corporation, 1996 - 1999 Module Name: devcaps.c Abstract: This module contains the implementation for the Microsoft Biometric Device Library Environment: Kernel mode only. Notes: Revision History: - Created December 2002 by Reid Kuhn --*/ #include #include #include #include #include #include #include "bdlint.h" #define PRODUCT_NOT_REQUESTED 0 #define PRODUCT_HANDLE_REQUESTED 1 #define PRODUCT_BLOCK_REQUESTED 2 NTSTATUS BDLRegisteredCancelGetNotificationIRP ( IN PDEVICE_OBJECT pDeviceObject, IN PIRP pIrp ); // // Supporting functions for checking ID's // // BOOLEAN BDLCheckComponentId ( IN PBDL_INTERNAL_DEVICE_EXTENSION pBDLExtension, IN ULONG ComponentId, OUT ULONG *pComponentIndex ) { ULONG i; for (i = 0; i < pBDLExtension->DeviceCapabilities.NumComponents; i++) { if (pBDLExtension->DeviceCapabilities.rgComponents[i].ComponentId == ComponentId) { break; } } if (i >= pBDLExtension->DeviceCapabilities.NumComponents) { return (FALSE); } *pComponentIndex = i; return (TRUE); } BOOLEAN BDLCheckChannelId ( IN PBDL_INTERNAL_DEVICE_EXTENSION pBDLExtension, IN ULONG ComponentIndex, IN ULONG ChannelId, OUT ULONG *pChannelIndex ) { ULONG i; for (i = 0; i < pBDLExtension->DeviceCapabilities.rgComponents[ComponentIndex].NumChannels; i++) { if (pBDLExtension->DeviceCapabilities.rgComponents[ComponentIndex].rgChannels[i].ChannelId == ChannelId) { break; } } if (i >= pBDLExtension->DeviceCapabilities.rgComponents[ComponentIndex].NumChannels) { return (FALSE); } *pChannelIndex = i; return (TRUE); } BOOLEAN BDLCheckControlIdInArray ( IN BDL_CONTROL *rgControls, IN ULONG NumControls, IN ULONG ControlId, OUT BDL_CONTROL **ppBDLControl ) { ULONG i; for (i = 0; i < NumControls; i++) { if (rgControls[i].ControlId == ControlId) { break; } } if (i >= NumControls) { return (FALSE); } *ppBDLControl = &(rgControls[i]); return (TRUE); } BOOLEAN BDLCheckControlId ( IN PBDL_INTERNAL_DEVICE_EXTENSION pBDLExtension, IN ULONG ComponentId, IN ULONG ChannelId, IN ULONG ControlId, OUT BDL_CONTROL **ppBDLControl ) { ULONG i, j; *ppBDLControl = NULL; // // If ComponentId is 0 then it is a device level control // if (ComponentId == 0) { // // Check device level control ID // if (BDLCheckControlIdInArray( pBDLExtension->DeviceCapabilities.rgControls, pBDLExtension->DeviceCapabilities.NumControls, ControlId, ppBDLControl) == FALSE) { BDLDebug( BDL_DEBUG_ERROR, ("%s %s: BDL!BDLCheckControlId: Bad Device level ControlId\n", __DATE__, __TIME__)) return (FALSE); } } else { // // Check the ComponentId // if (BDLCheckComponentId(pBDLExtension, ComponentId, &i) == FALSE) { BDLDebug( BDL_DEBUG_ERROR, ("%s %s: BDL!BDLCheckControlId: Bad ComponentId\n", __DATE__, __TIME__)) return (FALSE); } if (ChannelId == 0) { // // Check Component level control ID // if (BDLCheckControlIdInArray( pBDLExtension->DeviceCapabilities.rgComponents[i].rgControls, pBDLExtension->DeviceCapabilities.rgComponents[i].NumControls, ControlId, ppBDLControl) == FALSE) { BDLDebug( BDL_DEBUG_ERROR, ("%s %s: BDL!BDLCheckControlId: Bad Component level ControlId\n", __DATE__, __TIME__)) return (FALSE); } } else { // // Check channel ID // if (BDLCheckChannelId(pBDLExtension, i, ChannelId, &j) == FALSE) { BDLDebug( BDL_DEBUG_ERROR, ("%s %s: BDL!BDLCheckControlId: Bad ChannelId\n", __DATE__, __TIME__)) return (FALSE); } // // Check channel level control ID // if (BDLCheckControlIdInArray( pBDLExtension->DeviceCapabilities.rgComponents[i].rgChannels[j].rgControls, pBDLExtension->DeviceCapabilities.rgComponents[i].rgChannels[j].NumControls, ControlId, ppBDLControl) == FALSE) { BDLDebug( BDL_DEBUG_ERROR, ("%s %s: BDL!BDLCheckControlId: Bad channel level ControlId\n", __DATE__, __TIME__)) return (FALSE); } } } return (TRUE); } NTSTATUS BDLIOCTL_Startup ( IN PBDL_INTERNAL_DEVICE_EXTENSION pBDLExtension, IN ULONG InpuBufferLength, IN ULONG OutputBufferLength, IN PVOID pBuffer, OUT ULONG *pOutputBufferUsed ) { NTSTATUS status = STATUS_SUCCESS; BDLDebug( BDL_DEBUG_TRACE, ("%s %s: BDL!BDLIOCTL_Startup: Enter\n", __DATE__, __TIME__)) // // Call the BDD // status = pBDLExtension->pDriverExtension->bdsiFunctions.pfbdsiStartup( &(pBDLExtension->BdlExtenstion)); if (status != STATUS_SUCCESS) { BDLDebug( BDL_DEBUG_ERROR, ("%s %s: BDL!BDLIOCTL_Startup: pfbdsiStartup failed with %lx\n", __DATE__, __TIME__, status)) } // // Set the number of bytes used // *pOutputBufferUsed = 0; BDLDebug( BDL_DEBUG_TRACE, ("%s %s: BDL!BDLIOCTL_Startup: Leave\n", __DATE__, __TIME__)) return (status); } NTSTATUS BDLIOCTL_Shutdown ( IN PBDL_INTERNAL_DEVICE_EXTENSION pBDLExtension, IN ULONG InpuBufferLength, IN ULONG OutputBufferLength, IN PVOID pBuffer, OUT ULONG *pOutputBufferUsed ) { NTSTATUS status = STATUS_SUCCESS; BDLDebug( BDL_DEBUG_TRACE, ("%s %s: BDL!BDLIOCTL_Shutdown: Enter\n", __DATE__, __TIME__)) // // Call the BDD // status = pBDLExtension->pDriverExtension->bdsiFunctions.pfbdsiShutdown( &(pBDLExtension->BdlExtenstion)); if (status != STATUS_SUCCESS) { BDLDebug( BDL_DEBUG_ERROR, ("%s %s: BDL!BDLIOCTL_Shutdown: pfbdsiShutdown failed with %lx\n", __DATE__, __TIME__, status)) } // // Set the number of bytes used // *pOutputBufferUsed = 0; BDLDebug( BDL_DEBUG_TRACE, ("%s %s: BDL!BDLIOCTL_Shutdown: Leave\n", __DATE__, __TIME__)) return (status); } NTSTATUS BDLIOCTL_GetDeviceInfo ( IN PBDL_INTERNAL_DEVICE_EXTENSION pBDLExtension, IN ULONG InpuBufferLength, IN ULONG OutputBufferLength, IN PVOID pBuffer, OUT ULONG *pOutputBufferUsed ) { NTSTATUS status = STATUS_SUCCESS; ULONG RequiredOutputSize = 0; PUCHAR pv = pBuffer; BDLDebug( BDL_DEBUG_TRACE, ("%s %s: BDL!BDLIOCTL_GetDeviceInfo: Enter\n", __DATE__, __TIME__)) // // Make sure there is enough space for the return buffer // RequiredOutputSize = SIZEOF_GETDEVICEINFO_OUTPUTBUFFER; if (RequiredOutputSize > OutputBufferLength) { BDLDebug( BDL_DEBUG_ERROR, ("%s %s: BDL!BDLIOCTL_GetDeviceInfo: Output buffer is too small\n", __DATE__, __TIME__)) status = STATUS_BUFFER_TOO_SMALL; goto Return; } // // Write the device info to the output buffer // pv = pBuffer; RtlCopyMemory( pv, &(pBDLExtension->wszSerialNumber[0]), sizeof(pBDLExtension->wszSerialNumber)); pv += sizeof(pBDLExtension->wszSerialNumber); *((ULONG *) pv) = pBDLExtension->HWVersionMajor; pv += sizeof(ULONG); *((ULONG *) pv) = pBDLExtension->HWVersionMinor; pv += sizeof(ULONG); *((ULONG *) pv) = pBDLExtension->HWBuildNumber; pv += sizeof(ULONG); *((ULONG *) pv) = pBDLExtension->BDDVersionMajor; pv += sizeof(ULONG); *((ULONG *) pv) = pBDLExtension->BDDVersionMinor; pv += sizeof(ULONG); *((ULONG *) pv) = pBDLExtension->BDDBuildNumber; // // Set the number of bytes used // *pOutputBufferUsed = RequiredOutputSize; Return: BDLDebug( BDL_DEBUG_TRACE, ("%s %s: BDL!BDLIOCTL_GetDeviceInfo: Leave\n", __DATE__, __TIME__)) return (status); } NTSTATUS BDLIOCTL_DoChannel ( IN PBDL_INTERNAL_DEVICE_EXTENSION pBDLExtension, IN ULONG InpuBufferLength, IN ULONG OutputBufferLength, IN PVOID pBuffer, OUT ULONG *pOutputBufferUsed ) { NTSTATUS status = STATUS_SUCCESS; ULONG NumProducts = 0; ULONG NumSourceLists = 0; ULONG NumSources = 0; PUCHAR pv = pBuffer; BDDI_PARAMS_DOCHANNEL bddiDoChannelParams; ULONG i, j, x, y; ULONG ProductCreationType; ULONG RequiredInputSize = 0; ULONG RequiredOutputSize = 0; HANDLE hCancelEvent = NULL; KIRQL irql; BOOLEAN fHandleListLocked = FALSE; BDDI_PARAMS_CLOSEHANDLE bddiCloseHandleParams; BDLDebug( BDL_DEBUG_TRACE, ("%s %s: BDL!BDLIOCTL_DoChannel: Enter\n", __DATE__, __TIME__)) // // Initialize the DoChannelParams struct // RtlZeroMemory(&bddiDoChannelParams, sizeof(bddiDoChannelParams)); bddiDoChannelParams.Size = sizeof(bddiDoChannelParams); // // Make sure the input buffer is at least the minimum size (see BDDIOCTL // spec for details) // RequiredInputSize = SIZEOF_DOCHANNEL_INPUTBUFFER; if (InpuBufferLength < RequiredInputSize) { BDLDebug( BDL_DEBUG_ERROR, ("%s %s: BDL!BDLIOCTL_DoChannel: Bad input buffer\n", __DATE__, __TIME__)) status = STATUS_INVALID_PARAMETER; goto ErrorReturn; } // // Get all of the minimum input parameters (put the ones that are used // in the DoChannel call directly into the DoChannelParams struct // bddiDoChannelParams.ComponentId = *((ULONG *) pv); pv += sizeof(ULONG); bddiDoChannelParams.ChannelId = *((ULONG *) pv); pv += sizeof(ULONG); hCancelEvent = *((HANDLE *) pv); pv += sizeof(HANDLE); bddiDoChannelParams.hStateData = *((BDD_DATA_HANDLE *) pv); pv += sizeof(BDD_DATA_HANDLE); NumProducts = *((ULONG *) pv); pv += sizeof(ULONG); NumSourceLists = *((ULONG *) pv); pv += sizeof(ULONG); // // Check the size of the input buffer to make sure it is large enough // so that we don't run off the end when getting the products array and // sources lists array // // Note that this only checks based on each source list being 0 length, // so we need to check again before getting each source list. // RequiredInputSize += (NumProducts * sizeof(ULONG)) + (NumSourceLists * sizeof(ULONG)); if (InpuBufferLength < RequiredInputSize) { BDLDebug( BDL_DEBUG_ERROR, ("%s %s: BDL!BDLIOCTL_DoChannel: Bad input buffer\n", __DATE__, __TIME__)) status = STATUS_INVALID_PARAMETER; goto ErrorReturn; } // // Check the size of the output buffer to make sure it is large enough // to accommodate the standard output + all the products // RequiredOutputSize = SIZEOF_DOCHANNEL_OUTPUTBUFFER + (sizeof(BDD_HANDLE) * NumProducts); if (OutputBufferLength < RequiredOutputSize) { BDLDebug( BDL_DEBUG_ERROR, ("%s %s: BDL!BDLIOCTL_DoChannel: Bad input buffer\n", __DATE__, __TIME__)) status = STATUS_BUFFER_TOO_SMALL; goto ErrorReturn; } // // Check the ComponentId and ChannelId // if (BDLCheckComponentId(pBDLExtension, bddiDoChannelParams.ComponentId, &i) == FALSE) { BDLDebug( BDL_DEBUG_ERROR, ("%s %s: BDL!BDLIOCTL_DoChannel: Bad ComponentId\n", __DATE__, __TIME__)) status = STATUS_INVALID_PARAMETER; goto ErrorReturn; } if (BDLCheckChannelId(pBDLExtension, i, bddiDoChannelParams.ChannelId, &j) == FALSE) { BDLDebug( BDL_DEBUG_ERROR, ("%s %s: BDL!BDLIOCTL_DoChannel: Bad ChannelId\n", __DATE__, __TIME__)) status = STATUS_INVALID_PARAMETER; goto ErrorReturn; } // // Check to make sure the NumProducts and NumSourceLists are correct // if (NumProducts != pBDLExtension->DeviceCapabilities.rgComponents[i].rgChannels[j].NumProducts) { BDLDebug( BDL_DEBUG_ERROR, ("%s %s: BDL!BDLIOCTL_DoChannel: Bad number of Source Lists\n", __DATE__, __TIME__)) status = STATUS_INVALID_PARAMETER; goto ErrorReturn; } if (NumSourceLists != pBDLExtension->DeviceCapabilities.rgComponents[i].rgChannels[j].NumSourceLists) { BDLDebug( BDL_DEBUG_ERROR, ("%s %s: BDL!BDLIOCTL_DoChannel: Bad number of Source Lists\n", __DATE__, __TIME__)) status = STATUS_INVALID_PARAMETER; goto ErrorReturn; } // // Allocate the space for the product pointer array then get each product // request type from the input block // bddiDoChannelParams.rgpProducts = ExAllocatePoolWithTag( PagedPool, sizeof(PBDDI_ITEM) * NumProducts, BDL_ULONG_TAG); RtlZeroMemory(bddiDoChannelParams.rgpProducts, sizeof(PBDDI_ITEM) * NumProducts); for (x = 0; x < NumProducts; x++) { ProductCreationType = *((ULONG *) pv); pv += sizeof(ULONG); switch (ProductCreationType) { case PRODUCT_NOT_REQUESTED: bddiDoChannelParams.rgpProducts[x] = NULL; break; case PRODUCT_HANDLE_REQUESTED: // // Make sure the channel supports handle type return // if (!(BIO_ITEMTYPE_HANDLE & pBDLExtension->DeviceCapabilities.rgComponents[i].rgChannels[j].rgProducts[x].Flags)) { BDLDebug( BDL_DEBUG_ERROR, ("%s %s: BDL!BDLIOCTL_DoChannel: Bad product type request\n", __DATE__, __TIME__)) status = STATUS_INVALID_PARAMETER; goto ErrorReturn; } bddiDoChannelParams.rgpProducts[x] = ExAllocatePoolWithTag( PagedPool, sizeof(BDDI_ITEM), BDL_ULONG_TAG); if (bddiDoChannelParams.rgpProducts[x] == NULL) { BDLDebug( BDL_DEBUG_ERROR, ("%s %s: BDL!BDLIOCTL_DoChannel:ExAllocatePoolWithTag failed\n", __DATE__, __TIME__)) status = STATUS_NO_MEMORY; goto ErrorReturn; } bddiDoChannelParams.rgpProducts[x]->Type = BIO_ITEMTYPE_HANDLE; bddiDoChannelParams.rgpProducts[x]->Data.Handle = NULL; break; case PRODUCT_BLOCK_REQUESTED: // // Make sure the channel supports handle type return // if (!(BIO_ITEMTYPE_BLOCK & pBDLExtension->DeviceCapabilities.rgComponents[i].rgChannels[j].rgProducts[x].Flags)) { BDLDebug( BDL_DEBUG_ERROR, ("%s %s: BDL!BDLIOCTL_DoChannel: Bad product type request\n", __DATE__, __TIME__)) status = STATUS_INVALID_PARAMETER; goto ErrorReturn; } bddiDoChannelParams.rgpProducts[x] = ExAllocatePoolWithTag( PagedPool, sizeof(BDDI_ITEM), BDL_ULONG_TAG); if (bddiDoChannelParams.rgpProducts[x] == NULL) { BDLDebug( BDL_DEBUG_ERROR, ("%s %s: BDL!BDLIOCTL_DoChannel:ExAllocatePoolWithTag failed\n", __DATE__, __TIME__)) status = STATUS_NO_MEMORY; goto ErrorReturn; } bddiDoChannelParams.rgpProducts[x]->Type = BIO_ITEMTYPE_BLOCK; bddiDoChannelParams.rgpProducts[x]->Data.Block.pBuffer = NULL; bddiDoChannelParams.rgpProducts[x]->Data.Block.cBuffer = 0; break; default: BDLDebug( BDL_DEBUG_ERROR, ("%s %s: BDL!BDLIOCTL_DoChannel: Bad Product Request\n", __DATE__, __TIME__)) status = STATUS_INVALID_PARAMETER; goto ErrorReturn; break; } } // // Allocate space for the source lists // bddiDoChannelParams.rgSourceLists = ExAllocatePoolWithTag( PagedPool, sizeof(BDDI_SOURCELIST) * NumSourceLists, BDL_ULONG_TAG); RtlZeroMemory(bddiDoChannelParams.rgSourceLists, sizeof(BDDI_SOURCELIST) * NumSourceLists); // // We are going to start messing with the handle list, so lock it // BDLLockHandleList(pBDLExtension, &irql); fHandleListLocked = TRUE; // // Get each source list from input buffer // for (x = 0; x < NumSourceLists; x++) { NumSources = *((ULONG *) pv); pv += sizeof(ULONG); // // Check the size of the input buffer to make sure it is large enough // so that we don't run off the end when getting this source lists // RequiredInputSize += NumSources * sizeof(BDD_HANDLE); if (InpuBufferLength < RequiredInputSize) { BDLDebug( BDL_DEBUG_ERROR, ("%s %s: BDL!BDLIOCTL_DoChannel: Bad input buffer\n", __DATE__, __TIME__)) status = STATUS_INVALID_PARAMETER; goto ErrorReturn; } // // Allocate the array of sources and then get each source in the list // bddiDoChannelParams.rgSourceLists[x].rgpSources = ExAllocatePoolWithTag( PagedPool, sizeof(PBDDI_ITEM) * NumSources, BDL_ULONG_TAG); if (bddiDoChannelParams.rgpProducts[x] == NULL) { BDLDebug( BDL_DEBUG_ERROR, ("%s %s: BDL!BDLIOCTL_DoChannel:ExAllocatePoolWithTag failed\n", __DATE__, __TIME__)) status = STATUS_NO_MEMORY; goto ErrorReturn; } bddiDoChannelParams.rgSourceLists[x].NumSources = NumSources; for (y = 0; y < NumSources; y++) { bddiDoChannelParams.rgSourceLists[x].rgpSources[y] = *((BDD_HANDLE *) pv); pv += sizeof(BDD_HANDLE); if (BDLValidateHandleIsInList( &(pBDLExtension->HandleList), bddiDoChannelParams.rgSourceLists[x].rgpSources[y]) == FALSE) { BDLDebug( BDL_DEBUG_ERROR, ("%s %s: BDL!BDLIOCTL_DoChannel: Bad input handle\n", __DATE__, __TIME__)) status = STATUS_INVALID_PARAMETER; goto ErrorReturn; } } } // // If there is a cancel event then get the kernel mode event pointer // from the user mode event handle // if (hCancelEvent != NULL) { status = ObReferenceObjectByHandle( hCancelEvent, EVENT_QUERY_STATE | EVENT_MODIFY_STATE, NULL, KernelMode, &(bddiDoChannelParams.CancelEvent), NULL); if (status != STATUS_SUCCESS) { BDLDebug( BDL_DEBUG_ERROR, ("%s %s: BDL!BDLIOCTL_DoChannel: ObReferenceObjectByHandle failed with %lx\n", __DATE__, __TIME__, status)) goto ErrorReturn; } } // // Call the BDD // status = pBDLExtension->pDriverExtension->bddiFunctions.pfbddiDoChannel( &(pBDLExtension->BdlExtenstion), &bddiDoChannelParams); if (status != STATUS_SUCCESS) { BDLDebug( BDL_DEBUG_ERROR, ("%s %s: BDL!BDLIOCTL_DoChannel: pfbddiDoChannel failed with %lx\n", __DATE__, __TIME__, status)) goto ErrorReturn; } // // Write the output data to the output buffer // pv = pBuffer; *((ULONG *) pv) = bddiDoChannelParams.BIOReturnCode; pv += sizeof(ULONG); *((BDD_DATA_HANDLE *) pv) = bddiDoChannelParams.hStateData; pv += sizeof(BDD_DATA_HANDLE); // // Add all the product handles to the output buffer and to the handle list // for (x = 0; x < NumProducts; x++) { *((BDD_HANDLE *) pv) = bddiDoChannelParams.rgpProducts[x]; pv += sizeof(BDD_HANDLE); if (bddiDoChannelParams.rgpProducts[x] != NULL) { status = BDLAddHandleToList( &(pBDLExtension->HandleList), bddiDoChannelParams.rgpProducts[x]); if (status != STATUS_SUCCESS) { // // Remove the handles that were already added to the handle list // for (y = 0; y < x; y++) { BDLRemoveHandleFromList( &(pBDLExtension->HandleList), bddiDoChannelParams.rgpProducts[y]); } BDLDebug( BDL_DEBUG_ERROR, ("%s %s: BDL!BDLIOCTL_DoChannel: BDLAddHandleToList failed with %lx\n", __DATE__, __TIME__, status)) goto ErrorReturn; } } } *pOutputBufferUsed = RequiredOutputSize; Return: if (fHandleListLocked == TRUE) { BDLReleaseHandleList(pBDLExtension, irql); } if (bddiDoChannelParams.rgpProducts != NULL) { ExFreePoolWithTag(bddiDoChannelParams.rgpProducts, BDL_ULONG_TAG); } if (bddiDoChannelParams.rgSourceLists != NULL) { for (x = 0; x < NumSourceLists; x++) { if (bddiDoChannelParams.rgSourceLists[x].rgpSources != NULL) { ExFreePoolWithTag(bddiDoChannelParams.rgSourceLists[x].rgpSources, BDL_ULONG_TAG); } } ExFreePoolWithTag(bddiDoChannelParams.rgSourceLists, BDL_ULONG_TAG); } if (bddiDoChannelParams.CancelEvent != NULL) { ObDereferenceObject(bddiDoChannelParams.CancelEvent); } BDLDebug( BDL_DEBUG_TRACE, ("%s %s: BDL!BDLIOCTL_DoChannel: Leave\n", __DATE__, __TIME__)) return (status); ErrorReturn: for (x = 0; x < NumProducts; x++) { if (bddiDoChannelParams.rgpProducts[x] != NULL) { if (bddiDoChannelParams.rgpProducts[x]->Type == BIO_ITEMTYPE_HANDLE) { if (bddiDoChannelParams.rgpProducts[x]->Data.Handle != NULL) { bddiCloseHandleParams.Size = sizeof(bddiCloseHandleParams); bddiCloseHandleParams.hData = bddiDoChannelParams.rgpProducts[x]->Data.Handle; pBDLExtension->pDriverExtension->bddiFunctions.pfbddiCloseHandle( &(pBDLExtension->BdlExtenstion), &bddiCloseHandleParams); } } else { if (bddiDoChannelParams.rgpProducts[x]->Data.Block.pBuffer != NULL) { bdliFree(bddiDoChannelParams.rgpProducts[x]->Data.Block.pBuffer); } } ExFreePoolWithTag(bddiDoChannelParams.rgpProducts[x], BDL_ULONG_TAG); } } goto Return; } NTSTATUS BDLIOCTL_GetControl ( IN PBDL_INTERNAL_DEVICE_EXTENSION pBDLExtension, IN ULONG InpuBufferLength, IN ULONG OutputBufferLength, IN PVOID pBuffer, OUT ULONG *pOutputBufferUsed ) { NTSTATUS status = STATUS_SUCCESS; ULONG RequiredOutputSize = 0; BDDI_PARAMS_GETCONTROL bddiGetControlParams; PUCHAR pv = pBuffer; ULONG i, j; BDL_CONTROL *pBDLControl = NULL; BDLDebug( BDL_DEBUG_TRACE, ("%s %s: BDL!BDLIOCTL_GetControl: Enter\n", __DATE__, __TIME__)) // // Make sure the input buffer is at least the minimum size (see BDDIOCTL // spec for details) // if (InpuBufferLength < SIZEOF_GETCONTROL_INPUTBUFFER) { BDLDebug( BDL_DEBUG_ERROR, ("%s %s: BDL!BDLIOCTL_GetControl: Bad input buffer size\n", __DATE__, __TIME__)) status = STATUS_INVALID_PARAMETER; goto Return; } // // Make sure there is enough space for the return buffer // RequiredOutputSize = SIZEOF_GETCONTROL_OUTPUTBUFFER; if (RequiredOutputSize > OutputBufferLength) { BDLDebug( BDL_DEBUG_ERROR, ("%s %s: BDL!BDLIOCTL_GetControl: Output buffer is too small\n", __DATE__, __TIME__)) status = STATUS_BUFFER_TOO_SMALL; goto Return; } // // Initialize the BDD struct // RtlZeroMemory(&bddiGetControlParams, sizeof(bddiGetControlParams)); bddiGetControlParams.Size = sizeof(bddiGetControlParams); // // Get the input parameters from the buffer // bddiGetControlParams.ComponentId = *((ULONG *) pv); pv += sizeof(ULONG); bddiGetControlParams.ChannelId = *((ULONG *) pv); pv += sizeof(ULONG); bddiGetControlParams.ControlId = *((ULONG *) pv); // // Check control ID // if (BDLCheckControlId( pBDLExtension, bddiGetControlParams.ComponentId, bddiGetControlParams.ChannelId, bddiGetControlParams.ControlId, &pBDLControl) == FALSE) { BDLDebug( BDL_DEBUG_ERROR, ("%s %s: BDL!BDLIOCTL_GetControl: Bad ControlId\n", __DATE__, __TIME__)) status = STATUS_INVALID_PARAMETER; goto Return; } // // Call the BDD // status = pBDLExtension->pDriverExtension->bddiFunctions.pfbddiGetControl( &(pBDLExtension->BdlExtenstion), &bddiGetControlParams); if (status != STATUS_SUCCESS) { BDLDebug( BDL_DEBUG_ERROR, ("%s %s: BDL!BDLIOCTL_GetControl: pfbddiGetControl failed with %lx\n", __DATE__, __TIME__, status)) goto Return; } // // Write the output info to the output buffer // pv = pBuffer; *((ULONG *) pv) = bddiGetControlParams.Value; pv += sizeof(ULONG); RtlCopyMemory(pv, bddiGetControlParams.wszString, sizeof(bddiGetControlParams.wszString)); // // Set the number of bytes used // *pOutputBufferUsed = RequiredOutputSize; Return: BDLDebug( BDL_DEBUG_TRACE, ("%s %s: BDL!BDLIOCTL_GetControl: Leave\n", __DATE__, __TIME__)) return (status); } NTSTATUS BDLIOCTL_SetControl ( IN PBDL_INTERNAL_DEVICE_EXTENSION pBDLExtension, IN ULONG InpuBufferLength, IN ULONG OutputBufferLength, IN PVOID pBuffer, OUT ULONG *pOutputBufferUsed ) { NTSTATUS status = STATUS_SUCCESS; BDDI_PARAMS_SETCONTROL bddiSetControlParams; PUCHAR pv = pBuffer; ULONG i, j; BDL_CONTROL *pBDLControl = NULL; BDLDebug( BDL_DEBUG_TRACE, ("%s %s: BDL!BDLIOCTL_SetControl: Enter\n", __DATE__, __TIME__)) // // Make sure the input buffer is at least the minimum size (see BDDIOCTL // spec for details) // if (InpuBufferLength < SIZEOF_SETCONTROL_INPUTBUFFER) { BDLDebug( BDL_DEBUG_ERROR, ("%s %s: BDL!BDLIOCTL_SetControl: Bad input buffer size\n", __DATE__, __TIME__)) status = STATUS_INVALID_PARAMETER; goto Return; } // // Initialize the BDD struct // RtlZeroMemory(&bddiSetControlParams, sizeof(bddiSetControlParams)); bddiSetControlParams.Size = sizeof(bddiSetControlParams); // // Get the input parameters from the buffer // bddiSetControlParams.ComponentId = *((ULONG *) pv); pv += sizeof(ULONG); bddiSetControlParams.ChannelId = *((ULONG *) pv); pv += sizeof(ULONG); bddiSetControlParams.ControlId = *((ULONG *) pv); pv += sizeof(ULONG); bddiSetControlParams.Value = *((ULONG *) pv); pv += sizeof(ULONG); RtlCopyMemory( &(bddiSetControlParams.wszString[0]), pv, sizeof(bddiSetControlParams.wszString)); // // Check control ID // if (BDLCheckControlId( pBDLExtension, bddiSetControlParams.ComponentId, bddiSetControlParams.ChannelId, bddiSetControlParams.ControlId, &pBDLControl) == FALSE) { BDLDebug( BDL_DEBUG_ERROR, ("%s %s: BDL!BDLIOCTL_SetControl: Bad ControlId\n", __DATE__, __TIME__)) status = STATUS_INVALID_PARAMETER; goto Return; } // // First make sure this isn't a read only value, then validate the // actual value // if (pBDLControl->Flags & BIO_CONTROL_FLAG_READONLY) { BDLDebug( BDL_DEBUG_ERROR, ("%s %s: BDL!BDLIOCTL_SetControl: trying to set a read only control\n", __DATE__, __TIME__)) status = STATUS_INVALID_PARAMETER; goto Return; } if ((bddiSetControlParams.Value < pBDLControl->NumericMinimum) || (bddiSetControlParams.Value > pBDLControl->NumericMaximum) || (((bddiSetControlParams.Value - pBDLControl->NumericMinimum) % pBDLControl->NumericDivisor) != 0 )) { BDLDebug( BDL_DEBUG_ERROR, ("%s %s: BDL!BDLIOCTL_SetControl: trying to set an invalid value\n", __DATE__, __TIME__)) status = STATUS_INVALID_PARAMETER; goto Return; } // // Call the BDD // status = pBDLExtension->pDriverExtension->bddiFunctions.pfbddiSetControl( &(pBDLExtension->BdlExtenstion), &bddiSetControlParams); if (status != STATUS_SUCCESS) { BDLDebug( BDL_DEBUG_ERROR, ("%s %s: BDL!BDLIOCTL_SetControl: pfbddiSetControl failed with %lx\n", __DATE__, __TIME__, status)) goto Return; } // // Set the number of bytes used // *pOutputBufferUsed = 0; Return: BDLDebug( BDL_DEBUG_TRACE, ("%s %s: BDL!BDLIOCTL_SetControl: Leave\n", __DATE__, __TIME__)) return (status); } NTSTATUS BDLIOCTL_CreateHandleFromData ( IN PBDL_INTERNAL_DEVICE_EXTENSION pBDLExtension, IN ULONG InpuBufferLength, IN ULONG OutputBufferLength, IN PVOID pBuffer, OUT ULONG *pOutputBufferUsed ) { NTSTATUS status = STATUS_SUCCESS; ULONG RequiredOutputSize = 0; BDDI_PARAMS_CREATEHANDLE_FROMDATA bddiCreateHandleFromDataParams; PUCHAR pv = pBuffer; ULONG RequiredInputSize = 0; ULONG fTempHandle; BDDI_ITEM *pNewItem = NULL; KIRQL irql; BDLDebug( BDL_DEBUG_TRACE, ("%s %s: BDL!BDLIOCTL_CreateHandleFromData: Enter\n", __DATE__, __TIME__)) // // Make sure the input buffer is at least the minimum size (see BDDIOCTL // spec for details) // RequiredInputSize = SIZEOF_CREATEHANDLEFROMDATA_INPUTBUFFER; if (InpuBufferLength < RequiredInputSize) { BDLDebug( BDL_DEBUG_ERROR, ("%s %s: BDL!BDLIOCTL_CreateHandleFromData: Bad input buffer size\n", __DATE__, __TIME__)) status = STATUS_INVALID_PARAMETER; goto ErrorReturn; } // // Make sure there is enough space for the return buffer // RequiredOutputSize = SIZEOF_CREATEHANDLEFROMDATA_OUTPUTBUFFER; if (RequiredOutputSize > OutputBufferLength) { BDLDebug( BDL_DEBUG_ERROR, ("%s %s: BDL!BDLIOCTL_CreateHandleFromData: Output buffer is too small\n", __DATE__, __TIME__)) status = STATUS_BUFFER_TOO_SMALL; goto ErrorReturn; } // // Initialize the BDD struct // RtlZeroMemory(&bddiCreateHandleFromDataParams, sizeof(bddiCreateHandleFromDataParams)); bddiCreateHandleFromDataParams.Size = sizeof(bddiCreateHandleFromDataParams); // // Get the input parameters from the buffer // RtlCopyMemory(&(bddiCreateHandleFromDataParams.guidFormatId), pv, sizeof(GUID)); pv += sizeof(GUID); fTempHandle = *((ULONG *) pv); pv += sizeof(ULONG); bddiCreateHandleFromDataParams.cBuffer = *((ULONG *) pv); pv += sizeof(ULONG); bddiCreateHandleFromDataParams.pBuffer = pv; // // Check to make sure size of pBuffer isn't too large // RequiredInputSize += bddiCreateHandleFromDataParams.cBuffer; if (InpuBufferLength < RequiredInputSize) { BDLDebug( BDL_DEBUG_ERROR, ("%s %s: BDL!BDLIOCTL_CreateHandleFromData: Bad input buffer size\n", __DATE__, __TIME__)) status = STATUS_INVALID_PARAMETER; goto ErrorReturn; } // // Create the new item // pNewItem = ExAllocatePoolWithTag(PagedPool, sizeof(BDDI_ITEM), BDL_ULONG_TAG); if (pNewItem == NULL) { BDLDebug( BDL_DEBUG_ERROR, ("%s %s: BDL!BDLIOCTL_CreateHandleFromData: ExAllocatePoolWithTag failed\n", __DATE__, __TIME__)) status = STATUS_NO_MEMORY; goto ErrorReturn; } // // If this is a temp handle then create it locally, otherwise call the BDD // if (fTempHandle) { pNewItem->Type = BIO_ITEMTYPE_BLOCK; pNewItem->Data.Block.pBuffer = bdliAlloc( &(pBDLExtension->BdlExtenstion), bddiCreateHandleFromDataParams.cBuffer, 0); if (pNewItem->Data.Block.pBuffer == NULL) { BDLDebug( BDL_DEBUG_ERROR, ("%s %s: BDL!BDLIOCTL_CreateHandleFromData: bdliAlloc failed\n", __DATE__, __TIME__)) status = STATUS_NO_MEMORY; goto ErrorReturn; } pNewItem->Data.Block.cBuffer = bddiCreateHandleFromDataParams.cBuffer; RtlCopyMemory( pNewItem->Data.Block.pBuffer, pv, bddiCreateHandleFromDataParams.cBuffer); } else { pNewItem->Type = BIO_ITEMTYPE_HANDLE; // // Call the BDD // status = pBDLExtension->pDriverExtension->bddiFunctions.pfbddiCreateHandleFromData( &(pBDLExtension->BdlExtenstion), &bddiCreateHandleFromDataParams); if (status != STATUS_SUCCESS) { BDLDebug( BDL_DEBUG_ERROR, ("%s %s: BDL!BDLIOCTL_CreateHandleFromData: pfbddiCreateHandleFromData failed with %lx\n", __DATE__, __TIME__, status)) goto ErrorReturn; } pNewItem->Data.Handle = bddiCreateHandleFromDataParams.hData; } // // Add this handle to the list // BDLLockHandleList(pBDLExtension, &irql); status = BDLAddHandleToList(&(pBDLExtension->HandleList), pNewItem); BDLReleaseHandleList(pBDLExtension, irql); if (status != STATUS_SUCCESS) { BDLDebug( BDL_DEBUG_ERROR, ("%s %s: BDL!BDLIOCTL_CreateHandleFromData: BDLAddHandleToList failed with %lx\n", __DATE__, __TIME__, status)) goto ErrorReturn; } // // Write the output info to the output buffer // pv = pBuffer; *((BDD_HANDLE *) pv) = pNewItem; // // Set the number of bytes used // *pOutputBufferUsed = RequiredOutputSize; Return: BDLDebug( BDL_DEBUG_TRACE, ("%s %s: BDL!BDLIOCTL_CreateHandleFromData: Leave\n", __DATE__, __TIME__)) return (status); ErrorReturn: if (pNewItem != NULL) { if ((pNewItem->Type == BIO_ITEMTYPE_BLOCK) && (pNewItem->Data.Block.pBuffer != NULL)) { bdliFree(pNewItem->Data.Block.pBuffer); } ExFreePoolWithTag(pNewItem, BDL_ULONG_TAG); } goto Return; } NTSTATUS BDLIOCTL_CloseHandle ( IN PBDL_INTERNAL_DEVICE_EXTENSION pBDLExtension, IN ULONG InpuBufferLength, IN ULONG OutputBufferLength, IN PVOID pBuffer, OUT ULONG *pOutputBufferUsed ) { NTSTATUS status = STATUS_SUCCESS; BDDI_PARAMS_CLOSEHANDLE bddiCloseHandleParams; ULONG RequiredInputSize = 0; KIRQL irql; BDDI_ITEM *pBDDIItem = NULL; BOOLEAN fItemInList = FALSE; BDLDebug( BDL_DEBUG_TRACE, ("%s %s: BDL!BDLIOCTL_CloseHandle: Enter\n", __DATE__, __TIME__)) // // Make sure the input buffer is at least the minimum size (see BDDIOCTL // spec for details) // RequiredInputSize = SIZEOF_CLOSEHANDLE_INPUTBUFFER; if (InpuBufferLength < RequiredInputSize) { BDLDebug( BDL_DEBUG_ERROR, ("%s %s: BDL!BDLIOCTL_CloseHandle: Bad input buffer size\n", __DATE__, __TIME__)) status = STATUS_INVALID_PARAMETER; goto Return; } // // Initialize the BDD struct // RtlZeroMemory(&bddiCloseHandleParams, sizeof(bddiCloseHandleParams)); bddiCloseHandleParams.Size = sizeof(bddiCloseHandleParams); // // Get the input parameters from the buffer // pBDDIItem = *((BDD_HANDLE *) pBuffer); // // Validate the handle is in the list // BDLLockHandleList(pBDLExtension, &irql); fItemInList = BDLRemoveHandleFromList(&(pBDLExtension->HandleList), pBDDIItem); BDLReleaseHandleList(pBDLExtension, irql); if (fItemInList == FALSE) { BDLDebug( BDL_DEBUG_ERROR, ("%s %s: BDL!BDLIOCTL_CloseHandle: Bad handle\n", __DATE__, __TIME__)) status = STATUS_INVALID_PARAMETER; goto Return; } // // If this is a local handle then just clean it up, otherwise call the BDD // if (pBDDIItem->Type == BIO_ITEMTYPE_BLOCK) { bdliFree(pBDDIItem->Data.Block.pBuffer); } else { bddiCloseHandleParams.hData = pBDDIItem->Data.Handle; // // Call the BDD // status = pBDLExtension->pDriverExtension->bddiFunctions.pfbddiCloseHandle( &(pBDLExtension->BdlExtenstion), &bddiCloseHandleParams); if (status != STATUS_SUCCESS) { BDLDebug( BDL_DEBUG_ERROR, ("%s %s: BDL!BDLIOCTL_CloseHandle: pfbddiCloseHandle failed with %lx\n", __DATE__, __TIME__, status)) goto Return; } } ExFreePoolWithTag(pBDDIItem, BDL_ULONG_TAG); // // Set the number of bytes used // *pOutputBufferUsed = 0; Return: BDLDebug( BDL_DEBUG_TRACE, ("%s %s: BDL!BDLIOCTL_CloseHandle: Leave\n", __DATE__, __TIME__)) return (status); } NTSTATUS BDLIOCTL_GetDataFromHandle ( IN PBDL_INTERNAL_DEVICE_EXTENSION pBDLExtension, IN ULONG InpuBufferLength, IN ULONG OutputBufferLength, IN PVOID pBuffer, OUT ULONG *pOutputBufferUsed ) { NTSTATUS status = STATUS_SUCCESS; ULONG RequiredOutputSize = 0; BDDI_PARAMS_GETDATA_FROMHANDLE bddiGetDataFromHandleParams; BDDI_PARAMS_CLOSEHANDLE bddiCloseHandleParams; PUCHAR pv = pBuffer; ULONG RequiredInputSize = 0; ULONG RemainingBufferSize = 0; KIRQL irql; BDDI_ITEM *pBDDIItem = NULL; BOOLEAN fItemInList = FALSE; BOOLEAN fCloseHandle = FALSE; BDLDebug( BDL_DEBUG_TRACE, ("%s %s: BDL!BDLIOCTL_GetDataFromHandle: Enter\n", __DATE__, __TIME__)) // // Make sure the input buffer is at least the minimum size (see BDDIOCTL // spec for details) // RequiredInputSize = SIZEOF_GETDATAFROMHANDLE_INPUTBUFFER; if (InpuBufferLength < RequiredInputSize) { BDLDebug( BDL_DEBUG_ERROR, ("%s %s: BDL!BDLIOCTL_GetDataFromHandle: Bad input buffer size\n", __DATE__, __TIME__)) status = STATUS_INVALID_PARAMETER; goto Return; } // // Make sure there is enough space for the return buffer // RequiredOutputSize = SIZEOF_GETDATAFROMHANDLE_OUTPUTBUFFER; if (RequiredOutputSize > OutputBufferLength) { BDLDebug( BDL_DEBUG_ERROR, ("%s %s: BDL!BDLIOCTL_GetDataFromHandle: Output buffer is too small\n", __DATE__, __TIME__)) status = STATUS_BUFFER_TOO_SMALL; goto Return; } // // Calculate the size remaining in the output buffer // RemainingBufferSize = OutputBufferLength - RequiredOutputSize; // // Initialize the BDD struct // RtlZeroMemory(&bddiGetDataFromHandleParams, sizeof(bddiGetDataFromHandleParams)); bddiGetDataFromHandleParams.Size = sizeof(bddiGetDataFromHandleParams); // // Get the input parameters from the buffer // pBDDIItem = *((BDD_HANDLE *) pv); pv += sizeof(BDD_HANDLE); if (*((ULONG *) pv) == 1) { fCloseHandle = TRUE; } { fCloseHandle = FALSE; } // // Validate the handle is in the list // BDLLockHandleList(pBDLExtension, &irql); if (fCloseHandle) { fItemInList = BDLRemoveHandleFromList(&(pBDLExtension->HandleList), pBDDIItem); } else { fItemInList = BDLValidateHandleIsInList(&(pBDLExtension->HandleList), pBDDIItem); } BDLReleaseHandleList(pBDLExtension, irql); if (fItemInList == FALSE) { BDLDebug( BDL_DEBUG_ERROR, ("%s %s: BDL!BDLIOCTL_GetDataFromHandle: Bad handle\n", __DATE__, __TIME__)) status = STATUS_INVALID_PARAMETER; goto Return; } pv = pBuffer; // // If this is a local handle then just hand back the data, otherwise call the BDD // if (pBDDIItem->Type == BIO_ITEMTYPE_BLOCK) { // // See if the output buffer is large enough // if (pBDDIItem->Data.Block.cBuffer > RemainingBufferSize) { bddiGetDataFromHandleParams.pBuffer = NULL; bddiGetDataFromHandleParams.BIOReturnCode = BIO_BUFFER_TOO_SMALL; } else { // // Set the output buffer to be the IOCTL output buffer + the offset // of the other output params which preceed the output data buffer // bddiGetDataFromHandleParams.pBuffer = pv + RequiredOutputSize; // // Copy the data // RtlCopyMemory( bddiGetDataFromHandleParams.pBuffer, pBDDIItem->Data.Block.pBuffer, pBDDIItem->Data.Block.cBuffer); bddiGetDataFromHandleParams.BIOReturnCode = ERROR_SUCCESS; } bddiGetDataFromHandleParams.cBuffer = pBDDIItem->Data.Block.cBuffer; if (fCloseHandle) { bdliFree(pBDDIItem->Data.Block.pBuffer); } } else { bddiGetDataFromHandleParams.hData = pBDDIItem->Data.Handle; bddiGetDataFromHandleParams.cBuffer = RemainingBufferSize; if (RemainingBufferSize == 0) { bddiGetDataFromHandleParams.pBuffer = NULL; } else { // // Set the output buffer to be the IOCTL output buffer + the offset // of the other output params which preceed the output data buffer // bddiGetDataFromHandleParams.pBuffer = pv + RequiredOutputSize; } // // Call the BDD // status = pBDLExtension->pDriverExtension->bddiFunctions.pfbddiGetDataFromHandle( &(pBDLExtension->BdlExtenstion), &bddiGetDataFromHandleParams); if (status != STATUS_SUCCESS) { BDLDebug( BDL_DEBUG_ERROR, ("%s %s: BDL!BDLIOCTL_GetDataFromHandle: pfbddiCloseHandle failed with %lx\n", __DATE__, __TIME__, status)) goto Return; } if (fCloseHandle) { RtlZeroMemory(&bddiCloseHandleParams, sizeof(bddiCloseHandleParams)); bddiCloseHandleParams.Size = sizeof(bddiCloseHandleParams); bddiCloseHandleParams.hData = pBDDIItem->Data.Handle; // // Call the BDD to close the handle - don't check the return status because // we really don't want to fail the operation if just closing the handle fails // pBDLExtension->pDriverExtension->bddiFunctions.pfbddiCloseHandle( &(pBDLExtension->BdlExtenstion), &bddiCloseHandleParams); } } if (fCloseHandle) { ExFreePoolWithTag(pBDDIItem, BDL_ULONG_TAG); } // // Write the return info to the output buffer // pv = pBuffer; *((ULONG *) pv) = bddiGetDataFromHandleParams.BIOReturnCode; pv += sizeof(ULONG); *((ULONG *) pv) = bddiGetDataFromHandleParams.cBuffer; // // Set the number of bytes used // *pOutputBufferUsed = RequiredOutputSize; if (bddiGetDataFromHandleParams.pBuffer != NULL) { *pOutputBufferUsed += bddiGetDataFromHandleParams.cBuffer; } Return: BDLDebug( BDL_DEBUG_TRACE, ("%s %s: BDL!BDLIOCTL_GetDataFromHandle: Leave\n", __DATE__, __TIME__)) return (status); } NTSTATUS BDLIOCTL_RegisterNotify ( IN PBDL_INTERNAL_DEVICE_EXTENSION pBDLExtension, IN ULONG InpuBufferLength, IN ULONG OutputBufferLength, IN PVOID pBuffer, OUT ULONG *pOutputBufferUsed ) { NTSTATUS status = STATUS_SUCCESS; BDDI_PARAMS_REGISTERNOTIFY bddiRegisterNotifyParams; PUCHAR pv = pBuffer; ULONG RequiredInputSize = 0; BDL_CONTROL *pBDLControl = NULL; KIRQL irql, OldIrql; PLIST_ENTRY pRegistrationListEntry = NULL; PLIST_ENTRY pControlChangeEntry = NULL; BDL_CONTROL_CHANGE_REGISTRATION *pControlChangeRegistration = NULL; BDL_IOCTL_CONTROL_CHANGE_ITEM *pControlChangeItem = NULL; BOOLEAN fLockAcquired = FALSE; BOOLEAN fRegistrationFound = FALSE; PLIST_ENTRY pTemp = NULL; PIRP pIrpToComplete = NULL; BDLDebug( BDL_DEBUG_TRACE, ("%s %s: BDL!BDLIOCTL_RegisterNotify: Enter\n", __DATE__, __TIME__)) // // Make sure the input buffer is at least the minimum size (see BDDIOCTL // spec for details) // RequiredInputSize = SIZEOF_REGISTERNOTIFY_INPUTBUFFER; if (InpuBufferLength < RequiredInputSize) { BDLDebug( BDL_DEBUG_ERROR, ("%s %s: BDL!BDLIOCTL_RegisterNotify: Bad input buffer size\n", __DATE__, __TIME__)) status = STATUS_INVALID_PARAMETER; goto Return; } // // Initialize the BDD struct // RtlZeroMemory(&bddiRegisterNotifyParams, sizeof(bddiRegisterNotifyParams)); bddiRegisterNotifyParams.Size = sizeof(bddiRegisterNotifyParams); // // Get the input parameters from the buffer // bddiRegisterNotifyParams.fRegister = *((ULONG *) pv) == 1; pv += sizeof(ULONG); bddiRegisterNotifyParams.ComponentId = *((ULONG *) pv); pv += sizeof(ULONG); bddiRegisterNotifyParams.ChannelId = *((ULONG *) pv); pv += sizeof(ULONG); bddiRegisterNotifyParams.ControlId = *((ULONG *) pv); // // Check control ID // if (BDLCheckControlId( pBDLExtension, bddiRegisterNotifyParams.ComponentId, bddiRegisterNotifyParams.ChannelId, bddiRegisterNotifyParams.ControlId, &pBDLControl) == FALSE) { BDLDebug( BDL_DEBUG_ERROR, ("%s %s: BDL!BDLIOCTL_RegisterNotify: Bad ControlId\n", __DATE__, __TIME__)) status = STATUS_INVALID_PARAMETER; goto Return; } // // Make sure this is an async control // if (!(pBDLControl->Flags | BIO_CONTROL_FLAG_ASYNCHRONOUS)) { BDLDebug( BDL_DEBUG_ERROR, ("%s %s: BDL!BDLIOCTL_RegisterNotify: trying to register for a non async control\n", __DATE__, __TIME__)) status = STATUS_INVALID_PARAMETER; goto Return; } // // Note that we must raise the irql to dispatch level because we are synchronizing // with a dispatch routine (BDLControlChangeDpc) that adds items to the queue at // dispatch level // KeRaiseIrql(DISPATCH_LEVEL, &OldIrql); KeAcquireSpinLock(&(pBDLExtension->ControlChangeStruct.ControlChangeLock), &irql); fLockAcquired = TRUE; // // Check to see if this notification registration exists (must exist for unregister, must not // exist for register). // pRegistrationListEntry = pBDLExtension->ControlChangeStruct.ControlChangeRegistrationList.Flink; while (pRegistrationListEntry->Flink != pBDLExtension->ControlChangeStruct.ControlChangeRegistrationList.Flink) { pControlChangeRegistration = CONTAINING_RECORD( pRegistrationListEntry, BDL_CONTROL_CHANGE_REGISTRATION, ListEntry); if ((pControlChangeRegistration->ComponentId == bddiRegisterNotifyParams.ComponentId) && (pControlChangeRegistration->ChannelId == bddiRegisterNotifyParams.ChannelId) && (pControlChangeRegistration->ControlId == bddiRegisterNotifyParams.ControlId)) { fRegistrationFound = TRUE; // // The notification registration does exist, so if this is a register call then fail // if (bddiRegisterNotifyParams.fRegister == TRUE) { BDLDebug( BDL_DEBUG_ERROR, ("%s %s: BDL!BDLIOCTL_RegisterNotify: trying to re-register\n", __DATE__, __TIME__)) status = STATUS_INVALID_PARAMETER; goto Return; } // // Remove the notification registration from the list // RemoveEntryList(pRegistrationListEntry); ExFreePoolWithTag(pControlChangeRegistration, BDL_ULONG_TAG); // // Remove any pending notifications for the control which is being unregistered // pControlChangeEntry = pBDLExtension->ControlChangeStruct.IOCTLControlChangeQueue.Flink; while (pControlChangeEntry->Flink != pBDLExtension->ControlChangeStruct.IOCTLControlChangeQueue.Flink) { pControlChangeItem = CONTAINING_RECORD( pControlChangeEntry, BDL_IOCTL_CONTROL_CHANGE_ITEM, ListEntry); pTemp = pControlChangeEntry; pControlChangeEntry = pControlChangeEntry->Flink; if ((pControlChangeItem->ComponentId == bddiRegisterNotifyParams.ComponentId) && (pControlChangeItem->ChannelId == bddiRegisterNotifyParams.ChannelId) && (pControlChangeItem->ControlId == bddiRegisterNotifyParams.ControlId)) { RemoveEntryList(pTemp); ExFreePoolWithTag(pControlChangeItem, BDL_ULONG_TAG); } } // // If the last notification registration just got removed, then complete // the pending get notification IRP (if one exists) after releasing the lock. // if (IsListEmpty(&(pBDLExtension->ControlChangeStruct.ControlChangeRegistrationList)) && (pBDLExtension->ControlChangeStruct.pIrp != NULL)) { pIrpToComplete = pBDLExtension->ControlChangeStruct.pIrp; pBDLExtension->ControlChangeStruct.pIrp = NULL; } break; } pRegistrationListEntry = pRegistrationListEntry->Flink; } // // If the registration was not found, and this is an unregister, return an error // if ((fRegistrationFound == FALSE) && (bddiRegisterNotifyParams.fRegister == FALSE)) { BDLDebug( BDL_DEBUG_ERROR, ("%s %s: BDL!BDLIOCTL_RegisterNotify: trying to re-register\n", __DATE__, __TIME__)) status = STATUS_INVALID_PARAMETER; goto Return; } // // Add the notification to the list if this is a registration // if (bddiRegisterNotifyParams.fRegister == TRUE) { pControlChangeRegistration = ExAllocatePoolWithTag( PagedPool, sizeof(BDL_CONTROL_CHANGE_REGISTRATION), BDL_ULONG_TAG); if (pControlChangeRegistration == NULL) { BDLDebug( BDL_DEBUG_ERROR, ("%s %s: BDL!BDLIOCTL_RegisterNotify: ExAllocatePoolWithTag failed\n", __DATE__, __TIME__)) status = STATUS_NO_MEMORY; goto Return; } pControlChangeRegistration->ComponentId = bddiRegisterNotifyParams.ComponentId; pControlChangeRegistration->ChannelId = bddiRegisterNotifyParams.ChannelId; pControlChangeRegistration->ControlId = bddiRegisterNotifyParams.ControlId; InsertHeadList( &(pBDLExtension->ControlChangeStruct.ControlChangeRegistrationList), &(pControlChangeRegistration->ListEntry)); } KeReleaseSpinLock(&(pBDLExtension->ControlChangeStruct.ControlChangeLock), irql); KeLowerIrql(OldIrql); fLockAcquired = FALSE; if (pIrpToComplete != NULL) { pIrpToComplete->IoStatus.Information = 0; pIrpToComplete->IoStatus.Status = STATUS_NO_MORE_ENTRIES; IoCompleteRequest(pIrpToComplete, IO_NO_INCREMENT); } // // Call the BDD // status = pBDLExtension->pDriverExtension->bddiFunctions.pfbddiRegisterNotify( &(pBDLExtension->BdlExtenstion), &bddiRegisterNotifyParams); if (status != STATUS_SUCCESS) { BDLDebug( BDL_DEBUG_ERROR, ("%s %s: BDL!BDLIOCTL_RegisterNotify: pfbddiRegisterNotify failed with %lx\n", __DATE__, __TIME__, status)) // // FIX FIX - if this fails and it is a register then we need to remove the // registration from the list of registrations... since it was already added // above. // ASSERT(0); goto Return; } // // Set the number of bytes used // *pOutputBufferUsed = 0; Return: if (fLockAcquired == TRUE) { KeReleaseSpinLock(&(pBDLExtension->ControlChangeStruct.ControlChangeLock), irql); KeLowerIrql(OldIrql); } BDLDebug( BDL_DEBUG_TRACE, ("%s %s: BDL!BDLIOCTL_RegisterNotify: Leave\n", __DATE__, __TIME__)) return (status); } NTSTATUS BDLIOCTL_GetNotification ( IN PBDL_INTERNAL_DEVICE_EXTENSION pBDLExtension, IN ULONG InpuBufferLength, IN ULONG OutputBufferLength, IN PVOID pBuffer, IN PIRP pIrp, OUT ULONG *pOutputBufferUsed ) { NTSTATUS status = STATUS_SUCCESS; ULONG RequiredOutputSize = 0; PUCHAR pv = pBuffer; KIRQL irql, OldIrql; PLIST_ENTRY pListEntry = NULL; BDL_IOCTL_CONTROL_CHANGE_ITEM *pControlChangeItem = NULL; BOOLEAN fLockAcquired = FALSE; BDLDebug( BDL_DEBUG_TRACE, ("%s %s: BDL!BDLIOCTL_GetNotification: Enter\n", __DATE__, __TIME__)) // // Make sure there is enough space for the return buffer // RequiredOutputSize = SIZEOF_GETNOTIFICATION_OUTPUTBUFFER; if (RequiredOutputSize > OutputBufferLength) { BDLDebug( BDL_DEBUG_ERROR, ("%s %s: BDL!BDLIOCTL_GetNotification: Output buffer is too small\n", __DATE__, __TIME__)) status = STATUS_BUFFER_TOO_SMALL; goto Return; } // // Lock the notification queue and see if there are any outstanding notifications. // Note that we must raise the irql to dispatch level because we are synchronizing // with a DPC routine (BDLControlChangeDpc) that adds items to the queue at // dispatch level // KeRaiseIrql(DISPATCH_LEVEL, &OldIrql); KeAcquireSpinLock(&(pBDLExtension->ControlChangeStruct.ControlChangeLock), &irql); fLockAcquired = TRUE; // // If there is already an IRP posted then that is a bug in the BSP // if (pBDLExtension->ControlChangeStruct.pIrp != NULL) { BDLDebug( BDL_DEBUG_ERROR, ("%s %s: BDL!BDLIOCTL_GetNotification: Output buffer is too small\n", __DATE__, __TIME__)) status = STATUS_INVALID_DEVICE_STATE; goto Return; } if (IsListEmpty(&(pBDLExtension->ControlChangeStruct.ControlChangeRegistrationList))) { // // There are no change notifications registered, so complete the IRP with the // special status that indicates that // BDLDebug( BDL_DEBUG_TRACE, ("%s %s: BDL!BDLIOCTL_GetNotification: ControlChangeRegistrationList empty\n", __DATE__, __TIME__)) status = STATUS_NO_MORE_ENTRIES; goto Return; } else if (IsListEmpty(&(pBDLExtension->ControlChangeStruct.IOCTLControlChangeQueue))) { // // There are currently no control changes in the list, so just save this Irp // and return STATUS_PENDING // BDLDebug( BDL_DEBUG_TRACE, ("%s %s: BDL!BDLIOCTL_GetNotification: ControlChanges empty\n", __DATE__, __TIME__)) // // Set up the cancel routine for this IRP // if (IoSetCancelRoutine(pIrp, BDLRegisteredCancelGetNotificationIRP) != NULL) { BDLDebug( BDL_DEBUG_ERROR, ("%s %s: BDL!BDLIOCTL_GetNotification: pCancel was not NULL\n", __DATE__, __TIME__)) } pBDLExtension->ControlChangeStruct.pIrp = pIrp; status = STATUS_PENDING; goto Return; } else { // // Grab the first control change item in the queue // pListEntry = RemoveHeadList(&(pBDLExtension->ControlChangeStruct.IOCTLControlChangeQueue)); pControlChangeItem = CONTAINING_RECORD(pListEntry, BDL_IOCTL_CONTROL_CHANGE_ITEM, ListEntry); } KeReleaseSpinLock(&(pBDLExtension->ControlChangeStruct.ControlChangeLock), irql); KeLowerIrql(OldIrql); fLockAcquired = FALSE; // // We are here because there is currently a control change to report, so write the return // info to the output buffer // pv = pBuffer; *((ULONG *) pv) = pControlChangeItem->ComponentId; pv += sizeof(ULONG); *((ULONG *) pv) = pControlChangeItem->ChannelId; pv += sizeof(ULONG); *((ULONG *) pv) = pControlChangeItem->ControlId; pv += sizeof(ULONG); *((ULONG *) pv) = pControlChangeItem->Value; // // Free up the change item // ExFreePoolWithTag(pControlChangeItem, BDL_ULONG_TAG); // // Set the number of bytes used // *pOutputBufferUsed = RequiredOutputSize; Return: if (fLockAcquired) { KeReleaseSpinLock(&(pBDLExtension->ControlChangeStruct.ControlChangeLock), irql); KeLowerIrql(OldIrql); } BDLDebug( BDL_DEBUG_TRACE, ("%s %s: BDL!BDLIOCTL_GetNotification: Leave\n", __DATE__, __TIME__)) return (status); } VOID BDLCancelGetNotificationIRP ( IN PBDL_INTERNAL_DEVICE_EXTENSION pBDLExtension ) { PIRP pIrpToCancel = NULL; KIRQL irql, OldIrql; BDLDebug( BDL_DEBUG_TRACE, ("%s %s: BDL!BDLCancelGetNotificationIRP: Enter\n", __DATE__, __TIME__)) // // Remove the GetNotification IRP from the the ControlChangeStruct. // Need to make sure the IRP is still there, since theoretically we // could be trying to complete it at the same time it is cancelled. // KeRaiseIrql(DISPATCH_LEVEL, &OldIrql); KeAcquireSpinLock(&(pBDLExtension->ControlChangeStruct.ControlChangeLock), &irql); if (pBDLExtension->ControlChangeStruct.pIrp != NULL) { pIrpToCancel = pBDLExtension->ControlChangeStruct.pIrp; pBDLExtension->ControlChangeStruct.pIrp = NULL; } KeReleaseSpinLock(&(pBDLExtension->ControlChangeStruct.ControlChangeLock), irql); KeLowerIrql(OldIrql); // // Complete the GetNotification IRP as cancelled // if (pIrpToCancel != NULL) { pIrpToCancel->IoStatus.Information = 0; pIrpToCancel->IoStatus.Status = STATUS_CANCELLED; IoCompleteRequest(pIrpToCancel, IO_NO_INCREMENT); } BDLDebug( BDL_DEBUG_TRACE, ("%s %s: BDL!BDLCancelGetNotificationIRP: Leave\n", __DATE__, __TIME__)) } NTSTATUS BDLRegisteredCancelGetNotificationIRP ( IN PDEVICE_OBJECT pDeviceObject, IN PIRP pIrp ) { PBDL_INTERNAL_DEVICE_EXTENSION pBDLExtension = pDeviceObject->DeviceExtension; BDLDebug( BDL_DEBUG_TRACE, ("%s %s: BDL!BDLRegisteredCancelGetNotificationIRP: Enter\n", __DATE__, __TIME__)) ASSERT(pIrp == pBDLExtension->ControlChangeStruct.pIrp); // // Since this function is called already holding the CancelSpinLock // we need to release it // IoReleaseCancelSpinLock(pIrp->CancelIrql); // // Cancel the IRP // BDLCancelGetNotificationIRP(pBDLExtension); BDLDebug( BDL_DEBUG_TRACE, ("%s %s: BDL!BDLRegisteredCancelGetNotificationIRP: Leave\n", __DATE__, __TIME__)) return (STATUS_CANCELLED); }