Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

2290 lines
70 KiB

/*--------------------------------------------------------------------------
*
* Copyright (C) Cyclades Corporation, 1996-2001.
* All rights reserved.
*
* Cyclom-Y Port Driver
*
* This file : cyyioctl.c
*
* Description: This module contains the code related to ioctl calls
* in the Cyclom-Y Port driver.
*
* Notes: This code supports Windows 2000 and Windows XP,
* x86 and IA64 processors.
*
* Complies with Cyclades SW Coding Standard rev 1.3.
*
*--------------------------------------------------------------------------
*/
/*-------------------------------------------------------------------------
*
* Change History
*
*--------------------------------------------------------------------------
*
*--------------------------------------------------------------------------*/
#include "precomp.h"
BOOLEAN
CyyGetModemUpdate(
IN PVOID Context
);
BOOLEAN
CyyGetCommStatus(
IN PVOID Context
);
BOOLEAN
CyySetEscapeChar(
IN PVOID Context
);
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGESER,CyySetBaud)
#pragma alloc_text(PAGESER,CyySetLineControl)
#pragma alloc_text(PAGESER,CyyIoControl)
#pragma alloc_text(PAGESER,CyySetChars)
#pragma alloc_text(PAGESER,CyyGetModemUpdate)
#pragma alloc_text(PAGESER,CyyGetCommStatus)
#pragma alloc_text(PAGESER,CyyGetProperties)
#pragma alloc_text(PAGESER,CyySetEscapeChar)
//#pragma alloc_text(PAGESER,CyyCDCmd)
#pragma alloc_text(PAGESER,CyyGetStats)
#pragma alloc_text(PAGESER,CyyClearStats)
#pragma alloc_text(PAGESER, CyySetMCRContents)
#pragma alloc_text(PAGESER, CyyGetMCRContents)
#pragma alloc_text(PAGESER, CyySetFCRContents)
#pragma alloc_text(PAGESER, CyyInternalIoControl)
#endif
static const PHYSICAL_ADDRESS CyyPhysicalZero = {0};
BOOLEAN
CyyGetStats(
IN PVOID Context
)
/*++
Routine Description:
In sync with the interrpt service routine (which sets the perf stats)
return the perf stats to the caller.
Arguments:
Context - Pointer to a the irp.
Return Value:
This routine always returns FALSE.
--*/
{
PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation((PIRP)Context);
PCYY_DEVICE_EXTENSION extension = irpSp->DeviceObject->DeviceExtension;
PSERIALPERF_STATS sp = ((PIRP)Context)->AssociatedIrp.SystemBuffer;
CYY_LOCKED_PAGED_CODE();
*sp = extension->PerfStats;
return FALSE;
}
BOOLEAN
CyyClearStats(
IN PVOID Context
)
/*++
Routine Description:
In sync with the interrpt service routine (which sets the perf stats)
clear the perf stats.
Arguments:
Context - Pointer to a the extension.
Return Value:
This routine always returns FALSE.
--*/
{
CYY_LOCKED_PAGED_CODE();
RtlZeroMemory(
&((PCYY_DEVICE_EXTENSION)Context)->PerfStats,
sizeof(SERIALPERF_STATS)
);
RtlZeroMemory(&((PCYY_DEVICE_EXTENSION)Context)->WmiPerfData,
sizeof(SERIAL_WMI_PERF_DATA));
return FALSE;
}
BOOLEAN
CyySetChars(
IN PVOID Context
)
/*--------------------------------------------------------------------------
CyySetChars()
Routine Description: set special characters for the driver.
Arguments:
Context - Pointer to a structure that contains a pointer to
the device extension and a pointer to a special characters
structure.
Return Value: This routine always returns FALSE.
--------------------------------------------------------------------------*/
{
PCYY_DEVICE_EXTENSION Extension = ((PCYY_IOCTL_SYNC)Context)->Extension;
PUCHAR chip = Extension->Cd1400;
ULONG bus = Extension->IsPci;
CYY_LOCKED_PAGED_CODE();
Extension->SpecialChars =
*((PSERIAL_CHARS)(((PCYY_IOCTL_SYNC)Context)->Data));
CD1400_WRITE(chip,bus,CAR,Extension->CdChannel & 0x03);
CD1400_WRITE(chip,bus,SCHR1,Extension->SpecialChars.XonChar);
CD1400_WRITE(chip,bus,SCHR2,Extension->SpecialChars.XoffChar);
return FALSE;
}
BOOLEAN
CyySetBaud(
IN PVOID Context
)
/*--------------------------------------------------------------------------
CyySetBaud()
Routine Description: set the baud rate of the device.
Arguments:
Context - Pointer to a structure that contains a pointer to
the device extension.
Return Value: This routine always returns FALSE if error;
TRUE if successful.
--------------------------------------------------------------------------*/
{
PCYY_DEVICE_EXTENSION Extension = ((PCYY_IOCTL_BAUD)Context)->Extension;
ULONG baud = ((PCYY_IOCTL_BAUD)Context)->Baud;
PUCHAR chip = Extension->Cd1400;
ULONG bus = Extension->IsPci;
ULONG baud_period,i;
UCHAR cor3value;
static ULONG cor_values[] = {8, 32, 128, 512, 2048, 0};
CYY_LOCKED_PAGED_CODE();
for(i = 0 ; cor_values[i] > 0 ; i++) {
baud_period = (10 * Extension->CDClock)/baud;
baud_period = baud_period/cor_values[i];
baud_period = (baud_period + 5)/10;
if(baud_period <= 0xff && baud_period > 0) break;
}
if(cor_values[i] == 0) return(FALSE);
CD1400_WRITE(chip,bus,CAR, Extension->CdChannel & 0x03);
CD1400_WRITE(chip,bus,TCOR, (UCHAR)i);
CD1400_WRITE(chip,bus,RCOR, (UCHAR)i);
CD1400_WRITE(chip,bus,TBPR, (UCHAR)baud_period);
CD1400_WRITE(chip,bus,RBPR, (UCHAR)baud_period);
CD1400_WRITE(chip,bus,RTPR, (UCHAR)Extension->Rtpr); // Receive Time-out Period
// Register (value in ms)
// set the threshold
if (Extension->RxFifoTriggerUsed == FALSE) {
cor3value = CD1400_READ(chip,bus,COR3);
cor3value &= 0xf0;
if(baud <= 9600) {
CD1400_WRITE(chip,bus,COR3, cor3value | 0x0a);
} else if (baud <= 38400) {
CD1400_WRITE(chip,bus,COR3, cor3value | 0x06);
} else {
CD1400_WRITE(chip,bus,COR3, cor3value | 0x04);
}
CyyCDCmd(Extension,CCR_CORCHG_COR3);
}
return TRUE;
}
VOID
CyyCDCmd(
PCYY_DEVICE_EXTENSION Extension,
UCHAR cmd
)
/*--------------------------------------------------------------------------
CyyCDCmd()
Routine Description: Send a command to a CD1400.
Arguments:
Extension - pointer to the serial device extension.
cmd - command to be sent.
Return Value: none.
--------------------------------------------------------------------------*/
{
KIRQL irql;
UCHAR value = 0xff;
LARGE_INTEGER startOfSpin, nextQuery, difference, oneSecond;
PUCHAR chip = Extension->Cd1400;
ULONG bus = Extension->IsPci;
oneSecond.QuadPart = 10*1000*1000; // unit is 100ns
KeQueryTickCount(&startOfSpin);
value = CD1400_READ(chip,bus,CCR);
while (value != 0) {
KeQueryTickCount(&nextQuery);
difference.QuadPart = nextQuery.QuadPart - startOfSpin.QuadPart;
ASSERT(KeQueryTimeIncrement() <= MAXLONG);
//*************************
// Error Injection
//if (difference.QuadPart * KeQueryTimeIncrement() <
// oneSecond.QuadPart)
//*************************
if (difference.QuadPart * KeQueryTimeIncrement() >=
oneSecond.QuadPart) {
#if DBG
DbgPrint("\n ***** CCR not zero! *****\n");
#endif
if (Extension->CmdFailureLog == FALSE) {
irql = KeGetCurrentIrql();
if (irql <= DISPATCH_LEVEL) {
CyyLogError(Extension->DeviceObject->DriverObject,
NULL,CyyPhysicalZero,CyyPhysicalZero,
0,0,0,Extension->PortIndex+1,STATUS_SUCCESS,CYY_CCR_NOT_ZERO,
0,NULL,0,NULL);
Extension->CmdFailureLog = TRUE;
}
}
break;
}
value = CD1400_READ(chip,bus,CCR);
};
if (value == 0) {
CD1400_WRITE(chip,bus,CCR,cmd);
}
}
BOOLEAN
CyySetLineControl(
IN PVOID Context
)
/*--------------------------------------------------------------------------
CyySetLineControl()
Routine Description: set the COR1 (DATA,PARITY,STOP)
Arguments:
Context - Pointer to the device extension.
Return Value: This routine always returns FALSE.
--------------------------------------------------------------------------*/
{
PCYY_DEVICE_EXTENSION Extension = Context;
PUCHAR chip = Extension->Cd1400;
ULONG bus = Extension->IsPci;
CYY_LOCKED_PAGED_CODE();
CD1400_WRITE(chip,bus,CAR, Extension->CdChannel & 0x03);
CD1400_WRITE(chip,bus,COR1, Extension->cor1);
CyyCDCmd(Extension,CCR_CORCHG_COR1);
return FALSE;
}
BOOLEAN
CyyGetModemUpdate(
IN PVOID Context
)
/*--------------------------------------------------------------------------
CyyGetModemUpdate()
Routine Description: this routine is simply used to call the interrupt
level routine that handles modem status update.
Arguments:
Context - Pointer to a structure that contains a pointer to
the device extension and a pointer to a ulong.
Return Value: This routine always returns FALSE.
--------------------------------------------------------------------------*/
{
PCYY_DEVICE_EXTENSION Extension = ((PCYY_IOCTL_SYNC)Context)->Extension;
ULONG *Result = (ULONG *)(((PCYY_IOCTL_SYNC)Context)->Data);
CYY_LOCKED_PAGED_CODE();
*Result = CyyHandleModemUpdate(Extension,FALSE);
return FALSE;
}
BOOLEAN
CyySetMCRContents(IN PVOID Context)
/*++
Routine Description:
This routine is simply used to set the contents of the MCR
Arguments:
Context - Pointer to a structure that contains a pointer to
the device extension and a pointer to a ulong.
Return Value:
This routine always returns FALSE.
--*/
{
PCYY_DEVICE_EXTENSION Extension = ((PCYY_IOCTL_SYNC)Context)->Extension;
ULONG *Result = (ULONG *)(((PCYY_IOCTL_SYNC)Context)->Data);
PUCHAR chip = Extension->Cd1400;
ULONG bus = Extension->IsPci;
CYY_LOCKED_PAGED_CODE();
// Let's convert the UART modem control to our hw
CD1400_WRITE(chip,bus, CAR, Extension->CdChannel & 0x03);
if (*Result & SERIAL_MCR_DTR) {
CD1400_WRITE(chip,bus, Extension->MSVR_DTR, Extension->DTRset);
} else {
CD1400_WRITE(chip,bus, Extension->MSVR_DTR, 0x00);
}
if (*Result & SERIAL_MCR_RTS) {
CD1400_WRITE(chip,bus, Extension->MSVR_RTS, Extension->RTSset);
} else {
CD1400_WRITE(chip,bus, Extension->MSVR_RTS, 0x00);
}
if (*Result & SERIAL_MCR_OUT2) {
// Enable IRQ
CD1400_WRITE(chip,bus,SRER,0x90); // Enable MdmCh, RxData.
} else {
CD1400_WRITE(chip,bus,SRER,0x00); // Disable MdmCh, RxData, TxRdy
}
// Fanny: Strange, Result is being used instead of *Result.
// //
// // This is severe casting abuse!!!
// //
//
// WRITE_MODEM_CONTROL(Extension->Controller, (UCHAR)PtrToUlong(Result));
return FALSE;
}
BOOLEAN
CyyGetMCRContents(IN PVOID Context)
/*++
Routine Description:
This routine is simply used to get the contents of the MCR
Arguments:
Context - Pointer to a structure that contains a pointer to
the device extension and a pointer to a ulong.
Return Value:
This routine always returns FALSE.
--*/
{
PCYY_DEVICE_EXTENSION Extension = ((PCYY_IOCTL_SYNC)Context)->Extension;
ULONG *Result = (ULONG *)(((PCYY_IOCTL_SYNC)Context)->Data);
PUCHAR chip = Extension->Cd1400;
ULONG bus = Extension->IsPci;
UCHAR var;
*Result = 0;
CYY_LOCKED_PAGED_CODE();
CD1400_WRITE(chip,bus, CAR, Extension->CdChannel & 0x03);
var = CD1400_READ(chip,bus,Extension->MSVR_DTR);
if (var & Extension->DTRset) {
*Result |= SERIAL_MCR_DTR;
}
var = CD1400_READ(chip,bus,Extension->MSVR_RTS);
if (var & Extension->RTSset) {
*Result |= SERIAL_MCR_RTS;
}
var = CD1400_READ(chip,bus,SRER);
if (var & 0x90) {
*Result |= SERIAL_MCR_OUT2;
}
// *Result = READ_MODEM_CONTROL(Extension->Controller);
return FALSE;
}
BOOLEAN
CyySetFCRContents(IN PVOID Context)
/*++
Routine Description:
This routine is simply used to set the contents of the FCR
Arguments:
Context - Pointer to a structure that contains a pointer to
the device extension and a pointer to a ulong.
Return Value:
This routine always returns FALSE.
--*/
{
PCYY_DEVICE_EXTENSION Extension = ((PCYY_IOCTL_SYNC)Context)->Extension;
ULONG *Result = (ULONG *)(((PCYY_IOCTL_SYNC)Context)->Data);
PUCHAR chip = Extension->Cd1400;
ULONG bus = Extension->IsPci;
UCHAR cor3value;
CYY_LOCKED_PAGED_CODE();
CD1400_WRITE(chip,bus,CAR, Extension->CdChannel & 0x03);
if (*Result & SERIAL_FCR_TXMT_RESET) {
CyyCDCmd(Extension,CCR_FLUSH_TXFIFO);
}
if (*Result & SERIAL_FCR_RCVR_RESET) {
CyyDbgPrintEx(DPFLTR_WARNING_LEVEL, "SERIAL_FCR_RCVR_RESET received. "
"CD1400 rx fifo can't be reset.\n");
}
Extension->RxFifoTrigger = (UCHAR)*Result & 0xc0;
cor3value = CD1400_READ(chip,bus,COR3);
cor3value &= 0xf0;
switch (Extension->RxFifoTrigger) {
case SERIAL_1_BYTE_HIGH_WATER:
cor3value |= 0x01;
break;
case SERIAL_4_BYTE_HIGH_WATER:
cor3value |= 0x04;
break;
case SERIAL_8_BYTE_HIGH_WATER:
cor3value |= 0x08;
break;
case SERIAL_14_BYTE_HIGH_WATER:
cor3value |= MAX_CHAR_FIFO;
break;
}
CD1400_WRITE(chip,bus,COR3, cor3value);
CyyCDCmd(Extension,CCR_CORCHG_COR3);
Extension->RxFifoTriggerUsed = TRUE;
// //
// // This is severe casting abuse!!!
// //
//
// WRITE_FIFO_CONTROL(Extension->Controller, (UCHAR)*Result); Bld 2128: PtrToUlong replaced by *
return FALSE;
}
BOOLEAN
CyyGetCommStatus(
IN PVOID Context
)
/*--------------------------------------------------------------------------
CyyGetCommStatus()
Routine Description: get the current state of the serial driver.
Arguments:
Context - Pointer to a structure that contains a pointer to the
device extension and a pointer to a serial status record.
Return Value: This routine always returns FALSE.
--------------------------------------------------------------------------*/
{
PCYY_DEVICE_EXTENSION Extension = ((PCYY_IOCTL_SYNC)Context)->Extension;
PSERIAL_STATUS Stat = ((PCYY_IOCTL_SYNC)Context)->Data;
CYY_LOCKED_PAGED_CODE();
Stat->Errors = Extension->ErrorWord;
Extension->ErrorWord = 0;
//
// Eof isn't supported in binary mode
//
Stat->EofReceived = FALSE;
Stat->AmountInInQueue = Extension->CharsInInterruptBuffer;
Stat->AmountInOutQueue = Extension->TotalCharsQueued;
if (Extension->WriteLength) {
// By definition if we have a writelength the we have
// a current write irp.
ASSERT(Extension->CurrentWriteIrp);
ASSERT(Stat->AmountInOutQueue >= Extension->WriteLength);
Stat->AmountInOutQueue -=
IoGetCurrentIrpStackLocation(Extension->CurrentWriteIrp)
->Parameters.Write.Length - (Extension->WriteLength);
}
Stat->WaitForImmediate = Extension->TransmitImmediate;
Stat->HoldReasons = 0;
if (Extension->TXHolding) {
if (Extension->TXHolding & CYY_TX_CTS) {
Stat->HoldReasons |= SERIAL_TX_WAITING_FOR_CTS;
}
if (Extension->TXHolding & CYY_TX_DSR) {
Stat->HoldReasons |= SERIAL_TX_WAITING_FOR_DSR;
}
if (Extension->TXHolding & CYY_TX_DCD) {
Stat->HoldReasons |= SERIAL_TX_WAITING_FOR_DCD;
}
if (Extension->TXHolding & CYY_TX_XOFF) {
Stat->HoldReasons |= SERIAL_TX_WAITING_FOR_XON;
}
if (Extension->TXHolding & CYY_TX_BREAK) {
Stat->HoldReasons |= SERIAL_TX_WAITING_ON_BREAK;
}
}
if (Extension->RXHolding & CYY_RX_DSR) {
Stat->HoldReasons |= SERIAL_RX_WAITING_FOR_DSR;
}
if (Extension->RXHolding & CYY_RX_XOFF) {
Stat->HoldReasons |= SERIAL_TX_WAITING_XOFF_SENT;
}
return FALSE;
}
BOOLEAN
CyySetEscapeChar(
IN PVOID Context
)
/*--------------------------------------------------------------------------
CyySetEscapeChar()
Routine Description: This is used to set the character that will be
used to escape line status and modem status information when the
application has set up that line status and modem status should be
passed back in the data stream.
Arguments:
Context - Pointer to the irp that is to specify the escape character.
Implicitly - An escape character of 0 means no escaping.
Return Value: This routine always returns FALSE.
--------------------------------------------------------------------------*/
{
PCYY_DEVICE_EXTENSION extension =
IoGetCurrentIrpStackLocation((PIRP)Context)
->DeviceObject->DeviceExtension;
extension->EscapeChar =
*(PUCHAR)((PIRP)Context)->AssociatedIrp.SystemBuffer;
return FALSE;
}
NTSTATUS
CyyIoControl(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*--------------------------------------------------------------------------
CyyIoControl()
Description: This routine provides the initial processing for
all of the Ioctls for the serial device.
Arguments:
DeviceObject - Pointer to the device object for this device
Irp - Pointer to the IRP for the current request
Return Value:
The function value is the final status of the call
--------------------------------------------------------------------------*/
{
NTSTATUS Status;
PIO_STACK_LOCATION IrpSp;
PCYY_DEVICE_EXTENSION Extension = DeviceObject->DeviceExtension;
KIRQL OldIrql;
#if DBG
ULONG debugdtr = 0;
ULONG debugrts = 0;
#endif
NTSTATUS prologueStatus;
CYY_LOCKED_PAGED_CODE();
//
// We expect to be open so all our pages are locked down. This is, after
// all, an IO operation, so the device should be open first.
//
if (Extension->DeviceIsOpened != TRUE) {
Irp->IoStatus.Status = STATUS_INVALID_DEVICE_REQUEST;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return STATUS_INVALID_DEVICE_REQUEST;
}
if ((prologueStatus = CyyIRPPrologue(Irp, Extension))
!= STATUS_SUCCESS) {
if(prologueStatus != STATUS_PENDING) {
Irp->IoStatus.Status = prologueStatus;
CyyCompleteRequest(Extension, Irp, IO_NO_INCREMENT);
}
return prologueStatus;
}
CyyDbgPrintEx(CYYIRPPATH, "Dispatch entry for: %x\n", Irp);
if (CyyCompleteIfError(DeviceObject,Irp) != STATUS_SUCCESS) {
return STATUS_CANCELLED;
}
IrpSp = IoGetCurrentIrpStackLocation(Irp);
Irp->IoStatus.Information = 0L;
Status = STATUS_SUCCESS;
switch (IrpSp->Parameters.DeviceIoControl.IoControlCode) {
case IOCTL_SERIAL_SET_BAUD_RATE : {
ULONG BaudRate;
if (IrpSp->Parameters.DeviceIoControl.InputBufferLength <
sizeof(SERIAL_BAUD_RATE)) {
Status = STATUS_BUFFER_TOO_SMALL;
break;
} else {
BaudRate = ((PSERIAL_BAUD_RATE)
(Irp->AssociatedIrp.SystemBuffer))->BaudRate;
}
if ((BaudRate == 0) || (BaudRate > 230400)) {
Status = STATUS_INVALID_PARAMETER;
break;
}
if ((Extension->CDClock == 25000000) && (BaudRate > 115200)) {
Status = STATUS_INVALID_PARAMETER;
break;
}
//
// Make sure we are at power D0
//
if (NT_SUCCESS(Status)) {
if (Extension->PowerState != PowerDeviceD0) {
Status = CyyGotoPowerState(Extension->Pdo, Extension,
PowerDeviceD0);
if (!NT_SUCCESS(Status)) {
break;
}
}
}
KeAcquireSpinLock(&Extension->ControlLock,&OldIrql);
if (NT_SUCCESS(Status)) {
CYY_IOCTL_BAUD S;
BOOLEAN result;
Extension->CurrentBaud = BaudRate;
Extension->WmiCommData.BaudRate = BaudRate;
S.Extension = Extension;
S.Baud = BaudRate;
result = KeSynchronizeExecution(Extension->Interrupt,CyySetBaud,&S);
if (result == 0) {
Status = STATUS_INVALID_PARAMETER;
}
}
KeReleaseSpinLock(&Extension->ControlLock,OldIrql);
break;
}
case IOCTL_SERIAL_GET_BAUD_RATE: {
PSERIAL_BAUD_RATE Br = (PSERIAL_BAUD_RATE)Irp->AssociatedIrp.SystemBuffer;
if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength <
sizeof(SERIAL_BAUD_RATE)) {
Status = STATUS_BUFFER_TOO_SMALL;
break;
}
KeAcquireSpinLock(&Extension->ControlLock,&OldIrql);
Br->BaudRate = Extension->CurrentBaud;
KeReleaseSpinLock(&Extension->ControlLock,OldIrql);
Irp->IoStatus.Information = sizeof(SERIAL_BAUD_RATE);
break;
}
case IOCTL_SERIAL_GET_MODEM_CONTROL: {
CYY_IOCTL_SYNC S;
if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength <
sizeof(ULONG)) {
Status = STATUS_BUFFER_TOO_SMALL;
break;
}
Irp->IoStatus.Information = sizeof(ULONG);
S.Extension = Extension;
S.Data = Irp->AssociatedIrp.SystemBuffer;
KeAcquireSpinLock(
&Extension->ControlLock,
&OldIrql
);
KeSynchronizeExecution(
Extension->Interrupt,
CyyGetMCRContents,
&S
);
KeReleaseSpinLock(
&Extension->ControlLock,
OldIrql
);
break;
}
case IOCTL_SERIAL_SET_MODEM_CONTROL: {
CYY_IOCTL_SYNC S;
if (IrpSp->Parameters.DeviceIoControl.InputBufferLength <
sizeof(ULONG)) {
Status = STATUS_BUFFER_TOO_SMALL;
break;
}
S.Extension = Extension;
S.Data = Irp->AssociatedIrp.SystemBuffer;
//
// Make sure we are at power D0
//
if (Extension->PowerState != PowerDeviceD0) {
Status = CyyGotoPowerState(Extension->Pdo, Extension,
PowerDeviceD0);
if (!NT_SUCCESS(Status)) {
break;
}
}
KeAcquireSpinLock(
&Extension->ControlLock,
&OldIrql
);
KeSynchronizeExecution(
Extension->Interrupt,
CyySetMCRContents,
&S
);
KeReleaseSpinLock(
&Extension->ControlLock,
OldIrql
);
break;
}
case IOCTL_SERIAL_SET_FIFO_CONTROL: {
CYY_IOCTL_SYNC S;
if (IrpSp->Parameters.DeviceIoControl.InputBufferLength <
sizeof(ULONG)) {
Status = STATUS_BUFFER_TOO_SMALL;
break;
}
S.Extension = Extension;
S.Data = Irp->AssociatedIrp.SystemBuffer;
//
// Make sure we are at power D0
//
if (Extension->PowerState != PowerDeviceD0) {
Status = CyyGotoPowerState(Extension->Pdo, Extension,
PowerDeviceD0);
if (!NT_SUCCESS(Status)) {
break;
}
}
KeAcquireSpinLock(
&Extension->ControlLock,
&OldIrql
);
KeSynchronizeExecution(
Extension->Interrupt,
CyySetFCRContents,
&S
);
KeReleaseSpinLock(
&Extension->ControlLock,
OldIrql
);
break;
}
case IOCTL_SERIAL_SET_LINE_CONTROL: {
PSERIAL_LINE_CONTROL Lc =
((PSERIAL_LINE_CONTROL)(Irp->AssociatedIrp.SystemBuffer));
UCHAR LData;
UCHAR LStop;
UCHAR LParity;
UCHAR Mask = 0xff;
if (IrpSp->Parameters.DeviceIoControl.InputBufferLength <
sizeof(SERIAL_LINE_CONTROL)) {
Status = STATUS_BUFFER_TOO_SMALL;
break;
}
//
// Make sure we are at power D0
//
if (Extension->PowerState != PowerDeviceD0) {
Status = CyyGotoPowerState(Extension->Pdo, Extension,
PowerDeviceD0);
if (!NT_SUCCESS(Status)) {
break;
}
}
switch (Lc->WordLength) {
case 5: LData = COR1_5_DATA; Mask = 0x1f;
break;
case 6: LData = COR1_6_DATA; Mask = 0x3f;
break;
case 7: LData = COR1_7_DATA; Mask = 0x7f;
break;
case 8: LData = COR1_8_DATA; Mask = 0xff;
break;
default: Status = STATUS_INVALID_PARAMETER;
goto DoneWithIoctl;
}
Extension->WmiCommData.BitsPerByte = Lc->WordLength;
switch (Lc->Parity) {
case NO_PARITY: {
Extension->WmiCommData.Parity = SERIAL_WMI_PARITY_NONE;
LParity = COR1_NONE_PARITY;
break;
}
case EVEN_PARITY: {
Extension->WmiCommData.Parity = SERIAL_WMI_PARITY_EVEN;
LParity = COR1_EVEN_PARITY;
break;
}
case ODD_PARITY: {
Extension->WmiCommData.Parity = SERIAL_WMI_PARITY_ODD;
LParity = COR1_ODD_PARITY;
break;
}
case SPACE_PARITY: {
Extension->WmiCommData.Parity = SERIAL_WMI_PARITY_SPACE;
LParity = COR1_SPACE_PARITY;
break;
}
case MARK_PARITY: {
Extension->WmiCommData.Parity = SERIAL_WMI_PARITY_MARK;
LParity = COR1_MARK_PARITY;
break;
}
default: {
Status = STATUS_INVALID_PARAMETER;
goto DoneWithIoctl;
break;
}
}
switch (Lc->StopBits) {
case STOP_BIT_1: {
Extension->WmiCommData.StopBits = SERIAL_WMI_STOP_1;
LStop = COR1_1_STOP;
break;
}
case STOP_BITS_1_5: {
Extension->WmiCommData.StopBits = SERIAL_WMI_STOP_1_5;
LStop = COR1_1_5_STOP;
break;
}
case STOP_BITS_2: {
Extension->WmiCommData.StopBits = SERIAL_WMI_STOP_2;
LStop = COR1_2_STOP;
break;
}
default: {
Status = STATUS_INVALID_PARAMETER;
goto DoneWithIoctl;
}
}
KeAcquireSpinLock(&Extension->ControlLock,&OldIrql);
Extension->cor1 = (UCHAR)(LData | LParity | LStop);
Extension->ValidDataMask = (UCHAR) Mask;
KeSynchronizeExecution(
Extension->Interrupt,
CyySetLineControl,
Extension
);
KeReleaseSpinLock(&Extension->ControlLock,OldIrql);
break;
}
case IOCTL_SERIAL_GET_LINE_CONTROL: {
PSERIAL_LINE_CONTROL Lc = (PSERIAL_LINE_CONTROL)Irp->AssociatedIrp.SystemBuffer;
if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength <
sizeof(SERIAL_LINE_CONTROL)) {
Status = STATUS_BUFFER_TOO_SMALL;
break;
}
KeAcquireSpinLock(&Extension->ControlLock,&OldIrql);
if ((Extension->cor1 & COR1_DATA_MASK) == COR1_5_DATA) {
Lc->WordLength = 5;
} else if ((Extension->cor1 & COR1_DATA_MASK) == COR1_6_DATA) {
Lc->WordLength = 6;
} else if ((Extension->cor1 & COR1_DATA_MASK) == COR1_7_DATA) {
Lc->WordLength = 7;
} else if ((Extension->cor1 & COR1_DATA_MASK) == COR1_8_DATA) {
Lc->WordLength = 8;
}
if ((Extension->cor1 & COR1_PARITY_MASK) == COR1_NONE_PARITY) {
Lc->Parity = NO_PARITY;
} else if ((Extension->cor1 & COR1_PARITY_MASK) == COR1_ODD_PARITY) {
Lc->Parity = ODD_PARITY;
} else if ((Extension->cor1 & COR1_PARITY_MASK) == COR1_EVEN_PARITY) {
Lc->Parity = EVEN_PARITY;
} else if ((Extension->cor1 & COR1_PARITY_MASK) == COR1_MARK_PARITY) {
Lc->Parity = MARK_PARITY;
} else if ((Extension->cor1 & COR1_PARITY_MASK) == COR1_SPACE_PARITY) {
Lc->Parity = SPACE_PARITY;
}
if ((Extension->cor1 & COR1_STOP_MASK) == COR1_2_STOP) {
if (Lc->WordLength == 5) {
Lc->StopBits = STOP_BITS_1_5;
} else {
Lc->StopBits = STOP_BITS_2;
}
} else {
Lc->StopBits = STOP_BIT_1;
}
Irp->IoStatus.Information = sizeof(SERIAL_LINE_CONTROL);
KeReleaseSpinLock(&Extension->ControlLock,OldIrql);
break;
}
case IOCTL_SERIAL_SET_TIMEOUTS: {
PSERIAL_TIMEOUTS NewTimeouts =
((PSERIAL_TIMEOUTS)(Irp->AssociatedIrp.SystemBuffer));
if (IrpSp->Parameters.DeviceIoControl.InputBufferLength <
sizeof(SERIAL_TIMEOUTS)) {
Status = STATUS_BUFFER_TOO_SMALL;
break;
}
if ((NewTimeouts->ReadIntervalTimeout == MAXULONG) &&
(NewTimeouts->ReadTotalTimeoutMultiplier == MAXULONG) &&
(NewTimeouts->ReadTotalTimeoutConstant == MAXULONG)) {
Status = STATUS_INVALID_PARAMETER;
break;
}
KeAcquireSpinLock(&Extension->ControlLock,&OldIrql);
Extension->Timeouts.ReadIntervalTimeout =
NewTimeouts->ReadIntervalTimeout;
Extension->Timeouts.ReadTotalTimeoutMultiplier =
NewTimeouts->ReadTotalTimeoutMultiplier;
Extension->Timeouts.ReadTotalTimeoutConstant =
NewTimeouts->ReadTotalTimeoutConstant;
Extension->Timeouts.WriteTotalTimeoutMultiplier =
NewTimeouts->WriteTotalTimeoutMultiplier;
Extension->Timeouts.WriteTotalTimeoutConstant =
NewTimeouts->WriteTotalTimeoutConstant;
KeReleaseSpinLock(&Extension->ControlLock,OldIrql);
break;
}
case IOCTL_SERIAL_GET_TIMEOUTS: {
if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength <
sizeof(SERIAL_TIMEOUTS)) {
Status = STATUS_BUFFER_TOO_SMALL;
break;
}
KeAcquireSpinLock(&Extension->ControlLock,&OldIrql);
*((PSERIAL_TIMEOUTS)Irp->AssociatedIrp.SystemBuffer) =
Extension->Timeouts;
Irp->IoStatus.Information = sizeof(SERIAL_TIMEOUTS);
KeReleaseSpinLock(&Extension->ControlLock,OldIrql);
break;
}
case IOCTL_SERIAL_SET_CHARS: {
CYY_IOCTL_SYNC S;
PSERIAL_CHARS NewChars =
((PSERIAL_CHARS)(Irp->AssociatedIrp.SystemBuffer));
if (IrpSp->Parameters.DeviceIoControl.InputBufferLength <
sizeof(SERIAL_CHARS)) {
Status = STATUS_BUFFER_TOO_SMALL;
break;
}
#if 0
if (NewChars->XonChar == NewChars->XoffChar) {
Status = STATUS_INVALID_PARAMETER;
break;
}
#endif
// We acquire the control lock so that only
// one request can GET or SET the characters
// at a time. The sets could be synchronized
// by the interrupt spinlock, but that wouldn't
// prevent multiple gets at the same time.
S.Extension = Extension;
S.Data = NewChars;
KeAcquireSpinLock(&Extension->ControlLock,&OldIrql);
// Under the protection of the lock, make sure that
// the xon and xoff characters aren't the same as
// the escape character.
if (Extension->EscapeChar) {
if ((Extension->EscapeChar == NewChars->XonChar) ||
(Extension->EscapeChar == NewChars->XoffChar)) {
Status = STATUS_INVALID_PARAMETER;
KeReleaseSpinLock(&Extension->ControlLock,OldIrql);
break;
}
}
Extension->WmiCommData.XonCharacter = NewChars->XonChar;
Extension->WmiCommData.XoffCharacter = NewChars->XoffChar;
KeSynchronizeExecution(Extension->Interrupt,CyySetChars,&S);
KeReleaseSpinLock(&Extension->ControlLock,OldIrql);
break;
}
case IOCTL_SERIAL_GET_CHARS: {
if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength <
sizeof(SERIAL_CHARS)) {
Status = STATUS_BUFFER_TOO_SMALL;
break;
}
KeAcquireSpinLock(&Extension->ControlLock,&OldIrql);
*((PSERIAL_CHARS)Irp->AssociatedIrp.SystemBuffer) =
Extension->SpecialChars;
Irp->IoStatus.Information = sizeof(SERIAL_CHARS);
KeReleaseSpinLock(&Extension->ControlLock,OldIrql);
break;
}
case IOCTL_SERIAL_SET_DTR:
case IOCTL_SERIAL_CLR_DTR: {
//
// Make sure we are at power D0
//
if (Extension->PowerState != PowerDeviceD0) {
Status = CyyGotoPowerState(Extension->Pdo, Extension,
PowerDeviceD0);
if (!NT_SUCCESS(Status)) {
break;
}
}
// We acquire the lock so that we can check whether
// automatic dtr flow control is enabled. If it is
// then we return an error since the app is not allowed
// to touch this if it is automatic.
KeAcquireSpinLock(&Extension->ControlLock,&OldIrql);
if ((Extension->HandFlow.ControlHandShake & SERIAL_DTR_MASK)
== SERIAL_DTR_HANDSHAKE) {
// this is a bug from the sample driver.
//Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
Status = STATUS_INVALID_PARAMETER;
} else {
KeSynchronizeExecution(
Extension->Interrupt,
((IrpSp->Parameters.DeviceIoControl.IoControlCode ==
IOCTL_SERIAL_SET_DTR)?
(CyySetDTR):(CyyClrDTR)),
Extension
);
}
KeReleaseSpinLock(&Extension->ControlLock,OldIrql);
break;
}
case IOCTL_SERIAL_RESET_DEVICE: {
break;
}
case IOCTL_SERIAL_SET_RTS:
case IOCTL_SERIAL_CLR_RTS: {
//
// Make sure we are at power D0
//
if (Extension->PowerState != PowerDeviceD0) {
Status = CyyGotoPowerState(Extension->Pdo, Extension,
PowerDeviceD0);
if (!NT_SUCCESS(Status)) {
break;
}
}
// We acquire the lock so that we can check whether
// automatic rts flow control or transmit toggleing
// is enabled. If it is then we return an error since
// the app is not allowed to touch this if it is automatic
// or toggling.
KeAcquireSpinLock(&Extension->ControlLock,&OldIrql);
if (((Extension->HandFlow.FlowReplace & SERIAL_RTS_MASK)
== SERIAL_RTS_HANDSHAKE) ||
((Extension->HandFlow.FlowReplace & SERIAL_RTS_MASK)
== SERIAL_TRANSMIT_TOGGLE)) {
// this is a bug from the sample driver.
//Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
Status = STATUS_INVALID_PARAMETER;
} else {
KeSynchronizeExecution(
Extension->Interrupt,
((IrpSp->Parameters.DeviceIoControl.IoControlCode ==
IOCTL_SERIAL_SET_RTS)?
(CyySetRTS):(CyyClrRTS)),
Extension
);
}
KeReleaseSpinLock(&Extension->ControlLock,OldIrql);
break;
}
case IOCTL_SERIAL_SET_XOFF: {
KeSynchronizeExecution(
Extension->Interrupt,
CyyPretendXoff,
Extension
);
break;
}
case IOCTL_SERIAL_SET_XON: {
KeSynchronizeExecution(
Extension->Interrupt,
CyyPretendXon,
Extension
);
break;
}
case IOCTL_SERIAL_SET_BREAK_ON: {
//
// Make sure we are at power D0
//
if (Extension->PowerState != PowerDeviceD0) {
Status = CyyGotoPowerState(Extension->Pdo, Extension,
PowerDeviceD0);
if (!NT_SUCCESS(Status)) {
break;
}
}
KeSynchronizeExecution(
Extension->Interrupt,
CyyTurnOnBreak,
Extension
);
break;
}
case IOCTL_SERIAL_SET_BREAK_OFF: {
//
// Make sure we are at power D0
//
if (Extension->PowerState != PowerDeviceD0) {
Status = CyyGotoPowerState(Extension->Pdo, Extension,
PowerDeviceD0);
if (!NT_SUCCESS(Status)) {
break;
}
}
KeSynchronizeExecution(
Extension->Interrupt,
CyyTurnOffBreak,
Extension
);
break;
}
case IOCTL_SERIAL_SET_QUEUE_SIZE: {
// Type ahead buffer is fixed, so we just validate
// that the users request is not bigger that our
// own internal buffer size.
PSERIAL_QUEUE_SIZE Rs =
((PSERIAL_QUEUE_SIZE)(Irp->AssociatedIrp.SystemBuffer));
if (IrpSp->Parameters.DeviceIoControl.InputBufferLength <
sizeof(SERIAL_QUEUE_SIZE)) {
Status = STATUS_BUFFER_TOO_SMALL;
break;
}
//
// We have to allocate the memory for the new
// buffer while we're still in the context of the
// caller. We don't even try to protect this
// with a lock because the value could be stale
// as soon as we release the lock - The only time
// we will know for sure is when we actually try
// to do the resize.
//
if (Rs->InSize <= Extension->BufferSize) {
Status = STATUS_SUCCESS;
break;
}
try {
IrpSp->Parameters.DeviceIoControl.Type3InputBuffer =
ExAllocatePoolWithQuota(
NonPagedPool,
Rs->InSize
);
} except (EXCEPTION_EXECUTE_HANDLER) {
IrpSp->Parameters.DeviceIoControl.Type3InputBuffer = NULL;
Status = GetExceptionCode();
}
if (!IrpSp->Parameters.DeviceIoControl.Type3InputBuffer) {
break;
}
//
// Well the data passed was big enough. Do the request.
//
// There are two reason we place it in the read queue:
//
// 1) We want to serialize these resize requests so that
// they don't contend with each other.
//
// 2) We want to serialize these requests with reads since
// we don't want reads and resizes contending over the
// read buffer.
//
return CyyStartOrQueue(
Extension,
Irp,
&Extension->ReadQueue,
&Extension->CurrentReadIrp,
CyyStartRead
);
break;
}
case IOCTL_SERIAL_GET_WAIT_MASK: {
if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength <
sizeof(ULONG)) {
Status = STATUS_BUFFER_TOO_SMALL;
break;
}
// Simple scalar read. No reason to acquire a lock.
Irp->IoStatus.Information = sizeof(ULONG);
*((ULONG *)Irp->AssociatedIrp.SystemBuffer) = Extension->IsrWaitMask;
break;
}
case IOCTL_SERIAL_SET_WAIT_MASK: {
ULONG NewMask;
CyyDbgPrintEx(CYYIRPPATH, "In Ioctl processing for set mask\n");
if (IrpSp->Parameters.DeviceIoControl.InputBufferLength <
sizeof(ULONG)) {
CyyDbgPrintEx(CYYDIAG3, "Invalid size fo the buffer %d\n",
IrpSp->Parameters
.DeviceIoControl.InputBufferLength);
Status = STATUS_BUFFER_TOO_SMALL;
break;
} else {
NewMask = *((ULONG *)Irp->AssociatedIrp.SystemBuffer);
}
// Make sure that the mask only contains valid waitable events.
if (NewMask & ~(SERIAL_EV_RXCHAR |
SERIAL_EV_RXFLAG |
SERIAL_EV_TXEMPTY |
SERIAL_EV_CTS |
SERIAL_EV_DSR |
SERIAL_EV_RLSD |
SERIAL_EV_BREAK |
SERIAL_EV_ERR |
SERIAL_EV_RING |
SERIAL_EV_PERR |
SERIAL_EV_RX80FULL |
SERIAL_EV_EVENT1 |
SERIAL_EV_EVENT2)) {
CyyDbgPrintEx(CYYDIAG3, "Unknown mask %x\n", NewMask);
Status = STATUS_INVALID_PARAMETER;
break;
}
// Either start this irp or put it on the queue.
CyyDbgPrintEx(CYYIRPPATH, "Starting or queuing set mask irp %x"
"\n", Irp);
return CyyStartOrQueue(Extension,Irp,&Extension->MaskQueue,
&Extension->CurrentMaskIrp,
CyyStartMask);
}
case IOCTL_SERIAL_WAIT_ON_MASK: {
CyyDbgPrintEx(CYYIRPPATH, "In Ioctl processing for wait mask\n");
if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength <
sizeof(ULONG)) {
CyyDbgPrintEx(CYYDIAG3, "Invalid size for the buffer %d\n",
IrpSp->Parameters
.DeviceIoControl.OutputBufferLength);
Status = STATUS_BUFFER_TOO_SMALL;
break;
}
// Either start this irp or put it on the queue.
CyyDbgPrintEx(CYYIRPPATH, "Starting or queuing wait mask irp"
"%x\n", Irp);
return CyyStartOrQueue(
Extension,
Irp,
&Extension->MaskQueue,
&Extension->CurrentMaskIrp,
CyyStartMask
);
}
case IOCTL_SERIAL_IMMEDIATE_CHAR: {
KIRQL OldIrql;
if (IrpSp->Parameters.DeviceIoControl.InputBufferLength <
sizeof(UCHAR)) {
Status = STATUS_BUFFER_TOO_SMALL;
break;
}
IoAcquireCancelSpinLock(&OldIrql);
if (Extension->CurrentImmediateIrp) {
Status = STATUS_INVALID_PARAMETER;
IoReleaseCancelSpinLock(OldIrql);
} else {
// We can queue the char. We need to set
// a cancel routine because flow control could
// keep the char from transmitting. Make sure
// that the irp hasn't already been canceled.
if (Irp->Cancel) {
IoReleaseCancelSpinLock(OldIrql);
Status = STATUS_CANCELLED;
} else {
Extension->CurrentImmediateIrp = Irp;
Extension->TotalCharsQueued++;
IoReleaseCancelSpinLock(OldIrql);
CyyStartImmediate(Extension);
return STATUS_PENDING;
}
}
break;
}
case IOCTL_SERIAL_PURGE: {
ULONG Mask;
if (IrpSp->Parameters.DeviceIoControl.InputBufferLength <
sizeof(ULONG)) {
Status = STATUS_BUFFER_TOO_SMALL;
break;
}
// Check to make sure that the mask is valid
Mask = *((ULONG *)(Irp->AssociatedIrp.SystemBuffer));
if ((!Mask) || (Mask & (~(SERIAL_PURGE_TXABORT |
SERIAL_PURGE_RXABORT |
SERIAL_PURGE_TXCLEAR |
SERIAL_PURGE_RXCLEAR )))) {
Status = STATUS_INVALID_PARAMETER;
break;
}
// Either start this irp or put it on the queue.
return CyyStartOrQueue(
Extension,
Irp,
&Extension->PurgeQueue,
&Extension->CurrentPurgeIrp,
CyyStartPurge
);
}
case IOCTL_SERIAL_GET_HANDFLOW: {
if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength <
sizeof(SERIAL_HANDFLOW)) {
Status = STATUS_BUFFER_TOO_SMALL;
break;
}
Irp->IoStatus.Information = sizeof(SERIAL_HANDFLOW);
KeAcquireSpinLock(&Extension->ControlLock,&OldIrql);
*((PSERIAL_HANDFLOW)Irp->AssociatedIrp.SystemBuffer) =
Extension->HandFlow;
KeReleaseSpinLock(&Extension->ControlLock,OldIrql);
break;
}
case IOCTL_SERIAL_SET_HANDFLOW: {
CYY_IOCTL_SYNC S;
PSERIAL_HANDFLOW HandFlow = Irp->AssociatedIrp.SystemBuffer;
if (IrpSp->Parameters.DeviceIoControl.InputBufferLength <
sizeof(SERIAL_HANDFLOW)) {
Status = STATUS_BUFFER_TOO_SMALL;
break;
}
// Make sure that there are no invalid bits set.
if (HandFlow->ControlHandShake & SERIAL_CONTROL_INVALID) {
Status = STATUS_INVALID_PARAMETER;
break;
}
if (HandFlow->FlowReplace & SERIAL_FLOW_INVALID) {
Status = STATUS_INVALID_PARAMETER;
break;
}
// Make sure that the app hasn't set an invlid DTR mode.
if((HandFlow->ControlHandShake&SERIAL_DTR_MASK)==SERIAL_DTR_MASK) {
Status = STATUS_INVALID_PARAMETER;
break;
}
// Make sure that haven't set totally invalid xon/xoff limits.
if ((HandFlow->XonLimit < 0) ||
((ULONG)HandFlow->XonLimit > Extension->BufferSize)) {
Status = STATUS_INVALID_PARAMETER;
break;
}
if ((HandFlow->XoffLimit < 0) ||
((ULONG)HandFlow->XoffLimit > Extension->BufferSize)) {
Status = STATUS_INVALID_PARAMETER;
break;
}
S.Extension = Extension;
S.Data = HandFlow;
KeAcquireSpinLock(&Extension->ControlLock,&OldIrql);
// Under the protection of the lock, make sure that
// we aren't turning on error replacement when we
// are doing line status/modem status insertion.
if (Extension->EscapeChar) {
if (HandFlow->FlowReplace & SERIAL_ERROR_CHAR) {
Status = STATUS_INVALID_PARAMETER;
KeReleaseSpinLock(&Extension->ControlLock,OldIrql);
break;
}
}
KeSynchronizeExecution(Extension->Interrupt,CyySetHandFlow,&S);
KeReleaseSpinLock(&Extension->ControlLock,OldIrql);
break;
}
case IOCTL_SERIAL_GET_MODEMSTATUS: {
CYY_IOCTL_SYNC S;
if(IrpSp->Parameters.DeviceIoControl.OutputBufferLength <
sizeof(ULONG)) {
Status = STATUS_BUFFER_TOO_SMALL;
break;
}
Irp->IoStatus.Information = sizeof(ULONG);
S.Extension = Extension;
S.Data = Irp->AssociatedIrp.SystemBuffer;
KeAcquireSpinLock(&Extension->ControlLock,&OldIrql);
KeSynchronizeExecution(Extension->Interrupt,CyyGetModemUpdate,&S);
KeReleaseSpinLock(&Extension->ControlLock,OldIrql);
break;
}
case IOCTL_SERIAL_GET_DTRRTS: {
CYY_IOCTL_SYNC S;
ULONG ModemControl;
if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength <
sizeof(ULONG)) {
Status = STATUS_BUFFER_TOO_SMALL;
break;
}
Irp->IoStatus.Information = sizeof(ULONG);
Irp->IoStatus.Status = STATUS_SUCCESS;
// Reading this hardware has no effect on the device.
#if 0
ModemControl = READ_MODEM_CONTROL(Extension->Controller);
ModemControl &= SERIAL_DTR_STATE | SERIAL_RTS_STATE;
*(PULONG)Irp->AssociatedIrp.SystemBuffer = ModemControl;
#endif
S.Extension = Extension;
S.Data = &ModemControl;
KeAcquireSpinLock(&Extension->ControlLock,&OldIrql);
KeSynchronizeExecution(Extension->Interrupt,CyyGetDTRRTS,&S);
KeReleaseSpinLock(&Extension->ControlLock,OldIrql);
*(PULONG)Irp->AssociatedIrp.SystemBuffer = ModemControl;
break;
}
case IOCTL_SERIAL_GET_COMMSTATUS: {
CYY_IOCTL_SYNC S;
if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength <
sizeof(SERIAL_STATUS)) {
Status = STATUS_BUFFER_TOO_SMALL;
break;
}
Irp->IoStatus.Information = sizeof(SERIAL_STATUS);
S.Extension = Extension;
S.Data = Irp->AssociatedIrp.SystemBuffer;
// Acquire the cancel spin lock so nothing changes.
IoAcquireCancelSpinLock(&OldIrql);
KeSynchronizeExecution(Extension->Interrupt,CyyGetCommStatus,&S);
IoReleaseCancelSpinLock(OldIrql);
break;
}
case IOCTL_SERIAL_GET_PROPERTIES: {
if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength <
sizeof(SERIAL_COMMPROP)) {
Status = STATUS_BUFFER_TOO_SMALL;
break;
}
// No synchronization is required since information is "static".
CyyGetProperties(Extension,Irp->AssociatedIrp.SystemBuffer);
Irp->IoStatus.Information = sizeof(SERIAL_COMMPROP);
Irp->IoStatus.Status = STATUS_SUCCESS;
break;
}
case IOCTL_SERIAL_XOFF_COUNTER: {
PSERIAL_XOFF_COUNTER Xc = Irp->AssociatedIrp.SystemBuffer;
if (IrpSp->Parameters.DeviceIoControl.InputBufferLength <
sizeof(SERIAL_XOFF_COUNTER)) {
Status = STATUS_BUFFER_TOO_SMALL;
break;
}
if (Xc->Counter <= 0) {
Status = STATUS_INVALID_PARAMETER;
break;
}
//
// There is no output, so make that clear now
//
Irp->IoStatus.Information = 0; // Added in build 2128
//
// So far so good. Put the irp onto the write queue.
//
return CyyStartOrQueue(
Extension,
Irp,
&Extension->WriteQueue,
&Extension->CurrentWriteIrp,
CyyStartWrite
);
}
case IOCTL_SERIAL_LSRMST_INSERT: {
PUCHAR escapeChar = Irp->AssociatedIrp.SystemBuffer;
//FANNY: DECLARATION OF S WAS ADDED FOLLOWING NEW SERIAL SOURCE, BUT
// S IS NOT USED!!!!
CYY_IOCTL_SYNC S;
if (IrpSp->Parameters.DeviceIoControl.InputBufferLength <
sizeof(UCHAR)) {
Status = STATUS_BUFFER_TOO_SMALL;
break;
}
KeAcquireSpinLock(&Extension->ControlLock,&OldIrql);
if (*escapeChar) {
// We've got some escape work to do. We will make sure that
// the character is not the same as the Xon or Xoff character,
// or that we are already doing error replacement.
if ((*escapeChar == Extension->SpecialChars.XoffChar) ||
(*escapeChar == Extension->SpecialChars.XonChar) ||
(Extension->HandFlow.FlowReplace & SERIAL_ERROR_CHAR)) {
Status = STATUS_INVALID_PARAMETER;
KeReleaseSpinLock(&Extension->ControlLock,OldIrql);
break;
}
}
S.Extension = Extension;
S.Data = Irp->AssociatedIrp.SystemBuffer;
KeSynchronizeExecution(Extension->Interrupt,CyySetEscapeChar,Irp);
KeReleaseSpinLock(&Extension->ControlLock,OldIrql);
break;
}
case IOCTL_SERIAL_CONFIG_SIZE: {
if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength <
sizeof(ULONG)) {
Status = STATUS_BUFFER_TOO_SMALL;
break;
}
Irp->IoStatus.Information = sizeof(ULONG);
Irp->IoStatus.Status = STATUS_SUCCESS;
*(PULONG)Irp->AssociatedIrp.SystemBuffer = 0;
break;
}
case IOCTL_SERIAL_GET_STATS: {
if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength <
sizeof(SERIALPERF_STATS)) {
Status = STATUS_BUFFER_TOO_SMALL;
break;
}
Irp->IoStatus.Information = sizeof(SERIALPERF_STATS);
Irp->IoStatus.Status = STATUS_SUCCESS;
KeSynchronizeExecution(
Extension->Interrupt,
CyyGetStats,
Irp
);
break;
}
case IOCTL_SERIAL_CLEAR_STATS: {
KeSynchronizeExecution(
Extension->Interrupt,
CyyClearStats,
Extension
);
break;
}
default: {
Status = STATUS_INVALID_PARAMETER;
break;
}
}
DoneWithIoctl:;
Irp->IoStatus.Status = Status;
CyyCompleteRequest(Extension, Irp, 0);
return Status;
}
VOID
CyyGetProperties(
IN PCYY_DEVICE_EXTENSION Extension,
IN PSERIAL_COMMPROP Properties
)
/*--------------------------------------------------------------------------
CyyGetProperties()
Routine Description: This function returns the capabilities of this
particular serial device.
Arguments:
Extension - The serial device extension.
Properties - The structure used to return the properties
Return Value:
None.
--------------------------------------------------------------------------*/
{
CYY_LOCKED_PAGED_CODE();
RtlZeroMemory(Properties,sizeof(SERIAL_COMMPROP));
Properties->PacketLength = sizeof(SERIAL_COMMPROP);
Properties->PacketVersion = 2;
Properties->ServiceMask = SERIAL_SP_SERIALCOMM;
Properties->MaxTxQueue = 0;
Properties->MaxRxQueue = 0;
Properties->MaxBaud = SERIAL_BAUD_USER;
Properties->SettableBaud = Extension->SupportedBauds;
Properties->ProvSubType = SERIAL_SP_RS232;
Properties->ProvCapabilities = SERIAL_PCF_DTRDSR |
SERIAL_PCF_RTSCTS |
SERIAL_PCF_CD |
SERIAL_PCF_PARITY_CHECK |
SERIAL_PCF_XONXOFF |
SERIAL_PCF_SETXCHAR |
SERIAL_PCF_TOTALTIMEOUTS |
SERIAL_PCF_INTTIMEOUTS;
Properties->SettableParams = SERIAL_SP_PARITY |
SERIAL_SP_BAUD |
SERIAL_SP_DATABITS |
SERIAL_SP_STOPBITS |
SERIAL_SP_HANDSHAKING |
SERIAL_SP_PARITY_CHECK |
SERIAL_SP_CARRIER_DETECT;
Properties->SettableData = SERIAL_DATABITS_5 |
SERIAL_DATABITS_6 |
SERIAL_DATABITS_7 |
SERIAL_DATABITS_8;
Properties->SettableStopParity = SERIAL_STOPBITS_10 |
SERIAL_STOPBITS_15 |
SERIAL_STOPBITS_20 |
SERIAL_PARITY_NONE |
SERIAL_PARITY_ODD |
SERIAL_PARITY_EVEN |
SERIAL_PARITY_MARK |
SERIAL_PARITY_SPACE;
Properties->CurrentTxQueue = 0;
Properties->CurrentRxQueue = Extension->BufferSize;
}
BOOLEAN
CyySetRxFifoThresholdUsingRxFifoTrigger(
IN PVOID Context
)
/*--------------------------------------------------------------------------
CyySetRxFifoThresholdUsingRxFifoTrigger()
Routine Description: set the baud rate of the device.
Arguments:
Context - Pointer to a structure that contains a pointer to
the device extension.
Return Value: This routine always returns FALSE if error;
TRUE if successful.
--------------------------------------------------------------------------*/
{
PCYY_DEVICE_EXTENSION pDevExt = Context;
PUCHAR chip = pDevExt->Cd1400;
ULONG bus = pDevExt->IsPci;
UCHAR cor3value;
CYY_LOCKED_PAGED_CODE();
CD1400_WRITE(chip,bus,CAR, pDevExt->CdChannel & 0x03);
cor3value = CD1400_READ(chip,bus,COR3);
cor3value &= 0xf0;
switch (pDevExt->RxFifoTrigger & 0xc0) {
case SERIAL_1_BYTE_HIGH_WATER:
cor3value |= 0x01;
break;
case SERIAL_4_BYTE_HIGH_WATER:
cor3value |= 0x04;
break;
case SERIAL_8_BYTE_HIGH_WATER:
cor3value |= 0x08;
break;
case SERIAL_14_BYTE_HIGH_WATER:
cor3value |= MAX_CHAR_FIFO;
break;
}
CD1400_WRITE(chip,bus,COR3, cor3value);
CyyCDCmd(pDevExt,CCR_CORCHG_COR3);
pDevExt->RxFifoTriggerUsed = TRUE;
return TRUE;
}
BOOLEAN
CyySetRxFifoThresholdUsingBaudRate(
IN PVOID Context
)
/*--------------------------------------------------------------------------
CyySetRxFifoThresholdUsingBaudRate()
Routine Description: set the baud rate of the device.
Arguments:
Context - Pointer to a structure that contains a pointer to
the device extension.
Return Value: This routine always returns FALSE if error;
TRUE if successful.
--------------------------------------------------------------------------*/
{
PCYY_DEVICE_EXTENSION pDevExt = Context;
PUCHAR chip = pDevExt->Cd1400;
ULONG bus = pDevExt->IsPci;
ULONG baud = pDevExt->CurrentBaud;
UCHAR cor3value;
CYY_LOCKED_PAGED_CODE();
// Restore COR3 accordingly to baud rate
cor3value = CD1400_READ(chip,bus,COR3);
cor3value &= 0xf0;
if(baud <= 9600) {
CD1400_WRITE(chip,bus,COR3, cor3value | 0x0a);
} else if (baud <= 38400) {
CD1400_WRITE(chip,bus,COR3, cor3value | 0x06);
} else {
CD1400_WRITE(chip,bus,COR3, cor3value | 0x04);
}
CyyCDCmd(pDevExt,CCR_CORCHG_COR3);
pDevExt->RxFifoTriggerUsed = FALSE;
return TRUE;
}
NTSTATUS
CyyInternalIoControl(IN PDEVICE_OBJECT PDevObj, IN PIRP PIrp)
/*++
Routine Description:
This routine provides the initial processing for all of the
internal Ioctrls for the serial device.
Arguments:
PDevObj - Pointer to the device object for this device
PIrp - Pointer to the IRP for the current request
Return Value:
The function value is the final status of the call
--*/
{
//
// The status that gets returned to the caller and
// set in the Irp.
//
NTSTATUS status;
//
// The current stack location. This contains all of the
// information we need to process this particular request.
//
PIO_STACK_LOCATION pIrpStack;
//
// Just what it says. This is the serial specific device
// extension of the device object create for the serial driver.
//
PCYY_DEVICE_EXTENSION pDevExt = PDevObj->DeviceExtension;
//
// A temporary to hold the old IRQL so that it can be
// restored once we complete/validate this request.
//
KIRQL OldIrql;
NTSTATUS prologueStatus;
SYSTEM_POWER_STATE cap; // Added in build 2128
CYY_LOCKED_PAGED_CODE();
if ((prologueStatus = CyyIRPPrologue(PIrp, pDevExt))
!= STATUS_SUCCESS) {
if (prologueStatus != STATUS_PENDING) {
CyyCompleteRequest(pDevExt, PIrp, IO_NO_INCREMENT);
}
return prologueStatus;
}
CyyDbgPrintEx(CYYIRPPATH, "Dispatch entry for: %x\n", PIrp);
if (CyyCompleteIfError(PDevObj, PIrp) != STATUS_SUCCESS) {
return STATUS_CANCELLED;
}
pIrpStack = IoGetCurrentIrpStackLocation(PIrp);
PIrp->IoStatus.Information = 0L;
status = STATUS_SUCCESS;
switch (pIrpStack->Parameters.DeviceIoControl.IoControlCode) {
//
// Send a wait-wake IRP
//
case IOCTL_SERIAL_INTERNAL_DO_WAIT_WAKE:
//
// Make sure we can do wait-wake based on what the device reported
//
for (cap = PowerSystemSleeping1; cap < PowerSystemMaximum; cap++) { // Added in bld 2128
if ((pDevExt->DeviceStateMap[cap] >= PowerDeviceD0)
&& (pDevExt->DeviceStateMap[cap] <= pDevExt->DeviceWake)) {
break;
}
}
if (cap < PowerSystemMaximum) {
pDevExt->SendWaitWake = TRUE;
status = STATUS_SUCCESS;
} else {
status = STATUS_NOT_SUPPORTED;
}
break;
case IOCTL_SERIAL_INTERNAL_CANCEL_WAIT_WAKE:
pDevExt->SendWaitWake = FALSE;
if (pDevExt->PendingWakeIrp != NULL) {
IoCancelIrp(pDevExt->PendingWakeIrp);
}
status = STATUS_SUCCESS;
break;
//
// Put the serial port in a "filter-driver" appropriate state
//
// WARNING: This code assumes it is being called by a trusted kernel
// entity and no checking is done on the validity of the settings
// passed to IOCTL_SERIAL_INTERNAL_RESTORE_SETTINGS
//
// If validity checking is desired, the regular ioctl's should be used
//
case IOCTL_SERIAL_INTERNAL_BASIC_SETTINGS:
case IOCTL_SERIAL_INTERNAL_RESTORE_SETTINGS: {
SERIAL_BASIC_SETTINGS basic;
PSERIAL_BASIC_SETTINGS pBasic;
SHORT AppropriateDivisor;
CYY_IOCTL_SYNC S;
if (pIrpStack->Parameters.DeviceIoControl.IoControlCode
== IOCTL_SERIAL_INTERNAL_BASIC_SETTINGS) {
//
// Check the buffer size
//
if (pIrpStack->Parameters.DeviceIoControl.OutputBufferLength <
sizeof(SERIAL_BASIC_SETTINGS)) {
status = STATUS_BUFFER_TOO_SMALL;
break;
}
//
// Everything is 0 -- timeouts and flow control and fifos. If
// We add additional features, this zero memory method
// may not work.
//
RtlZeroMemory(&basic, sizeof(SERIAL_BASIC_SETTINGS));
basic.TxFifo = 1;
basic.RxFifo = SERIAL_1_BYTE_HIGH_WATER;
PIrp->IoStatus.Information = sizeof(SERIAL_BASIC_SETTINGS);
pBasic = (PSERIAL_BASIC_SETTINGS)PIrp->AssociatedIrp.SystemBuffer;
//
// Save off the old settings
//
RtlCopyMemory(&pBasic->Timeouts, &pDevExt->Timeouts,
sizeof(SERIAL_TIMEOUTS));
RtlCopyMemory(&pBasic->HandFlow, &pDevExt->HandFlow,
sizeof(SERIAL_HANDFLOW));
pBasic->RxFifo = pDevExt->RxFifoTrigger;
pBasic->TxFifo = pDevExt->TxFifoAmount;
//
// Point to our new settings
//
pBasic = &basic;
pDevExt->RxFifoTrigger = (UCHAR)pBasic->RxFifo;
// Set COR3 accordingly to RxFifoTrigger
KeSynchronizeExecution(pDevExt->Interrupt,
CyySetRxFifoThresholdUsingRxFifoTrigger, pDevExt);
} else { // restoring settings
if (pIrpStack->Parameters.DeviceIoControl.InputBufferLength
< sizeof(SERIAL_BASIC_SETTINGS)) {
status = STATUS_BUFFER_TOO_SMALL;
break;
}
pBasic = (PSERIAL_BASIC_SETTINGS)PIrp->AssociatedIrp.SystemBuffer;
// Restore COR3 accordingly to baud rate
KeSynchronizeExecution(pDevExt->Interrupt,
CyySetRxFifoThresholdUsingBaudRate, pDevExt);
}
KeAcquireSpinLock(&pDevExt->ControlLock, &OldIrql);
//
// Set the timeouts
//
RtlCopyMemory(&pDevExt->Timeouts, &pBasic->Timeouts,
sizeof(SERIAL_TIMEOUTS));
//
// Set flowcontrol
//
S.Extension = pDevExt;
S.Data = &pBasic->HandFlow;
KeSynchronizeExecution(pDevExt->Interrupt, CyySetHandFlow, &S);
// CHANGED FANNY
// if (pDevExt->FifoPresent) {
// pDevExt->TxFifoAmount = pBasic->TxFifo;
// pDevExt->RxFifoTrigger = (UCHAR)pBasic->RxFifo;
//
// WRITE_FIFO_CONTROL(pDevExt->Controller, (UCHAR)0);
// READ_RECEIVE_BUFFER(pDevExt->Controller);
// WRITE_FIFO_CONTROL(pDevExt->Controller,
// (UCHAR)(SERIAL_FCR_ENABLE | pDevExt->RxFifoTrigger
// | SERIAL_FCR_RCVR_RESET
// | SERIAL_FCR_TXMT_RESET));
// } else {
// pDevExt->TxFifoAmount = pDevExt->RxFifoTrigger = 0;
// WRITE_FIFO_CONTROL(pDevExt->Controller, (UCHAR)0);
// }
if ((pBasic->TxFifo > MAX_CHAR_FIFO) || (pBasic->TxFifo < 1)) {
pDevExt->TxFifoAmount = MAX_CHAR_FIFO;
} else {
pDevExt->TxFifoAmount = pBasic->TxFifo;
}
KeReleaseSpinLock(&pDevExt->ControlLock, OldIrql);
break;
}
default:
status = STATUS_INVALID_PARAMETER;
break;
}
PIrp->IoStatus.Status = status;
CyyCompleteRequest(pDevExt, PIrp, 0);
return status;
}