851 lines
20 KiB
C
851 lines
20 KiB
C
|
||
/*
|
||
* Copyright (c) Microsoft Corporation, 1993. All Rights Reserved.
|
||
*/
|
||
|
||
/*
|
||
* vcinit.c
|
||
*
|
||
*
|
||
* 32-bit Video Capture driver
|
||
* kernel-mode support library - initialisation routines
|
||
*
|
||
* Geraint Davies, Feb 93
|
||
*/
|
||
|
||
#include <vckernel.h>
|
||
#include "vckpriv.h"
|
||
|
||
|
||
|
||
|
||
/* --- internal functions -------------------------------------------- */
|
||
|
||
|
||
/*
|
||
* create a possible device name by appending a given number to
|
||
* the standard basename, and appending that to the string \device.
|
||
* Puts the result in the counted string pName, which must be initialised
|
||
* to point to sufficient storage.
|
||
*/
|
||
NTSTATUS
|
||
VC_CreateNumberedName(
|
||
int DeviceNumber, // number to append
|
||
LPWSTR pHeader, // header for device (eg \Device)
|
||
LPWSTR pBase, // base name - eg VidCap, WaveOut
|
||
PUNICODE_STRING pUniqueName // result goes here
|
||
)
|
||
{
|
||
UNICODE_STRING UnicodeNumber;
|
||
WCHAR Number[12]; // big enough for any 32-bit number
|
||
|
||
/*
|
||
* start the string off with \device
|
||
* -assumes the string is correctly inited to 0 length.
|
||
*/
|
||
if (pHeader != NULL) {
|
||
RtlAppendUnicodeToString(pUniqueName, pHeader);
|
||
}
|
||
|
||
|
||
/*
|
||
* add the base name (eg VidCap)
|
||
*/
|
||
RtlAppendUnicodeToString(pUniqueName, pBase);
|
||
|
||
/*
|
||
* convert the number to a unicode string and append that
|
||
*/
|
||
UnicodeNumber.Buffer = Number;
|
||
UnicodeNumber.MaximumLength = sizeof(Number);
|
||
|
||
RtlIntegerToUnicodeString(DeviceNumber, 10, &UnicodeNumber);
|
||
RtlAppendUnicodeStringToString(pUniqueName, &UnicodeNumber);
|
||
|
||
/* done!: we now have eg \Device\VidCap0 - its up to the caller
|
||
* to establish if this is unique or not
|
||
*/
|
||
return(STATUS_SUCCESS);
|
||
}
|
||
|
||
|
||
|
||
/*
|
||
* given a name in \device, create a symbolic link into \dosdevices
|
||
* for this name so that the device can be opened by Win32 applications.
|
||
*/
|
||
NTSTATUS
|
||
VC_CreateLink(PUNICODE_STRING pusName)
|
||
{
|
||
UNICODE_STRING LinkObject;
|
||
WCHAR LinkName[80];
|
||
|
||
LinkName[0] = UNICODE_NULL;
|
||
|
||
RtlInitUnicodeString(&LinkObject, LinkName);
|
||
|
||
LinkObject.MaximumLength = sizeof(LinkName);
|
||
|
||
RtlAppendUnicodeToString(&LinkObject, L"\\DosDevices");
|
||
|
||
// DeviceSize is a known compile time constant - use a define
|
||
// rather than a variable as this reduces code size.
|
||
#define DeviceSize (sizeof(L"\\Device") - sizeof(UNICODE_NULL))
|
||
|
||
pusName->Buffer += DeviceSize / sizeof(WCHAR);
|
||
pusName->Length -= DeviceSize;
|
||
|
||
RtlAppendUnicodeStringToString(&LinkObject, pusName);
|
||
|
||
pusName->Buffer -= DeviceSize / sizeof(WCHAR);
|
||
pusName->Length += DeviceSize;
|
||
|
||
return (IoCreateSymbolicLink(&LinkObject, pusName));
|
||
}
|
||
|
||
|
||
|
||
/*
|
||
* translate the bus-relative port address using the HAL,
|
||
* and map the port address into memory if necessary
|
||
*/
|
||
VOID VC_MapPort(PDEVICE_INFO pDevInfo, PUCHAR PortBase, ULONG NrOfPorts)
|
||
{
|
||
PHYSICAL_ADDRESS PortAddress;
|
||
PHYSICAL_ADDRESS MappedAddress;
|
||
|
||
pDevInfo->PortMemType = 1; // i/o space
|
||
PortAddress.LowPart = (ULONG) PortBase;
|
||
PortAddress.HighPart = 0;
|
||
HalTranslateBusAddress(
|
||
pDevInfo->BusType,
|
||
pDevInfo->BusNumber,
|
||
PortAddress,
|
||
&pDevInfo->PortMemType,
|
||
&MappedAddress);
|
||
|
||
if (pDevInfo->PortMemType == 0) {
|
||
/* memory mapped i/o - map the physical address into system space*/
|
||
pDevInfo->PortBase = MmMapIoSpace(MappedAddress, NrOfPorts, FALSE);
|
||
dprintf2(("ports at 0x%x", pDevInfo->PortBase));
|
||
|
||
} else {
|
||
pDevInfo->PortBase = (PUCHAR) MappedAddress.LowPart;
|
||
dprintf2(("ports at 0x%x (i/o space)", pDevInfo->PortBase));
|
||
}
|
||
pDevInfo->NrOfPorts = NrOfPorts;
|
||
}
|
||
|
||
/*
|
||
* translate the bus-relative frame buffer address using the HAL,
|
||
* and map the whole buffer into system memory.
|
||
*/
|
||
VOID VC_MapMemory(PDEVICE_INFO pDevInfo, PUCHAR Base, ULONG Length)
|
||
{
|
||
PHYSICAL_ADDRESS BaseAddress;
|
||
PHYSICAL_ADDRESS MappedAddress;
|
||
|
||
pDevInfo->FrameMemType = 0; // memory space
|
||
BaseAddress.LowPart = (ULONG) Base;
|
||
BaseAddress.HighPart = 0;
|
||
HalTranslateBusAddress(
|
||
pDevInfo->BusType,
|
||
pDevInfo->BusNumber,
|
||
BaseAddress,
|
||
&pDevInfo->FrameMemType,
|
||
&MappedAddress);
|
||
|
||
if (pDevInfo->FrameMemType == 0) {
|
||
/* memory mapped i/o - map the physical address into system space*/
|
||
pDevInfo->FrameBase = MmMapIoSpace(MappedAddress, Length, FALSE);
|
||
dprintf2(("frame buffer at 0x%x", pDevInfo->FrameBase));
|
||
} else {
|
||
pDevInfo->FrameBase = (PUCHAR) MappedAddress.LowPart;
|
||
dprintf(("using raw address 0x%x", pDevInfo->FrameBase));
|
||
}
|
||
|
||
pDevInfo->FrameLength = Length;
|
||
|
||
}
|
||
|
||
|
||
|
||
/*
|
||
* Report Port, Interrupt and Frame Buffer Memory resources and detect any
|
||
* conflict. return TRUE if conflict reported, false if everything ok.
|
||
*/
|
||
BOOLEAN VC_ReportResources(
|
||
PDRIVER_OBJECT pDriverObject,
|
||
PDEVICE_INFO pDevInfo,
|
||
PUCHAR PortBase, ULONG NrOfPorts,
|
||
ULONG Interrupt, BOOLEAN bLatched,
|
||
PUCHAR pFrameBuffer, ULONG FrameLength)
|
||
{
|
||
UCHAR ResBuffer[3 * sizeof(CM_RESOURCE_LIST) ];
|
||
PCM_RESOURCE_LIST ResourceList;
|
||
PCM_PARTIAL_RESOURCE_DESCRIPTOR Descriptor;
|
||
BOOLEAN ResourceConflict;
|
||
NTSTATUS Status;
|
||
|
||
|
||
ResourceList = (PCM_RESOURCE_LIST) ResBuffer;
|
||
Descriptor = ResourceList->List[0].PartialResourceList.PartialDescriptors;
|
||
ResourceConflict = FALSE;
|
||
|
||
/* init the buffer to 0 */
|
||
RtlZeroMemory(ResBuffer, sizeof(ResBuffer));
|
||
|
||
/* we have only one list of resources (there's only one bus!?) */
|
||
ResourceList->Count = 1;
|
||
ResourceList->List[0].InterfaceType = pDevInfo->BusType;
|
||
ResourceList->List[0].BusNumber = pDevInfo->BusNumber;
|
||
|
||
|
||
/* add the i/o ports in use to the list */
|
||
ResourceList->List[0].PartialResourceList.Count++;
|
||
|
||
Descriptor->Type = CmResourceTypePort;
|
||
Descriptor->ShareDisposition = CmResourceShareDeviceExclusive;
|
||
Descriptor->u.Port.Start.LowPart = (ULONG) PortBase;
|
||
Descriptor->u.Port.Length = NrOfPorts;
|
||
Descriptor->Flags = (USHORT)((pDevInfo->PortMemType == 0 ? CM_RESOURCE_PORT_MEMORY :
|
||
CM_RESOURCE_PORT_IO));
|
||
|
||
Descriptor++;
|
||
|
||
|
||
/* add the interrupt to the list */
|
||
ResourceList->List[0].PartialResourceList.Count++;
|
||
|
||
Descriptor->Type = CmResourceTypeInterrupt;
|
||
Descriptor->ShareDisposition = CmResourceShareDeviceExclusive;
|
||
Descriptor->u.Interrupt.Level = Interrupt;
|
||
Descriptor->u.Interrupt.Vector = Interrupt;
|
||
Descriptor->Flags = (USHORT)((bLatched ? CM_RESOURCE_INTERRUPT_LATCHED :
|
||
CM_RESOURCE_INTERRUPT_LEVEL_SENSITIVE));
|
||
|
||
Descriptor++;
|
||
|
||
/* add the frame buffer to the list */
|
||
ResourceList->List[0].PartialResourceList.Count++;
|
||
|
||
Descriptor->Type = CmResourceTypeMemory;
|
||
Descriptor->ShareDisposition = CmResourceShareDeviceExclusive;
|
||
Descriptor->u.Memory.Start.LowPart = (ULONG) pFrameBuffer;
|
||
Descriptor->u.Memory.Length = FrameLength;
|
||
Descriptor->Flags = 0;
|
||
|
||
Descriptor++;
|
||
|
||
|
||
/* now report the resources */
|
||
Status = IoReportResourceUsage(NULL,
|
||
pDriverObject,
|
||
ResourceList,
|
||
(PUCHAR)Descriptor - (PUCHAR)ResourceList,
|
||
NULL,
|
||
NULL,
|
||
0,
|
||
FALSE,
|
||
&ResourceConflict);
|
||
|
||
|
||
return(ResourceConflict);
|
||
}
|
||
|
||
|
||
|
||
|
||
|
||
NTSTATUS
|
||
VC_GetBusCallout(
|
||
IN PVOID Context,
|
||
IN PUNICODE_STRING PathName,
|
||
IN INTERFACE_TYPE BusType,
|
||
IN ULONG BusNumber,
|
||
IN PKEY_VALUE_FULL_INFORMATION *BusInformation,
|
||
IN CONFIGURATION_TYPE ControllerType,
|
||
IN ULONG ControllerNumber,
|
||
IN PKEY_VALUE_FULL_INFORMATION *ControllerInformation,
|
||
IN CONFIGURATION_TYPE PeripheralType,
|
||
IN ULONG PeripheralNumber,
|
||
IN PKEY_VALUE_FULL_INFORMATION *PeripheralInformation
|
||
)
|
||
/*++
|
||
|
||
Routine Description
|
||
|
||
Called back from IoQueryDeviceDescription in VC_GetBus to
|
||
verify that the bus type asked for is found. Notify VC_GetBus via
|
||
the context info (ptr to a BOOL).
|
||
|
||
Arguments
|
||
|
||
via
|
||
|
||
Return Value
|
||
|
||
STATUS_SUCCESS if ok, otherwise an error status
|
||
--*/
|
||
{
|
||
// the fact that we are called is sufficient to indicate that
|
||
// the bus has been found.
|
||
* (PBOOLEAN)Context = TRUE;
|
||
|
||
return(STATUS_SUCCESS);
|
||
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
VC_GetBus(
|
||
IN INTERFACE_TYPE BusType,
|
||
OUT PULONG pBusNumber
|
||
)
|
||
/*++
|
||
|
||
Routine Description
|
||
|
||
Find if there is a bus of type BusType.
|
||
|
||
Arguments
|
||
BusType - bus type: isa or eisa..
|
||
pBusNumber - return location for bus number if found
|
||
|
||
Return Value
|
||
|
||
STATUS_SUCCESS if found, or STATUS_DEVICE_DOES_NOT_EXIST if no such bus.
|
||
|
||
|
||
--*/
|
||
{
|
||
BOOLEAN fSuccess = FALSE;
|
||
|
||
*pBusNumber = 0;
|
||
|
||
IoQueryDeviceDescription(
|
||
&BusType,
|
||
pBusNumber,
|
||
NULL,
|
||
NULL,
|
||
NULL,
|
||
NULL,
|
||
VC_GetBusCallout,
|
||
(PVOID) &fSuccess);
|
||
|
||
if (fSuccess) {
|
||
return(STATUS_SUCCESS);
|
||
} else {
|
||
return(STATUS_DEVICE_DOES_NOT_EXIST);
|
||
}
|
||
}
|
||
|
||
|
||
|
||
|
||
/*
|
||
* save the path to our registry entry (including parameters subkey)
|
||
* in paged-pool memory.
|
||
*/
|
||
PWCHAR
|
||
VC_SaveRegistryPathName(
|
||
PUNICODE_STRING RegistryPathName
|
||
)
|
||
{
|
||
int Length;
|
||
PWCHAR SavedString;
|
||
|
||
Length =
|
||
RegistryPathName->Length + sizeof(PARMS_SUBKEY) +
|
||
sizeof(UNICODE_NULL); // Include backslash
|
||
|
||
|
||
SavedString =
|
||
ExAllocatePool(PagedPool, Length); // Only access on caller thread
|
||
|
||
if (SavedString == NULL) {
|
||
|
||
return NULL;
|
||
}
|
||
|
||
//
|
||
// Copy the character data
|
||
//
|
||
|
||
RtlCopyMemory(SavedString, RegistryPathName->Buffer,
|
||
RegistryPathName->Length);
|
||
|
||
SavedString[RegistryPathName->Length / sizeof(WCHAR)] = L'\\';
|
||
SavedString[RegistryPathName->Length / sizeof(WCHAR) + 1] = UNICODE_NULL;
|
||
|
||
//
|
||
// Append the parameters suffix prepended by a backslash
|
||
//
|
||
|
||
wcscat(SavedString, PARMS_SUBKEY);
|
||
|
||
return SavedString;
|
||
}
|
||
|
||
|
||
|
||
/* --- external functions -------------------------------------------- */
|
||
|
||
|
||
/*
|
||
* VC_Init
|
||
*
|
||
* Create the device object, and any necessary related setup, and
|
||
* allocate device extension data. The device extension data is
|
||
* a DEVICE_INFO struct plus however much data the caller wants for
|
||
* hardware-specific data.
|
||
*
|
||
* parameters:
|
||
* pDriverObject - pointer to driver object (arg to DriverEntry)
|
||
* RegistryPathName - entry for this driver in registry (arg to DriverEntry)
|
||
* HWInfoSize - amount of data to allocate at end of DeviceExtension
|
||
*
|
||
* returns pointer to device extension data as DEVICE_INFO struct.
|
||
*/
|
||
PDEVICE_INFO
|
||
VC_Init(
|
||
PDRIVER_OBJECT pDriverObject,
|
||
PUNICODE_STRING RegistryPathName,
|
||
ULONG HWInfoSize)
|
||
{
|
||
NTSTATUS Status;
|
||
UNICODE_STRING usName; // name of device to create
|
||
WCHAR usNameBuffer[MAX_VIDCAP_NAME_LENGTH];
|
||
PDEVICE_INFO pDevInfo; // h-w specific device extension data
|
||
PDEVICE_OBJECT pDevObj;
|
||
int DeviceNumber;
|
||
|
||
#if DBG
|
||
dprintf1(("VC Init called"));
|
||
if (VCDebugLevel > 4) {
|
||
DbgBreakPoint();
|
||
}
|
||
#endif
|
||
|
||
|
||
/*
|
||
* create the device object
|
||
* - we need to create a unique name for this device, by trying
|
||
* to create names with 0, then 1 etc appended until the name is unique.
|
||
*/
|
||
for(DeviceNumber = 0; DeviceNumber < MAX_VIDCAP_DEVICES; DeviceNumber++) {
|
||
|
||
usName.Buffer = usNameBuffer;
|
||
usName.MaximumLength = sizeof(usNameBuffer);
|
||
usName.Length = 0;
|
||
|
||
VC_CreateNumberedName(
|
||
DeviceNumber,
|
||
L"\\Device\\",
|
||
DD_VIDCAP_DEVICE_NAME_U,
|
||
&usName
|
||
);
|
||
|
||
|
||
Status = IoCreateDevice(pDriverObject,
|
||
sizeof(DEVICE_INFO) + HWInfoSize,
|
||
&usName,
|
||
0,
|
||
FILE_DEVICE_SOUND, // device type:multimedia == sound?
|
||
FALSE, // not exclusive - more than one thread access
|
||
&pDevObj);
|
||
|
||
if (NT_SUCCESS(Status)) {
|
||
break;
|
||
}
|
||
}
|
||
|
||
|
||
if (!NT_SUCCESS(Status)) {
|
||
dprintf(("Cannot create device object"));
|
||
VC_Cleanup(pDriverObject);
|
||
return(NULL);
|
||
}
|
||
|
||
/* get pointer to device extension and init data*/
|
||
pDevInfo = (PDEVICE_INFO) pDevObj->DeviceExtension;
|
||
|
||
pDevInfo->DeviceNumber = DeviceNumber;
|
||
pDevInfo->State = State_Idle;
|
||
pDevInfo->DeviceInUse = 0;
|
||
pDevInfo->pSystemBuffer = NULL;
|
||
|
||
/* ptr back to device object */
|
||
pDevInfo->pDeviceObject = pDevObj;
|
||
|
||
/* initialise the device mutex */
|
||
KeInitializeMutex(&pDevInfo->Mutex, 1);
|
||
|
||
/* init dpc data and register with Io system */
|
||
pDevInfo->DpcRequested = FALSE;
|
||
IoInitializeDpcRequest(pDevObj, VC_Deferred);
|
||
|
||
/* initialise queue of add-buffer irps */
|
||
InitializeListHead(&pDevInfo->BufferHead);
|
||
|
||
/* initialise queue of wait-error irps */
|
||
InitializeListHead(&pDevInfo->WaitErrorHead);
|
||
pDevInfo->nSkipped = 0;
|
||
|
||
/* initialise the spinlock - this is used to protect the
|
||
* DPC routine for callers to VC_SynchronizeDPC
|
||
*/
|
||
KeInitializeSpinLock(&pDevInfo->DeviceSpinLock);
|
||
|
||
|
||
|
||
/*
|
||
* create a link into dosdevices for this same name so that win32
|
||
* apps can open the device (they need to open eg \\.\VidCap0 )
|
||
*/
|
||
if (!NT_SUCCESS(VC_CreateLink(&usName))) {
|
||
|
||
dprintf(("cannot create link"));
|
||
VC_Cleanup(pDriverObject);
|
||
return(NULL);
|
||
}
|
||
|
||
/*
|
||
* save the registry pathname in pDevInfo, appending the
|
||
* Parameters subkey
|
||
*/
|
||
pDevInfo->ParametersKey = VC_SaveRegistryPathName(RegistryPathName);
|
||
if (pDevInfo->ParametersKey == NULL) {
|
||
VC_Cleanup(pDriverObject);
|
||
return(NULL);
|
||
}
|
||
|
||
/*
|
||
* now store the device name in the registry. We need to store the
|
||
* base name + number, not the leading \Device or \DosDevices, since
|
||
* these are not seen by the win32 app (they prepend \\.\ to this
|
||
* base name).
|
||
*/
|
||
usName.Buffer = usNameBuffer;
|
||
usName.MaximumLength = sizeof(usNameBuffer);
|
||
usName.Length = 0;
|
||
|
||
VC_CreateNumberedName(
|
||
DeviceNumber,
|
||
NULL,
|
||
DD_VIDCAP_DEVICE_NAME_U,
|
||
&usName
|
||
);
|
||
|
||
/*
|
||
* convert the counted string to a null-terminated string
|
||
* before passing to the registry. Note that the Length field of
|
||
* counted strings is a byte length.
|
||
*/
|
||
usName.Buffer[usName.Length / sizeof(WCHAR)] = UNICODE_NULL;
|
||
|
||
Status = RtlWriteRegistryValue(
|
||
RTL_REGISTRY_ABSOLUTE,
|
||
pDevInfo->ParametersKey,
|
||
REG_DEVNAME,
|
||
REG_SZ,
|
||
usName.Buffer,
|
||
usName.Length
|
||
);
|
||
|
||
|
||
|
||
|
||
|
||
/* set up dispatch table */
|
||
pDriverObject->DriverUnload = VC_Cleanup;
|
||
pDriverObject->MajorFunction[IRP_MJ_CREATE] = VC_Dispatch;
|
||
pDriverObject->MajorFunction[IRP_MJ_CLOSE] = VC_Dispatch;
|
||
pDriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = VC_Dispatch;
|
||
|
||
return (pDevInfo);
|
||
|
||
}
|
||
|
||
/*
|
||
* VC_GetResources
|
||
*
|
||
* map port and frame buffer into system address space or i/o space, and
|
||
* report resource usage of the ports, interrupt and physical memory
|
||
* address used.
|
||
*
|
||
* Note: We do not connect the interrupt: this is not done until
|
||
* a subsequent call to VC_ConnectInterrupt. We do, however, report
|
||
* usage of the interrupt.
|
||
*
|
||
* Don't call VC_Cleanup to clean up if failure: the h/w driver will
|
||
* call VC_Cleanup AFTER writing an errorcode to the registry
|
||
*
|
||
* we return TRUE if success, or FALSE if we couldn't get the resources.
|
||
*/
|
||
BOOLEAN
|
||
VC_GetResources(
|
||
PDEVICE_INFO pDevInfo,
|
||
PDRIVER_OBJECT pDriverObject,
|
||
PUCHAR PortBase,
|
||
ULONG NrOfPorts,
|
||
ULONG Interrupt,
|
||
BOOLEAN bLatched,
|
||
PUCHAR pFrameBuffer,
|
||
ULONG FrameLength)
|
||
{
|
||
NTSTATUS Status;
|
||
|
||
/* find which bus we're on - check it is isa or (if no isa) eisa */
|
||
pDevInfo->BusType = Isa;
|
||
Status = VC_GetBus(pDevInfo->BusType, &pDevInfo->BusNumber);
|
||
if (!NT_SUCCESS(Status)) {
|
||
|
||
pDevInfo->BusType = Eisa;
|
||
Status = VC_GetBus(pDevInfo->BusType, &pDevInfo->BusNumber);
|
||
|
||
if (!NT_SUCCESS(Status)) {
|
||
return(FALSE);
|
||
}
|
||
}
|
||
|
||
/*
|
||
* map the port address - we need to do this before reporting resources
|
||
* since we need to work out whether it is memory-mapped or i/o-mapped
|
||
*/
|
||
VC_MapPort(pDevInfo, PortBase, NrOfPorts);
|
||
|
||
|
||
/* report the resources (port, interrupt, framebuffer) that we use
|
||
* and check that there is no conflict
|
||
*/
|
||
if (VC_ReportResources(pDriverObject, pDevInfo, PortBase, NrOfPorts, Interrupt, bLatched,
|
||
pFrameBuffer, FrameLength)) {
|
||
dprintf1(("resource conflict detected"));
|
||
return(FALSE);
|
||
}
|
||
|
||
/* map the frame buffer into system memory */
|
||
VC_MapMemory(pDevInfo, pFrameBuffer, FrameLength);
|
||
|
||
|
||
return (TRUE);
|
||
}
|
||
|
||
|
||
/*
|
||
* VC_ConnectInterrupt
|
||
*
|
||
* This assumes that VC_GetResources has already been called to report the
|
||
* resource usage, and that the VC_CALLBACK table has been set up
|
||
* to handle interrupts.
|
||
*
|
||
* returns TRUE if success.
|
||
*/
|
||
BOOLEAN VC_ConnectInterrupt(
|
||
PDEVICE_INFO pDevInfo,
|
||
ULONG Interrupt,
|
||
BOOLEAN bLatched)
|
||
{
|
||
KAFFINITY Affinity;
|
||
KIRQL InterruptRQL;
|
||
ULONG Vector;
|
||
NTSTATUS Status;
|
||
|
||
Vector = HalGetInterruptVector(pDevInfo->BusType,
|
||
pDevInfo->BusNumber,
|
||
Interrupt,
|
||
Interrupt,
|
||
&InterruptRQL,
|
||
&Affinity);
|
||
|
||
Status = IoConnectInterrupt(&pDevInfo->InterruptObject,
|
||
VC_InterruptService,
|
||
pDevInfo,
|
||
NULL,
|
||
Vector,
|
||
InterruptRQL,
|
||
InterruptRQL,
|
||
bLatched ? Latched : LevelSensitive,
|
||
FALSE,
|
||
Affinity,
|
||
FALSE);
|
||
|
||
return ((BOOLEAN) NT_SUCCESS(Status));
|
||
}
|
||
|
||
|
||
/*
|
||
* clean up any allocations etc that can be freed on last close.
|
||
*
|
||
* called at device unload and at last close
|
||
*/
|
||
VOID
|
||
VC_Close(PDEVICE_INFO pDevInfo)
|
||
{
|
||
PIRP pIrp;
|
||
/*
|
||
* cancel any outstanding irps - for this we need to hold the
|
||
* cancel spinlock
|
||
*/
|
||
|
||
/* cancel any in the queue of wait-error requests */
|
||
for (;;) {
|
||
pIrp = VC_ExtractNextIrp(&pDevInfo->WaitErrorHead, FALSE);
|
||
if (!pIrp) {
|
||
break;
|
||
}
|
||
|
||
pIrp->IoStatus.Information = 0;
|
||
pIrp->IoStatus.Status = STATUS_CANCELLED;
|
||
|
||
IoCompleteRequest(pIrp, IO_NO_INCREMENT);
|
||
|
||
}
|
||
|
||
|
||
/* cancel any in the queue of add-buffer requests */
|
||
for (;;) {
|
||
pIrp = VC_ExtractNextIrp(&pDevInfo->BufferHead, FALSE);
|
||
if (!pIrp) {
|
||
break;
|
||
}
|
||
|
||
pIrp->IoStatus.Information = 0;
|
||
pIrp->IoStatus.Status = STATUS_CANCELLED;
|
||
|
||
/* free the pages and mdl we allocated */
|
||
//MmUnlockPages(pIrp->MdlAddress);
|
||
//IoFreeMdl(pIrp->MdlAddress);
|
||
|
||
IoCompleteRequest(pIrp, IO_NO_INCREMENT);
|
||
|
||
}
|
||
|
||
/* free system buffer if we have one */
|
||
if (pDevInfo->pSystemBuffer) {
|
||
ExFreePool(pDevInfo->pSystemBuffer);
|
||
}
|
||
|
||
|
||
}
|
||
|
||
|
||
|
||
|
||
|
||
|
||
VOID
|
||
VC_Cleanup(
|
||
PDRIVER_OBJECT pDriverObject
|
||
)
|
||
/*++
|
||
|
||
Routine Description
|
||
|
||
Cleanup on driver unload or aborted load.
|
||
|
||
Arguments
|
||
pDriverObject - pointer to a driver object
|
||
|
||
Return Value
|
||
|
||
None
|
||
|
||
--*/
|
||
{
|
||
PDEVICE_INFO pDevInfo;
|
||
UNICODE_STRING usLink;
|
||
WCHAR LinkName[MAX_VIDCAP_NAME_LENGTH];
|
||
NTSTATUS Status;
|
||
|
||
|
||
if (pDriverObject->DeviceObject == NULL) {
|
||
/*
|
||
* if there is no device created yet, then there can
|
||
* be nothing to do
|
||
*/
|
||
return;
|
||
}
|
||
|
||
pDevInfo = (PDEVICE_INFO) pDriverObject->DeviceObject->DeviceExtension;
|
||
|
||
|
||
// clean up irp q and sysbuffer
|
||
VC_Close(pDevInfo);
|
||
|
||
/* call the device-specific cleanup if there is one */
|
||
if (pDevInfo->Callback.CleanupFunc != NULL) {
|
||
pDevInfo->Callback.CleanupFunc(pDevInfo);
|
||
}
|
||
|
||
dprintf2(("completed h/w cleanup"));
|
||
|
||
/* free memory mapping if memory-mapped i/o */
|
||
if (pDevInfo->PortMemType == 0) {
|
||
MmUnmapIoSpace(pDevInfo->PortBase, pDevInfo->NrOfPorts);
|
||
dprintf2(("unmapped ports"));
|
||
}
|
||
|
||
|
||
if (pDevInfo->FrameBase != NULL) {
|
||
if (pDevInfo->FrameMemType == 0) {
|
||
MmUnmapIoSpace(pDevInfo->FrameBase, pDevInfo->FrameLength);
|
||
dprintf2(("unmapped frame buffer"));
|
||
}
|
||
}
|
||
|
||
|
||
if (pDevInfo->InterruptObject) {
|
||
IoDisconnectInterrupt(pDevInfo->InterruptObject);
|
||
}
|
||
|
||
dprintf2(("disconnected interrupt"));
|
||
|
||
|
||
/* un-report any resources used by the driver and the device */
|
||
{
|
||
CM_RESOURCE_LIST EmptyList;
|
||
BOOLEAN ResourceConflict;
|
||
|
||
EmptyList.Count = 0;
|
||
IoReportResourceUsage(NULL,
|
||
pDriverObject,
|
||
&EmptyList,
|
||
sizeof(ULONG),
|
||
pDriverObject->DeviceObject,
|
||
&EmptyList,
|
||
sizeof(ULONG),
|
||
FALSE,
|
||
&ResourceConflict);
|
||
}
|
||
|
||
/* free paged-pool registry pathname */
|
||
if (pDevInfo->ParametersKey != NULL) {
|
||
ExFreePool(pDevInfo->ParametersKey);
|
||
pDevInfo->ParametersKey = NULL;
|
||
}
|
||
|
||
|
||
/* free symbolic link */
|
||
usLink.Buffer = LinkName;
|
||
usLink.MaximumLength = sizeof(LinkName);
|
||
usLink.Length = 0;
|
||
|
||
VC_CreateNumberedName(
|
||
pDevInfo->DeviceNumber,
|
||
L"\\DosDevices\\",
|
||
DD_VIDCAP_DEVICE_NAME_U,
|
||
&usLink
|
||
);
|
||
|
||
Status = IoDeleteSymbolicLink(&usLink);
|
||
|
||
/* free device object */
|
||
|
||
IoDeleteDevice(pDriverObject->DeviceObject);
|
||
|
||
dprintf1(("driver unloaded"));
|
||
|
||
}
|