|
|
/*++
Copyright (c) 1991 Microsoft Corporation
Module Name:
vdmprint.c
Abstract:
This module contains the support for printing ports which could be handled in kernel without going to ntvdm.exe
Author:
Sudeep Bharati (sudeepb) 16-Jan-1993
Revision History: William Hsieh (williamh) 31-May-1996 rewrote for Dongle support
--*/
#include "vdmp.h"
#include <ntddvdm.h>
NTSTATUS VdmpFlushPrinterWriteData ( IN USHORT Adapter );
#define DATA_PORT_OFFSET 0
#define STATUS_PORT_OFFSET 1
#define CONTROL_PORT_OFFSET 2
#define LPT1_PORT_STATUS 0x3bd
#define LPT2_PORT_STATUS 0x379
#define LPT3_PORT_STATUS 0x279
#define LPT_MASK 0xff0
#define IRQ 0x10
#define NOTBUSY 0x80
#define HOST_LPT_BUSY (1 << 0)
#define STATUS_REG_MASK 0x07
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, VdmPrinterStatus)
#pragma alloc_text(PAGE, VdmPrinterWriteData)
#pragma alloc_text(PAGE, VdmpFlushPrinterWriteData)
#pragma alloc_text(PAGE, VdmpPrinterInitialize)
#pragma alloc_text(PAGE, VdmpPrinterDirectIoOpen)
#pragma alloc_text(PAGE, VdmpPrinterDirectIoClose)
#endif
BOOLEAN VdmPrinterStatus ( IN ULONG iPort, IN ULONG cbInstructionSize, IN PKTRAP_FRAME TrapFrame )
/*++
Routine Description:
This routine handles the read operation on the printer status port
Arguments: iPort - port on which the io was trapped cbInstructionSize - Instruction size to update TsEip TrapFrame - Trap Frame
Return Value:
True if successful, False otherwise
--*/ { UCHAR PrtMode; HANDLE PrintHandle; volatile PUCHAR HostStatus; volatile PUCHAR AdapterStatus; volatile PUCHAR AdapterControl; USHORT adapter; KIRQL OldIrql; PVDM_TIB VdmTib; NTSTATUS Status; PULONG printer_status; LOGICAL IssueIoControl; PVDM_PRINTER_INFO PrtInfo; PIO_STATUS_BLOCK IoStatusBlock; PVDM_PROCESS_OBJECTS VdmObjects;
PAGED_CODE();
Status = VdmpGetVdmTib(&VdmTib);
if (!NT_SUCCESS(Status)) { return FALSE; }
OldIrql = PASSIVE_LEVEL; PrintHandle = (HANDLE)-1; PrtInfo = &VdmTib->PrinterInfo; IoStatusBlock = (PIO_STATUS_BLOCK) &VdmTib->TempArea1; printer_status = &VdmTib->PrinterInfo.prt_Scratch; IssueIoControl = FALSE;
try {
//
// First figure out which PRT we are dealing with. The
// port addresses in the PrinterInfo are base address of each
// PRT sorted in the adapter order.
//
*FIXED_NTVDMSTATE_LINEAR_PC_AT |= VDM_IDLEACTIVITY;
if ((USHORT)iPort == PrtInfo->prt_PortAddr[0] + STATUS_PORT_OFFSET) { adapter = 0; } else if ((USHORT)iPort == PrtInfo->prt_PortAddr[1] + STATUS_PORT_OFFSET) { adapter = 1; } else if ((USHORT)iPort == PrtInfo->prt_PortAddr[2] + STATUS_PORT_OFFSET) { adapter = 2; } else { // something must be wrong in our code, better check it out
ASSERT (FALSE); return FALSE; }
PrtMode = PrtInfo->prt_Mode[adapter];
VdmObjects = (PVDM_PROCESS_OBJECTS) (PsGetCurrentProcess()->VdmObjects);
AdapterStatus = VdmObjects->PrinterStatus + adapter;
if (PRT_MODE_SIMULATE_STATUS_PORT == PrtMode) {
//
// We are simulating a printer status read.
// Get the current status from softpc.
//
HostStatus = VdmObjects->PrinterHostState + adapter;
if (!(*AdapterStatus & NOTBUSY) && !(*HostStatus & HOST_LPT_BUSY)) {
AdapterControl = VdmObjects->PrinterControl + adapter;
if (*AdapterControl & IRQ) { return FALSE; } *AdapterStatus = (*AdapterStatus | NOTBUSY); } *printer_status = (ULONG)(*AdapterStatus | STATUS_REG_MASK);
TrapFrame->Eax &= 0xffffff00; TrapFrame->Eax |= (UCHAR)*printer_status; TrapFrame->Eip += cbInstructionSize; } else if (PRT_MODE_DIRECT_IO == PrtMode) {
//
// We have to read the I/O directly (of course, through file system
// which in turn goes to the driver).
// Before performing the read, flush out all pending output data
// in our buffer. This is done because the status we are about
// to read may depend on the pending output data.
//
if (PrtInfo->prt_BytesInBuffer[adapter]) { Status = VdmpFlushPrinterWriteData (adapter); #ifdef DBG
if (!NT_SUCCESS(Status)) { DbgPrint("VdmPrintStatus: failed to flush buffered data, status = %ls\n", Status); } #endif
}
//
// Capture this argument first as this reference may cause an
// exception.
//
PrintHandle = PrtInfo->prt_Handle[adapter];
//
// Lower irql to PASSIVE before doing any I/O.
//
OldIrql = KeGetCurrentIrql ();
KeLowerIrql (PASSIVE_LEVEL);
IssueIoControl = TRUE; } else {
//
// We don't simulate it here.
//
return FALSE; }
} except(EXCEPTION_EXECUTE_HANDLER) { Status = GetExceptionCode(); }
if (IssueIoControl == TRUE) {
Status = NtDeviceIoControlFile(PrintHandle, NULL, // notification event
NULL, // APC routine
NULL, // Apc Context
IoStatusBlock, IOCTL_VDM_PAR_READ_STATUS_PORT, NULL, 0, printer_status, sizeof(ULONG));
try {
if (!NT_SUCCESS(Status) || !NT_SUCCESS(IoStatusBlock->Status)) {
//
// fake a status to make it looks like the port is not connected
// to a printer.
//
*printer_status = 0x7F; #ifdef DBG
DbgPrint("VdmPrinterStatus: failed to get status from printer, status = %lx\n", Status); #endif
//
// Always tell the caller that we have simulated the operation.
//
Status = STATUS_SUCCESS; }
TrapFrame->Eax &= 0xffffff00; TrapFrame->Eax |= (UCHAR)*printer_status; TrapFrame->Eip += cbInstructionSize; } except (EXCEPTION_EXECUTE_HANDLER) { Status = GetExceptionCode(); }
//
// Regardless of any exceptions that may have occurred, we must
// restore our caller's IRQL since we lowered it.
//
KeRaiseIrql (OldIrql, &OldIrql); }
if (!NT_SUCCESS(Status)) { return FALSE; }
return TRUE; }
BOOLEAN VdmPrinterWriteData ( IN ULONG iPort, IN ULONG cbInstructionSize, IN PKTRAP_FRAME TrapFrame ) { PVDM_PRINTER_INFO PrtInfo; USHORT adapter; PVDM_TIB VdmTib; NTSTATUS Status;
PAGED_CODE();
Status = VdmpGetVdmTib(&VdmTib);
if (!NT_SUCCESS(Status)) { return FALSE; }
PrtInfo = &VdmTib->PrinterInfo;
try {
//
// First figure out which PRT we are dealing with. The
// port addresses in the PrinterInfo are base address of each
// PRT sorted in the adapter order.
//
*FIXED_NTVDMSTATE_LINEAR_PC_AT |= VDM_IDLEACTIVITY;
if ((USHORT)iPort == PrtInfo->prt_PortAddr[0] + DATA_PORT_OFFSET) { adapter = 0; } else if ((USHORT)iPort == PrtInfo->prt_PortAddr[1] + DATA_PORT_OFFSET) { adapter = 1; } else if ((USHORT)iPort == PrtInfo->prt_PortAddr[2] + DATA_PORT_OFFSET) { adapter = 2; } else { // something must be wrong in our code, better check it out
ASSERT(FALSE); return FALSE; }
if (PRT_MODE_DIRECT_IO == PrtInfo->prt_Mode[adapter]) {
PrtInfo->prt_Buffer[adapter][PrtInfo->prt_BytesInBuffer[adapter]] = (UCHAR)TrapFrame->Eax;
//
// buffer full, then flush it out
//
if (++PrtInfo->prt_BytesInBuffer[adapter] >= PRT_DATA_BUFFER_SIZE) { VdmpFlushPrinterWriteData(adapter); }
TrapFrame->Eip += cbInstructionSize; } else { Status = STATUS_ILLEGAL_INSTRUCTION; } } except(EXCEPTION_EXECUTE_HANDLER) { Status = GetExceptionCode(); }
if (!NT_SUCCESS(Status)) { return FALSE; } return TRUE;
}
NTSTATUS VdmpFlushPrinterWriteData ( IN USHORT adapter ) { KIRQL OldIrql; PVDM_TIB VdmTib; HANDLE PrintHandle; NTSTATUS Status; PVDM_PRINTER_INFO PrtInfo; PIO_STATUS_BLOCK IoStatusBlock; PVOID InputBuffer; ULONG InputBufferLength;
PAGED_CODE();
Status = VdmpGetVdmTib (&VdmTib);
if (!NT_SUCCESS(Status)) { return FALSE; }
InputBuffer = NULL; InputBufferLength = 0; PrintHandle = (HANDLE)-1;
PrtInfo = &VdmTib->PrinterInfo; IoStatusBlock = (PIO_STATUS_BLOCK)&VdmTib->TempArea1;
try { if (PrtInfo->prt_Handle[adapter] && PrtInfo->prt_BytesInBuffer[adapter] && PRT_MODE_DIRECT_IO == PrtInfo->prt_Mode[adapter]) {
PrintHandle = PrtInfo->prt_Handle[adapter]; InputBuffer = &PrtInfo->prt_Buffer[adapter][0]; InputBufferLength = PrtInfo->prt_BytesInBuffer[adapter]; } else { Status = STATUS_INVALID_PARAMETER; } } except (EXCEPTION_EXECUTE_HANDLER) { Status = GetExceptionCode(); }
if (NT_SUCCESS(Status)) { OldIrql = KeGetCurrentIrql();
KeLowerIrql(PASSIVE_LEVEL);
Status = NtDeviceIoControlFile(PrintHandle, NULL, // notification event
NULL, // APC routine
NULL, // APC context
IoStatusBlock, IOCTL_VDM_PAR_WRITE_DATA_PORT, InputBuffer, InputBufferLength, NULL, 0);
try { PrtInfo->prt_BytesInBuffer[adapter] = 0; if (!NT_SUCCESS(Status)) { #ifdef DBG
DbgPrint("IOCTL_VDM_PAR_WRITE_DATA_PORT failed %lx %x\n", Status, IoStatusBlock->Status); #endif
Status = IoStatusBlock->Status; } } except (EXCEPTION_EXECUTE_HANDLER) { Status = GetExceptionCode(); }
KeRaiseIrql (OldIrql, &OldIrql); }
return Status;
}
NTSTATUS VdmpPrinterInitialize ( IN PVOID ServiceData ) /*++
Routine Description:
This routine probes and caches the data associated with kernel mode printer emulation.
Arguments:
ServiceData - Not used.
Return Value:
--*/ { PUCHAR State, PrtStatus, Control, HostState; PVDM_TIB VdmTib; PVDM_PROCESS_OBJECTS VdmObjects; NTSTATUS Status;
UNREFERENCED_PARAMETER (ServiceData);
//
// Note: We only support two printers in the kernel.
//
Status = VdmpGetVdmTib(&VdmTib);
if (!NT_SUCCESS(Status)) { return FALSE; }
try { State = VdmTib->PrinterInfo.prt_State; PrtStatus = VdmTib->PrinterInfo.prt_Status; Control = VdmTib->PrinterInfo.prt_Control; HostState = VdmTib->PrinterInfo.prt_HostState;
//
// Probe the locations for two printers
//
ProbeForWrite( State, 2 * sizeof(UCHAR), sizeof(UCHAR) );
ProbeForWrite( PrtStatus, 2 * sizeof(UCHAR), sizeof(UCHAR) );
ProbeForWrite( Control, 2 * sizeof(UCHAR), sizeof(UCHAR) );
ProbeForWrite( HostState, 2 * sizeof(UCHAR), sizeof(UCHAR) );
} except (EXCEPTION_EXECUTE_HANDLER) { Status = GetExceptionCode(); State = 0; // satisfy no_opt compilation
PrtStatus = 0; // satisfy no_opt compilation
Control = 0; // satisfy no_opt compilation
HostState = 0; // satisfy no_opt compilation
}
if (NT_SUCCESS(Status)) { VdmObjects = PsGetCurrentProcess()->VdmObjects; VdmObjects->PrinterState = State; VdmObjects->PrinterStatus = PrtStatus; VdmObjects->PrinterControl = Control; VdmObjects->PrinterHostState = HostState; }
return Status; }
NTSTATUS VdmpPrinterDirectIoOpen ( IN PVOID ServiceData ) { PAGED_CODE();
UNREFERENCED_PARAMETER (ServiceData);
return STATUS_SUCCESS;
} NTSTATUS VdmpPrinterDirectIoClose ( IN PVOID ServiceData ) { LOGICAL FlushData; NTSTATUS Status; PVDM_PRINTER_INFO PrtInfo; USHORT Adapter; PVDM_TIB VdmTib;
PAGED_CODE();
if (NULL == ServiceData) { return STATUS_ACCESS_VIOLATION; }
//
// First we fetch vdm tib and do some damage control in case
// this is bad user-mode memory
// PrtInfo points to a stricture
try { VdmTib = NtCurrentTeb()->Vdm; if (VdmTib == NULL) { return STATUS_ACCESS_VIOLATION; }
ProbeForWrite(VdmTib, sizeof(VDM_TIB), sizeof(UCHAR));
//
// Now verify that servicedata ptr is valid.
//
ProbeForRead(ServiceData, sizeof(USHORT), sizeof(UCHAR)); Adapter = *(PUSHORT)ServiceData;
} except (EXCEPTION_EXECUTE_HANDLER) { return GetExceptionCode(); }
Status = STATUS_SUCCESS;
PrtInfo = &VdmTib->PrinterInfo;
FlushData = FALSE;
try { if (Adapter < VDM_NUMBER_OF_LPT) { if (PRT_MODE_DIRECT_IO == PrtInfo->prt_Mode[Adapter] && PrtInfo->prt_BytesInBuffer[Adapter]) {
FlushData = TRUE; } } else { Status = STATUS_INVALID_PARAMETER; } } except (EXCEPTION_EXECUTE_HANDLER) { Status = GetExceptionCode(); }
if (FlushData == TRUE) { Status = VdmpFlushPrinterWriteData (Adapter); }
return Status; }
|