NT4/private/ntos/vdm/i386/vdmprint.c
2020-09-30 17:12:29 +02:00

318 lines
8.0 KiB
C

/*++
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 "vdmprint.h"
#include <i386.h>
#include <v86emul.h>
#include "..\..\..\..\inc\ntddvdm.h"
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, VdmPrinterStatus)
#pragma alloc_text(PAGE, VdmPrinterWriteData)
#pragma alloc_text(PAGE, VdmFlushPrinterWriteData)
#pragma alloc_text(PAGE, VdmpPrinterDirectIoOpen)
#pragma alloc_text(PAGE, VdmpPrinterDirectIoClose)
#endif
BOOLEAN
VdmPrinterStatus(
ULONG iPort,
ULONG cbInstructionSize,
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 successfull, False otherwise
--*/
{
UCHAR PrtMode;
USHORT adapter;
ULONG * printer_status;
KIRQL OldIrql;
PIO_STATUS_BLOCK IoStatusBlock;
PVDM_PRINTER_INFO PrtInfo;
PVDM_TIB VdmTib;
NTSTATUS Status = STATUS_SUCCESS;
PAGED_CODE();
// why we have printer stuff in TIB? It would be more appropriate
// if we have them in the process object. The main reason(I think)
// is that we can get rid of synchronization.
//
VdmTib = NtCurrentTeb()->Vdm;
PrtInfo = &VdmTib->PrinterInfo;
IoStatusBlock = (PIO_STATUS_BLOCK) &VdmTib->TempArea1;
printer_status = &VdmTib->PrinterInfo.prt_Scratch;
*pNtVDMState |= VDM_IDLEACTIVITY;
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.
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];
if(PRT_MODE_SIMULATE_STATUS_PORT == PrtMode) {
// we are simulating printer status read.
// get the current status from softpc.
if (!(get_status(adapter) & NOTBUSY) &&
!(host_lpt_status(adapter) & HOST_LPT_BUSY)) {
if (get_control(adapter) & IRQ)
return FALSE;
set_status(adapter, get_status(adapter) | NOTBUSY);
}
*printer_status = (ULONG)(get_status(adapter) | STATUS_REG_MASK);
}
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 read, flush out all pending output data in our
// buffer. This is done because the status we are about to read
// may depend on those pending output data.
//
if (PrtInfo->prt_BytesInBuffer[adapter]) {
Status = VdmFlushPrinterWriteData(adapter);
#ifdef DBG
if (!NT_SUCCESS(Status)) {
DbgPrint("VdmPrintStatus: failed to flush buffered data, status = %ls\n", Status);
}
#endif
}
OldIrql = KeGetCurrentIrql();
// lower irql to passive before doing any IO
KeLowerIrql(PASSIVE_LEVEL);
Status = NtDeviceIoControlFile(PrtInfo->prt_Handle[adapter],
NULL, // notification event
NULL, // APC routine
NULL, // Apc Context
IoStatusBlock,
IOCTL_VDM_PAR_READ_STATUS_PORT,
NULL,
0,
printer_status,
sizeof(ULONG)
);
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;
}
KeRaiseIrql(OldIrql, &OldIrql);
}
else
// we don't simulate it here
return FALSE;
TrapFrame->Eax &= 0xffffff00;
TrapFrame->Eax |= (UCHAR)*printer_status;
TrapFrame->Eip += cbInstructionSize;
} except(EXCEPTION_EXECUTE_HANDLER) {
Status = GetExceptionCode();
}
return (NT_SUCCESS(Status));
}
BOOLEAN VdmPrinterWriteData(
ULONG iPort,
ULONG cbInstructionSize,
PKTRAP_FRAME TrapFrame
)
{
UCHAR PrtMode;
PVDM_PRINTER_INFO PrtInfo;
USHORT adapter;
PVDM_TIB VdmTib;
NTSTATUS Status = STATUS_SUCCESS;
PAGED_CODE();
VdmTib = NtCurrentTeb()->Vdm;
PrtInfo = &VdmTib->PrinterInfo;
*pNtVDMState |= VDM_IDLEACTIVITY;
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
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){
VdmFlushPrinterWriteData(adapter);
}
TrapFrame->Eip += cbInstructionSize;
}
else
Status = STATUS_ILLEGAL_INSTRUCTION;
} except(EXCEPTION_EXECUTE_HANDLER) {
Status = GetExceptionCode();
}
return(NT_SUCCESS(Status));
}
NTSTATUS
VdmFlushPrinterWriteData(USHORT adapter)
{
KIRQL OldIrql;
PVDM_TIB VdmTib;
PVDM_PRINTER_INFO PrtInfo;
PIO_STATUS_BLOCK IoStatusBlock;
NTSTATUS Status;
PAGED_CODE();
Status = STATUS_SUCCESS;
VdmTib = NtCurrentTeb()->Vdm;
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]) {
OldIrql = KeGetCurrentIrql();
KeLowerIrql(PASSIVE_LEVEL);
Status = NtDeviceIoControlFile(PrtInfo->prt_Handle[adapter],
NULL, // notification event
NULL, // APC routine
NULL, // APC context
IoStatusBlock,
IOCTL_VDM_PAR_WRITE_DATA_PORT,
&PrtInfo->prt_Buffer[adapter][0],
PrtInfo->prt_BytesInBuffer[adapter],
NULL,
0
);
PrtInfo->prt_BytesInBuffer[adapter] = 0;
KeRaiseIrql(OldIrql, &OldIrql);
if (!NT_SUCCESS(Status)) {
#ifdef DBG
DbgPrint("IOCTL_VDM_PAR_WRITE_DATA_PORT failed %lx %x\n",
Status, IoStatusBlock->Status);
#endif
Status = IoStatusBlock->Status;
}
}
else
Status = STATUS_INVALID_PARAMETER;
} except (EXCEPTION_EXECUTE_HANDLER) {
Status = GetExceptionCode();
}
return Status;
}
NTSTATUS
VdmpPrinterDirectIoOpen(PVOID ServiceData)
{
PAGED_CODE();
return STATUS_SUCCESS;
}
NTSTATUS
VdmpPrinterDirectIoClose(PVOID ServiceData)
{
NTSTATUS Status;
PVDM_PRINTER_INFO PrtInfo;
USHORT Adapter;
PAGED_CODE();
Status = STATUS_SUCCESS;
PrtInfo =&(((PVDM_TIB)NtCurrentTeb()->Vdm)->PrinterInfo);
try {
Adapter = *(USHORT *)ServiceData;
if (Adapter < VDM_NUMBER_OF_LPT) {
if (PRT_MODE_DIRECT_IO == PrtInfo->prt_Mode[Adapter] &&
PrtInfo->prt_BytesInBuffer[Adapter]) {
Status = VdmFlushPrinterWriteData(Adapter);
}
}
else
Status = STATUS_INVALID_PARAMETER;
} except (EXCEPTION_EXECUTE_HANDLER) {
Status = GetExceptionCode();
}
return Status;
}