1237 lines
29 KiB
C
1237 lines
29 KiB
C
|
/*++
|
|||
|
|
|||
|
Copyright (c) 1992-4 Microsoft Corporation
|
|||
|
|
|||
|
Module Name:
|
|||
|
|
|||
|
scsiprnt.c
|
|||
|
|
|||
|
Abstract:
|
|||
|
|
|||
|
The printer class driver tranlates IRPs to SRBs with embedded CDBs
|
|||
|
and sends them to its devices through the port driver.
|
|||
|
|
|||
|
Author:
|
|||
|
|
|||
|
Mike Glass (mglass)
|
|||
|
|
|||
|
Environment:
|
|||
|
|
|||
|
kernel mode only
|
|||
|
|
|||
|
Notes:
|
|||
|
|
|||
|
Revision History:
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
#include "ntddk.h"
|
|||
|
#include "ntddser.h"
|
|||
|
#include "scsi.h"
|
|||
|
#include "class.h"
|
|||
|
|
|||
|
#define MAX_SCSI_PRINT_XFER 0x00ffffff
|
|||
|
|
|||
|
VOID
|
|||
|
SplitRequest(
|
|||
|
IN PDEVICE_OBJECT DeviceObject,
|
|||
|
IN PIRP Irp,
|
|||
|
IN ULONG MaximumBytes
|
|||
|
);
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
ScsiPrinterOpenClose(
|
|||
|
IN PDEVICE_OBJECT DeviceObject,
|
|||
|
IN PIRP Irp
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine is called to establish a connection to the printer
|
|||
|
class driver. It does no more than return STATUS_SUCCESS.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
DeviceObject - Device object for a printer.
|
|||
|
Irp - Open or Close request packet
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
NT Status - STATUS_SUCCESS
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
//
|
|||
|
// Set status in Irp.
|
|||
|
//
|
|||
|
|
|||
|
Irp->IoStatus.Status = STATUS_SUCCESS;
|
|||
|
|
|||
|
//
|
|||
|
// Complete request at raised IRQ.
|
|||
|
//
|
|||
|
|
|||
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|||
|
return STATUS_SUCCESS;
|
|||
|
|
|||
|
} // end ScsiPrinterOpenClose()
|
|||
|
|
|||
|
|
|||
|
VOID
|
|||
|
BuildPrintRequest(
|
|||
|
PDEVICE_OBJECT DeviceObject,
|
|||
|
PIRP Irp
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Build SRB and CDB requests to scsi printer.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
DeviceObject - Device object representing this printer device.
|
|||
|
Irp - System IO request packet.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
|
|||
|
PIO_STACK_LOCATION currentIrpStack = IoGetCurrentIrpStackLocation(Irp);
|
|||
|
PIO_STACK_LOCATION nextIrpStack = IoGetNextIrpStackLocation(Irp);
|
|||
|
PSCSI_REQUEST_BLOCK srb;
|
|||
|
PCDB cdb;
|
|||
|
ULONG transferLength;
|
|||
|
|
|||
|
//
|
|||
|
// Allocate an Srb.
|
|||
|
//
|
|||
|
|
|||
|
if (deviceExtension->SrbZone != NULL &&
|
|||
|
(srb = ExInterlockedAllocateFromZone(
|
|||
|
deviceExtension->SrbZone,
|
|||
|
deviceExtension->SrbZoneSpinLock)) != NULL) {
|
|||
|
|
|||
|
srb->SrbFlags = SRB_FLAGS_ALLOCATED_FROM_ZONE;
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
//
|
|||
|
// Allocate Srb from nonpaged pool.
|
|||
|
// This call must succeed.
|
|||
|
//
|
|||
|
|
|||
|
srb = ExAllocatePool(NonPagedPoolMustSucceed, SCSI_REQUEST_BLOCK_SIZE);
|
|||
|
|
|||
|
srb->SrbFlags = 0;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Write length to SRB.
|
|||
|
//
|
|||
|
|
|||
|
srb->Length = SCSI_REQUEST_BLOCK_SIZE;
|
|||
|
|
|||
|
//
|
|||
|
// Set up IRP Address.
|
|||
|
//
|
|||
|
|
|||
|
srb->OriginalRequest = Irp;
|
|||
|
|
|||
|
//
|
|||
|
// Set up target id and logical unit number.
|
|||
|
//
|
|||
|
|
|||
|
srb->PathId = deviceExtension->PathId;
|
|||
|
srb->TargetId = deviceExtension->TargetId;
|
|||
|
srb->Lun = deviceExtension->Lun;
|
|||
|
|
|||
|
srb->Function = SRB_FUNCTION_EXECUTE_SCSI;
|
|||
|
|
|||
|
srb->DataBuffer = MmGetMdlVirtualAddress(Irp->MdlAddress);
|
|||
|
|
|||
|
//
|
|||
|
// Save byte count of transfer in SRB Extension.
|
|||
|
//
|
|||
|
|
|||
|
srb->DataTransferLength = currentIrpStack->Parameters.Write.Length;
|
|||
|
|
|||
|
//
|
|||
|
// Transfer length should never be greater than 3 bytes.
|
|||
|
//
|
|||
|
|
|||
|
ASSERT(srb->DataTransferLength <= MAX_SCSI_PRINT_XFER);
|
|||
|
|
|||
|
//
|
|||
|
// Initialize the queue actions field.
|
|||
|
//
|
|||
|
|
|||
|
srb->QueueAction = SRB_SIMPLE_TAG_REQUEST;
|
|||
|
|
|||
|
//
|
|||
|
// Queue sort key is not used.
|
|||
|
//
|
|||
|
|
|||
|
srb->QueueSortKey = 0;
|
|||
|
|
|||
|
//
|
|||
|
// Indicate auto request sense by specifying buffer and size.
|
|||
|
//
|
|||
|
|
|||
|
srb->SenseInfoBuffer = deviceExtension->SenseData;
|
|||
|
|
|||
|
srb->SenseInfoBufferLength = SENSE_BUFFER_SIZE;
|
|||
|
|
|||
|
//
|
|||
|
// Set timeout value in seconds.
|
|||
|
//
|
|||
|
|
|||
|
srb->TimeOutValue = deviceExtension->TimeOutValue;
|
|||
|
|
|||
|
//
|
|||
|
// Zero statuses.
|
|||
|
//
|
|||
|
|
|||
|
srb->SrbStatus = srb->ScsiStatus = 0;
|
|||
|
|
|||
|
srb->NextSrb = 0;
|
|||
|
|
|||
|
//
|
|||
|
// Get number of bytes to transfer.
|
|||
|
//
|
|||
|
|
|||
|
transferLength = currentIrpStack->Parameters.Write.Length;
|
|||
|
|
|||
|
//
|
|||
|
// Get pointer to CDB in SRB.
|
|||
|
//
|
|||
|
|
|||
|
cdb = (PCDB)srb->Cdb;
|
|||
|
|
|||
|
//
|
|||
|
// Indicate that 6-byte CDB's will be used.
|
|||
|
//
|
|||
|
|
|||
|
srb->CdbLength = 6;
|
|||
|
|
|||
|
cdb->PRINT.LogicalUnitNumber = deviceExtension->Lun;
|
|||
|
|
|||
|
//
|
|||
|
// Zero out reserved field.
|
|||
|
//
|
|||
|
|
|||
|
cdb->PRINT.Reserved = 0;
|
|||
|
|
|||
|
//
|
|||
|
// Move little endian values into CDB in big endian format.
|
|||
|
//
|
|||
|
|
|||
|
cdb->PRINT.TransferLength[2] = ((PFOUR_BYTE)&transferLength)->Byte0;
|
|||
|
cdb->PRINT.TransferLength[1] = ((PFOUR_BYTE)&transferLength)->Byte1;
|
|||
|
cdb->PRINT.TransferLength[0] = ((PFOUR_BYTE)&transferLength)->Byte2;
|
|||
|
|
|||
|
cdb->PRINT.Control = 0;
|
|||
|
|
|||
|
//
|
|||
|
// Set transfer direction flag and Cdb command.
|
|||
|
// The operation field is at the same location
|
|||
|
// for 6- and 10-byte CDBs.
|
|||
|
//
|
|||
|
|
|||
|
srb->SrbFlags |= SRB_FLAGS_DATA_OUT;
|
|||
|
cdb->PRINT.OperationCode = SCSIOP_PRINT;
|
|||
|
|
|||
|
//
|
|||
|
// Or in the default flags from the device object.
|
|||
|
//
|
|||
|
|
|||
|
srb->SrbFlags |= deviceExtension->SrbFlags;
|
|||
|
|
|||
|
//
|
|||
|
// Set up major SCSI function.
|
|||
|
//
|
|||
|
|
|||
|
nextIrpStack->MajorFunction = IRP_MJ_SCSI;
|
|||
|
|
|||
|
//
|
|||
|
// Save SRB address in next stack for port driver.
|
|||
|
//
|
|||
|
|
|||
|
nextIrpStack->Parameters.Scsi.Srb = srb;
|
|||
|
|
|||
|
//
|
|||
|
// Save retry count in current IRP stack.
|
|||
|
//
|
|||
|
|
|||
|
currentIrpStack->Parameters.Others.Argument4 = (PVOID)MAXIMUM_RETRIES;
|
|||
|
|
|||
|
//
|
|||
|
// Set up IoCompletion routine address.
|
|||
|
//
|
|||
|
|
|||
|
IoSetCompletionRoutine(Irp, ScsiClassIoComplete, srb, TRUE, TRUE, TRUE);
|
|||
|
|
|||
|
return;
|
|||
|
|
|||
|
} // end BuildPrintRequest()
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
ScsiPrinterWrite(
|
|||
|
IN PDEVICE_OBJECT DeviceObject,
|
|||
|
IN PIRP Irp
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This is the entry called by the I/O system for print requests.
|
|||
|
It builds the SRB and sends it to the port driver.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
DeviceObject - the system object for the device.
|
|||
|
Irp - IRP involved.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
NT Status
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
|
|||
|
PIO_STACK_LOCATION currentIrpStack = IoGetCurrentIrpStackLocation(Irp);
|
|||
|
ULONG transferByteCount = currentIrpStack->Parameters.Write.Length;
|
|||
|
ULONG maximumTransferLength =
|
|||
|
deviceExtension->PortCapabilities->MaximumTransferLength;
|
|||
|
ULONG transferPages;
|
|||
|
|
|||
|
DebugPrint((3,"ScsiPrinterWrite: Enter routine\n"));
|
|||
|
|
|||
|
//
|
|||
|
// Mark IRP with status pending.
|
|||
|
//
|
|||
|
|
|||
|
IoMarkIrpPending(Irp);
|
|||
|
|
|||
|
//
|
|||
|
// Calculate number of pages in this transfer.
|
|||
|
//
|
|||
|
|
|||
|
transferPages = ADDRESS_AND_SIZE_TO_SPAN_PAGES(
|
|||
|
MmGetMdlVirtualAddress(Irp->MdlAddress),
|
|||
|
currentIrpStack->Parameters.Write.Length);
|
|||
|
|
|||
|
//
|
|||
|
// Check if hardware maximum transfer length is larger than SCSI
|
|||
|
// print command can handle. If so, lower the maximum allowed to
|
|||
|
// the SCSI print maximum.
|
|||
|
//
|
|||
|
|
|||
|
if (maximumTransferLength > MAX_SCSI_PRINT_XFER)
|
|||
|
maximumTransferLength = MAX_SCSI_PRINT_XFER;
|
|||
|
|
|||
|
//
|
|||
|
// Check if request length is greater than the maximum number of
|
|||
|
// bytes that the hardware can transfer.
|
|||
|
//
|
|||
|
|
|||
|
if (currentIrpStack->Parameters.Write.Length > maximumTransferLength ||
|
|||
|
transferPages > deviceExtension->PortCapabilities->MaximumPhysicalPages) {
|
|||
|
|
|||
|
transferPages =
|
|||
|
deviceExtension->PortCapabilities->MaximumPhysicalPages - 1;
|
|||
|
|
|||
|
if (maximumTransferLength > transferPages << PAGE_SHIFT ) {
|
|||
|
maximumTransferLength = transferPages << PAGE_SHIFT;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Check that maximum transfer size is not zero.
|
|||
|
//
|
|||
|
|
|||
|
if (maximumTransferLength == 0) {
|
|||
|
maximumTransferLength = PAGE_SIZE;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Request greater than port driver maximum.
|
|||
|
// Break up into smaller routines.
|
|||
|
//
|
|||
|
|
|||
|
SplitRequest(DeviceObject,
|
|||
|
Irp,
|
|||
|
maximumTransferLength);
|
|||
|
|
|||
|
return STATUS_PENDING;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Build SRB and CDB for this IRP.
|
|||
|
//
|
|||
|
|
|||
|
BuildPrintRequest(DeviceObject, Irp);
|
|||
|
|
|||
|
//
|
|||
|
// Return the results of the call to the port driver.
|
|||
|
//
|
|||
|
|
|||
|
return IoCallDriver(deviceExtension->PortDeviceObject, Irp);
|
|||
|
|
|||
|
} // end ScsiPrinterWrite()
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
ScsiPrinterDeviceControl(
|
|||
|
IN PDEVICE_OBJECT DeviceObject,
|
|||
|
IN PIRP Irp
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This is the NT device control handler for Printers.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
DeviceObject - for this Printer
|
|||
|
|
|||
|
Irp - IO Request packet
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
|
|||
|
PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
|
|||
|
SCSI_REQUEST_BLOCK srb;
|
|||
|
PCDB cdb = (PCDB)srb.Cdb;
|
|||
|
PVOID outputBuffer = Irp->AssociatedIrp.SystemBuffer;
|
|||
|
ULONG bytesTransferred = 0;
|
|||
|
NTSTATUS status;
|
|||
|
|
|||
|
DebugPrint((3,"ScsiPrinterDeviceControl: Enter routine\n"));
|
|||
|
|
|||
|
//
|
|||
|
// Zero CDB in SRB on stack.
|
|||
|
//
|
|||
|
|
|||
|
RtlZeroMemory(cdb, MAXIMUM_CDB_SIZE);
|
|||
|
|
|||
|
switch (irpStack->Parameters.DeviceIoControl.IoControlCode) {
|
|||
|
|
|||
|
case IOCTL_SERIAL_SET_TIMEOUTS: {
|
|||
|
|
|||
|
PSERIAL_TIMEOUTS newTimeouts =
|
|||
|
((PSERIAL_TIMEOUTS)(Irp->AssociatedIrp.SystemBuffer));
|
|||
|
|
|||
|
if (irpStack->Parameters.DeviceIoControl.InputBufferLength <
|
|||
|
sizeof(SERIAL_TIMEOUTS)) {
|
|||
|
|
|||
|
status = STATUS_BUFFER_TOO_SMALL;
|
|||
|
} else if (newTimeouts->WriteTotalTimeoutConstant < 2000) {
|
|||
|
|
|||
|
status = STATUS_INVALID_PARAMETER;
|
|||
|
} else {
|
|||
|
deviceExtension->TimeOutValue =
|
|||
|
newTimeouts->WriteTotalTimeoutConstant / 1000;
|
|||
|
status = STATUS_SUCCESS;
|
|||
|
}
|
|||
|
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
case IOCTL_SERIAL_GET_TIMEOUTS:
|
|||
|
|
|||
|
if (irpStack->Parameters.DeviceIoControl.OutputBufferLength <
|
|||
|
sizeof(SERIAL_TIMEOUTS)) {
|
|||
|
|
|||
|
status = STATUS_BUFFER_TOO_SMALL;
|
|||
|
} else {
|
|||
|
|
|||
|
RtlZeroMemory(
|
|||
|
Irp->AssociatedIrp.SystemBuffer,
|
|||
|
sizeof(SERIAL_TIMEOUTS)
|
|||
|
);
|
|||
|
|
|||
|
Irp->IoStatus.Information = sizeof(SERIAL_TIMEOUTS);
|
|||
|
((PSERIAL_TIMEOUTS)Irp->AssociatedIrp.SystemBuffer)->
|
|||
|
WriteTotalTimeoutConstant =
|
|||
|
deviceExtension->TimeOutValue * 1000;
|
|||
|
|
|||
|
status = STATUS_SUCCESS;
|
|||
|
}
|
|||
|
|
|||
|
break;
|
|||
|
|
|||
|
default:
|
|||
|
|
|||
|
//
|
|||
|
// Pass the request to the common device control routine.
|
|||
|
//
|
|||
|
|
|||
|
return(ScsiClassDeviceControl(DeviceObject, Irp));
|
|||
|
|
|||
|
break;
|
|||
|
|
|||
|
} // end switch()
|
|||
|
|
|||
|
//
|
|||
|
// Update IRP with completion status.
|
|||
|
//
|
|||
|
|
|||
|
Irp->IoStatus.Status = status;
|
|||
|
|
|||
|
//
|
|||
|
// Complete the request.
|
|||
|
//
|
|||
|
|
|||
|
IoCompleteRequest(Irp, IO_DISK_INCREMENT);
|
|||
|
DebugPrint((2, "ScsiPrinterDeviceControl: Status is %lx\n", status));
|
|||
|
return status;
|
|||
|
|
|||
|
} // end ScsiPrinterDeviceControl()
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
CreatePrinterDeviceObject(
|
|||
|
IN PDRIVER_OBJECT DriverObject,
|
|||
|
IN PUNICODE_STRING RegistryPath,
|
|||
|
IN PDEVICE_OBJECT PortDeviceObject,
|
|||
|
IN UCHAR PortNumber,
|
|||
|
IN PULONG DeviceCount,
|
|||
|
IN PIO_SCSI_CAPABILITIES PortCapabilities,
|
|||
|
IN PSCSI_INQUIRY_DATA LunInfo
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine creates an object for the device and then calls the
|
|||
|
SCSI port driver for media capacity and sector size.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
DriverObject - Pointer to driver object created by system.
|
|||
|
PortDeviceObject - to connect to SCSI port driver.
|
|||
|
DeviceCount - Number of previously installed Printers.
|
|||
|
PortCapabilities - Pointer to structure returned by SCSI port
|
|||
|
driver describing adapter capabilites (and limitations).
|
|||
|
LunInfo - Pointer to configuration information for this device.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
UCHAR ntNameBuffer[64];
|
|||
|
UCHAR dosNameBuffer[64];
|
|||
|
STRING ntNameString;
|
|||
|
STRING dosString;
|
|||
|
UNICODE_STRING ntUnicodeString;
|
|||
|
UNICODE_STRING dosUnicodeString;
|
|||
|
NTSTATUS status;
|
|||
|
PDEVICE_OBJECT deviceObject = NULL;
|
|||
|
PDEVICE_EXTENSION deviceExtension;
|
|||
|
PVOID senseData = NULL;
|
|||
|
|
|||
|
//
|
|||
|
// Claim the device.
|
|||
|
//
|
|||
|
|
|||
|
status = ScsiClassClaimDevice(PortDeviceObject,
|
|||
|
LunInfo,
|
|||
|
FALSE,
|
|||
|
&PortDeviceObject);
|
|||
|
|
|||
|
if (!NT_SUCCESS(status)) {
|
|||
|
return(status);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Create device object for this device.
|
|||
|
//
|
|||
|
|
|||
|
sprintf(ntNameBuffer,
|
|||
|
"\\Device\\Printer%d",
|
|||
|
*DeviceCount);
|
|||
|
|
|||
|
RtlInitString(&ntNameString,
|
|||
|
ntNameBuffer);
|
|||
|
|
|||
|
DebugPrint((2,"CreatePrinterDeviceObjects: Create device object %s\n",
|
|||
|
ntNameBuffer));
|
|||
|
|
|||
|
//
|
|||
|
// Convert ANSI string to Unicode.
|
|||
|
//
|
|||
|
|
|||
|
status = RtlAnsiStringToUnicodeString(&ntUnicodeString,
|
|||
|
&ntNameString,
|
|||
|
TRUE);
|
|||
|
|
|||
|
if (!NT_SUCCESS(status)) {
|
|||
|
|
|||
|
DebugPrint((1,
|
|||
|
"CreateDiskDeviceObjects: Cannot convert string %s\n",
|
|||
|
ntNameBuffer));
|
|||
|
|
|||
|
//
|
|||
|
// Release the device since an error occured.
|
|||
|
//
|
|||
|
|
|||
|
ScsiClassClaimDevice(PortDeviceObject,
|
|||
|
LunInfo,
|
|||
|
TRUE,
|
|||
|
NULL);
|
|||
|
|
|||
|
return(status);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Create device object for this Printer.
|
|||
|
//
|
|||
|
|
|||
|
status = IoCreateDevice(DriverObject,
|
|||
|
sizeof(DEVICE_EXTENSION),
|
|||
|
&ntUnicodeString,
|
|||
|
FILE_DEVICE_PRINTER,
|
|||
|
0,
|
|||
|
FALSE,
|
|||
|
&deviceObject);
|
|||
|
|
|||
|
if (!NT_SUCCESS(status)) {
|
|||
|
DebugPrint((1,"CreatePrinterDeviceObjects: Can not create device %s\n",
|
|||
|
ntNameBuffer));
|
|||
|
|
|||
|
RtlFreeUnicodeString(&ntUnicodeString);
|
|||
|
deviceObject = NULL;
|
|||
|
goto CreatePrinterDeviceObjectExit;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Create the DOS printer driver name.
|
|||
|
//
|
|||
|
|
|||
|
sprintf(dosNameBuffer,
|
|||
|
"\\DosDevices\\LPT%d",
|
|||
|
*DeviceCount + IoGetConfigurationInformation()->ParallelCount + 1);
|
|||
|
|
|||
|
RtlInitString(&dosString, dosNameBuffer);
|
|||
|
|
|||
|
status = RtlAnsiStringToUnicodeString(&dosUnicodeString,
|
|||
|
&dosString,
|
|||
|
TRUE);
|
|||
|
|
|||
|
if(!NT_SUCCESS(status)) {
|
|||
|
dosUnicodeString.Buffer = NULL;
|
|||
|
}
|
|||
|
|
|||
|
if (dosUnicodeString.Buffer != NULL && ntUnicodeString.Buffer != NULL) {
|
|||
|
IoAssignArcName(&dosUnicodeString, &ntUnicodeString);
|
|||
|
}
|
|||
|
|
|||
|
if (dosUnicodeString.Buffer != NULL) {
|
|||
|
RtlFreeUnicodeString(&dosUnicodeString);
|
|||
|
}
|
|||
|
|
|||
|
if (ntUnicodeString.Buffer != NULL ) {
|
|||
|
RtlFreeUnicodeString(&ntUnicodeString);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Indicate that IRPs should include MDLs.
|
|||
|
//
|
|||
|
|
|||
|
deviceObject->Flags |= DO_DIRECT_IO;
|
|||
|
|
|||
|
//
|
|||
|
// Check if this is during initialization. If not indicate that
|
|||
|
// system initialization already took place and this printer
|
|||
|
// is ready to be accessed.
|
|||
|
//
|
|||
|
|
|||
|
if (!RegistryPath) {
|
|||
|
deviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Set up required stack size in device object.
|
|||
|
//
|
|||
|
|
|||
|
deviceObject->StackSize = PortDeviceObject->StackSize + 1;
|
|||
|
|
|||
|
deviceExtension = deviceObject->DeviceExtension;
|
|||
|
|
|||
|
//
|
|||
|
// Allocate spinlock for split request completion.
|
|||
|
//
|
|||
|
|
|||
|
KeInitializeSpinLock(&deviceExtension->SplitRequestSpinLock);
|
|||
|
|
|||
|
//
|
|||
|
// This is the physical device.
|
|||
|
//
|
|||
|
|
|||
|
deviceExtension->PhysicalDevice = deviceObject;
|
|||
|
|
|||
|
//
|
|||
|
// Copy port device object to device extension.
|
|||
|
//
|
|||
|
|
|||
|
deviceExtension->PortDeviceObject = PortDeviceObject;
|
|||
|
|
|||
|
//
|
|||
|
// Save address of port driver capabilities.
|
|||
|
//
|
|||
|
|
|||
|
deviceExtension->PortCapabilities = PortCapabilities;
|
|||
|
|
|||
|
//
|
|||
|
// Disable synchronous transfer for Printer requests.
|
|||
|
//
|
|||
|
|
|||
|
deviceExtension->SrbFlags = SRB_FLAGS_DISABLE_SYNCH_TRANSFER;
|
|||
|
|
|||
|
//
|
|||
|
// Allocate request sense buffer.
|
|||
|
//
|
|||
|
|
|||
|
senseData = ExAllocatePool(NonPagedPoolCacheAligned, SENSE_BUFFER_SIZE);
|
|||
|
|
|||
|
if (senseData == NULL) {
|
|||
|
|
|||
|
//
|
|||
|
// The buffer cannot be allocated.
|
|||
|
//
|
|||
|
|
|||
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|||
|
goto CreatePrinterDeviceObjectExit;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Set the sense data pointer in the device extension.
|
|||
|
//
|
|||
|
|
|||
|
deviceExtension->SenseData = senseData;
|
|||
|
|
|||
|
//
|
|||
|
// Printers are not partitionable so starting offset is 0.
|
|||
|
//
|
|||
|
|
|||
|
deviceExtension->StartingOffset.LowPart = 0;
|
|||
|
deviceExtension->StartingOffset.HighPart = 0;
|
|||
|
|
|||
|
//
|
|||
|
// Path/TargetId/LUN describes a device location on the SCSI bus.
|
|||
|
// This information comes from the LunInfo buffer.
|
|||
|
//
|
|||
|
|
|||
|
deviceExtension->PortNumber = PortNumber;
|
|||
|
deviceExtension->PathId = LunInfo->PathId;
|
|||
|
deviceExtension->TargetId = LunInfo->TargetId;
|
|||
|
deviceExtension->Lun = LunInfo->Lun;
|
|||
|
|
|||
|
//
|
|||
|
// Set timeout value in seconds.
|
|||
|
//
|
|||
|
|
|||
|
deviceExtension->TimeOutValue = 360;
|
|||
|
|
|||
|
//
|
|||
|
// Back pointer to device object.
|
|||
|
//
|
|||
|
|
|||
|
deviceExtension->DeviceObject = deviceObject;
|
|||
|
|
|||
|
return(STATUS_SUCCESS);
|
|||
|
|
|||
|
CreatePrinterDeviceObjectExit:
|
|||
|
|
|||
|
//
|
|||
|
// Release the device since an error occured.
|
|||
|
//
|
|||
|
|
|||
|
ScsiClassClaimDevice(PortDeviceObject,
|
|||
|
LunInfo,
|
|||
|
TRUE,
|
|||
|
NULL);
|
|||
|
|
|||
|
if (senseData != NULL) {
|
|||
|
ExFreePool(senseData);
|
|||
|
}
|
|||
|
|
|||
|
if (deviceObject != NULL) {
|
|||
|
IoDeleteDevice(deviceObject);
|
|||
|
}
|
|||
|
|
|||
|
return status;
|
|||
|
|
|||
|
} // end CreatePrinterDeviceObject()
|
|||
|
|
|||
|
|
|||
|
BOOLEAN
|
|||
|
FindScsiPrinters(
|
|||
|
IN PDRIVER_OBJECT DriverObject,
|
|||
|
IN PUNICODE_STRING RegistryPath,
|
|||
|
IN PDEVICE_OBJECT PortDeviceObject,
|
|||
|
IN UCHAR PortNumber
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Connect to SCSI port driver. Get adapter capabilities and
|
|||
|
SCSI bus configuration information. Search inquiry data
|
|||
|
for Printer devices to process.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
DriverObject - Printer class driver object.
|
|||
|
PortDeviceObject - SCSI port driver device object.
|
|||
|
PortNumber - The system ordinal for this scsi adapter.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
TRUE if printer device present on this SCSI adapter.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PIO_SCSI_CAPABILITIES portCapabilities;
|
|||
|
ULONG printerCount;
|
|||
|
PCHAR buffer;
|
|||
|
PSCSI_INQUIRY_DATA lunInfo;
|
|||
|
PSCSI_ADAPTER_BUS_INFO adapterInfo;
|
|||
|
PINQUIRYDATA inquiryData;
|
|||
|
ULONG scsiBus;
|
|||
|
NTSTATUS status;
|
|||
|
BOOLEAN foundDevice = FALSE;
|
|||
|
|
|||
|
//
|
|||
|
// Call port driver to get adapter capabilities.
|
|||
|
//
|
|||
|
|
|||
|
status = ScsiClassGetCapabilities(PortDeviceObject, &portCapabilities);
|
|||
|
|
|||
|
if (!NT_SUCCESS(status)) {
|
|||
|
DebugPrint((1,"FindScsiDevices: ScsiClassGetCapabilities failed\n"));
|
|||
|
return foundDevice;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Call port driver to get inquiry information to find Printers.
|
|||
|
//
|
|||
|
|
|||
|
status = ScsiClassGetInquiryData(PortDeviceObject,
|
|||
|
(PSCSI_ADAPTER_BUS_INFO *)&buffer);
|
|||
|
|
|||
|
if (!NT_SUCCESS(status)) {
|
|||
|
DebugPrint((1,"FindScsiDevices: ScsiClassGetInquiryData failed\n"));
|
|||
|
return foundDevice;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// This is a problem because it would be nice to dynamically install
|
|||
|
// additional printers. Since everytime this code is executed
|
|||
|
// the count starts at zero, this can't be done. The problem is trivial
|
|||
|
// to solve by adding a printer count to IoConfigurationData.
|
|||
|
//
|
|||
|
|
|||
|
printerCount = 0;
|
|||
|
adapterInfo = (PVOID)buffer;
|
|||
|
|
|||
|
//
|
|||
|
// For each SCSI bus this adapter supports ...
|
|||
|
//
|
|||
|
|
|||
|
for (scsiBus=0; scsiBus < adapterInfo->NumberOfBuses; scsiBus++) {
|
|||
|
|
|||
|
//
|
|||
|
// Get the SCSI bus scan data for this bus.
|
|||
|
//
|
|||
|
|
|||
|
lunInfo = (PVOID)(buffer + adapterInfo->BusData[scsiBus].InquiryDataOffset);
|
|||
|
|
|||
|
//
|
|||
|
// Search list for unclaimed disk devices.
|
|||
|
//
|
|||
|
|
|||
|
while (adapterInfo->BusData[scsiBus].InquiryDataOffset) {
|
|||
|
|
|||
|
inquiryData = (PVOID)lunInfo->InquiryData;
|
|||
|
|
|||
|
if ((inquiryData->DeviceType == PRINTER_DEVICE) &&
|
|||
|
(!lunInfo->DeviceClaimed)) {
|
|||
|
|
|||
|
DebugPrint((1,"FindScsiDevices: Vendor string is %.24s\n",
|
|||
|
inquiryData->VendorId));
|
|||
|
|
|||
|
//
|
|||
|
// Create device objects for Printer
|
|||
|
//
|
|||
|
|
|||
|
status = CreatePrinterDeviceObject(DriverObject,
|
|||
|
RegistryPath,
|
|||
|
PortDeviceObject,
|
|||
|
PortNumber,
|
|||
|
&printerCount,
|
|||
|
portCapabilities,
|
|||
|
lunInfo);
|
|||
|
|
|||
|
if (NT_SUCCESS(status)) {
|
|||
|
|
|||
|
//
|
|||
|
// Increment printer count.
|
|||
|
//
|
|||
|
|
|||
|
printerCount++;
|
|||
|
|
|||
|
//
|
|||
|
// Indicate that a printer was found.
|
|||
|
//
|
|||
|
|
|||
|
foundDevice = TRUE;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Get next LunInfo.
|
|||
|
//
|
|||
|
|
|||
|
if (lunInfo->NextInquiryDataOffset == 0) {
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
lunInfo = (PVOID)(buffer + lunInfo->NextInquiryDataOffset);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
ExFreePool(buffer);
|
|||
|
return foundDevice;
|
|||
|
|
|||
|
} // end FindScsiPrinters()
|
|||
|
|
|||
|
|
|||
|
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
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
ULONG portNumber = 0;
|
|||
|
NTSTATUS status;
|
|||
|
PFILE_OBJECT fileObject;
|
|||
|
PDEVICE_OBJECT portDeviceObject;
|
|||
|
STRING deviceNameString;
|
|||
|
CCHAR deviceNameBuffer[64];
|
|||
|
UNICODE_STRING unicodeDeviceName;
|
|||
|
BOOLEAN foundOne = FALSE;
|
|||
|
|
|||
|
DebugPrint((1,"\n\nSCSI Printer Class Driver\n"));
|
|||
|
|
|||
|
//
|
|||
|
// Set up the device driver entry points.
|
|||
|
//
|
|||
|
|
|||
|
DriverObject->MajorFunction[IRP_MJ_WRITE] = ScsiPrinterWrite;
|
|||
|
DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = ScsiPrinterDeviceControl;
|
|||
|
DriverObject->MajorFunction[IRP_MJ_CREATE] = ScsiPrinterOpenClose;
|
|||
|
DriverObject->MajorFunction[IRP_MJ_CLOSE] = ScsiPrinterOpenClose;
|
|||
|
|
|||
|
//
|
|||
|
// Open port driver device objects by name.
|
|||
|
//
|
|||
|
|
|||
|
do {
|
|||
|
|
|||
|
//
|
|||
|
// Create port driver name.
|
|||
|
//
|
|||
|
|
|||
|
sprintf(deviceNameBuffer,
|
|||
|
"\\Device\\ScsiPort%d",
|
|||
|
portNumber);
|
|||
|
|
|||
|
DebugPrint((2,"ScsiPrinterInitialize: Open %s\n",
|
|||
|
deviceNameBuffer));
|
|||
|
|
|||
|
RtlInitString(&deviceNameString,
|
|||
|
deviceNameBuffer);
|
|||
|
|
|||
|
status = RtlAnsiStringToUnicodeString(&unicodeDeviceName,
|
|||
|
&deviceNameString,
|
|||
|
TRUE);
|
|||
|
|
|||
|
if (!NT_SUCCESS(status)) {
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
status = IoGetDeviceObjectPointer(&unicodeDeviceName,
|
|||
|
FILE_READ_ATTRIBUTES,
|
|||
|
&fileObject,
|
|||
|
&portDeviceObject);
|
|||
|
|
|||
|
RtlFreeUnicodeString(&unicodeDeviceName);
|
|||
|
|
|||
|
if (NT_SUCCESS(status)) {
|
|||
|
|
|||
|
//
|
|||
|
// SCSI port driver exists.
|
|||
|
//
|
|||
|
|
|||
|
if (FindScsiPrinters(DriverObject,
|
|||
|
RegistryPath,
|
|||
|
portDeviceObject,
|
|||
|
(UCHAR)portNumber++)) {
|
|||
|
|
|||
|
foundOne = TRUE;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Dereference the file object since the port device pointer is no
|
|||
|
// longer needed. The claim device code references the port driver
|
|||
|
// pointer that is actually being used.
|
|||
|
//
|
|||
|
|
|||
|
ObDereferenceObject(fileObject);
|
|||
|
}
|
|||
|
|
|||
|
} while (NT_SUCCESS(status));
|
|||
|
|
|||
|
if (foundOne) {
|
|||
|
return STATUS_SUCCESS;
|
|||
|
} else {
|
|||
|
return STATUS_NO_SUCH_DEVICE;
|
|||
|
}
|
|||
|
|
|||
|
} // end DriverEntry()
|
|||
|
|
|||
|
VOID
|
|||
|
SplitRequest(
|
|||
|
IN PDEVICE_OBJECT DeviceObject,
|
|||
|
IN PIRP Irp,
|
|||
|
IN ULONG MaximumBytes
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Break request into smaller requests. Each new request will be the
|
|||
|
maximum transfer size that the port driver can handle or if it
|
|||
|
is the final request, it may be the residual size.
|
|||
|
|
|||
|
The number of IRPs required to process this request is written in the
|
|||
|
current stack of the original IRP. Then as each new IRP completes
|
|||
|
the count in the original IRP is decremented. When the count goes to
|
|||
|
zero, the original IRP is completed.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
DeviceObject - Pointer to the class device object to be addressed.
|
|||
|
|
|||
|
Irp - Pointer to Irp the orginal request.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
|
|||
|
PIO_STACK_LOCATION currentIrpStack = IoGetCurrentIrpStackLocation(Irp);
|
|||
|
PIO_STACK_LOCATION nextIrpStack = IoGetNextIrpStackLocation(Irp);
|
|||
|
ULONG transferByteCount = currentIrpStack->Parameters.Read.Length;
|
|||
|
LARGE_INTEGER startingOffset = currentIrpStack->Parameters.Read.ByteOffset;
|
|||
|
PVOID dataBuffer = MmGetMdlVirtualAddress(Irp->MdlAddress);
|
|||
|
ULONG dataLength = MaximumBytes;
|
|||
|
ULONG irpCount = (transferByteCount + MaximumBytes - 1) / MaximumBytes;
|
|||
|
PSCSI_REQUEST_BLOCK srb;
|
|||
|
ULONG i;
|
|||
|
|
|||
|
DebugPrint((2, "SplitRequest: Requires %d IRPs\n", irpCount));
|
|||
|
DebugPrint((2, "SplitRequest: Original IRP %lx\n", Irp));
|
|||
|
|
|||
|
//
|
|||
|
// If all partial transfers complete successfully then the status and
|
|||
|
// bytes transferred are already set up. Failing a partial-transfer IRP
|
|||
|
// will set status to error and bytes transferred to 0 during
|
|||
|
// IoCompletion. Setting bytes transferred to 0 if an IRP fails allows
|
|||
|
// asynchronous partial transfers. This is an optimization for the
|
|||
|
// successful case.
|
|||
|
//
|
|||
|
|
|||
|
Irp->IoStatus.Status = STATUS_SUCCESS;
|
|||
|
Irp->IoStatus.Information = transferByteCount;
|
|||
|
|
|||
|
//
|
|||
|
// Save number of IRPs to complete count on current stack
|
|||
|
// of original IRP.
|
|||
|
//
|
|||
|
|
|||
|
nextIrpStack->Parameters.Others.Argument1 = (PVOID) irpCount;
|
|||
|
|
|||
|
for (i = 0; i < irpCount; i++) {
|
|||
|
|
|||
|
PIRP newIrp;
|
|||
|
PIO_STACK_LOCATION newIrpStack;
|
|||
|
|
|||
|
//
|
|||
|
// Allocate new IRP.
|
|||
|
//
|
|||
|
|
|||
|
newIrp = IoAllocateIrp(DeviceObject->StackSize, FALSE);
|
|||
|
|
|||
|
if (newIrp == NULL) {
|
|||
|
|
|||
|
DebugPrint((1,"SplitRequest: Can't allocate Irp\n"));
|
|||
|
|
|||
|
//
|
|||
|
// If an Irp can't be allocated then the orginal request cannot
|
|||
|
// be executed. If this is the first request then just fail the
|
|||
|
// orginal request; otherwise just return. When the pending
|
|||
|
// requests complete, they will complete the original request.
|
|||
|
// In either case set the IRP status to failure.
|
|||
|
//
|
|||
|
|
|||
|
Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
|
|||
|
Irp->IoStatus.Information = 0;
|
|||
|
|
|||
|
if (i == 0) {
|
|||
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|||
|
}
|
|||
|
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
DebugPrint((2, "SplitRequest: New IRP %lx\n", newIrp));
|
|||
|
|
|||
|
//
|
|||
|
// Write MDL address to new IRP. In the port driver the SRB data
|
|||
|
// buffer field is used as an offset into the MDL, so the same MDL
|
|||
|
// can be used for each partial transfer. This saves having to build
|
|||
|
// a new MDL for each partial transfer.
|
|||
|
//
|
|||
|
|
|||
|
newIrp->MdlAddress = Irp->MdlAddress;
|
|||
|
|
|||
|
//
|
|||
|
// At this point there is no current stack. IoSetNextIrpStackLocation
|
|||
|
// will make the first stack location the current stack so that the
|
|||
|
// SRB address can be written there.
|
|||
|
//
|
|||
|
|
|||
|
IoSetNextIrpStackLocation(newIrp);
|
|||
|
newIrpStack = IoGetCurrentIrpStackLocation(newIrp);
|
|||
|
|
|||
|
newIrpStack->MajorFunction = currentIrpStack->MajorFunction;
|
|||
|
newIrpStack->Parameters.Read.Length = dataLength;
|
|||
|
newIrpStack->Parameters.Read.ByteOffset = startingOffset;
|
|||
|
newIrpStack->DeviceObject = DeviceObject;
|
|||
|
|
|||
|
//
|
|||
|
// Build SRB and CDB.
|
|||
|
//
|
|||
|
|
|||
|
BuildPrintRequest(DeviceObject, newIrp);
|
|||
|
|
|||
|
//
|
|||
|
// Adjust SRB for this partial transfer.
|
|||
|
//
|
|||
|
|
|||
|
newIrpStack = IoGetNextIrpStackLocation(newIrp);
|
|||
|
|
|||
|
srb = newIrpStack->Parameters.Others.Argument1;
|
|||
|
srb->DataBuffer = dataBuffer;
|
|||
|
|
|||
|
//
|
|||
|
// Write original IRP address to new IRP.
|
|||
|
//
|
|||
|
|
|||
|
newIrp->AssociatedIrp.MasterIrp = Irp;
|
|||
|
|
|||
|
//
|
|||
|
// Set the completion routine to ScsiClassIoCompleteAssociated.
|
|||
|
//
|
|||
|
|
|||
|
IoSetCompletionRoutine(newIrp,
|
|||
|
ScsiClassIoCompleteAssociated,
|
|||
|
srb,
|
|||
|
TRUE,
|
|||
|
TRUE,
|
|||
|
TRUE);
|
|||
|
|
|||
|
//
|
|||
|
// Call port driver with new request.
|
|||
|
//
|
|||
|
|
|||
|
IoCallDriver(deviceExtension->PortDeviceObject, newIrp);
|
|||
|
|
|||
|
//
|
|||
|
// Set up for next request.
|
|||
|
//
|
|||
|
|
|||
|
dataBuffer = (PCHAR)dataBuffer + MaximumBytes;
|
|||
|
|
|||
|
transferByteCount -= MaximumBytes;
|
|||
|
|
|||
|
if (transferByteCount > MaximumBytes) {
|
|||
|
|
|||
|
dataLength = MaximumBytes;
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
dataLength = transferByteCount;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Adjust disk byte offset.
|
|||
|
//
|
|||
|
|
|||
|
startingOffset.QuadPart = startingOffset.QuadPart + MaximumBytes;
|
|||
|
}
|
|||
|
|
|||
|
return;
|
|||
|
|
|||
|
} // end SplitRequest()
|
|||
|
|