windows-nt/Source/XPSP1/NT/printscan/wia/kernel/serscan/serscan.c
2020-09-26 16:20:57 +08:00

771 lines
17 KiB
C
Raw 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) 1998 Microsoft Corporation
Module Name:
serscan.c
Abstract:
This module contains the code for a serial imaging devices
suport class driver.
Author:
Vlad Sadovsky vlads 10-April-1998
Environment:
Kernel mode
Revision History :
vlads 04/10/1998 Created first draft
--*/
#include "serscan.h"
#include "serlog.h"
#include <initguid.h>
#include <devguid.h>
#include <wiaintfc.h>
#if DBG
ULONG SerScanDebugLevel = -1;
#endif
const PHYSICAL_ADDRESS PhysicalZero = {0};
//
// Keep track of the number of Serial port devices created...
//
ULONG g_NumPorts = 0;
//
// Definition of OpenCloseMutex.
//
extern ULONG OpenCloseReferenceCount = 1;
extern PFAST_MUTEX OpenCloseMutex = NULL;
//
//
#ifdef ALLOC_PRAGMA
#pragma alloc_text(INIT, DriverEntry)
#pragma alloc_text(PAGE, SerScanAddDevice)
#endif
NTSTATUS
DriverEntry(
IN PDRIVER_OBJECT DriverObject,
IN PUNICODE_STRING RegistryPath
)
/*++
Routine Description:
This routine is called at system initialization time to initialize
this driver.
Arguments:
DriverObject - Supplies the driver object.
RegistryPath - Supplies the registry path for this driver.
Return Value:
STATUS_SUCCESS - We could initialize at least one device.
STATUS_NO_SUCH_DEVICE - We could not in itialize even one device.
--*/
{
int i;
PAGED_CODE();
#if DBG
DebugDump(SERINITDEV,("Entering DriverEntry\n"));
#endif
//
// Initialize the Driver Object with driver's entry points
//
DriverObject->DriverExtension->AddDevice = SerScanAddDevice;
DriverObject->DriverUnload = SerScanUnload;
#ifdef DEAD_CODE
for (i=0; i < IRP_MJ_MAXIMUM_FUNCTION; i++) {
DriverObject->MajorFunction[i]= SerScanPassThrough;
}
#endif
DriverObject->MajorFunction[IRP_MJ_CREATE] = SerScanCreateOpen;
DriverObject->MajorFunction[IRP_MJ_CLOSE] = SerScanClose;
DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = SerScanDeviceControl;
DriverObject->MajorFunction[IRP_MJ_PNP] = SerScanPnp;
DriverObject->MajorFunction[IRP_MJ_POWER] = SerScanPower;
//
// Following are possibly not needed, keep them here to allow
// easier tracing in when debugging. All of them resort to pass-through
// behaviour
//
#ifdef DEAD_CODE
DriverObject->MajorFunction[IRP_MJ_CLEANUP] = SerScanCleanup;
DriverObject->MajorFunction[IRP_MJ_QUERY_INFORMATION] = SerScanQueryInformationFile;
DriverObject->MajorFunction[IRP_MJ_SET_INFORMATION] = SerScanSetInformationFile;
#endif
DriverObject->MajorFunction[IRP_MJ_READ] = SerScanPassThrough;
DriverObject->MajorFunction[IRP_MJ_WRITE] = SerScanPassThrough;
return STATUS_SUCCESS;
}
NTSTATUS
SerScanAddDevice(
IN PDRIVER_OBJECT pDriverObject,
IN PDEVICE_OBJECT pPhysicalDeviceObject
)
/*++
Routine Description:
This routine is called to create a new instance of the device.
It creates FDO and attaches it to PDO
Arguments:
pDriverObject - pointer to the driver object for this instance of port.
pPhysicalDeviceObject - pointer to the device object that represents the port.
Return Value:
STATUS_SUCCESS - if successful.
STATUS_UNSUCCESSFUL - otherwise.
--*/
{
UNICODE_STRING ClassName;
UNICODE_STRING LinkName;
NTSTATUS Status;
PDEVICE_EXTENSION Extension;
PDEVICE_OBJECT pDeviceObject;
PAGED_CODE();
DebugDump(SERINITDEV,("Entering AddDevice\n"));
//
// Get the Class and Link names.
//
if (!SerScanMakeNames (g_NumPorts, &ClassName, &LinkName)) {
SerScanLogError(pDriverObject,
NULL,
PhysicalZero,
PhysicalZero,
0,
0,
0,
1,
STATUS_SUCCESS,
SER_INSUFFICIENT_RESOURCES);
DebugDump(SERERRORS,("SerScan: Could not form Unicode name strings.\n"));
return STATUS_INSUFFICIENT_RESOURCES;
}
//
// Create the device object for this device.
//
Status = IoCreateDevice(pDriverObject,
sizeof(DEVICE_EXTENSION),
&ClassName,
FILE_DEVICE_SCANNER,
0,
TRUE,
&pDeviceObject);
if (!NT_SUCCESS(Status)) {
ExFreePool(ClassName.Buffer);
ExFreePool(LinkName.Buffer);
SerScanLogError(pDriverObject,
NULL,
PhysicalZero,
PhysicalZero,
0,
0,
0,
9,
STATUS_SUCCESS,
SER_INSUFFICIENT_RESOURCES);
DebugDump(SERERRORS, ("SERPORT: Could not create a device for %d\n", g_NumPorts));
return Status;
}
//
// The device object has a pointer to an area of non-paged
// pool allocated for this device. This will be the device
// extension.
//
Extension = pDeviceObject->DeviceExtension;
//
// Zero all of the memory associated with the device
// extension.
//
RtlZeroMemory(Extension, sizeof(DEVICE_EXTENSION));
//
// Get a "back pointer" to the device object.
//
Extension->DeviceObject = pDeviceObject;
Extension->Pdo = pPhysicalDeviceObject;
Extension->AttachedDeviceObject = NULL;
Extension->AttachedFileObject = NULL;
//
// Setup buffered I/O
//
pDeviceObject->Flags |= DO_BUFFERED_IO;
//
// Indicate our power code is pageable
//
pDeviceObject->Flags |= DO_POWER_PAGABLE;
//
// Attach our new Device to our parents stack.
//
Extension->LowerDevice = IoAttachDeviceToDeviceStack(
pDeviceObject,
pPhysicalDeviceObject);
if (NULL == Extension->LowerDevice) {
ExFreePool(ClassName.Buffer);
ExFreePool(LinkName.Buffer);
IoDeleteDevice(pDeviceObject);
return STATUS_UNSUCCESSFUL;
}
Extension->ClassName = ClassName;
Extension->SymbolicLinkName = LinkName;
Status = SerScanHandleSymbolicLink(
pPhysicalDeviceObject,
&Extension->InterfaceNameString,
TRUE
);
//
// We have created the device, so increment the counter
// that keeps track.
//
g_NumPorts++;
//
// Initiliaze the rest of device extension
//
Extension->ReferenceCount = 1;
Extension->Removing = FALSE;
Extension->OpenCount = 0;
KeInitializeEvent(&Extension->RemoveEvent,
NotificationEvent,
FALSE
);
// ExInitializeResourceLite(&Extension->Resource);
ExInitializeFastMutex(&Extension->Mutex);
//
// Clear InInit flag to indicate device object can be used
//
pDeviceObject->Flags &= ~(DO_DEVICE_INITIALIZING);
return STATUS_SUCCESS;
}
BOOLEAN
SerScanMakeNames(
IN ULONG SerialPortNumber,
OUT PUNICODE_STRING ClassName,
OUT PUNICODE_STRING LinkName
)
/*++
Routine Description:
This routine generates the names \Device\SerScanN.
This routine will allocate pool so that the buffers of
these unicode strings need to be eventually freed.
Arguments:
SerialPortNumber - Supplies the serial port number.
ClassName - Returns the class name.
LinkName - Returns the link name.
Return Value:
FALSE - Failure.
TRUE - Success.
--*/
{
UNICODE_STRING Prefix;
UNICODE_STRING Digits;
UNICODE_STRING LinkPrefix;
UNICODE_STRING LinkDigits;
WCHAR DigitsBuffer[10];
WCHAR LinkDigitsBuffer[10];
UNICODE_STRING ClassSuffix;
UNICODE_STRING LinkSuffix;
NTSTATUS Status;
//
// Put together local variables for constructing names.
//
RtlInitUnicodeString(&Prefix, L"\\Device\\");
RtlInitUnicodeString(&LinkPrefix, L"\\DosDevices\\");
//
// WORKWORK: Change the name to be device specific.
//
RtlInitUnicodeString(&ClassSuffix, SERSCAN_NT_SUFFIX);
RtlInitUnicodeString(&LinkSuffix, SERSCAN_LINK_NAME);
Digits.Length = 0;
Digits.MaximumLength = 20;
Digits.Buffer = DigitsBuffer;
LinkDigits.Length = 0;
LinkDigits.MaximumLength = 20;
LinkDigits.Buffer = LinkDigitsBuffer;
Status = RtlIntegerToUnicodeString(SerialPortNumber, 10, &Digits);
if (!NT_SUCCESS(Status)) {
return FALSE;
}
Status = RtlIntegerToUnicodeString(SerialPortNumber + 1, 10, &LinkDigits);
if (!NT_SUCCESS(Status)) {
return FALSE;
}
//
// Make the class name.
//
ClassName->Length = 0;
ClassName->MaximumLength = Prefix.Length + ClassSuffix.Length +
Digits.Length + sizeof(WCHAR);
ClassName->Buffer = ExAllocatePool(PagedPool, ClassName->MaximumLength);
if (!ClassName->Buffer) {
return FALSE;
}
RtlZeroMemory(ClassName->Buffer, ClassName->MaximumLength);
RtlAppendUnicodeStringToString(ClassName, &Prefix);
RtlAppendUnicodeStringToString(ClassName, &ClassSuffix);
RtlAppendUnicodeStringToString(ClassName, &Digits);
//
// Make the link name.
//
LinkName->Length = 0;
LinkName->MaximumLength = LinkPrefix.Length + LinkSuffix.Length +
LinkDigits.Length + sizeof(WCHAR);
LinkName->Buffer = ExAllocatePool(PagedPool, LinkName->MaximumLength);
if (!LinkName->Buffer) {
ExFreePool(ClassName->Buffer);
return FALSE;
}
RtlZeroMemory(LinkName->Buffer, LinkName->MaximumLength);
RtlAppendUnicodeStringToString(LinkName, &LinkPrefix);
RtlAppendUnicodeStringToString(LinkName, &LinkSuffix);
RtlAppendUnicodeStringToString(LinkName, &LinkDigits);
return TRUE;
}
NTSTATUS
SerScanCleanup(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
This routine is the dispatch for a cleanup requests.
Arguments:
DeviceObject - Supplies the device object.
Irp - Supplies the I/O request packet.
Return Value:
STATUS_SUCCESS - Success.
--*/
{
NTSTATUS Status;
PDEVICE_EXTENSION Extension;
Extension = DeviceObject->DeviceExtension;
//
// Call down to the parent and wait on the Cleanup IRP to complete...
//
Status = SerScanCallParent(Extension,
Irp,
WAIT,
NULL);
DebugDump(SERIRPPATH,
("SerScan: [Cleanup] After CallParent Status = %x\n",
Status));
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return Status;
}
VOID
SerScanCancelRequest(
PDEVICE_OBJECT DeviceObject,
PIRP Irp
)
/*++
Routine Description:
This routine is used to cancel any request in the Serial driver.
Arguments:
DeviceObject - Pointer to the device object for this device
Irp - Pointer to the IRP to be canceled.
Return Value:
None.
--*/
{
NTSTATUS Status;
PDEVICE_EXTENSION Extension;
Extension = DeviceObject->DeviceExtension;
//
// Call down to the parent and wait on the Cleanup IRP to complete...
//
Status = SerScanCallParent(Extension,
Irp,
WAIT,
NULL);
DebugDump(SERIRPPATH,
("SerScan: [Cleanup] After CallParent Status = %x\n",
Status));
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return;
}
NTSTATUS
SerScanQueryInformationFile(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
This routine is used to query the end of file information on
the opened Serial port. Any other file information request
is retured with an invalid parameter.
This routine always returns an end of file of 0.
Arguments:
DeviceObject - Supplies the device object.
Irp - Supplies the I/O request packet.
Return Value:
STATUS_SUCCESS - Success.
STATUS_INVALID_PARAMETER - Invalid file information request.
STATUS_BUFFER_TOO_SMALL - Buffer too small.
--*/
{
NTSTATUS Status;
PDEVICE_EXTENSION Extension;
Extension = DeviceObject->DeviceExtension;
Status = SerScanCallParent(Extension,
Irp,
WAIT,
NULL);
DebugDump(SERIRPPATH,
("SerScan: [Cleanup] After CallParent Status = %x\n",
Status));
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return Status;
}
NTSTATUS
SerScanSetInformationFile(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
This routine is used to set the end of file information on
the opened Serial port. Any other file information request
is retured with an invalid parameter.
This routine always ignores the actual end of file since
the query information code always returns an end of file of 0.
Arguments:
DeviceObject - Supplies the device object.
Irp - Supplies the I/O request packet.
Return Value:
STATUS_SUCCESS - Success.
STATUS_INVALID_PARAMETER - Invalid file information request.
--*/
{
NTSTATUS Status;
PDEVICE_EXTENSION Extension;
Extension = DeviceObject->DeviceExtension;
Status = SerScanCallParent(Extension,
Irp,
WAIT,
NULL);
DebugDump(SERIRPPATH,
("SerScan: [Cleanup] After CallParent Status = %x\n",
Status));
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return Status;
}
VOID
SerScanUnload(
IN PDRIVER_OBJECT DriverObject
)
/*++
Routine Description:
This routine loops through the device list and cleans up after
each of the devices.
Arguments:
DriverObject - Supplies the driver object.
Return Value:
None.
--*/
{
PDEVICE_OBJECT CurrentDevice;
PDEVICE_OBJECT NextDevice;
PDEVICE_EXTENSION Extension;
DebugDump(SERUNLOAD,
("SerScan: In SerUnload\n"));
CurrentDevice = DriverObject->DeviceObject;
while (NULL != CurrentDevice){
Extension = CurrentDevice->DeviceExtension;
if(NULL != Extension->SymbolicLinkName.Buffer){
if (Extension->CreatedSymbolicLink) {
IoDeleteSymbolicLink(&Extension->SymbolicLinkName);
RtlDeleteRegistryValue(RTL_REGISTRY_DEVICEMAP,
L"Serial Scanners",
Extension->SymbolicLinkName.Buffer);
} // if (Extension->CreatedSymbolicLink)
ExFreePool(Extension->SymbolicLinkName.Buffer);
Extension->SymbolicLinkName.Buffer = NULL;
} // if(NULL != Extension->SymbolicLinkName.Buffer)
if(NULL != Extension->ClassName.Buffer){
ExFreePool(Extension->ClassName.Buffer);
Extension->ClassName.Buffer = NULL;
} // if(NULL != Extension->ClassName.Buffer)
NextDevice = CurrentDevice->NextDevice;
IoDeleteDevice(CurrentDevice);
CurrentDevice = NextDevice;
} // while (CurrentDevice = DriverObject->DeviceObject)
}
NTSTATUS
SerScanHandleSymbolicLink(
PDEVICE_OBJECT DeviceObject,
PUNICODE_STRING InterfaceName,
BOOLEAN Create
)
/*++
Routine Description:
Arguments:
DriverObject - Supplies the driver object.
Return Value:
None.
--*/
{
NTSTATUS Status;
Status = STATUS_SUCCESS;
if (Create) {
Status=IoRegisterDeviceInterface(
DeviceObject,
&GUID_DEVINTERFACE_IMAGE,
NULL,
InterfaceName
);
DebugDump(SERINITDEV,("Called IoRegisterDeviceInterface . Returned=0x%X\n",Status));
if (NT_SUCCESS(Status)) {
IoSetDeviceInterfaceState(
InterfaceName,
TRUE
);
DebugDump(SERINITDEV,("Called IoSetDeviceInterfaceState(TRUE) . \n"));
}
} else {
if (InterfaceName->Buffer != NULL) {
IoSetDeviceInterfaceState(
InterfaceName,
FALSE
);
DebugDump(SERINITDEV,("Called IoSetDeviceInterfaceState(FALSE) . \n"));
RtlFreeUnicodeString(
InterfaceName
);
InterfaceName->Buffer = NULL;
}
}
return Status;
}