windows-nt/Source/XPSP1/NT/drivers/storage/scsiprnt/pnp.c
2020-09-26 16:20:57 +08:00

1994 lines
51 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) Microsoft Corporation, 1997 - 1999
Module Name:
pnp.c
Abstract:
pnp code for the pnp print class
Author:
George Chrysanthakopoulos May-1998
Environment:
Kernel mode
Revision History :
dankn, 22-Jul-99 : Added ability to block & resubmit failed writes for
1394 printers to behave more like other print stacks
(i.e. USB) and therefore keep USBMON.DLL (the Win2k
port monitor) happy. USBMON does not deal well
with failed writes.
--*/
#include "printpnp.h"
#include "1394.h"
#include "ntddsbp2.h"
#include "stdarg.h"
#include "stdio.h"
#include "string.h"
VOID
PrinterUnload(
IN PDRIVER_OBJECT DriverObject
);
NTSTATUS
GetPortNumber(
HANDLE hFdoKey,
PUNICODE_STRING uni,
PULONG ulReturnNumber
);
VOID
PrinterFindDeviceIdKeys
(
PUCHAR *lppMFG,
PUCHAR *lppMDL,
PUCHAR *lppCLS,
PUCHAR *lppDES,
PUCHAR *lppAID,
PUCHAR *lppCID,
PUCHAR lpDeviceID
);
VOID
GetCheckSum(
PUCHAR Block,
USHORT Len,
PUSHORT CheckSum
);
PUCHAR
StringChr(PCHAR string, CHAR c);
VOID
StringSubst
(
PUCHAR lpS,
UCHAR chTargetChar,
UCHAR chReplacementChar,
USHORT cbS
);
NTSTATUS
DriverEntry(
IN PDRIVER_OBJECT DriverObject,
IN PUNICODE_STRING RegistryPath
);
#ifdef ALLOC_PRAGMA
#pragma alloc_text(INIT, DriverEntry)
#pragma alloc_text(PAGE, PrinterUnload)
#pragma alloc_text(PAGE, PrinterAddDevice)
#pragma alloc_text(PAGE, PrinterStartDevice)
#pragma alloc_text(PAGE, PrinterStartPdo)
#pragma alloc_text(PAGE, PrinterCreatePdo)
#pragma alloc_text(PAGE, PrinterQueryId)
#pragma alloc_text(PAGE, PrinterGetId)
#pragma alloc_text(PAGE, GetCheckSum)
#pragma alloc_text(PAGE, PrinterEnumerateDevice)
#pragma alloc_text(PAGE, PrinterRemoveDevice)
#pragma alloc_text(PAGE, CreatePrinterDeviceObject)
#pragma alloc_text(PAGE, PrinterInitFdo)
#pragma alloc_text(PAGE, GetPortNumber)
#pragma alloc_text(PAGE, PrinterRegisterPort)
#pragma alloc_text(PAGE, PrinterQueryPnpCapabilities)
#pragma alloc_text(PAGE, PrinterFindDeviceIdKeys)
#pragma alloc_text(PAGE, StringChr)
#pragma alloc_text(PAGE, StringSubst)
#endif
NTSTATUS
DriverEntry(
IN PDRIVER_OBJECT DriverObject,
IN PUNICODE_STRING RegistryPath
)
/*++
Routine Description:
This routine initializes the printer class driver. The driver
opens the port driver by name and then receives configuration
information used to attach to the Printer devices.
Arguments:
DriverObject
Return Value:
NT Status
--*/
{
CLASS_INIT_DATA InitializationData;
PAGED_CODE();
DEBUGPRINT1(("\n\nSCSI/SBP2 Printer Class Driver\n"));
//
// Zero InitData
//
RtlZeroMemory (&InitializationData, sizeof(CLASS_INIT_DATA));
//
// Set sizes
//
InitializationData.InitializationDataSize = sizeof(CLASS_INIT_DATA);
InitializationData.FdoData.DeviceExtensionSize = sizeof(FUNCTIONAL_DEVICE_EXTENSION) + sizeof(PRINTER_DATA);
InitializationData.FdoData.DeviceType = FILE_DEVICE_PRINTER;
InitializationData.FdoData.DeviceCharacteristics = 0;
//
// Set entry points
//
InitializationData.FdoData.ClassInitDevice = PrinterInitFdo;
InitializationData.FdoData.ClassStartDevice = PrinterStartDevice;
InitializationData.FdoData.ClassReadWriteVerification = PrinterReadWrite;
InitializationData.FdoData.ClassDeviceControl = PrinterDeviceControl;
InitializationData.FdoData.ClassRemoveDevice = PrinterRemoveDevice;
InitializationData.FdoData.ClassStopDevice = PrinterStopDevice;
InitializationData.FdoData.ClassShutdownFlush = NULL;
InitializationData.FdoData.ClassCreateClose = PrinterOpenClose;
InitializationData.PdoData.DeviceExtensionSize = sizeof(PHYSICAL_DEVICE_EXTENSION);
InitializationData.PdoData.DeviceType = FILE_DEVICE_PRINTER;
InitializationData.PdoData.DeviceCharacteristics = 0;
InitializationData.PdoData.ClassStartDevice = PrinterStartPdo;
InitializationData.PdoData.ClassInitDevice = PrinterInitPdo;
InitializationData.PdoData.ClassRemoveDevice = PrinterRemoveDevice;
InitializationData.PdoData.ClassStopDevice = PrinterStopDevice;
InitializationData.PdoData.ClassPowerDevice = NULL;
InitializationData.PdoData.ClassError = NULL;
InitializationData.PdoData.ClassReadWriteVerification = PrinterReadWrite;
InitializationData.PdoData.ClassCreateClose = NULL;
InitializationData.PdoData.ClassDeviceControl = PrinterDeviceControl;
InitializationData.PdoData.ClassQueryPnpCapabilities = PrinterQueryPnpCapabilities;
InitializationData.ClassEnumerateDevice = PrinterEnumerateDevice;
InitializationData.ClassQueryId = PrinterQueryId;
InitializationData.ClassAddDevice = PrinterAddDevice;
InitializationData.ClassUnload = PrinterUnload;
//
// Call the class init routine
//
return ClassInitialize( DriverObject, RegistryPath, &InitializationData);
} // end DriverEntry()
VOID
PrinterUnload(
IN PDRIVER_OBJECT DriverObject
)
/*++
Routine Description:
Does nothing really...
Arguments:
DriverObject - the driver being unloaded
Return Value:
none
--*/
{
PAGED_CODE();
UNREFERENCED_PARAMETER(DriverObject);
return;
}
NTSTATUS
PrinterAddDevice(
IN PDRIVER_OBJECT DriverObject,
IN PDEVICE_OBJECT PhysicalDeviceObject
)
/*++
Routine Description:
This routine creates and initializes a new FDO for the corresponding
PDO. It may perform property queries on the FDO but cannot do any
media access operations.
Arguments:
DriverObject - Printer class driver object.
Pdo - the physical device object we are being added to
Return Value:
status
--*/
{
ULONG printerCount = 0;
NTSTATUS status;
PAGED_CODE();
//
// Get the address of the count of the number of cdroms already initialized.
//
do {
status = CreatePrinterDeviceObject(
DriverObject,
PhysicalDeviceObject,
&printerCount);
printerCount++;
} while (status == STATUS_OBJECT_NAME_COLLISION);
DEBUGPRINT1(("SCSIPRNT: AddDevice, exit with status %x\n",status));
return status;
}
NTSTATUS
CreatePrinterDeviceObject(
IN PDRIVER_OBJECT DriverObject,
IN PDEVICE_OBJECT PhysicalDeviceObject,
IN PULONG DeviceCount
)
/*++
Routine Description:
This routine creates an object for the device.
Arguments:
DriverObject - Pointer to driver object created by system.
PortDeviceObject - to connect to the port driver.
DeviceCount - Number of previously installed devices of this type.
Return Value:
NTSTATUS
--*/
{
UCHAR ntNameBuffer[64];
STRING ntNameString;
NTSTATUS status;
PCLASS_DRIVER_EXTENSION driverExtension = ClassGetDriverExtension(DriverObject);
PDEVICE_OBJECT lowerDevice = NULL;
PDEVICE_OBJECT deviceObject = NULL;
PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = NULL;
PCOMMON_DEVICE_EXTENSION commonExtension = NULL;
CCHAR dosNameBuffer[64];
CCHAR deviceNameBuffer[64];
STRING deviceNameString;
STRING dosString;
UNICODE_STRING unicodeString;
PCLASS_DEV_INFO devInfo;
PPRINTER_DATA printerData;
ULONG lptNumber;
PAGED_CODE();
lowerDevice = IoGetAttachedDeviceReference(PhysicalDeviceObject);
status = ClassClaimDevice(lowerDevice, FALSE);
if(!NT_SUCCESS(status)) {
DEBUGPRINT1(("SCSIPRINT!CreatePrinterDeviceObject: Failed to claim device %x\n",status));
ObDereferenceObject(lowerDevice);
return status;
}
//
// initialize our driver extension..
//
devInfo = &(driverExtension->InitData.FdoData);
devInfo->DeviceType = FILE_DEVICE_PRINTER;
devInfo->DeviceCharacteristics = 0;
//
// Create device object for this device.
//
sprintf(ntNameBuffer, "\\Device\\Printer%d", *DeviceCount);
status = ClassCreateDeviceObject(DriverObject,
ntNameBuffer,
PhysicalDeviceObject,
TRUE,
&deviceObject);
if (!NT_SUCCESS(status)) {
DEBUGPRINT1(("SCSIPRINT!CreatePrinterDeviceObjects: Can not create device %s, status %x\n",
ntNameBuffer,status));
goto CreateDeviceObjectExit;
}
deviceObject->Flags |= DO_DIRECT_IO;
fdoExtension = deviceObject->DeviceExtension;
commonExtension = deviceObject->DeviceExtension;
printerData = (PPRINTER_DATA)(commonExtension->DriverData);
RtlZeroMemory(printerData,sizeof(PRINTER_DATA));
printerData->DeviceIdString = NULL;
//
// Back pointer to device object.
//
fdoExtension->CommonExtension.DeviceObject = deviceObject;
//
// This is the physical device.
//
fdoExtension->CommonExtension.PartitionZeroExtension = fdoExtension;
//
// Initialize lock count to zero. The lock count is used to
// disable the ejection mechanism when media is mounted.
//
fdoExtension->LockCount = 0;
//
// Save system printer number
//
fdoExtension->DeviceNumber = *DeviceCount;
//
// Set the alignment requirements for the device based on the
// host adapter requirements
//
if (lowerDevice->AlignmentRequirement > deviceObject->AlignmentRequirement) {
deviceObject->AlignmentRequirement = lowerDevice->AlignmentRequirement;
}
//
// Save the device descriptors
//
fdoExtension->AdapterDescriptor = NULL;
fdoExtension->DeviceDescriptor = NULL;
//
// Clear the SrbFlags and disable synchronous transfers
//
fdoExtension->SrbFlags = SRB_FLAGS_DISABLE_SYNCH_TRANSFER;
//
// Finally, attach to the PDO
//
fdoExtension->LowerPdo = PhysicalDeviceObject;
fdoExtension->CommonExtension.LowerDeviceObject =
IoAttachDeviceToDeviceStack(deviceObject, PhysicalDeviceObject);
if(fdoExtension->CommonExtension.LowerDeviceObject == NULL) {
DEBUGPRINT1(("SCSIPRINT!CreatePrinterDeviceObjects: Failed attach\n"));
status = STATUS_UNSUCCESSFUL;
goto CreateDeviceObjectExit;
}
//
// Recreate the deviceName
//
sprintf(deviceNameBuffer,
"\\Device\\Printer%d",
fdoExtension->DeviceNumber);
RtlInitString(&deviceNameString,
deviceNameBuffer);
status = RtlAnsiStringToUnicodeString(&unicodeString,
&deviceNameString,
TRUE);
ASSERT(NT_SUCCESS(status));
//
// offset the lptnumber to avoid parallel port numbers.
// note that there is an increment at the beginning of the do
// loop below as well.
//
lptNumber = fdoExtension->DeviceNumber+1;
do {
lptNumber++;
sprintf(dosNameBuffer,
"\\DosDevices\\LPT%d",
lptNumber);
RtlInitString(&dosString, dosNameBuffer);
status = RtlAnsiStringToUnicodeString(&printerData->UnicodeLinkName,
&dosString,
TRUE);
if(!NT_SUCCESS(status)) {
printerData->UnicodeLinkName.Buffer = NULL;
break;
}
if ((printerData->UnicodeLinkName.Buffer != NULL) && (unicodeString.Buffer != NULL)) {
status = IoAssignArcName(&printerData->UnicodeLinkName, &unicodeString);
} else {
status = STATUS_UNSUCCESSFUL;
}
if (!NT_SUCCESS(status)) {
RtlFreeUnicodeString(&printerData->UnicodeLinkName);
DEBUGPRINT1(("SCSIPRINT!CreatePrinterDeviceObjects: Failed creating Arc Name, status %x\n",status));
}
} while (status == STATUS_OBJECT_NAME_COLLISION);
if (unicodeString.Buffer != NULL ) {
RtlFreeUnicodeString(&unicodeString);
}
if (!NT_SUCCESS(status)) {
goto CreateDeviceObjectExit;
}
printerData->LptNumber = lptNumber;
ObDereferenceObject(lowerDevice);
//
// The device is initialized properly - mark it as such.
//
deviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
return status;
CreateDeviceObjectExit:
ClassClaimDevice(lowerDevice, TRUE);
ObDereferenceObject(lowerDevice);
if (deviceObject != NULL) {
IoDeleteDevice(deviceObject);
}
DEBUGPRINT1(("SCSIPRINT!CreatePrinterDeviceObjects: Exiting with status %x\n",status));
return status;
} // end CreateDeviceObject()
NTSTATUS
PrinterInitFdo(
IN PDEVICE_OBJECT Fdo
)
{
return STATUS_SUCCESS;
}
NTSTATUS
PrinterStartDevice(
IN PDEVICE_OBJECT Fdo
)
/*++
Routine Description:
This routine will complete the cd-rom initialization. This includes
allocating sense info buffers and srb s-lists, reading drive capacity
and setting up Media Change Notification (autorun).
This routine will not clean up allocate resources if it fails - that
is left for device stop/removal
Arguments:
Fdo - a pointer to the functional device object for this device
Return Value:
status
--*/
{
PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
PCLASS_DRIVER_EXTENSION driverExtension = ClassGetDriverExtension(Fdo->DriverObject);
UNICODE_STRING dosUnicodeString;
STORAGE_PROPERTY_ID propertyId;
PVOID senseData = NULL;
PIRP irp;
PIO_STACK_LOCATION irpStack;
KEVENT event;
PPRINTER_DATA printerData = NULL;
GUID * printerGuid;
HANDLE hInterfaceKey;
UCHAR rawString[256];
PUCHAR vendorId, productId;
ULONG timeOut;
NTSTATUS status;
PSBP2_REQUEST sbp2Request = NULL;
//
// Allocate request sense buffer.
//
senseData = ExAllocatePoolWithTag(NonPagedPoolCacheAligned,
SENSE_BUFFER_SIZE,
PRINTER_TAG);
if (senseData == NULL) {
//
// The buffer cannot be allocated.
//
status = STATUS_INSUFFICIENT_RESOURCES;
return status;
}
//
// Build the lookaside list for srb'. Should only
// need a couple.
//
ClassInitializeSrbLookasideList(&(fdoExtension->CommonExtension), PRINTER_SRB_LIST_SIZE);
//
// Set the sense data pointer in the device extension.
//
fdoExtension->SenseData = senseData;
//
// printers are not partitionable so starting offset is 0.
//
fdoExtension->CommonExtension.StartingOffset.LowPart = 0;
fdoExtension->CommonExtension.StartingOffset.HighPart = 0;
//
// Set timeout value in seconds.
//
timeOut = ClassQueryTimeOutRegistryValue(Fdo);
if (timeOut) {
fdoExtension->TimeOutValue = timeOut;
} else {
fdoExtension->TimeOutValue = PRINTER_TIMEOUT;
}
printerData = (PPRINTER_DATA)(fdoExtension->CommonExtension.DriverData);
KeInitializeSpinLock(&printerData->SplitRequestSpinLock);
//
// Call port driver to get adapter capabilities.
//
propertyId = StorageAdapterProperty;
status = ClassGetDescriptor(fdoExtension->CommonExtension.LowerDeviceObject,
&propertyId,
&(fdoExtension->AdapterDescriptor));
if(!NT_SUCCESS(status)) {
DEBUGPRINT1(( "PrinterStartDevice: unable to retrieve adapter descriptor "
"[%#08lx]\n", status));
ExFreePool(senseData);
return status;
}
//
// Call port driver to get device capabilities.
//
propertyId = StorageDeviceProperty;
status = ClassGetDescriptor(fdoExtension->CommonExtension.LowerDeviceObject,
&propertyId,
&(fdoExtension->DeviceDescriptor));
if(!NT_SUCCESS(status)) {
DEBUGPRINT1(( "PrinterStartAddDevice: unable to retrieve device descriptor "
"[%#08lx]\n", status));
ExFreePool(senseData);
return status;
}
if (printerData->DeviceIdString == NULL) {
printerData->DeviceIdString = ExAllocatePool(PagedPool,256);
if (!printerData->DeviceIdString) {
return STATUS_INSUFFICIENT_RESOURCES;
}
if (fdoExtension->AdapterDescriptor->BusType == BusType1394) {
//
// to retrieve the model/vendor id from the port driver below, send a queryId
// to our PDO
//
sbp2Request = ExAllocatePool(NonPagedPool,sizeof(SBP2_REQUEST));
if (sbp2Request == NULL) {
return STATUS_INSUFFICIENT_RESOURCES;
}
irp = IoAllocateIrp((CCHAR)(Fdo->StackSize), FALSE);
if (irp == NULL) {
DEBUGPRINT1(("PrinterQueryId: Can't allocate irp\n"));
return STATUS_INSUFFICIENT_RESOURCES;
}
RtlZeroMemory(sbp2Request,sizeof(SBP2_REQUEST));
//
// set the sbp2 api call
//
sbp2Request->RequestNumber = SBP2_REQUEST_RETRIEVE_TEXT_LEAFS;
sbp2Request->u.RetrieveTextLeaf.fulFlags |= SBP2REQ_RETRIEVE_TEXT_LEAF_INDIRECT;
sbp2Request->u.RetrieveTextLeaf.Key = 0x14; // LUN key, followed 0x81 key w/ 1284 ID
//
// Construct the IRP stack for the lower level driver.
//
irpStack = IoGetNextIrpStackLocation(irp);
irpStack->MajorFunction = IRP_MJ_DEVICE_CONTROL;
irpStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_SBP2_REQUEST;
irpStack->Parameters.Others.Argument1 = sbp2Request;
KeInitializeEvent(&event,
NotificationEvent,
FALSE);
IoSetCompletionRoutine(irp,
PrinterCompletionRoutine,
&event,
TRUE,
TRUE,
TRUE);
status = IoCallDriver(fdoExtension->CommonExtension.LowerDeviceObject, irp);
KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
if(!NT_SUCCESS(status) || !NT_SUCCESS(irp->IoStatus.Status) ||
(sbp2Request->u.RetrieveTextLeaf.Buffer == NULL)) {
status = irp->IoStatus.Status;
DEBUGPRINT1(("PrinterStartDevice: 1284 retrieve failed status %xx\n",status));
sprintf(printerData->DeviceIdString,"1394 Printer");
} else {
//
// A pointer to the 1284 id TEXTUAL_LEAF is now stored in the
// sbp2Request->u.RetrieveTextLeaf.Buffer field.
//
// We want to make sure it's NULL terminated before we parse
// it, so we'll move contents of TL_Data (the actual string)
// back to the front of the buffer (overwriting all existing
// TL_Xxx fields), then zero the following 4 bytes.
//
// Note that sbp2Req->u.RetrTextLeaf.ulLength is the size in
// bytes of the entire leaf & data, minus the TL_CRC and
// TL_Length fields.
//
RtlMoveMemory(
sbp2Request->u.RetrieveTextLeaf.Buffer,
((PUCHAR) sbp2Request->u.RetrieveTextLeaf.Buffer +
FIELD_OFFSET(TEXTUAL_LEAF,TL_Data)),
sbp2Request->u.RetrieveTextLeaf.ulLength -
2 * sizeof (ULONG) // TL_Spec_Id & TL_Lang_Id fields
);
*((PULONG) ((PUCHAR) sbp2Request->u.RetrieveTextLeaf.Buffer +
sbp2Request->u.RetrieveTextLeaf.ulLength -
2 * sizeof (ULONG))) = 0;
status = PrinterGetId(sbp2Request->u.RetrieveTextLeaf.Buffer,BusQueryDeviceID,rawString,NULL);
if (!NT_SUCCESS(status)) {
sprintf(printerData->DeviceIdString,"1394 Printer");
status = STATUS_SUCCESS;
} else {
RtlCopyMemory(printerData->DeviceIdString,rawString,256);
}
ExFreePool(sbp2Request->u.RetrieveTextLeaf.Buffer);
}
IoFreeIrp(irp);
ExFreePool(sbp2Request);
//
// register with the pnp GUID so the pnp printer port enumerator loads and finds us...
//
printerGuid=(GUID *)&PNPPRINT_GUID;
status=IoRegisterDeviceInterface(fdoExtension->LowerPdo,printerGuid,NULL,&printerData->UnicodeDeviceString);
if (NT_SUCCESS(status)) {
status=IoSetDeviceInterfaceState(&printerData->UnicodeDeviceString,TRUE);
if (!NT_SUCCESS(status)) {
DEBUGPRINT1(("PrinterStartDevice: Failed setting interfaceState %x\n",status));
return status;
}
} else {
printerData->UnicodeDeviceString.Buffer = NULL;
}
//
// retrieve available port number and write it in our registry key..
// the key is closed in GetPortNumber
//
status = IoOpenDeviceInterfaceRegistryKey(&printerData->UnicodeDeviceString,KEY_ALL_ACCESS,&hInterfaceKey);
if (NT_SUCCESS(status)) {
status = GetPortNumber(hInterfaceKey, &printerData->UnicodeDeviceString, &printerData->PortNumber);
} else {
DEBUGPRINT1(("PrinterStartDevice Failed opening registry key%x\n",status));
}
} else {
vendorId = (PUCHAR) fdoExtension->DeviceDescriptor + fdoExtension->DeviceDescriptor->VendorIdOffset;
productId = (PUCHAR) fdoExtension->DeviceDescriptor + fdoExtension->DeviceDescriptor->ProductIdOffset;
printerData->PortNumber = fdoExtension->DeviceNumber;
sprintf(printerData->DeviceIdString,"Printer&Ven_%s&Prod_%s",
vendorId,
productId);
}
}
//
// If this is a 1394 printer then we want to enable blocking
// writes by default (to keep USBMON.DLL happy)
//
if (fdoExtension->AdapterDescriptor->BusType == BusType1394) {
printerData->WriteCompletionRoutine = PrinterWriteComplete;
KeInitializeTimer (&printerData->Timer);
KeInitializeDpc(
&printerData->TimerDpc,
PrinterWriteTimeoutDpc,
fdoExtension
);
} else {
printerData->WriteCompletionRoutine = ClassIoComplete;
}
ClassUpdateInformationInRegistry(Fdo, "LPT", printerData->LptNumber, NULL, 0);
return status;
} // end PrinterStartDevice()
NTSTATUS
GetPortNumber(
HANDLE hFdoKey,
PUNICODE_STRING fdoUnicodeString,
PULONG ulReturnNumber
)
{
UCHAR buf[sizeof (KEY_VALUE_PARTIAL_INFORMATION) + sizeof (ULONG)];
PWSTR pDeviceList;
PWSTR pWalkDevice;
HANDLE hInterfaceKey;
UCHAR baseNameString[32];
ANSI_STRING ansiBaseNameString;
UNICODE_STRING uncValueName,uncLinkName,uncBaseNameValueName;
UNICODE_STRING uncBaseName, uncRecyclableValueName;
UNICODE_STRING uncPortDescription, uncPortDescriptionValueName;
ULONG dwPortNum;
ULONG ulSizeUsed;
ULONG i;
ULONG ulPortNumber=0;
UCHAR portArray[MAX_NUM_PRINTERS] ;
NTSTATUS status=STATUS_SUCCESS;
NTSTATUS qStatus;
PKEY_VALUE_PARTIAL_INFORMATION valueStruct;
GUID *printerGuid = (GUID *) &PNPPRINT_GUID;
for (i=0;i<MAX_NUM_PRINTERS;i++) {
portArray[i] = 0;
}
RtlInitUnicodeString(&uncValueName,PORT_NUM_VALUE_NAME);
RtlInitUnicodeString(&uncBaseName,BASE_1394_PORT_NAME);
RtlInitUnicodeString(&uncBaseNameValueName,BASE_PORT_NAME_VALUE_NAME);
RtlInitUnicodeString(&uncRecyclableValueName,RECYCLABLE_VALUE_NAME);
RtlInitUnicodeString(&uncPortDescription,BASE_PORT_DESCRIPTION);
RtlInitUnicodeString(&uncPortDescriptionValueName,BASE_PORT_DESCRIPTION_VALUE_NAME);
ulSizeUsed = sizeof (buf); //this is a byte to much. Oh well
valueStruct = (PKEY_VALUE_PARTIAL_INFORMATION) buf;
//
// first check if our own key, has already a port value..
//
status=ZwQueryValueKey(hFdoKey,&uncValueName,KeyValuePartialInformation,(PVOID)valueStruct,ulSizeUsed,&ulSizeUsed);
if (NT_SUCCESS(status)) {
DEBUGPRINT1(("\'PRINTER:GetPortNumber: Found existing port in our Own key\n"));
ulPortNumber=*((ULONG *)&(valueStruct->Data));
ZwClose(hFdoKey);
} else {
ZwClose (hFdoKey);
//
// search the registry for all ports present. If you find a hole, take it
// if no holes are found, take the next available slot
//
status=IoGetDeviceInterfaces(printerGuid,NULL,DEVICE_INTERFACE_INCLUDE_NONACTIVE,&pDeviceList);
if (!NT_SUCCESS(status)) {
DEBUGPRINT1(("\'PRINTER:GetPortNumber: Failed to retrive interfaces\n"));
return status;
}
pWalkDevice=pDeviceList;
while((*pWalkDevice!=0) && (NT_SUCCESS(status))) {
RtlInitUnicodeString(&uncLinkName,pWalkDevice);
if (!RtlCompareUnicodeString(fdoUnicodeString,&uncLinkName,FALSE)) {
//
// this key is the same as ours, skip it
//
pWalkDevice=pWalkDevice+wcslen(pWalkDevice)+1;
continue;
}
status=IoOpenDeviceInterfaceRegistryKey(&uncLinkName,KEY_ALL_ACCESS,&hInterfaceKey);
if (NT_SUCCESS(status)) {
qStatus = ZwQueryValueKey(hInterfaceKey,&uncValueName,KeyValuePartialInformation,valueStruct,ulSizeUsed,&ulSizeUsed);
if (NT_SUCCESS(qStatus)) {
dwPortNum = *((ULONG *)&(valueStruct->Data));
qStatus = ZwQueryValueKey(hInterfaceKey,&uncRecyclableValueName,KeyValuePartialInformation,valueStruct,ulSizeUsed,&ulSizeUsed);
if (!NT_SUCCESS(qStatus)) {
//
// port cant be recycled, mark as used so we dont try to grab it..
//
portArray[dwPortNum-1] = 1;
} else {
//
// port was marked recycled so we can reuse it..
//
DEBUGPRINT1(("\'GetPortNumber, Recyclable value found for port number %d\n",dwPortNum));
}
}
}
pWalkDevice=pWalkDevice+wcslen(pWalkDevice)+1;
ZwClose(hInterfaceKey);
}
ExFreePool(pDeviceList);
//
// now find the first hole, and use that port number as our port...
//
for (i=0;i<MAX_NUM_PRINTERS;i++) {
if (portArray[i]) {
ulPortNumber++;
} else {
ulPortNumber++;
break;
}
}
status = IoOpenDeviceInterfaceRegistryKey(fdoUnicodeString,KEY_ALL_ACCESS,&hFdoKey);
if (NT_SUCCESS(status)) {
//
// write the new port we just found under our FDO reg key..
//
status=ZwSetValueKey(hFdoKey,&uncValueName,0,REG_DWORD,&ulPortNumber,sizeof(ulPortNumber));
DEBUGPRINT1(("\'GetPortNumber, setting port number %d in fdo key status %x\n",ulPortNumber,status));
//
// also write our base port name
//
if (NT_SUCCESS(status)) {
status=ZwSetValueKey(hFdoKey,
&uncBaseNameValueName,
0,REG_SZ,
uncBaseName.Buffer,
uncBaseName.Length);
DEBUGPRINT1(("\'GetPortNumber, setting port name in fdo key status %x\n",status));
}
//
// write out our port description
//
if (NT_SUCCESS(status)) {
status=ZwSetValueKey(hFdoKey,
&uncPortDescriptionValueName,
0,REG_SZ,
uncPortDescription.Buffer,
uncPortDescription.Length);
DEBUGPRINT1(("\'GetPortNumber, setting port description in fdo key status %x\n",status));
}
ZwClose(hFdoKey);
}
}
DEBUGPRINT1(("\'GetPortNumber, grabbing port %d\n",ulPortNumber));
*ulReturnNumber = ulPortNumber;
return status;
}
NTSTATUS
PrinterInitPdo(
IN PDEVICE_OBJECT Fdo
)
{
return STATUS_SUCCESS;
}
NTSTATUS
PrinterStartPdo(
IN PDEVICE_OBJECT Pdo
)
/*++
Routine Description:
This routine will create the well known names for a PDO and register
it's device interfaces.
--*/
{
return STATUS_SUCCESS;
}
NTSTATUS
PrinterEnumerateDevice(
IN PDEVICE_OBJECT Fdo
)
/*++
Routine Description:
This routine is called by the class driver to update the PDO list off
of this FDO.
Since we always only have one static PDO, this is pretty simple..
Arguments:
Fdo - a pointer to the FDO being re-enumerated
Return Value:
status
--*/
{
PCOMMON_DEVICE_EXTENSION commonExtension = Fdo->DeviceExtension;
PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
PPHYSICAL_DEVICE_EXTENSION pdoExtension = NULL;
PDEVICE_OBJECT pdo = NULL;
ULONG numberListElements = 0;
NTSTATUS status;
ASSERT(commonExtension->IsFdo);
PAGED_CODE();
if (fdoExtension->AdapterDescriptor == NULL) {
//
// device removed..
//
return STATUS_UNSUCCESSFUL;
}
if (fdoExtension->AdapterDescriptor->BusType == BusType1394) {
if(fdoExtension->CommonExtension.ChildList == NULL) {
DebugPrint((1, "PrinterEnumerateDevice: Creating PDO\n"));
status = PrinterCreatePdo(Fdo, &pdo);
} else {
status = STATUS_SUCCESS;
}
} else {
status = STATUS_NOT_SUPPORTED;
}
return status;
} // end printerEnumerateDevice()
NTSTATUS
PrinterCreatePdo(
IN PDEVICE_OBJECT Fdo,
OUT PDEVICE_OBJECT *Pdo
)
/*++
Routine Description:
This routine will create and initialize a new device object
(PDO) and insert it into the FDO partition list.
Note that the PDO is actually never used. We create so the printer class
installer will run after the LPTENUM ids for this PDO were matched to the
printer inf..
Arguments:
Fdo - a pointer to the functional device object this PDO will be a child
of
Pdo - a location to store the pdo pointer upon successful completion
Return Value:
status
--*/
{
PFUNCTIONAL_DEVICE_EXTENSION fdoExtension = Fdo->DeviceExtension;
PCOMMON_DEVICE_EXTENSION commonExtension;
PDEVICE_OBJECT pdo = NULL;
PPHYSICAL_DEVICE_EXTENSION pdoExtension = NULL;
PPRINTER_DATA printerData = fdoExtension->CommonExtension.DriverData;
NTSTATUS status = STATUS_SUCCESS;
PAGED_CODE();
DebugPrint((2, "PrinterCreatePdo: Create device object %s\n",
printerData->DeviceName));
status = ClassCreateDeviceObject(Fdo->DriverObject,
printerData->DeviceName,
Fdo,
FALSE,
&pdo);
if (!NT_SUCCESS(status)) {
DebugPrint((1, "printerEnumerateDevice: Can't create device object for %s\n", printerData->DeviceName));
return status;
}
//
// Set up device extension fields.
//
pdoExtension = pdo->DeviceExtension;
commonExtension = pdo->DeviceExtension;
//
// Set up device object fields.
//
pdo->Flags |= DO_DIRECT_IO;
pdo->StackSize = (CCHAR)
commonExtension->LowerDeviceObject->StackSize + 1;
pdoExtension->IsMissing = FALSE;
commonExtension->DeviceObject = pdo;
commonExtension->PartitionZeroExtension = fdoExtension;
pdo->Flags &= ~DO_DEVICE_INITIALIZING;
*Pdo = pdo;
return status;
}
NTSTATUS
PrinterStopDevice(
IN PDEVICE_OBJECT DeviceObject,
IN UCHAR Type
)
{
DEBUGPRINT2((
"SCSIPRNT: PrinterStopDevice: DevObj=x%p, Type=%d\n",
DeviceObject,
(ULONG) Type
));
return STATUS_SUCCESS;
}
NTSTATUS
PrinterRemoveDevice(
IN PDEVICE_OBJECT DeviceObject,
IN UCHAR Type
)
/*++
Routine Description:
This routine is responsible for releasing any resources in use by the
driver and shutting down it's timer routine. This routine is called
when all outstanding requests have been completed and the device has
disappeared - no requests may be issued to the lower drivers.
Arguments:
DeviceObject - the device object being removed
Return Value:
none - this routine may not fail
--*/
{
PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
PFUNCTIONAL_DEVICE_EXTENSION deviceExtension =
DeviceObject->DeviceExtension;
PPRINTER_DATA printerData = deviceExtension->CommonExtension.DriverData;
if((Type == IRP_MN_QUERY_REMOVE_DEVICE) ||
(Type == IRP_MN_CANCEL_REMOVE_DEVICE)) {
return STATUS_SUCCESS;
}
if (commonExtension->IsFdo) {
if (printerData->DeviceIdString) {
ExFreePool(printerData->DeviceIdString);
printerData->DeviceIdString = NULL;
}
if (deviceExtension->DeviceDescriptor) {
ExFreePool(deviceExtension->DeviceDescriptor);
deviceExtension->DeviceDescriptor = NULL;
}
if (deviceExtension->AdapterDescriptor) {
ExFreePool(deviceExtension->AdapterDescriptor);
deviceExtension->AdapterDescriptor = NULL;
}
if (deviceExtension->SenseData) {
ExFreePool(deviceExtension->SenseData);
deviceExtension->SenseData = NULL;
}
if (printerData->UnicodeLinkName.Buffer != NULL ) {
IoDeassignArcName(&printerData->UnicodeLinkName);
RtlFreeUnicodeString(&printerData->UnicodeLinkName);
printerData->UnicodeLinkName.Buffer = NULL;
}
if (printerData->UnicodeDeviceString.Buffer != NULL ) {
IoSetDeviceInterfaceState(&printerData->UnicodeDeviceString,FALSE);
RtlFreeUnicodeString(&printerData->UnicodeDeviceString);
printerData->UnicodeDeviceString.Buffer = NULL;
}
ClassDeleteSrbLookasideList(commonExtension);
}
return STATUS_SUCCESS;
}
NTSTATUS
PrinterQueryId(
IN PDEVICE_OBJECT Pdo,
IN BUS_QUERY_ID_TYPE IdType,
IN PUNICODE_STRING UnicodeIdString
)
{
ANSI_STRING ansiIdString;
UCHAR rawString[256];
UCHAR finalString[256];
NTSTATUS status;
PPHYSICAL_DEVICE_EXTENSION pdoExtension = Pdo->DeviceExtension;
PCOMMON_DEVICE_EXTENSION commonExtension = Pdo->DeviceExtension;
PFUNCTIONAL_DEVICE_EXTENSION fdoExtension;
PPRINTER_DATA printerData;
PAGED_CODE();
ASSERT_PDO(Pdo);
fdoExtension = commonExtension->PartitionZeroExtension;
RtlZeroMemory(rawString,256);
RtlZeroMemory(finalString,256);
//
// FDOs printer data
//
printerData = fdoExtension->CommonExtension.DriverData;
if(IdType == BusQueryDeviceID) {
if (fdoExtension->AdapterDescriptor->BusType != BusType1394) {
sprintf(finalString,"SCSI\\%s",printerData->DeviceIdString);
} else {
//
// we want to fake our ids it so we use the legacy printing inf.
//
sprintf(finalString,"LPTENUM\\%s",printerData->DeviceIdString);
}
RtlCopyMemory(printerData->DeviceName,finalString,256);
DEBUGPRINT1(("\'PrinterQueryId, DeviceId =%s\n",printerData->DeviceName));
RtlInitAnsiString(&ansiIdString,finalString);
return RtlAnsiStringToUnicodeString(UnicodeIdString, &ansiIdString, TRUE);
}
if(IdType == BusQueryInstanceID) {
if (fdoExtension->AdapterDescriptor->BusType == BusType1394) {
sprintf(finalString,"1394_%03u",printerData->PortNumber);
} else {
sprintf(finalString,"SCSI%03u", printerData->PortNumber);
}
RtlInitAnsiString(&ansiIdString, finalString);
return RtlAnsiStringToUnicodeString(UnicodeIdString, &ansiIdString, TRUE);
}
if((IdType == BusQueryHardwareIDs) || (IdType == BusQueryCompatibleIDs)) {
sprintf(rawString,"%s",printerData->DeviceIdString);
sprintf(finalString,"%s",printerData->DeviceIdString);
if (fdoExtension->AdapterDescriptor->BusType == BusType1394) {
status = PrinterGetId(printerData->DeviceIdString,IdType,rawString,NULL);
if (IdType == BusQueryHardwareIDs) {
PrinterRegisterPort(Pdo->DeviceExtension);
}
if (NT_SUCCESS(status)) {
RtlZeroMemory(finalString,256);
sprintf(finalString,"%s",rawString);
}
}
DEBUGPRINT1(("\'PrinterQueryId, Combatible/Hw Id =%s\n",finalString));
RtlInitAnsiString(&ansiIdString, finalString);
UnicodeIdString->MaximumLength = (USHORT) RtlAnsiStringToUnicodeSize(&ansiIdString) + sizeof(UNICODE_NULL);
UnicodeIdString->Buffer = ExAllocatePoolWithTag(PagedPool,
UnicodeIdString->MaximumLength,
PRINTER_TAG);
if(UnicodeIdString->Buffer == NULL) {
return STATUS_INSUFFICIENT_RESOURCES;
}
RtlZeroMemory(UnicodeIdString->Buffer, UnicodeIdString->MaximumLength);
return RtlAnsiStringToUnicodeString(UnicodeIdString,
&ansiIdString,
FALSE);
}
return STATUS_NOT_IMPLEMENTED;
}
NTSTATUS
PrinterCompletionRoutine(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PKEVENT Event
)
{
KeSetEvent(Event, IO_NO_INCREMENT, FALSE);
return STATUS_MORE_PROCESSING_REQUIRED;
}
NTSTATUS
PrinterQueryPnpCapabilities(
IN PDEVICE_OBJECT DeviceObject,
IN PDEVICE_CAPABILITIES Capabilities
)
{
PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
PAGED_CODE();
ASSERT(DeviceObject);
ASSERT(Capabilities);
if(commonExtension->IsFdo) {
return STATUS_NOT_IMPLEMENTED;
} else {
Capabilities->RawDeviceOK = 1;
Capabilities->SurpriseRemovalOK = 1;
}
return STATUS_SUCCESS;
}
VOID
PrinterRegisterPort(
IN PPHYSICAL_DEVICE_EXTENSION DeviceExtension
)
{
HANDLE KeyHandle;
UCHAR RawString[256];
ANSI_STRING AnsiIdString;
NTSTATUS status;
UNICODE_STRING UnicodeTemp;
UNICODE_STRING UnicodeRegValueName;
PCOMMON_DEVICE_EXTENSION commonExtension = &DeviceExtension->CommonExtension;
PFUNCTIONAL_DEVICE_EXTENSION fdoExtension =
commonExtension->PartitionZeroExtension;
PDEVICE_OBJECT deviceObject = DeviceExtension->DeviceObject;
PPRINTER_DATA printerData = fdoExtension->CommonExtension.DriverData;
//
// register with the printer guid and create a Port value in the registry
// for talking to this printer (legacy junk, because the spooler expects it..)
//
status = IoOpenDeviceRegistryKey (deviceObject,
PLUGPLAY_REGKEY_DEVICE,
KEY_ALL_ACCESS,
&KeyHandle );
if (NT_SUCCESS(status)) {
//
// Create a new value under our instance, for the port number
//
sprintf(RawString,"PortName");
RtlInitAnsiString(&AnsiIdString,RawString);
RtlAnsiStringToUnicodeString(&UnicodeRegValueName,&AnsiIdString,TRUE);
if (fdoExtension->AdapterDescriptor->BusType == BusType1394) {
sprintf(RawString,"1394_%03u",printerData->PortNumber);
} else {
sprintf(RawString,"SCSI%03u",printerData->PortNumber);
}
RtlInitAnsiString(&AnsiIdString,RawString);
RtlAnsiStringToUnicodeString(&UnicodeTemp,&AnsiIdString,TRUE);
status = ZwSetValueKey(KeyHandle,
&UnicodeRegValueName,
0,
REG_SZ,
UnicodeTemp.Buffer,
UnicodeTemp.Length*sizeof(UCHAR));
ZwClose(KeyHandle);
RtlFreeUnicodeString(&UnicodeRegValueName);
RtlFreeUnicodeString(&UnicodeTemp);
}
}
NTSTATUS
PrinterGetId
(
IN PUCHAR DeviceIdString,
IN ULONG Type,
OUT PUCHAR resultString,
OUT PUCHAR descriptionString
)
/*
Description:
Creates Id's from the device id retrieved from the printer
Parameters:
DeviceId - String with raw device id
Type - What of id we want as a result
Id - requested id
Return Value:
NTSTATUS
*/
{
NTSTATUS status;
USHORT checkSum=0; // A 16 bit check sum
UCHAR nodeName[16] = "LPTENUM\\";
// The following are used to generate sub-strings from the Device ID string
// to get the DevNode name, and to update the registry
PUCHAR MFG = NULL; // Manufature name
PUCHAR MDL = NULL; // Model name
PUCHAR CLS = NULL; // Class name
PUCHAR AID = NULL; // Hardare ID
PUCHAR CID = NULL; // Compatible IDs
PUCHAR DES = NULL; // Device Description
status = STATUS_SUCCESS;
switch(Type) {
case BusQueryDeviceID:
// Extract the usefull fields from the DeviceID string. We want
// MANUFACTURE (MFG):
// MODEL (MDL):
// AUTOMATIC ID (AID):
// COMPATIBLE ID (CID):
// DESCRIPTION (DES):
// CLASS (CLS):
PrinterFindDeviceIdKeys(&MFG, &MDL, &CLS, &DES, &AID, &CID, DeviceIdString);
// Check to make sure we got MFG and MDL as absolute minimum fields. If not
// we cannot continue.
if (!MFG || !MDL)
{
status = STATUS_NOT_FOUND;
goto GetId_Cleanup;
}
//
// Concatenate the provided MFG and MDL P1284 fields
// Checksum the entire MFG+MDL string
//
sprintf(resultString, "%s%s\0",MFG,MDL);
if (descriptionString) {
sprintf(descriptionString, "%s %s\0",MFG,MDL);
}
break;
case BusQueryHardwareIDs:
GetCheckSum(DeviceIdString, (USHORT)strlen(DeviceIdString), &checkSum);
sprintf(resultString,"%s%.20s%4X",nodeName,DeviceIdString,checkSum);
break;
case BusQueryCompatibleIDs:
//
// return only 1 id
//
GetCheckSum(DeviceIdString, (USHORT)strlen(DeviceIdString), &checkSum);
sprintf(resultString,"%.20s%4X",DeviceIdString,checkSum);
break;
}
if (Type!=BusQueryDeviceID) {
//
// Convert and spaces in the Hardware ID to underscores
//
StringSubst ((PUCHAR) resultString, ' ', '_', (USHORT)strlen(resultString));
}
GetId_Cleanup:
return(status);
}
VOID
PrinterFindDeviceIdKeys
(
PUCHAR *lppMFG,
PUCHAR *lppMDL,
PUCHAR *lppCLS,
PUCHAR *lppDES,
PUCHAR *lppAID,
PUCHAR *lppCID,
PUCHAR lpDeviceID
)
/*
Description:
This function will parse a P1284 Device ID string looking for keys
of interest to the LPT enumerator. Got it from win95 lptenum
Parameters:
lppMFG Pointer to MFG string pointer
lppMDL Pointer to MDL string pointer
lppMDL Pointer to CLS string pointer
lppDES Pointer to DES string pointer
lppCIC Pointer to CID string pointer
lppAID Pointer to AID string pointer
lpDeviceID Pointer to the Device ID string
Return Value:
no return VALUE.
If found the lpp parameters are set to the approprate portions
of the DeviceID string, and they are NULL terminated.
The actual DeviceID string is used, and the lpp Parameters just
reference sections, with appropriate null thrown in.
*/
{
PUCHAR lpKey = lpDeviceID; // Pointer to the Key to look at
PUCHAR lpValue; // Pointer to the Key's value
USHORT wKeyLength; // Length for the Key (for stringcmps)
// While there are still keys to look at.
lpValue = StringChr(lpKey, '&');
if (lpValue) {
++lpValue;
lpKey = lpValue;
}
while (lpKey != NULL)
{
while (*lpKey == ' ')
++lpKey;
// Is there a terminating COLON character for the current key?
if (!(lpValue = StringChr(lpKey, ':')) )
{
// N: OOPS, somthing wrong with the Device ID
return;
}
// The actual start of the Key value is one past the COLON
++lpValue;
//
// Compute the Key length for Comparison, including the COLON
// which will serve as a terminator
//
wKeyLength = (USHORT)(lpValue - lpKey);
//
// Compare the Key to the Know quantities. To speed up the comparison
// a Check is made on the first character first, to reduce the number
// of strings to compare against.
// If a match is found, the appropriate lpp parameter is set to the
// key's value, and the terminating SEMICOLON is converted to a NULL
// In all cases lpKey is advanced to the next key if there is one.
//
switch (*lpKey)
{
case 'M':
// Look for MANUFACTURE (MFG) or MODEL (MDL)
if ((RtlCompareMemory(lpKey, "MANUFACTURER", wKeyLength)>5) ||
(RtlCompareMemory(lpKey, "MFG", wKeyLength)==3) )
{
*lppMFG = lpValue;
if ((lpKey = StringChr(lpValue, ';'))!=NULL)
{
*lpKey = '\0';
++lpKey;
}
}
else if ((RtlCompareMemory(lpKey, "MODEL", wKeyLength)==5) ||
(RtlCompareMemory(lpKey, "MDL", wKeyLength)==3) )
{
*lppMDL = lpValue;
if ((lpKey = StringChr(lpValue, ';'))!=0)
{
*lpKey = '\0';
++lpKey;
}
}
else
{
if ((lpKey = StringChr(lpValue, ';'))!=0)
{
*lpKey = '\0';
++lpKey;
}
}
break;
case 'C':
// Look for CLASS (CLS)
if ((RtlCompareMemory(lpKey, "CLASS", wKeyLength)==5) ||
(RtlCompareMemory(lpKey, "CLS", wKeyLength)==3) )
{
*lppCLS = lpValue;
if ((lpKey = StringChr(lpValue, ';'))!=0)
{
*lpKey = '\0';
++lpKey;
}
}
else if ((RtlCompareMemory(lpKey, "COMPATIBLEID", wKeyLength)>5) ||
(RtlCompareMemory(lpKey, "CID", wKeyLength)==3) )
{
*lppCID = lpValue;
if ((lpKey = StringChr(lpValue, ';'))!=0)
{
*lpKey = '\0';
++lpKey;
}
}
else
{
if ((lpKey = StringChr(lpValue,';'))!=0)
{
*lpKey = '\0';
++lpKey;
}
}
break;
case 'D':
// Look for DESCRIPTION (DES)
if (RtlCompareMemory(lpKey, "DESCRIPTION", wKeyLength) ||
RtlCompareMemory(lpKey, "DES", wKeyLength) )
{
*lppDES = lpValue;
if ((lpKey = StringChr(lpValue, ';'))!=0)
{
*lpKey = '\0';
++lpKey;
}
}
else
{
if ((lpKey = StringChr(lpValue, ';'))!=0)
{
*lpKey = '\0';
++lpKey;
}
}
break;
case 'A':
// Look for AUTOMATIC ID (AID)
if (RtlCompareMemory(lpKey, "AUTOMATICID", wKeyLength) ||
RtlCompareMemory(lpKey, "AID", wKeyLength) )
{
*lppAID = lpValue;
if ((lpKey = StringChr(lpValue, ';'))!=0)
{
*lpKey = '\0';
++lpKey;
}
}
else
{
if ((lpKey = StringChr(lpValue, ';'))!=0)
{
*lpKey = '\0';
++lpKey;
}
}
break;
default:
// The key is uninteresting. Go to the next Key
if ((lpKey = StringChr(lpValue, ';'))!=0)
{
*lpKey = '\0';
++lpKey;
}
break;
}
}
}
VOID
GetCheckSum(
PUCHAR Block,
USHORT Len,
PUSHORT CheckSum
)
{
USHORT i;
UCHAR lrc;
USHORT crc = 0;
unsigned short crc16a[] = {
0000000, 0140301, 0140601, 0000500,
0141401, 0001700, 0001200, 0141101,
0143001, 0003300, 0003600, 0143501,
0002400, 0142701, 0142201, 0002100,
};
unsigned short crc16b[] = {
0000000, 0146001, 0154001, 0012000,
0170001, 0036000, 0024000, 0162001,
0120001, 0066000, 0074000, 0132001,
0050000, 0116001, 0104001, 0043000,
};
//
// Calculate CRC using tables.
//
UCHAR tmp;
for ( i=0; i<Len; i++) {
tmp = Block[i] ^ (UCHAR)crc;
crc = (crc >> 8) ^ crc16a[tmp & 0x0f] ^ crc16b[tmp >> 4];
}
*CheckSum = crc;
}
PUCHAR
StringChr(PCHAR string, CHAR c)
{
ULONG i=0;
if (!string)
return(NULL);
while (*string) {
if (*string==c)
return(string);
string++;
i++;
}
return(NULL);
}
VOID
StringSubst
(
PUCHAR lpS,
UCHAR chTargetChar,
UCHAR chReplacementChar,
USHORT cbS
)
{
USHORT iCnt = 0;
while ((lpS != '\0') && (iCnt++ < cbS))
if (*lpS == chTargetChar)
*lpS++ = chReplacementChar;
else
++lpS;
}