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.
2397 lines
74 KiB
2397 lines
74 KiB
/*--------------------------------------------------------------------------
|
|
*
|
|
* Copyright (C) Cyclades Corporation, 1997-2001.
|
|
* All rights reserved.
|
|
*
|
|
* Cyclades-Z Port Driver
|
|
*
|
|
* This file: cyzioctl.c
|
|
*
|
|
* Description: This module contains the code related to ioctl
|
|
* calls in the Cyclades-Z 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
|
|
CyzGetModemUpdate(
|
|
IN PVOID Context
|
|
);
|
|
|
|
BOOLEAN
|
|
CyzGetCommStatus(
|
|
IN PVOID Context
|
|
);
|
|
|
|
BOOLEAN
|
|
CyzSetEscapeChar(
|
|
IN PVOID Context
|
|
);
|
|
|
|
BOOLEAN
|
|
CyzSetBasicFifoSettings(
|
|
IN PVOID Context
|
|
);
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#if 0 // These routines cannot be pageable because of spin lock.
|
|
//#pragma alloc_text(PAGESER,CyzSetBaud)
|
|
//#pragma alloc_text(PAGESER,CyzSetLineControl)
|
|
//#pragma alloc_text(PAGESER,CyzIoControl)
|
|
//#pragma alloc_text(PAGESER,CyzSetChars)
|
|
//#pragma alloc_text(PAGESER,CyzGetModemUpdate)
|
|
//#pragma alloc_text(PAGESER,CyzGetCommStatus)
|
|
#pragma alloc_text(PAGESER,CyzGetProperties)
|
|
//#pragma alloc_text(PAGESER,CyzSetEscapeChar)
|
|
//#pragma alloc_text(PAGESER,CyzGetStats)
|
|
//#pragma alloc_text(PAGESER,CyzClearStats)
|
|
//#pragma alloc_text(PAGESER,CyzSetMCRContents)
|
|
//#pragma alloc_text(PAGESER,CyzGetMCRContents)
|
|
//#pragma alloc_text(PAGESER,CyzSetFCRContents)
|
|
//#pragma alloc_text(PAGESER,CyzSetBasicFifoSettings)
|
|
//#pragma alloc_text(PAGESER,CyzInternalIoControl)
|
|
//#pragma alloc_text(PAGESER,CyzIssueCmd)
|
|
#endif
|
|
#endif
|
|
|
|
static const PHYSICAL_ADDRESS CyzPhysicalZero = {0};
|
|
|
|
|
|
BOOLEAN
|
|
CyzGetStats(
|
|
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);
|
|
PCYZ_DEVICE_EXTENSION extension = irpSp->DeviceObject->DeviceExtension;
|
|
PSERIALPERF_STATS sp = ((PIRP)Context)->AssociatedIrp.SystemBuffer;
|
|
|
|
CYZ_LOCKED_PAGED_CODE();
|
|
|
|
*sp = extension->PerfStats;
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
BOOLEAN
|
|
CyzClearStats(
|
|
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.
|
|
|
|
--*/
|
|
|
|
{
|
|
CYZ_LOCKED_PAGED_CODE();
|
|
|
|
RtlZeroMemory(
|
|
&((PCYZ_DEVICE_EXTENSION)Context)->PerfStats,
|
|
sizeof(SERIALPERF_STATS)
|
|
);
|
|
|
|
RtlZeroMemory(&((PCYZ_DEVICE_EXTENSION)Context)->WmiPerfData,
|
|
sizeof(SERIAL_WMI_PERF_DATA));
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
CyzSetChars(
|
|
IN PVOID Context
|
|
)
|
|
/*--------------------------------------------------------------------------
|
|
CyzSetChars()
|
|
|
|
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.
|
|
--------------------------------------------------------------------------*/
|
|
{
|
|
PCYZ_DEVICE_EXTENSION Extension = ((PCYZ_IOCTL_SYNC)Context)->Extension;
|
|
struct CH_CTRL *ch_ctrl;
|
|
|
|
CYZ_LOCKED_PAGED_CODE();
|
|
|
|
Extension->SpecialChars =
|
|
*((PSERIAL_CHARS)(((PCYZ_IOCTL_SYNC)Context)->Data));
|
|
|
|
ch_ctrl = Extension->ChCtrl;
|
|
CYZ_WRITE_ULONG(&ch_ctrl->flow_xon,Extension->SpecialChars.XonChar);
|
|
CYZ_WRITE_ULONG(&ch_ctrl->flow_xoff,Extension->SpecialChars.XoffChar);
|
|
CyzIssueCmd(Extension,C_CM_IOCTLW,0L,FALSE);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
BOOLEAN
|
|
CyzSetBaud(
|
|
IN PVOID Context
|
|
)
|
|
/*--------------------------------------------------------------------------
|
|
CyzSetBaud()
|
|
|
|
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.
|
|
--------------------------------------------------------------------------*/
|
|
{
|
|
PCYZ_DEVICE_EXTENSION Extension = ((PCYZ_IOCTL_BAUD)Context)->Extension;
|
|
ULONG baud = ((PCYZ_IOCTL_BAUD)Context)->Baud;
|
|
struct CH_CTRL *ch_ctrl;
|
|
|
|
CYZ_LOCKED_PAGED_CODE();
|
|
|
|
ch_ctrl = Extension->ChCtrl;
|
|
CYZ_WRITE_ULONG(&ch_ctrl->comm_baud,baud);
|
|
|
|
CyzIssueCmd(Extension,C_CM_IOCTLW,0L,FALSE);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
VOID
|
|
CyzIssueCmd(
|
|
PCYZ_DEVICE_EXTENSION Extension,
|
|
ULONG cmd,
|
|
ULONG param,
|
|
BOOLEAN wait)
|
|
/*--------------------------------------------------------------------------
|
|
CyzIssueCmd()
|
|
|
|
Routine Description: Send a command to the Cyclades-Z CPU.
|
|
|
|
Arguments:
|
|
|
|
Extension - pointer to the device extension.
|
|
cmd - command to be sent to hw.
|
|
param - pointer to parameters
|
|
wait - wait for command to be completed
|
|
|
|
Return Value: none.
|
|
--------------------------------------------------------------------------*/
|
|
{
|
|
|
|
struct BOARD_CTRL *board_ctrl;
|
|
PULONG pci_doorbell;
|
|
LARGE_INTEGER startOfSpin, nextQuery, difference, interval100ms; //oneSecond;
|
|
PCYZ_DISPATCH pDispatch = Extension->OurIsrContext;
|
|
|
|
#ifdef POLL
|
|
KIRQL OldIrql;
|
|
|
|
KeAcquireSpinLock(&pDispatch->PciDoorbellLock,&OldIrql);
|
|
#endif
|
|
|
|
pci_doorbell = &(Extension->Runtime)->pci_doorbell;
|
|
|
|
//oneSecond.QuadPart = 10*1000*1000; // unit is 100ns
|
|
interval100ms.QuadPart = 1000*1000; // unit is 100ns
|
|
|
|
KeQueryTickCount(&startOfSpin);
|
|
|
|
while ( (CYZ_READ_ULONG(pci_doorbell) & 0xff) != 0) {
|
|
|
|
KeQueryTickCount(&nextQuery);
|
|
difference.QuadPart = nextQuery.QuadPart - startOfSpin.QuadPart;
|
|
ASSERT(KeQueryTimeIncrement() <= MAXLONG);
|
|
//*************************
|
|
// Error Injection
|
|
//if (difference.QuadPart * KeQueryTimeIncrement() <
|
|
// interval100ms.QuadPart)
|
|
//*************************
|
|
if (difference.QuadPart * KeQueryTimeIncrement() >=
|
|
interval100ms.QuadPart) {
|
|
if (Extension->CmdFailureLog == FALSE) {
|
|
#if DBG
|
|
DbgPrint("\n ***** Cyzport Command Failure! *****\n");
|
|
#endif
|
|
CyzLogError(Extension->DeviceObject->DriverObject,NULL,
|
|
Extension->OriginalBoardMemory,CyzPhysicalZero,
|
|
0,0,0,Extension->PortIndex+1,STATUS_SUCCESS,
|
|
CYZ_COMMAND_FAILURE,0,NULL,0,NULL);
|
|
Extension->CmdFailureLog = TRUE;
|
|
}
|
|
#ifdef POLL
|
|
KeReleaseSpinLock(&pDispatch->PciDoorbellLock,OldIrql);
|
|
#endif
|
|
return;
|
|
}
|
|
}
|
|
|
|
board_ctrl = Extension->BoardCtrl;
|
|
CYZ_WRITE_ULONG(&board_ctrl->hcmd_channel,Extension->PortIndex);
|
|
CYZ_WRITE_ULONG(&board_ctrl->hcmd_param,param);
|
|
|
|
CYZ_WRITE_ULONG(pci_doorbell,cmd);
|
|
|
|
|
|
if (wait) {
|
|
KeQueryTickCount(&startOfSpin);
|
|
while ( (CYZ_READ_ULONG(pci_doorbell) & 0xff) != 0) {
|
|
|
|
KeQueryTickCount(&nextQuery);
|
|
difference.QuadPart = nextQuery.QuadPart - startOfSpin.QuadPart;
|
|
ASSERT(KeQueryTimeIncrement() <= MAXLONG);
|
|
if (difference.QuadPart * KeQueryTimeIncrement() >=
|
|
interval100ms.QuadPart) {
|
|
if (Extension->CmdFailureLog == FALSE) {
|
|
CyzLogError(Extension->DeviceObject->DriverObject,
|
|
NULL,Extension->OriginalBoardMemory,CyzPhysicalZero,
|
|
0,0,0,Extension->PortIndex+1,STATUS_SUCCESS,
|
|
CYZ_COMMAND_FAILURE,0,NULL,0,NULL);
|
|
Extension->CmdFailureLog = TRUE;
|
|
}
|
|
#ifdef POLL
|
|
KeReleaseSpinLock(&pDispatch->PciDoorbellLock,OldIrql);
|
|
#endif
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
#ifndef POLL
|
|
|
|
// I REPLACED C_CM_IOCTL BY C_CM_IOCTLW FOR POLL VERSION!!!!! (FANNY)
|
|
|
|
// If the cmd is C_CM_IOCTL, the firmware will reset the UART. There
|
|
// is a case where HoldingEmpty is FALSE, waiting for the C_CM_TXBEMPTY
|
|
// interrupt, and as the UART is being reset, this interrupt will never
|
|
// occur. To avoid dead lock, the HoldingEmpty flag will be reset to
|
|
// TRUE here.
|
|
|
|
if (cmd == C_CM_IOCTL) {
|
|
Extension->HoldingEmpty = TRUE;
|
|
}
|
|
#endif
|
|
|
|
#ifdef POLL
|
|
KeReleaseSpinLock(&pDispatch->PciDoorbellLock,OldIrql);
|
|
#endif
|
|
|
|
return;
|
|
} /* CyzIssueCmd */
|
|
|
|
|
|
BOOLEAN
|
|
CyzSetLineControl(
|
|
IN PVOID Context
|
|
)
|
|
/*--------------------------------------------------------------------------
|
|
CyzSetLineControl()
|
|
|
|
Routine Description: set the Data Parity, Data Length and Stop-bits.
|
|
|
|
Arguments:
|
|
|
|
Context - Pointer to the device extension.
|
|
|
|
Return Value: This routine always returns FALSE.
|
|
--------------------------------------------------------------------------*/
|
|
{
|
|
PCYZ_DEVICE_EXTENSION Extension = Context;
|
|
struct CH_CTRL *ch_ctrl;
|
|
|
|
CYZ_LOCKED_PAGED_CODE();
|
|
|
|
ch_ctrl = Extension->ChCtrl;
|
|
CYZ_WRITE_ULONG(&ch_ctrl->comm_data_l,Extension->CommDataLen);
|
|
CYZ_WRITE_ULONG(&ch_ctrl->comm_parity,Extension->CommParity);
|
|
|
|
CyzIssueCmd(Extension,C_CM_IOCTLW,0L,FALSE);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
CyzGetModemUpdate(
|
|
IN PVOID Context
|
|
)
|
|
/*--------------------------------------------------------------------------
|
|
CyzGetModemUpdate()
|
|
|
|
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.
|
|
--------------------------------------------------------------------------*/
|
|
{
|
|
PCYZ_DEVICE_EXTENSION Extension = ((PCYZ_IOCTL_SYNC)Context)->Extension;
|
|
ULONG *Result = (ULONG *)(((PCYZ_IOCTL_SYNC)Context)->Data);
|
|
|
|
CYZ_LOCKED_PAGED_CODE();
|
|
|
|
*Result = CyzHandleModemUpdate(Extension,FALSE,0);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
CyzSetMCRContents(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.
|
|
|
|
--*/
|
|
{
|
|
PCYZ_DEVICE_EXTENSION Extension = ((PCYZ_IOCTL_SYNC)Context)->Extension;
|
|
ULONG *Result = (ULONG *)(((PCYZ_IOCTL_SYNC)Context)->Data);
|
|
|
|
struct CH_CTRL *ch_ctrl = Extension->ChCtrl;
|
|
ULONG rs_control, op_mode;
|
|
|
|
CYZ_LOCKED_PAGED_CODE();
|
|
|
|
// Let's convert the UART modem control to our hw
|
|
|
|
rs_control = CYZ_READ_ULONG(&ch_ctrl->rs_control);
|
|
|
|
if (*Result & SERIAL_MCR_DTR) {
|
|
rs_control |= C_RS_DTR;
|
|
} else {
|
|
rs_control &= ~C_RS_DTR;
|
|
}
|
|
|
|
if (*Result & SERIAL_MCR_RTS) {
|
|
rs_control |= C_RS_RTS;
|
|
} else {
|
|
rs_control &= ~C_RS_RTS;
|
|
}
|
|
|
|
// For interrupt mode, remember to enable/disable the interrupt. C_CM_IRQ_ENBL or C_CM_IRQ_DSBL?
|
|
// 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
|
|
// }
|
|
|
|
CYZ_WRITE_ULONG(&ch_ctrl->rs_control,rs_control);
|
|
CyzIssueCmd(Extension,C_CM_IOCTLM,rs_control|C_RS_PARAM,FALSE);
|
|
|
|
|
|
// Check for loopback mode
|
|
op_mode = CYZ_READ_ULONG(&ch_ctrl->op_mode);
|
|
if (*Result & SERIAL_MCR_LOOP) {
|
|
op_mode |= C_CH_LOOPBACK;
|
|
} else {
|
|
op_mode &= ~C_CH_LOOPBACK;
|
|
}
|
|
CYZ_WRITE_ULONG(&ch_ctrl->op_mode, op_mode);
|
|
CyzIssueCmd(Extension,C_CM_IOCTL,0,FALSE);
|
|
|
|
|
|
// 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
|
|
CyzGetMCRContents(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.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
PCYZ_DEVICE_EXTENSION Extension = ((PCYZ_IOCTL_SYNC)Context)->Extension;
|
|
ULONG *Result = (ULONG *)(((PCYZ_IOCTL_SYNC)Context)->Data);
|
|
|
|
struct CH_CTRL *ch_ctrl;
|
|
ULONG rs_control,op_mode;
|
|
*Result = 0;
|
|
|
|
CYZ_LOCKED_PAGED_CODE();
|
|
|
|
ch_ctrl = Extension->ChCtrl;
|
|
rs_control = CYZ_READ_ULONG(&ch_ctrl->rs_control);
|
|
|
|
if (rs_control & C_RS_DTR) {
|
|
*Result |= SERIAL_MCR_DTR;
|
|
}
|
|
if (rs_control & C_RS_RTS) {
|
|
*Result |= SERIAL_MCR_RTS;
|
|
}
|
|
|
|
// For interrupt mode, we will need to update SERIAL_MCR_OUT bit as well.
|
|
|
|
op_mode = CYZ_READ_ULONG(&ch_ctrl->op_mode);
|
|
|
|
if (op_mode & C_CH_LOOPBACK) {
|
|
* Result |= SERIAL_MCR_LOOP;
|
|
}
|
|
|
|
// *Result = READ_MODEM_CONTROL(Extension->Controller);
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
CyzSetFCRContents(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.
|
|
|
|
--*/
|
|
{
|
|
PCYZ_DEVICE_EXTENSION Extension = ((PCYZ_IOCTL_SYNC)Context)->Extension;
|
|
ULONG *Result = (ULONG *)(((PCYZ_IOCTL_SYNC)Context)->Data);
|
|
|
|
CYZ_LOCKED_PAGED_CODE();
|
|
|
|
if (*Result & SERIAL_FCR_TXMT_RESET) {
|
|
CyzIssueCmd(Extension,C_CM_FLUSH_TX,0,FALSE);
|
|
}
|
|
if (*Result & SERIAL_FCR_RCVR_RESET) {
|
|
CyzIssueCmd(Extension,C_CM_FLUSH_RX,0,FALSE);
|
|
}
|
|
|
|
// For interrupt mode, we will need to set the rx fifo threshold.
|
|
|
|
|
|
// //
|
|
// // This is severe casting abuse!!!
|
|
// //
|
|
//
|
|
// WRITE_FIFO_CONTROL(Extension->Controller, (UCHAR)*Result); Bld 2128: PtrToUlong replaced by *
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
CyzGetCommStatus(
|
|
IN PVOID Context
|
|
)
|
|
/*--------------------------------------------------------------------------
|
|
CyzGetCommStatus()
|
|
|
|
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.
|
|
--------------------------------------------------------------------------*/
|
|
{
|
|
PCYZ_DEVICE_EXTENSION Extension = ((PCYZ_IOCTL_SYNC)Context)->Extension;
|
|
PSERIAL_STATUS Stat = ((PCYZ_IOCTL_SYNC)Context)->Data;
|
|
|
|
CYZ_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 & CYZ_TX_CTS) {
|
|
Stat->HoldReasons |= SERIAL_TX_WAITING_FOR_CTS;
|
|
}
|
|
|
|
if (Extension->TXHolding & CYZ_TX_DSR) {
|
|
Stat->HoldReasons |= SERIAL_TX_WAITING_FOR_DSR;
|
|
}
|
|
|
|
if (Extension->TXHolding & CYZ_TX_DCD) {
|
|
Stat->HoldReasons |= SERIAL_TX_WAITING_FOR_DCD;
|
|
}
|
|
|
|
if (Extension->TXHolding & CYZ_TX_XOFF) {
|
|
Stat->HoldReasons |= SERIAL_TX_WAITING_FOR_XON;
|
|
}
|
|
|
|
if (Extension->TXHolding & CYZ_TX_BREAK) {
|
|
Stat->HoldReasons |= SERIAL_TX_WAITING_ON_BREAK;
|
|
}
|
|
}
|
|
|
|
if (Extension->RXHolding & CYZ_RX_DSR) {
|
|
Stat->HoldReasons |= SERIAL_RX_WAITING_FOR_DSR;
|
|
}
|
|
|
|
if (Extension->RXHolding & CYZ_RX_XOFF) {
|
|
Stat->HoldReasons |= SERIAL_TX_WAITING_XOFF_SENT;
|
|
}
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
BOOLEAN
|
|
CyzSetEscapeChar(
|
|
IN PVOID Context
|
|
)
|
|
/*--------------------------------------------------------------------------
|
|
CyzSetEscapeChar()
|
|
|
|
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.
|
|
--------------------------------------------------------------------------*/
|
|
{
|
|
PCYZ_DEVICE_EXTENSION extension =
|
|
IoGetCurrentIrpStackLocation((PIRP)Context)
|
|
->DeviceObject->DeviceExtension;
|
|
|
|
extension->EscapeChar =
|
|
*(PUCHAR)((PIRP)Context)->AssociatedIrp.SystemBuffer;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
NTSTATUS
|
|
CyzIoControl(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
/*--------------------------------------------------------------------------
|
|
CyzIoControl()
|
|
|
|
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;
|
|
PCYZ_DEVICE_EXTENSION Extension = DeviceObject->DeviceExtension;
|
|
KIRQL OldIrql;
|
|
#ifdef POLL
|
|
KIRQL pollIrql;
|
|
#endif
|
|
|
|
NTSTATUS prologueStatus;
|
|
|
|
CYZ_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 = CyzIRPPrologue(Irp, Extension))
|
|
!= STATUS_SUCCESS) {
|
|
if(prologueStatus != STATUS_PENDING) {
|
|
Irp->IoStatus.Status = prologueStatus;
|
|
CyzCompleteRequest(Extension, Irp, IO_NO_INCREMENT);
|
|
}
|
|
return prologueStatus;
|
|
}
|
|
|
|
CyzDbgPrintEx(CYZIRPPATH, "Dispatch entry for: %x\n", Irp);
|
|
|
|
if (CyzCompleteIfError(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) {
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Make sure we are at power D0
|
|
//
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
if (Extension->PowerState != PowerDeviceD0) {
|
|
Status = CyzGotoPowerState(Extension->Pdo, Extension,
|
|
PowerDeviceD0);
|
|
if (!NT_SUCCESS(Status)) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
KeAcquireSpinLock(&Extension->ControlLock,&OldIrql);
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
CYZ_IOCTL_BAUD S;
|
|
|
|
Extension->CurrentBaud = BaudRate;
|
|
Extension->WmiCommData.BaudRate = BaudRate;
|
|
S.Extension = Extension;
|
|
S.Baud = BaudRate;
|
|
#ifdef POLL
|
|
KeAcquireSpinLock(&Extension->PollLock,&pollIrql);
|
|
CyzSetBaud(&S);
|
|
KeReleaseSpinLock(&Extension->PollLock,pollIrql);
|
|
#else
|
|
KeSynchronizeExecution(Extension->Interrupt,CyzSetBaud,&S);
|
|
#endif
|
|
}
|
|
|
|
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: {
|
|
CYZ_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
|
|
);
|
|
|
|
#ifdef POLL
|
|
KeAcquireSpinLock(&Extension->PollLock,&pollIrql);
|
|
CyzGetMCRContents(&S);
|
|
KeReleaseSpinLock(&Extension->PollLock,pollIrql);
|
|
#else
|
|
KeSynchronizeExecution(Extension->Interrupt,CyzGetMCRContents,&S);
|
|
#endif
|
|
|
|
KeReleaseSpinLock(
|
|
&Extension->ControlLock,
|
|
OldIrql
|
|
);
|
|
|
|
break;
|
|
}
|
|
case IOCTL_SERIAL_SET_MODEM_CONTROL: {
|
|
CYZ_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 = CyzGotoPowerState(Extension->Pdo, Extension,
|
|
PowerDeviceD0);
|
|
if (!NT_SUCCESS(Status)) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
KeAcquireSpinLock(
|
|
&Extension->ControlLock,
|
|
&OldIrql
|
|
);
|
|
|
|
#ifdef POLL
|
|
KeAcquireSpinLock(&Extension->PollLock,&pollIrql);
|
|
CyzSetMCRContents(&S);
|
|
KeReleaseSpinLock(&Extension->PollLock,pollIrql);
|
|
#else
|
|
KeSynchronizeExecution(Extension->Interrupt,CyzSetMCRContents,&S);
|
|
#endif
|
|
|
|
KeReleaseSpinLock(
|
|
&Extension->ControlLock,
|
|
OldIrql
|
|
);
|
|
|
|
break;
|
|
}
|
|
case IOCTL_SERIAL_SET_FIFO_CONTROL: {
|
|
CYZ_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 = CyzGotoPowerState(Extension->Pdo, Extension,
|
|
PowerDeviceD0);
|
|
if (!NT_SUCCESS(Status)) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
KeAcquireSpinLock(
|
|
&Extension->ControlLock,
|
|
&OldIrql
|
|
);
|
|
|
|
#ifdef POLL
|
|
KeAcquireSpinLock(&Extension->PollLock,&pollIrql);
|
|
CyzSetFCRContents(&S);
|
|
KeReleaseSpinLock(&Extension->PollLock,pollIrql);
|
|
#else
|
|
KeSynchronizeExecution(Extension->Interrupt,CyzSetFCRContents,&S);
|
|
#endif
|
|
|
|
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 = CyzGotoPowerState(Extension->Pdo, Extension,
|
|
PowerDeviceD0);
|
|
if (!NT_SUCCESS(Status)) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
switch (Lc->WordLength) {
|
|
case 5: LData = C_DL_CS5; Mask = 0x1f;
|
|
break;
|
|
|
|
case 6: LData = C_DL_CS6; Mask = 0x3f;
|
|
break;
|
|
|
|
case 7: LData = C_DL_CS7; Mask = 0x7f;
|
|
break;
|
|
|
|
case 8: LData = C_DL_CS8; 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 = C_PR_NONE;
|
|
break;
|
|
}
|
|
case EVEN_PARITY: {
|
|
Extension->WmiCommData.Parity = SERIAL_WMI_PARITY_EVEN;
|
|
LParity = C_PR_EVEN;
|
|
break;
|
|
}
|
|
case ODD_PARITY: {
|
|
Extension->WmiCommData.Parity = SERIAL_WMI_PARITY_ODD;
|
|
LParity = C_PR_ODD;
|
|
break;
|
|
}
|
|
case SPACE_PARITY: {
|
|
Extension->WmiCommData.Parity = SERIAL_WMI_PARITY_SPACE;
|
|
LParity = C_PR_SPACE;
|
|
break;
|
|
}
|
|
case MARK_PARITY: {
|
|
Extension->WmiCommData.Parity = SERIAL_WMI_PARITY_MARK;
|
|
LParity = C_PR_MARK;
|
|
break;
|
|
}
|
|
default: {
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
goto DoneWithIoctl;
|
|
}
|
|
}
|
|
|
|
switch (Lc->StopBits) {
|
|
case STOP_BIT_1: {
|
|
Extension->WmiCommData.StopBits = SERIAL_WMI_STOP_1;
|
|
LStop = C_DL_1STOP;
|
|
break;
|
|
}
|
|
case STOP_BITS_1_5: {
|
|
if (LData != C_DL_CS5) {
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
goto DoneWithIoctl;
|
|
}
|
|
Extension->WmiCommData.StopBits = SERIAL_WMI_STOP_1_5;
|
|
LStop = C_DL_15STOP;
|
|
break;
|
|
}
|
|
case STOP_BITS_2: {
|
|
if (LData == C_DL_CS5) {
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
goto DoneWithIoctl;
|
|
}
|
|
Extension->WmiCommData.StopBits = SERIAL_WMI_STOP_2;
|
|
LStop = C_DL_2STOP;
|
|
break;
|
|
}
|
|
default: {
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
goto DoneWithIoctl;
|
|
}
|
|
}
|
|
|
|
KeAcquireSpinLock(&Extension->ControlLock,&OldIrql);
|
|
|
|
Extension->CommDataLen = LData | LStop;
|
|
Extension->CommParity = LParity;
|
|
Extension->ValidDataMask = (UCHAR) Mask;
|
|
|
|
#ifdef POLL
|
|
KeAcquireSpinLock(&Extension->PollLock,&pollIrql);
|
|
CyzSetLineControl(Extension);
|
|
KeReleaseSpinLock(&Extension->PollLock,pollIrql);
|
|
#else
|
|
KeSynchronizeExecution(
|
|
Extension->Interrupt,
|
|
CyzSetLineControl,
|
|
Extension
|
|
);
|
|
#endif
|
|
|
|
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->CommDataLen & C_DL_CS) == C_DL_CS5) {
|
|
Lc->WordLength = 5;
|
|
} else if ((Extension->CommDataLen & C_DL_CS) == C_DL_CS6) {
|
|
Lc->WordLength = 6;
|
|
} else if ((Extension->CommDataLen & C_DL_CS) == C_DL_CS7) {
|
|
Lc->WordLength = 7;
|
|
} else if ((Extension->CommDataLen & C_DL_CS) == C_DL_CS8) {
|
|
Lc->WordLength = 8;
|
|
}
|
|
|
|
if (Extension->CommParity == C_PR_NONE) {
|
|
Lc->Parity = C_PR_NONE;
|
|
} else if (Extension->CommParity == C_PR_ODD) {
|
|
Lc->Parity = ODD_PARITY;
|
|
} else if (Extension->CommParity == C_PR_EVEN) {
|
|
Lc->Parity = EVEN_PARITY;
|
|
} else if (Extension->CommParity == C_PR_MARK) {
|
|
Lc->Parity = MARK_PARITY;
|
|
} else if (Extension->CommParity == C_PR_SPACE) {
|
|
Lc->Parity = SPACE_PARITY;
|
|
}
|
|
|
|
if ((Extension->CommDataLen & C_DL_STOP) == C_DL_2STOP) {
|
|
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: {
|
|
CYZ_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;
|
|
|
|
#ifdef POLL
|
|
KeAcquireSpinLock(&Extension->PollLock,&pollIrql);
|
|
CyzSetChars(&S);
|
|
KeReleaseSpinLock(&Extension->PollLock,pollIrql);
|
|
#else
|
|
KeSynchronizeExecution(Extension->Interrupt,CyzSetChars,&S);
|
|
#endif
|
|
|
|
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 = CyzGotoPowerState(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 {
|
|
#ifdef POLL
|
|
KeAcquireSpinLock(&Extension->PollLock,&pollIrql);
|
|
((IrpSp->Parameters.DeviceIoControl.IoControlCode ==
|
|
IOCTL_SERIAL_SET_DTR)?
|
|
(CyzSetDTR(Extension)):(CyzClrDTR(Extension)));
|
|
KeReleaseSpinLock(&Extension->PollLock,pollIrql);
|
|
#else
|
|
KeSynchronizeExecution(
|
|
Extension->Interrupt,
|
|
((IrpSp->Parameters.DeviceIoControl.IoControlCode ==
|
|
IOCTL_SERIAL_SET_DTR)?
|
|
(CyzSetDTR):(CyzClrDTR)),
|
|
Extension
|
|
);
|
|
#endif
|
|
}
|
|
|
|
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 = CyzGotoPowerState(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 {
|
|
#ifdef POLL
|
|
KeAcquireSpinLock(&Extension->PollLock,&pollIrql);
|
|
((IrpSp->Parameters.DeviceIoControl.IoControlCode ==
|
|
IOCTL_SERIAL_SET_RTS)?
|
|
(CyzSetRTS(Extension)):(CyzClrRTS(Extension)));
|
|
KeReleaseSpinLock(&Extension->PollLock,pollIrql);
|
|
#else
|
|
KeSynchronizeExecution(
|
|
Extension->Interrupt,
|
|
((IrpSp->Parameters.DeviceIoControl.IoControlCode ==
|
|
IOCTL_SERIAL_SET_RTS)?
|
|
(CyzSetRTS):(CyzClrRTS)),
|
|
Extension
|
|
);
|
|
#endif
|
|
}
|
|
KeReleaseSpinLock(&Extension->ControlLock,OldIrql);
|
|
break;
|
|
}
|
|
case IOCTL_SERIAL_SET_XOFF: {
|
|
|
|
#ifdef POLL
|
|
KeAcquireSpinLock(&Extension->PollLock,&pollIrql);
|
|
CyzPretendXoff(Extension);
|
|
KeReleaseSpinLock(&Extension->PollLock,pollIrql);
|
|
#else
|
|
KeSynchronizeExecution(
|
|
Extension->Interrupt,
|
|
CyzPretendXoff,
|
|
Extension
|
|
);
|
|
#endif
|
|
break;
|
|
|
|
}
|
|
case IOCTL_SERIAL_SET_XON: {
|
|
|
|
#ifdef POLL
|
|
KeAcquireSpinLock(&Extension->PollLock,&pollIrql);
|
|
CyzPretendXon(Extension);
|
|
KeReleaseSpinLock(&Extension->PollLock,pollIrql);
|
|
#else
|
|
KeSynchronizeExecution(
|
|
Extension->Interrupt,
|
|
CyzPretendXon,
|
|
Extension
|
|
);
|
|
#endif
|
|
break;
|
|
|
|
}
|
|
case IOCTL_SERIAL_SET_BREAK_ON: {
|
|
|
|
//
|
|
// Make sure we are at power D0
|
|
//
|
|
|
|
if (Extension->PowerState != PowerDeviceD0) {
|
|
Status = CyzGotoPowerState(Extension->Pdo, Extension,
|
|
PowerDeviceD0);
|
|
if (!NT_SUCCESS(Status)) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
#ifdef POLL
|
|
KeAcquireSpinLock(&Extension->PollLock,&pollIrql);
|
|
CyzTurnOnBreak(Extension);
|
|
KeReleaseSpinLock(&Extension->PollLock,pollIrql);
|
|
#else
|
|
KeSynchronizeExecution(
|
|
Extension->Interrupt,
|
|
CyzTurnOnBreak,
|
|
Extension
|
|
);
|
|
#endif
|
|
break;
|
|
}
|
|
case IOCTL_SERIAL_SET_BREAK_OFF: {
|
|
|
|
//
|
|
// Make sure we are at power D0
|
|
//
|
|
|
|
if (Extension->PowerState != PowerDeviceD0) {
|
|
Status = CyzGotoPowerState(Extension->Pdo, Extension,
|
|
PowerDeviceD0);
|
|
if (!NT_SUCCESS(Status)) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
#ifdef POLL
|
|
KeAcquireSpinLock(&Extension->PollLock,&pollIrql);
|
|
CyzTurnOffBreak(Extension);
|
|
KeReleaseSpinLock(&Extension->PollLock,pollIrql);
|
|
#else
|
|
KeSynchronizeExecution(
|
|
Extension->Interrupt,
|
|
CyzTurnOffBreak,
|
|
Extension
|
|
);
|
|
#endif
|
|
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 CyzStartOrQueue(
|
|
Extension,
|
|
Irp,
|
|
&Extension->ReadQueue,
|
|
&Extension->CurrentReadIrp,
|
|
CyzStartRead
|
|
);
|
|
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;
|
|
|
|
CyzDbgPrintEx(CYZIRPPATH, "In Ioctl processing for set mask\n");
|
|
|
|
if (IrpSp->Parameters.DeviceIoControl.InputBufferLength <
|
|
sizeof(ULONG)) {
|
|
|
|
CyzDbgPrintEx(CYZDIAG3, "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)) {
|
|
|
|
CyzDbgPrintEx(CYZDIAG3, "Unknown mask %x\n", NewMask);
|
|
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
break;
|
|
}
|
|
|
|
// Either start this irp or put it on the queue.
|
|
|
|
CyzDbgPrintEx(CYZIRPPATH, "Starting or queuing set mask irp %x"
|
|
"\n", Irp);
|
|
|
|
return CyzStartOrQueue(Extension,Irp,&Extension->MaskQueue,
|
|
&Extension->CurrentMaskIrp,
|
|
CyzStartMask);
|
|
|
|
}
|
|
case IOCTL_SERIAL_WAIT_ON_MASK: {
|
|
|
|
CyzDbgPrintEx(CYZIRPPATH, "In Ioctl processing for wait mask\n");
|
|
|
|
if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength <
|
|
sizeof(ULONG)) {
|
|
|
|
CyzDbgPrintEx(CYZDIAG3, "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.
|
|
|
|
CyzDbgPrintEx(CYZIRPPATH, "Starting or queuing wait mask irp"
|
|
"%x\n", Irp);
|
|
|
|
return CyzStartOrQueue(
|
|
Extension,
|
|
Irp,
|
|
&Extension->MaskQueue,
|
|
&Extension->CurrentMaskIrp,
|
|
CyzStartMask
|
|
);
|
|
|
|
}
|
|
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);
|
|
CyzStartImmediate(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));
|
|
|
|
LOGENTRY(LOG_MISC, ZSIG_PURGE,
|
|
Extension->PortIndex+1,
|
|
Mask,
|
|
0);
|
|
|
|
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 CyzStartOrQueue(
|
|
Extension,
|
|
Irp,
|
|
&Extension->PurgeQueue,
|
|
&Extension->CurrentPurgeIrp,
|
|
CyzStartPurge
|
|
);
|
|
}
|
|
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: {
|
|
CYZ_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;
|
|
}
|
|
}
|
|
|
|
#ifdef POLL
|
|
KeAcquireSpinLock(&Extension->PollLock,&pollIrql);
|
|
CyzSetHandFlow(&S);
|
|
KeReleaseSpinLock(&Extension->PollLock,pollIrql);
|
|
#else
|
|
KeSynchronizeExecution(Extension->Interrupt,CyzSetHandFlow,&S);
|
|
#endif
|
|
|
|
KeReleaseSpinLock(&Extension->ControlLock,OldIrql);
|
|
break;
|
|
}
|
|
case IOCTL_SERIAL_GET_MODEMSTATUS: {
|
|
CYZ_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);
|
|
#ifdef POLL
|
|
KeAcquireSpinLock(&Extension->PollLock,&pollIrql);
|
|
CyzGetModemUpdate(&S);
|
|
KeReleaseSpinLock(&Extension->PollLock,pollIrql);
|
|
#else
|
|
KeSynchronizeExecution(Extension->Interrupt,CyzGetModemUpdate,&S);
|
|
#endif
|
|
KeReleaseSpinLock(&Extension->ControlLock,OldIrql);
|
|
break;
|
|
}
|
|
case IOCTL_SERIAL_GET_DTRRTS: {
|
|
ULONG ModemControl = 0;
|
|
struct CH_CTRL *ch_ctrl;
|
|
ULONG rs_status;
|
|
|
|
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.
|
|
|
|
ch_ctrl = Extension->ChCtrl;
|
|
rs_status = CYZ_READ_ULONG(&ch_ctrl->rs_status);
|
|
if (rs_status & C_RS_DTR) {
|
|
ModemControl |= SERIAL_DTR_STATE;
|
|
}
|
|
if (rs_status & C_RS_RTS) {
|
|
ModemControl |= SERIAL_RTS_STATE;
|
|
}
|
|
|
|
#if 0
|
|
ModemControl = READ_MODEM_CONTROL(Extension->Controller);
|
|
|
|
ModemControl &= SERIAL_DTR_STATE | SERIAL_RTS_STATE;
|
|
#endif
|
|
|
|
*(PULONG)Irp->AssociatedIrp.SystemBuffer = ModemControl;
|
|
|
|
break;
|
|
|
|
}
|
|
case IOCTL_SERIAL_GET_COMMSTATUS: {
|
|
CYZ_IOCTL_SYNC S;
|
|
|
|
#ifdef POLL
|
|
KIRQL ControlIrql;
|
|
#endif
|
|
|
|
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);
|
|
|
|
#ifdef POLL
|
|
KeAcquireSpinLock(&Extension->PollLock,&pollIrql);
|
|
CyzGetCommStatus(&S);
|
|
KeReleaseSpinLock(&Extension->PollLock,pollIrql);
|
|
#else
|
|
KeSynchronizeExecution(Extension->Interrupt,CyzGetCommStatus,&S);
|
|
#endif
|
|
|
|
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".
|
|
|
|
CyzGetProperties(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 CyzStartOrQueue(
|
|
Extension,
|
|
Irp,
|
|
&Extension->WriteQueue,
|
|
&Extension->CurrentWriteIrp,
|
|
CyzStartWrite
|
|
);
|
|
}
|
|
case IOCTL_SERIAL_LSRMST_INSERT: {
|
|
|
|
PUCHAR escapeChar = Irp->AssociatedIrp.SystemBuffer;
|
|
CYZ_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;
|
|
|
|
#ifdef POLL
|
|
KeAcquireSpinLock(&Extension->PollLock,&pollIrql);
|
|
CyzSetEscapeChar(Irp);
|
|
KeReleaseSpinLock(&Extension->PollLock,pollIrql);
|
|
#else
|
|
KeSynchronizeExecution(Extension->Interrupt,CyzSetEscapeChar,Irp);
|
|
#endif
|
|
|
|
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;
|
|
|
|
#ifdef POLL
|
|
KeAcquireSpinLock(&Extension->PollLock,&pollIrql);
|
|
CyzGetStats(Irp);
|
|
KeReleaseSpinLock(&Extension->PollLock,pollIrql);
|
|
#else
|
|
KeSynchronizeExecution(
|
|
Extension->Interrupt,
|
|
CyzGetStats,
|
|
Irp
|
|
);
|
|
#endif
|
|
|
|
break;
|
|
}
|
|
case IOCTL_SERIAL_CLEAR_STATS: {
|
|
|
|
#ifdef POLL
|
|
KeAcquireSpinLock(&Extension->PollLock,&pollIrql);
|
|
CyzClearStats(Extension);
|
|
KeReleaseSpinLock(&Extension->PollLock,pollIrql);
|
|
#else
|
|
KeSynchronizeExecution(
|
|
Extension->Interrupt,
|
|
CyzClearStats,
|
|
Extension
|
|
);
|
|
#endif
|
|
break;
|
|
}
|
|
default: {
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
break;
|
|
}
|
|
}
|
|
|
|
DoneWithIoctl:;
|
|
|
|
Irp->IoStatus.Status = Status;
|
|
|
|
CyzCompleteRequest(Extension, Irp, 0);
|
|
|
|
return Status;
|
|
|
|
}
|
|
|
|
VOID
|
|
CyzGetProperties(
|
|
IN PCYZ_DEVICE_EXTENSION Extension,
|
|
IN PSERIAL_COMMPROP Properties
|
|
)
|
|
/*--------------------------------------------------------------------------
|
|
CyzGetProperties()
|
|
|
|
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.
|
|
--------------------------------------------------------------------------*/
|
|
{
|
|
CYZ_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
|
|
CyzSetBasicFifoSettings(
|
|
IN PVOID Context
|
|
)
|
|
/*--------------------------------------------------------------------------
|
|
CyzSetBasicFifoSettings()
|
|
|
|
Routine Description: This routine is used to set the FIFO settings
|
|
during the InternalIoControl.
|
|
|
|
Arguments:
|
|
|
|
Context - Pointer to a structure that contains a pointer to the device
|
|
extension and a pointer to the Basic structure.
|
|
|
|
Return Value: This routine always returns FALSE.
|
|
--------------------------------------------------------------------------*/
|
|
{
|
|
PCYZ_IOCTL_SYNC S = Context;
|
|
PCYZ_DEVICE_EXTENSION Extension = S->Extension;
|
|
PSERIAL_BASIC_SETTINGS pBasic = S->Data;
|
|
struct BUF_CTRL *buf_ctrl = Extension->BufCtrl;
|
|
struct CH_CTRL *ch_ctrl = Extension->ChCtrl;
|
|
ULONG commFlag;
|
|
|
|
#if 0
|
|
CyzIssueCmd(Extension,C_CM_FLUSH_TX,0,FALSE);
|
|
CyzIssueCmd(Extension,C_CM_FLUSH_RX,0,FALSE);
|
|
|
|
if (pBasic->TxFifo == 0x01) {
|
|
Extension->TxBufsize = pBasic->TxFifo + 1;
|
|
} else {
|
|
Extension->TxBufsize = pBasic->TxFifo;
|
|
}
|
|
|
|
|
|
CYZ_WRITE_ULONG(&buf_ctrl->tx_bufsize, Extension->TxBufsize);
|
|
|
|
Extension->RxFifoTrigger = pBasic->RxFifo;
|
|
#endif
|
|
|
|
CYZ_WRITE_ULONG(&buf_ctrl->rx_threshold,pBasic->RxFifo); //Actually, firmware threshold
|
|
if (pBasic->RxFifo == CYZ_BASIC_RXTRIGGER) {
|
|
commFlag = C_CF_NOFIFO; // Disable FIFO
|
|
} else {
|
|
commFlag = 0; // Enable FIFO
|
|
}
|
|
CYZ_WRITE_ULONG(&ch_ctrl->comm_flags,commFlag);
|
|
CyzIssueCmd(Extension,C_CM_IOCTL,0L,TRUE);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
CyzInternalIoControl(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.
|
|
//
|
|
PCYZ_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;
|
|
#ifdef POLL
|
|
KIRQL pollIrql;
|
|
#endif
|
|
|
|
NTSTATUS prologueStatus;
|
|
|
|
SYSTEM_POWER_STATE cap; // Added in build 2128
|
|
|
|
CYZ_LOCKED_PAGED_CODE();
|
|
|
|
|
|
if ((prologueStatus = CyzIRPPrologue(PIrp, pDevExt))
|
|
!= STATUS_SUCCESS) {
|
|
if (prologueStatus != STATUS_PENDING) {
|
|
CyzCompleteRequest(pDevExt, PIrp, IO_NO_INCREMENT);
|
|
}
|
|
return prologueStatus;
|
|
}
|
|
|
|
CyzDbgPrintEx(CYZIRPPATH, "Dispatch entry for: %x\n", PIrp);
|
|
|
|
if (CyzCompleteIfError(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;
|
|
CYZ_IOCTL_SYNC S;
|
|
struct BUF_CTRL *buf_ctrl = pDevExt->BufCtrl;
|
|
|
|
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;
|
|
basic.RxFifo = CYZ_BASIC_RXTRIGGER;
|
|
|
|
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->RxFifo = CYZ_READ_ULONG(&buf_ctrl->rx_threshold);
|
|
pBasic->TxFifo = pDevExt->TxBufsize; //pDevExt->TxFifoAmount;
|
|
|
|
//
|
|
// Point to our new settings
|
|
//
|
|
|
|
pBasic = &basic;
|
|
} 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;
|
|
|
|
}
|
|
|
|
KeAcquireSpinLock(&pDevExt->ControlLock, &OldIrql);
|
|
|
|
//
|
|
// Set the timeouts
|
|
//
|
|
|
|
RtlCopyMemory(&pDevExt->Timeouts, &pBasic->Timeouts,
|
|
sizeof(SERIAL_TIMEOUTS));
|
|
|
|
//
|
|
// Set flowcontrol
|
|
//
|
|
|
|
S.Extension = pDevExt;
|
|
S.Data = &pBasic->HandFlow;
|
|
#ifdef POLL
|
|
KeAcquireSpinLock(&pDevExt->PollLock,&pollIrql);
|
|
CyzSetHandFlow(&S);
|
|
KeReleaseSpinLock(&pDevExt->PollLock,pollIrql);
|
|
#else
|
|
KeSynchronizeExecution(pDevExt->Interrupt,CyzSetHandFlow,&S);
|
|
#endif
|
|
|
|
|
|
//
|
|
//
|
|
// Set TxFifo and RxFifo
|
|
//
|
|
// Code removed for now. No data is transmitted to the modem while in Basic
|
|
// settings, so at least to TxFifo this code doesn't matter. Besides, modem
|
|
// diagnostics with Supra Express 56K is not working well with tx_bufsize
|
|
// set to 2 (Only OK appears in the Response in the Modem applet, although
|
|
// we can see the whole string being sent by the modem). .
|
|
//
|
|
S.Data = pBasic;
|
|
#ifdef POLL
|
|
KeAcquireSpinLock(&pDevExt->PollLock,&pollIrql);
|
|
CyzSetBasicFifoSettings(&S);
|
|
KeReleaseSpinLock(&pDevExt->PollLock,pollIrql);
|
|
#else
|
|
KeSynchronizeExecution(pDevExt->Interrupt,CyzSetBasicFifoSettings,&S);
|
|
#endif
|
|
|
|
KeReleaseSpinLock(&pDevExt->ControlLock, OldIrql);
|
|
|
|
|
|
break;
|
|
}
|
|
|
|
default:
|
|
status = STATUS_INVALID_PARAMETER;
|
|
break;
|
|
|
|
}
|
|
|
|
PIrp->IoStatus.Status = status;
|
|
|
|
CyzCompleteRequest(pDevExt, PIrp, 0);
|
|
|
|
return status;
|
|
}
|
|
|
|
|