mirror of https://github.com/lianthony/NT4.0
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.
2741 lines
94 KiB
2741 lines
94 KiB
/*++
|
|
|
|
*****************************************************************************
|
|
* *
|
|
* This software contains proprietary and confidential information of *
|
|
* *
|
|
* Digi International Inc. *
|
|
* *
|
|
* By accepting transfer of this copy, Recipient agrees to retain this *
|
|
* software in confidence, to prevent disclosure to others, and to make *
|
|
* no use of this software other than that for which it was delivered. *
|
|
* This is an unpublished copyrighted work of Digi International Inc. *
|
|
* Except as permitted by federal law, 17 USC 117, copying is strictly *
|
|
* prohibited. *
|
|
* *
|
|
*****************************************************************************
|
|
|
|
Module Name:
|
|
|
|
ioctl.c
|
|
|
|
Abstract:
|
|
|
|
This module contains the NT IRP_MJ_DEVICE_CONTROL handler routine.
|
|
|
|
Revision History:
|
|
|
|
* $Log: /Components/Windows/NT/Async/FEP5/ioctl.c $
|
|
*
|
|
* 1 3/04/96 12:16p Stana
|
|
* Procedures to process IRP_MJ_DEVICE_CONTROL (DeviceIoControl) IRPs.
|
|
*
|
|
* Revision 1.1 1995/07/31 17:33:36 dirkh
|
|
* Initial revision
|
|
|
|
--*/
|
|
|
|
|
|
#include "header.h"
|
|
|
|
#ifndef _IOCTL_DOT_C
|
|
# define _IOCTL_DOT_C
|
|
static char RCSInfo_IoctlDotC[] = "$Header: /Components/Windows/NT/Async/FEP5/ioctl.c 1 3/04/96 12:16p Stana $";
|
|
#endif
|
|
|
|
#define DIGI_IOCTL_DBGOUT 0x00000001
|
|
#define DIGI_IOCTL_TRACE 0x00000002
|
|
#define DIGI_IOCTL_DBGBREAK 0x00000003
|
|
|
|
typedef struct _DIGI_IOCTL_
|
|
{
|
|
ULONG dwCommand;
|
|
ULONG dwBufferLength;
|
|
CHAR Char[1024];
|
|
} DIGI_IOCTL, *PDIGI_IOCTL;
|
|
|
|
NTSTATUS ControllerIoControl(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp);
|
|
|
|
|
|
NTSTATUS
|
|
SetSerialHandflow( PDIGI_CONTROLLER_EXTENSION ControllerExt,
|
|
PDEVICE_OBJECT DeviceObject,
|
|
PSERIAL_HANDFLOW HandFlow )
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine will properly setup the driver to handle the different
|
|
types of flowcontrol.
|
|
|
|
Note: By definition, the flow controls set here are suppose to be
|
|
'sticky' across opens.
|
|
|
|
|
|
Arguments:
|
|
|
|
ControllerExt - a pointer to this devices controllers extension.
|
|
|
|
DeviceObject - a pointer to this devices object.
|
|
|
|
HandFlow - a pointer to the requested flow control structure.
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS if it completes correctly.
|
|
|
|
--*/
|
|
{
|
|
DIGI_XFLAG IFlag;
|
|
USHORT RHigh, RMax;
|
|
PFEP_CHANNEL_STRUCTURE ChInfo;
|
|
KIRQL OldIrql;
|
|
LONG XonLimit, XoffLimit, QInSize;
|
|
LONG TempXoffLimit;
|
|
PDIGI_DEVICE_EXTENSION DeviceExt = DeviceObject->DeviceExtension;
|
|
|
|
UCHAR MStatSet, MStatClear, HFlowSet, HFlowClear;
|
|
|
|
NTSTATUS Status=STATUS_SUCCESS;
|
|
|
|
DigiDump( (DIGIFLOW|DIGIFLOWCTRL), (" Entering SetSerialHandflow.\n") );
|
|
|
|
TempXoffLimit = HandFlow->XoffLimit;
|
|
|
|
//
|
|
// Make sure that haven't set totally invalid xon/xoff
|
|
// limits.
|
|
//
|
|
|
|
ChInfo = (PFEP_CHANNEL_STRUCTURE)(ControllerExt->VirtualAddress +
|
|
DeviceExt->ChannelInfo.Offset);
|
|
|
|
EnableWindow( ControllerExt, DeviceExt->ChannelInfo.Window );
|
|
RHigh = READ_REGISTER_USHORT( &ChInfo->rhigh );
|
|
RMax = READ_REGISTER_USHORT( &ChInfo->rmax );
|
|
DisableWindow( ControllerExt );
|
|
|
|
RMax++;
|
|
|
|
DigiDump( (DIGIIOCTL|DIGIFLOWCTRL), (" RHigh: %d RMax: %d InSize: %d",
|
|
RHigh,
|
|
RMax,
|
|
DeviceExt->RequestedQSize.InSize) );
|
|
|
|
if( (unsigned)HandFlow->XonLimit > DeviceExt->RequestedQSize.InSize - HandFlow->XoffLimit )
|
|
{
|
|
DigiDump( (DIGIIOCTL|DIGIFLOWCTRL), (" XonLimit > BufferSize - XoffLimit! Recalc'ing XoffLimit.\n") );
|
|
|
|
//
|
|
// This really isn't the correct thing to do, but I do it because a
|
|
// lot of Win16 apps are written such that they think the XoffLimit
|
|
// value is relative the beginning of the buffer, and not the
|
|
// end of the buffer.
|
|
//
|
|
|
|
HandFlow->XoffLimit = (DeviceExt->RequestedQSize.InSize - HandFlow->XoffLimit);
|
|
}
|
|
|
|
if( DeviceExt->RequestedQSize.InSize > RMax )
|
|
{
|
|
QInSize = DeviceExt->RequestedQSize.InSize;
|
|
XoffLimit = RMax - ((HandFlow->XoffLimit * RMax) / QInSize);
|
|
XonLimit = (HandFlow->XonLimit * RMax) / QInSize;
|
|
}
|
|
else
|
|
{
|
|
QInSize = RMax;
|
|
XoffLimit = RMax - HandFlow->XoffLimit;
|
|
XonLimit = HandFlow->XonLimit;
|
|
}
|
|
|
|
DigiDump( (DIGIIOCTL|DIGIFLOWCTRL),
|
|
(" Possible new ctrl XoffLimit = %d\n"
|
|
" Possible new ctrl XonLimit = %d\n",
|
|
XoffLimit,
|
|
XonLimit) );
|
|
|
|
if( XoffLimit > (LONG)RMax )
|
|
{
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
DigiDump( (DIGIIOCTL|DIGIFLOWCTRL), (" XoffLimit > RMax, returning STATUS_INVALID_PARAMETER\n!") );
|
|
goto SetSerialHandflowExit;
|
|
}
|
|
|
|
DigiDump( (DIGIIOCTL|DIGIFLOWCTRL),
|
|
(" Possible new ctrl XoffLimit = %d\n"
|
|
" Possible new ctrl XonLimit = %d\n",
|
|
XoffLimit,
|
|
XonLimit) );
|
|
|
|
//
|
|
// Make sure that there are no invalid bits set in
|
|
// the control and handshake.
|
|
//
|
|
|
|
if( (HandFlow->ControlHandShake & SERIAL_CONTROL_INVALID)
|
|
|| (HandFlow->ControlHandShake & SERIAL_DTR_MASK) ==
|
|
SERIAL_DTR_MASK )
|
|
{
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
DigiDump( (DIGIIOCTL|DIGIFLOWCTRL), (" Invalid ControlHandShake, returning STATUS_INVALID_PARAMETER\n!") );
|
|
goto SetSerialHandflowExit;
|
|
}
|
|
|
|
if( HandFlow->FlowReplace & SERIAL_FLOW_INVALID )
|
|
{
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
DigiDump( (DIGIIOCTL|DIGIFLOWCTRL), (" Invalid FlowReplace, returning STATUS_INVALID_PARAMETER!\n") );
|
|
goto SetSerialHandflowExit;
|
|
}
|
|
|
|
//
|
|
// All the parameters are valid.
|
|
//
|
|
|
|
// Configure flow control limits.
|
|
|
|
// RAS changes the queue size, but doesn't intend to change flow control limits.
|
|
if( !( DeviceExt->SpecialFlags & DIGI_SPECIAL_FLAG_FAST_RAS ) )
|
|
{
|
|
DigiDump( (DIGIIOCTL|DIGIFLOWCTRL), (" SetSerialHandflow: FAST_RAS is OFF!\n") );
|
|
if( XonLimit > RHigh ) // Avoid rejection by FEP.
|
|
WriteCommandWord( DeviceExt, SET_RCV_HIGH, RMax - 1 );
|
|
WriteCommandWord( DeviceExt, SET_RCV_LOW, (USHORT)XonLimit );
|
|
WriteCommandWord( DeviceExt, SET_RCV_HIGH, (USHORT)XoffLimit);
|
|
|
|
// Based on 10ms polling frequency and 115.2Kbps bit rate,
|
|
// we acquire up to 115.2 bytes per polling iteration.
|
|
// Thus, to avoid flow control, we need notification up to
|
|
// two polling iterations prior to reaching XoffLimit.
|
|
// However, we need at least 50 bytes to make the interaction worthwhile.
|
|
// DH Calculate limit dynamically based on communication characteristics.
|
|
if( XoffLimit > 2*115 + 50 )
|
|
DeviceExt->ReceiveNotificationLimit = XoffLimit - 2*115;
|
|
else
|
|
if( RMax >= 100 )
|
|
DeviceExt->ReceiveNotificationLimit = 50;
|
|
else // shouldn't happen
|
|
DeviceExt->ReceiveNotificationLimit = RMax / 2;
|
|
|
|
} else {
|
|
DigiDump( (DIGIIOCTL|DIGIFLOWCTRL), (" SetSerialHandflow: FAST_RAS is ON!\n") );
|
|
|
|
//
|
|
// Reset ReceiveNotification to standard 80% Full notification.
|
|
//
|
|
DeviceExt->ReceiveNotificationLimit = (USHORT) ( ((ULONG)RMax) * 8UL / 10UL);
|
|
WriteCommandWord( DeviceExt, SET_RCV_LOW, (USHORT)XonLimit );
|
|
WriteCommandWord( DeviceExt, SET_RCV_HIGH, (USHORT)XoffLimit);
|
|
}
|
|
|
|
DigiDump( (DIGIIOCTL|DIGIFLOWCTRL), (" New ReceiveNotificationLimit: 0x%x\n", DeviceExt->ReceiveNotificationLimit) );
|
|
|
|
// Configure flow control signalling.
|
|
|
|
IFlag.Mask = MAXUSHORT;
|
|
IFlag.Src = 0;
|
|
IFlag.Command = SET_IFLAGS;
|
|
|
|
if( HandFlow->FlowReplace & SERIAL_AUTO_TRANSMIT )
|
|
IFlag.Src |= IFLAG_IXON;
|
|
else
|
|
IFlag.Mask &= ~IFLAG_IXON;
|
|
|
|
if( HandFlow->FlowReplace & SERIAL_AUTO_RECEIVE )
|
|
IFlag.Src |= IFLAG_IXOFF;
|
|
else
|
|
IFlag.Mask &= ~IFLAG_IXOFF;
|
|
|
|
SetXFlag( DeviceExt, &IFlag );
|
|
|
|
MStatClear = MStatSet = 0;
|
|
HFlowClear = HFlowSet = 0;
|
|
|
|
if( (DeviceExt->FlowReplace & SERIAL_RTS_MASK) !=
|
|
(HandFlow->FlowReplace & SERIAL_RTS_MASK) )
|
|
{
|
|
if( (HandFlow->FlowReplace & SERIAL_RTS_MASK) ==
|
|
SERIAL_RTS_HANDSHAKE )
|
|
{
|
|
HFlowSet |= ControllerExt->ModemSignalTable[RTS_SIGNAL];
|
|
}
|
|
else if( (HandFlow->FlowReplace & SERIAL_RTS_MASK) ==
|
|
SERIAL_RTS_CONTROL )
|
|
{
|
|
//
|
|
// We need to make sure RTS is asserted when certain 'things'
|
|
// occur, or when we are in a certain state.
|
|
//
|
|
MStatSet |= ControllerExt->ModemSignalTable[RTS_SIGNAL];
|
|
HFlowClear |= ControllerExt->ModemSignalTable[RTS_SIGNAL];
|
|
}
|
|
else if( (HandFlow->FlowReplace & SERIAL_RTS_MASK) ==
|
|
SERIAL_TRANSMIT_TOGGLE )
|
|
{
|
|
//DH not supported in FEP
|
|
}
|
|
else // RTS_DISABLED
|
|
{
|
|
MStatClear |= ControllerExt->ModemSignalTable[RTS_SIGNAL];
|
|
HFlowClear |= ControllerExt->ModemSignalTable[RTS_SIGNAL];
|
|
}
|
|
}
|
|
|
|
if( (DeviceExt->ControlHandShake & SERIAL_DTR_MASK) !=
|
|
(HandFlow->ControlHandShake & SERIAL_DTR_MASK) )
|
|
{
|
|
if( (HandFlow->ControlHandShake & SERIAL_DTR_MASK) ==
|
|
SERIAL_DTR_HANDSHAKE )
|
|
{
|
|
HFlowSet |= ControllerExt->ModemSignalTable[DTR_SIGNAL];
|
|
}
|
|
else if( (HandFlow->ControlHandShake & SERIAL_DTR_MASK) ==
|
|
SERIAL_DTR_CONTROL )
|
|
{
|
|
//
|
|
// We need to make sure DTR is asserted when certain 'things'
|
|
// occur, or when we are in a certain state.
|
|
//
|
|
MStatSet |= ControllerExt->ModemSignalTable[DTR_SIGNAL];
|
|
HFlowClear |= ControllerExt->ModemSignalTable[DTR_SIGNAL];
|
|
}
|
|
else // DTR_DISABLED
|
|
{
|
|
MStatClear |= ControllerExt->ModemSignalTable[DTR_SIGNAL];
|
|
HFlowClear |= ControllerExt->ModemSignalTable[DTR_SIGNAL];
|
|
}
|
|
}
|
|
|
|
|
|
if( ( HandFlow->ControlHandShake & SERIAL_CTS_HANDSHAKE )
|
|
!= ( DeviceExt->ControlHandShake & SERIAL_CTS_HANDSHAKE ) )
|
|
{
|
|
if( HandFlow->ControlHandShake & SERIAL_CTS_HANDSHAKE )
|
|
HFlowSet |= ControllerExt->ModemSignalTable[CTS_SIGNAL];
|
|
else
|
|
HFlowClear |= ControllerExt->ModemSignalTable[CTS_SIGNAL];
|
|
}
|
|
|
|
|
|
if( ( HandFlow->ControlHandShake & SERIAL_DSR_HANDSHAKE )
|
|
!= ( DeviceExt->ControlHandShake & SERIAL_DSR_HANDSHAKE ) )
|
|
{
|
|
if( HandFlow->ControlHandShake & SERIAL_DSR_HANDSHAKE )
|
|
HFlowSet |= ControllerExt->ModemSignalTable[DSR_SIGNAL];
|
|
else
|
|
HFlowClear |= ControllerExt->ModemSignalTable[DSR_SIGNAL];
|
|
}
|
|
|
|
|
|
if( ( HandFlow->ControlHandShake & SERIAL_DCD_HANDSHAKE )
|
|
!= ( DeviceExt->ControlHandShake & SERIAL_DCD_HANDSHAKE ) )
|
|
{
|
|
if( HandFlow->ControlHandShake & SERIAL_DCD_HANDSHAKE )
|
|
HFlowSet |= ControllerExt->ModemSignalTable[DCD_SIGNAL];
|
|
else
|
|
HFlowClear |= ControllerExt->ModemSignalTable[DCD_SIGNAL];
|
|
}
|
|
|
|
|
|
//
|
|
// Make sure we enable/disable flow controls before trying to
|
|
// explicitly set/clear modem control lines.
|
|
//
|
|
if( HFlowSet || HFlowClear )
|
|
{
|
|
DeviceExt->WriteOnlyModemSignalMask = (~HFlowSet) &
|
|
(ControllerExt->ModemSignalTable[DTR_SIGNAL]|ControllerExt->ModemSignalTable[RTS_SIGNAL]);
|
|
DeviceExt->WriteOnlyModemSignalMask &= ~HFlowClear;
|
|
WriteCommandBytes( DeviceExt, SET_HDW_FLOW_CONTROL,
|
|
HFlowSet, HFlowClear );
|
|
}
|
|
|
|
if( MStatSet || MStatClear )
|
|
{
|
|
DeviceExt->CurrentModemSignals |= MStatSet;
|
|
DeviceExt->CurrentModemSignals &= ~MStatClear;
|
|
DeviceExt->WriteOnlyModemSignalValue |= MStatSet;
|
|
DeviceExt->WriteOnlyModemSignalValue &= ~MStatClear;
|
|
|
|
WriteCommandBytes( DeviceExt, SET_MODEM_LINES,
|
|
MStatSet, MStatClear );
|
|
}
|
|
|
|
//
|
|
// If SERIAL_EV_ERR has been specified, then determine if DOSMODE needs
|
|
// to be turned on. Check and see if LSRMST mode has been turned on
|
|
// in the driver.
|
|
//
|
|
if( DeviceExt->WaitMask & SERIAL_EV_ERR )
|
|
{
|
|
}
|
|
|
|
//
|
|
// Remember the settings for next time.
|
|
//
|
|
KeAcquireSpinLock( &DeviceExt->ControlAccess, &OldIrql );
|
|
|
|
DeviceExt->FlowReplace = HandFlow->FlowReplace;
|
|
DeviceExt->ControlHandShake = HandFlow->ControlHandShake;
|
|
DeviceExt->XonLimit = HandFlow->XonLimit;
|
|
DeviceExt->XoffLimit = TempXoffLimit;
|
|
|
|
KeReleaseSpinLock( &DeviceExt->ControlAccess, OldIrql );
|
|
|
|
SetSerialHandflowExit:
|
|
DigiDump( (DIGIFLOW|DIGIFLOWCTRL), (" Exiting SetSerialHandflow.\n") );
|
|
return( Status );
|
|
|
|
} // end SetSerialHandflow
|
|
|
|
|
|
|
|
void
|
|
SetXFlag( IN PDIGI_DEVICE_EXTENSION DeviceExt,
|
|
IN PDIGI_XFLAG XFlag )
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
{
|
|
PFEP_CHANNEL_STRUCTURE ChInfo;
|
|
PDIGI_CONTROLLER_EXTENSION ControllerExt = DeviceExt->ParentControllerExt;
|
|
PUSHORT RegAddr;
|
|
USHORT OldXFlag, NewXFlag;
|
|
|
|
DigiDump( DIGIFLOW, (" Entering SetXFlag(cmd=%d,mask=0x%x,src=0x%x)\n",
|
|
XFlag->Command, XFlag->Mask, XFlag->Src ) );
|
|
|
|
ChInfo = (PFEP_CHANNEL_STRUCTURE)(ControllerExt->VirtualAddress +
|
|
DeviceExt->ChannelInfo.Offset);
|
|
|
|
switch( XFlag->Command )
|
|
{
|
|
case SET_CFLAGS:
|
|
RegAddr = &ChInfo->cflag;
|
|
break;
|
|
|
|
case SET_IFLAGS:
|
|
RegAddr = &ChInfo->iflag;
|
|
break;
|
|
|
|
case SET_OFLAGS:
|
|
RegAddr = &ChInfo->oflag;
|
|
break;
|
|
|
|
default:
|
|
ASSERT( XFlag->Command!=SET_CFLAGS && XFlag->Command!=SET_IFLAGS && XFlag->Command!=SET_OFLAGS );
|
|
return;
|
|
}
|
|
|
|
EnableWindow( ControllerExt, DeviceExt->ChannelInfo.Window );
|
|
OldXFlag = READ_REGISTER_USHORT( RegAddr );
|
|
DisableWindow( ControllerExt );
|
|
|
|
NewXFlag = (OldXFlag & XFlag->Mask) | XFlag->Src;
|
|
if( NewXFlag != OldXFlag )
|
|
{
|
|
WriteCommandWord( DeviceExt, XFlag->Command, NewXFlag );
|
|
}
|
|
|
|
DigiDump( DIGIFLOW, (" Exiting SetXFlag\n") );
|
|
|
|
return;
|
|
} // end SetXFlag
|
|
|
|
|
|
|
|
NTSTATUS
|
|
SerialIoControl( IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp )
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
PIO_STACK_LOCATION IrpSp;
|
|
PDIGI_CONTROLLER_EXTENSION ControllerExt;
|
|
PDIGI_DEVICE_EXTENSION DeviceExt = DeviceObject->DeviceExtension;
|
|
KIRQL OldIrql;
|
|
|
|
#if DBG
|
|
LARGE_INTEGER CurrentSystemTime;
|
|
#endif
|
|
|
|
ASSERT( IoGetCurrentIrpStackLocation(Irp)->MajorFunction == IRP_MJ_DEVICE_CONTROL );
|
|
|
|
if (DeviceObject==DeviceExt->ParentControllerExt->ControllerDeviceObject)
|
|
{
|
|
/*
|
|
** If this is for the controller, handle it elsewhere.
|
|
*/
|
|
return ControllerIoControl(DeviceObject, Irp);
|
|
}
|
|
|
|
InterlockedIncrement(&DeviceExt->ParentControllerExt->PerfData.IoctlRequests);
|
|
InterlockedIncrement(&DeviceExt->PerfData.IoctlRequests);
|
|
|
|
if( DeviceExt->DeviceState != DIGI_DEVICE_STATE_OPEN )
|
|
{
|
|
Irp->IoStatus.Status = STATUS_CANCELLED;
|
|
Irp->IoStatus.Information = 0;
|
|
DigiIoCompleteRequest( Irp, IO_NO_INCREMENT );
|
|
return STATUS_CANCELLED;
|
|
}
|
|
|
|
#if DBG
|
|
KeQuerySystemTime( &CurrentSystemTime );
|
|
#endif
|
|
DigiDump( (DIGIIRP|DIGIFLOW|DIGIIOCTL),
|
|
("Entering SerialIoControl: port = %s\tIRP = 0x%x\t%u:%u\n",
|
|
DeviceExt->DeviceDbgString, Irp, CurrentSystemTime.HighPart, CurrentSystemTime.LowPart) );
|
|
|
|
IrpSp = IoGetCurrentIrpStackLocation( Irp );
|
|
Irp->IoStatus.Information = 0L;
|
|
Status = STATUS_SUCCESS;
|
|
|
|
ControllerExt = (PDIGI_CONTROLLER_EXTENSION)(DeviceExt->ParentControllerExt);
|
|
|
|
switch( IrpSp->Parameters.DeviceIoControl.IoControlCode )
|
|
{
|
|
case IOCTL_SERIAL_SET_BAUD_RATE:
|
|
{
|
|
ULONG BaudRate;
|
|
SHORT Baud;
|
|
|
|
DigiDump( (DIGIIOCTL|DIGIBAUD), (" IOCTL_SERIAL_SET_BAUD_RATE: %s\n",
|
|
DeviceExt->DeviceDbgString) );
|
|
if( IrpSp->Parameters.DeviceIoControl.InputBufferLength <
|
|
sizeof(SERIAL_BAUD_RATE) )
|
|
{
|
|
Status = STATUS_BUFFER_TOO_SMALL;
|
|
break;
|
|
}
|
|
|
|
BaudRate = ((PSERIAL_BAUD_RATE)(Irp->AssociatedIrp.SystemBuffer))->BaudRate;
|
|
|
|
if( BaudRate == DeviceExt->BaudRate )
|
|
break; // no change (SetCommState does this)
|
|
|
|
DrainTransmit( ControllerExt, DeviceExt, Irp );
|
|
|
|
DigiDump( (DIGIIOCTL|DIGIBAUD),
|
|
(" Baud Rate = 0x%x (%d)\n", BaudRate, BaudRate) );
|
|
|
|
// Change the requested baud rate into a CFlag compatible setting.
|
|
switch( BaudRate )
|
|
{
|
|
case 50:
|
|
Baud = ControllerExt->BaudTable[SerialBaud50];
|
|
break;
|
|
case 75:
|
|
Baud = ControllerExt->BaudTable[SerialBaud75];
|
|
break;
|
|
case 110:
|
|
Baud = ControllerExt->BaudTable[SerialBaud110];
|
|
break;
|
|
case 135:
|
|
Baud = ControllerExt->BaudTable[SerialBaud135_5];
|
|
break;
|
|
case 150:
|
|
Baud = ControllerExt->BaudTable[SerialBaud150];
|
|
break;
|
|
case 200:
|
|
Baud = ControllerExt->BaudTable[SerialBaud200];
|
|
break;
|
|
case 300:
|
|
Baud = ControllerExt->BaudTable[SerialBaud300];
|
|
break;
|
|
case 600:
|
|
Baud = ControllerExt->BaudTable[SerialBaud600];
|
|
break;
|
|
case 1200:
|
|
Baud = ControllerExt->BaudTable[SerialBaud1200];
|
|
break;
|
|
case 1800:
|
|
Baud = ControllerExt->BaudTable[SerialBaud1800];
|
|
break;
|
|
case 2400:
|
|
Baud = ControllerExt->BaudTable[SerialBaud2400];
|
|
break;
|
|
case 4800:
|
|
Baud = ControllerExt->BaudTable[SerialBaud4800];
|
|
break;
|
|
case 7200:
|
|
Baud = ControllerExt->BaudTable[SerialBaud7200];
|
|
break;
|
|
case 9600:
|
|
Baud = ControllerExt->BaudTable[SerialBaud9600];
|
|
break;
|
|
case 14400:
|
|
Baud = ControllerExt->BaudTable[SerialBaud14400];
|
|
break;
|
|
case 19200:
|
|
Baud = ControllerExt->BaudTable[SerialBaud19200];
|
|
break;
|
|
case 28800:
|
|
Baud = ControllerExt->BaudTable[SerialBaud28800];
|
|
break;
|
|
case 38400:
|
|
Baud = ControllerExt->BaudTable[SerialBaud38400];
|
|
break;
|
|
case 56000:
|
|
case 57600:
|
|
Baud = ControllerExt->BaudTable[SerialBaud57600];
|
|
break;
|
|
case 115200:
|
|
Baud = ControllerExt->BaudTable[SerialBaud115200];
|
|
break;
|
|
case 128000:
|
|
Baud = ControllerExt->BaudTable[SerialBaud128000];
|
|
break;
|
|
case 230000:
|
|
case 230400:
|
|
case 256000:
|
|
Baud = ControllerExt->BaudTable[SerialBaud256000];
|
|
break;
|
|
case 512000:
|
|
Baud = ControllerExt->BaudTable[SerialBaud512000];
|
|
break;
|
|
|
|
default:
|
|
Baud = -1;
|
|
break;
|
|
}
|
|
|
|
DigiDump( (DIGIIOCTL|DIGIBAUD),
|
|
(" CFlag.Mask = 0x%.4x,\tCFlag.Src = 0x%.4x\n", CFLAG_BAUD_MASK, Baud) );
|
|
|
|
if( Baud != -1 )
|
|
{
|
|
DIGI_XFLAG CFlag;
|
|
#if DBG
|
|
PFEP_CHANNEL_STRUCTURE ChInfo;
|
|
USHORT NewCFlag;
|
|
#endif
|
|
|
|
DeviceExt->BaudRate = BaudRate;
|
|
|
|
CFlag.Mask = CFLAG_BAUD_MASK;
|
|
CFlag.Src = (USHORT)Baud;
|
|
CFlag.Command = SET_CFLAGS;
|
|
|
|
SetXFlag( DeviceExt, &CFlag );
|
|
|
|
#if DBG
|
|
ChInfo = (PFEP_CHANNEL_STRUCTURE)(ControllerExt->VirtualAddress +
|
|
DeviceExt->ChannelInfo.Offset);
|
|
|
|
EnableWindow( ControllerExt, DeviceExt->ChannelInfo.Window );
|
|
NewCFlag = READ_REGISTER_USHORT( &ChInfo->cflag );
|
|
DisableWindow( ControllerExt );
|
|
|
|
NewCFlag &= ~CFLAG_BAUD_MASK;
|
|
|
|
if( NewCFlag != CFlag.Src )
|
|
{
|
|
DigiDump( (DIGIASSERT|DIGIBAUD),
|
|
(" Baud Rate was NOT set on the controller, port = %s!!!\n",
|
|
DeviceExt->DeviceDbgString) );
|
|
DigiDump( (DIGIASSERT|DIGIBAUD),
|
|
(" CFlag.Mask = 0x%hx,\tCFlag.Src = 0x%hx, NewCFlag = 0x%hx\n",
|
|
CFlag.Mask, CFlag.Src, NewCFlag) );
|
|
|
|
ASSERT( FALSE );
|
|
}
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
DigiDump( (DIGIIOCTL|DIGINOTIMPLEMENTED|DIGIBAUD),
|
|
(" Invalid IOCTL_SERIAL_SET_BAUD_RATE (%u)\n",
|
|
BaudRate) );
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
}
|
|
break;
|
|
}
|
|
case IOCTL_SERIAL_GET_BAUD_RATE:
|
|
{
|
|
PSERIAL_BAUD_RATE Br = (PSERIAL_BAUD_RATE)Irp->AssociatedIrp.SystemBuffer;
|
|
PFEP_CHANNEL_STRUCTURE ChInfo;
|
|
USHORT CFlag;
|
|
SERIAL_BAUD_RATES PossibleBaudRates;
|
|
|
|
|
|
DigiDump( (DIGIIOCTL|DIGIBAUD), (" IOCTL_SERIAL_GET_BAUD_RATE: %s\n",
|
|
DeviceExt->DeviceDbgString) );
|
|
|
|
if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength <
|
|
sizeof(SERIAL_BAUD_RATE))
|
|
{
|
|
|
|
Status = STATUS_BUFFER_TOO_SMALL;
|
|
break;
|
|
}
|
|
|
|
ChInfo = (PFEP_CHANNEL_STRUCTURE)(ControllerExt->VirtualAddress +
|
|
DeviceExt->ChannelInfo.Offset);
|
|
|
|
EnableWindow( ControllerExt, DeviceExt->ChannelInfo.Window );
|
|
CFlag = READ_REGISTER_USHORT( &ChInfo->cflag );
|
|
DisableWindow( ControllerExt );
|
|
|
|
CFlag &= ~CFLAG_BAUD_MASK;
|
|
|
|
// Ok, now we loop through our baud rates to find the correct match.
|
|
Br->BaudRate = (ULONG)-1;
|
|
for( PossibleBaudRates = SerialBaud50; PossibleBaudRates < NUMBER_OF_BAUD_RATES;
|
|
PossibleBaudRates++ )
|
|
{
|
|
if( CFlag == (USHORT)(ControllerExt->BaudTable[PossibleBaudRates]) )
|
|
{
|
|
switch( PossibleBaudRates )
|
|
{
|
|
case SerialBaud50:
|
|
Br->BaudRate = 50;
|
|
break;
|
|
case SerialBaud75:
|
|
Br->BaudRate = 75;
|
|
break;
|
|
case SerialBaud110:
|
|
Br->BaudRate = 110;
|
|
break;
|
|
case SerialBaud135_5:
|
|
Br->BaudRate = 135;
|
|
break;
|
|
case SerialBaud150:
|
|
Br->BaudRate = 150;
|
|
break;
|
|
case SerialBaud300:
|
|
Br->BaudRate = 300;
|
|
break;
|
|
case SerialBaud600:
|
|
Br->BaudRate = 600;
|
|
break;
|
|
case SerialBaud1200:
|
|
Br->BaudRate = 1200;
|
|
break;
|
|
case SerialBaud1800:
|
|
Br->BaudRate = 1800;
|
|
break;
|
|
case SerialBaud2000:
|
|
Br->BaudRate = 2000;
|
|
break;
|
|
case SerialBaud2400:
|
|
Br->BaudRate = 2400;
|
|
break;
|
|
case SerialBaud3600:
|
|
Br->BaudRate = 3600;
|
|
break;
|
|
case SerialBaud4800:
|
|
Br->BaudRate = 4800;
|
|
break;
|
|
case SerialBaud7200:
|
|
Br->BaudRate = 7200;
|
|
break;
|
|
case SerialBaud9600:
|
|
Br->BaudRate = 9600;
|
|
break;
|
|
case SerialBaud14400:
|
|
Br->BaudRate = 14400;
|
|
break;
|
|
case SerialBaud19200:
|
|
Br->BaudRate = 19200;
|
|
break;
|
|
case SerialBaud28800:
|
|
Br->BaudRate = 28800;
|
|
break;
|
|
case SerialBaud38400:
|
|
Br->BaudRate = 38400;
|
|
break;
|
|
case SerialBaud56000:
|
|
Br->BaudRate = 56000;
|
|
break;
|
|
case SerialBaud57600:
|
|
Br->BaudRate = 57600;
|
|
break;
|
|
case SerialBaud115200:
|
|
Br->BaudRate = 115200;
|
|
break;
|
|
case SerialBaud128000:
|
|
Br->BaudRate = 128000;
|
|
break;
|
|
case SerialBaud256000:
|
|
Br->BaudRate = 256000;
|
|
break;
|
|
case SerialBaud512000:
|
|
Br->BaudRate = 512000;
|
|
break;
|
|
default:
|
|
DigiDump( DIGIASSERT, ("*********** Unknown Baud rate returned by controller!!! **********\n") );
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
if( Br->BaudRate == (ULONG)-1 )
|
|
{
|
|
DigiDump( (DIGIASSERT|DIGIIOCTL|DIGIBAUD),
|
|
(" INVALID BAUD RATE RETURNED FROM CONTROLLER: CFlag = 0x%hx\n",
|
|
CFlag) );
|
|
ASSERT( FALSE );
|
|
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
break;
|
|
}
|
|
|
|
DigiDump( (DIGIIOCTL|DIGIBAUD), (" -- Returning baud = 0x%x (%d)\n",
|
|
Br->BaudRate,Br->BaudRate) );
|
|
|
|
Irp->IoStatus.Information = sizeof(SERIAL_BAUD_RATE);
|
|
|
|
break;
|
|
}
|
|
case IOCTL_SERIAL_SET_LINE_CONTROL:
|
|
{
|
|
PSERIAL_LINE_CONTROL lc;
|
|
DIGI_XFLAG CFlag;
|
|
char *stopbits[] = { "1", "1.5", "2" };
|
|
char *parity[] = { "NONE", "ODD", "EVEN", "MARK", "SPACE" };
|
|
|
|
DigiDump( DIGIIOCTL, (" IOCTL_SERIAL_SET_LINE_CONTROL:\n") );
|
|
|
|
if( IrpSp->Parameters.DeviceIoControl.InputBufferLength <
|
|
sizeof(SERIAL_LINE_CONTROL) )
|
|
{
|
|
Status = STATUS_BUFFER_TOO_SMALL;
|
|
break;
|
|
}
|
|
|
|
lc = ((PSERIAL_LINE_CONTROL)(Irp->AssociatedIrp.SystemBuffer));
|
|
|
|
DigiDump( DIGIIOCTL,
|
|
(" StopBits = %hx (%s)\n"
|
|
" Parity = %hx (%s)\n"
|
|
" WordLength = %hx\n",
|
|
(USHORT)(lc->StopBits), stopbits[lc->StopBits],
|
|
(USHORT)(lc->Parity), parity[lc->Parity],
|
|
(USHORT)(lc->WordLength)) );
|
|
|
|
CFlag.Mask = 0xFFFF;
|
|
CFlag.Src = 0;
|
|
|
|
switch( lc->WordLength)
|
|
{
|
|
case 5:
|
|
CFlag.Src |= FEP_CS5;
|
|
break;
|
|
case 6:
|
|
CFlag.Src |= FEP_CS6;
|
|
break;
|
|
case 7:
|
|
CFlag.Src |= FEP_CS7;
|
|
break;
|
|
case 8:
|
|
CFlag.Src |= FEP_CS8;
|
|
break;
|
|
|
|
default:
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
DigiDump( DIGIIOCTL, (" **** Invalid Word Length ****\n") );
|
|
goto DoneWithIoctl;
|
|
}
|
|
|
|
CFlag.Mask &= CFLAG_LENGTH;
|
|
|
|
switch( lc->Parity )
|
|
{
|
|
case NO_PARITY:
|
|
CFlag.Src |= FEP_NO_PARITY;
|
|
break;
|
|
case ODD_PARITY:
|
|
CFlag.Src |= FEP_ODD_PARITY;
|
|
break;
|
|
case EVEN_PARITY:
|
|
CFlag.Src |= FEP_EVEN_PARITY;
|
|
break;
|
|
|
|
case MARK_PARITY:
|
|
case SPACE_PARITY:
|
|
default:
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
DigiDump( DIGIERRORS, (" **** Invalid Parity ****\n") );
|
|
goto DoneWithIoctl;
|
|
}
|
|
CFlag.Mask &= CFLAG_PARITY;
|
|
|
|
switch( lc->StopBits )
|
|
{
|
|
case STOP_BIT_1:
|
|
CFlag.Src |= FEP_STOP_BIT_1;
|
|
break;
|
|
|
|
case STOP_BITS_2:
|
|
if( lc->WordLength == 5 )
|
|
{
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
DigiDump( DIGIERRORS, (" **** Can't have 2 stop bits with 5 data bits ****\n") );
|
|
goto DoneWithIoctl;
|
|
}
|
|
CFlag.Src |= FEP_STOP_BIT_2;
|
|
break;
|
|
|
|
case STOP_BITS_1_5:
|
|
if( lc->WordLength != 5 )
|
|
{
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
DigiDump( DIGIERRORS, (" **** Can't have 1.5 stop bits except with 5 data bits ****\n") );
|
|
goto DoneWithIoctl;
|
|
}
|
|
CFlag.Src |= FEP_STOP_BIT_2; // FEP actually programs UART for 2 stop bits, but UART will generate 1.5 stop bits when 5 data bits are configured
|
|
break;
|
|
|
|
default:
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
DigiDump( DIGIERRORS, (" **** Invalid Stop Bits ****\n") );
|
|
goto DoneWithIoctl;
|
|
}
|
|
CFlag.Mask &= CFLAG_STOP_BIT;
|
|
CFlag.Command = SET_CFLAGS;
|
|
|
|
SetXFlag( DeviceExt, &CFlag );
|
|
break;
|
|
}
|
|
|
|
case IOCTL_SERIAL_GET_LINE_CONTROL:
|
|
{
|
|
PSERIAL_LINE_CONTROL Lc = (PSERIAL_LINE_CONTROL)Irp->AssociatedIrp.SystemBuffer;
|
|
PFEP_CHANNEL_STRUCTURE ChInfo;
|
|
USHORT CFlag;
|
|
|
|
DigiDump( DIGIIOCTL, (" IOCTL_SERIAL_GET_LINE_CONTROL:\n") );
|
|
|
|
if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength <
|
|
sizeof(SERIAL_LINE_CONTROL))
|
|
{
|
|
Status = STATUS_BUFFER_TOO_SMALL;
|
|
break;
|
|
}
|
|
|
|
ChInfo = (PFEP_CHANNEL_STRUCTURE)(ControllerExt->VirtualAddress +
|
|
DeviceExt->ChannelInfo.Offset);
|
|
|
|
EnableWindow( ControllerExt, DeviceExt->ChannelInfo.Window );
|
|
CFlag = READ_REGISTER_USHORT( &ChInfo->cflag );
|
|
DisableWindow( ControllerExt );
|
|
|
|
DigiDump( DIGIIOCTL, (" -- returning word length = ") );
|
|
switch( (USHORT)CFlag & ~CFLAG_LENGTH )
|
|
{
|
|
case FEP_CS5:
|
|
Lc->WordLength = 5;
|
|
break;
|
|
case FEP_CS6:
|
|
Lc->WordLength = 6;
|
|
break;
|
|
case FEP_CS7:
|
|
Lc->WordLength = 7;
|
|
break;
|
|
case FEP_CS8:
|
|
Lc->WordLength = 8;
|
|
break;
|
|
default:
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
DigiDump( DIGIERRORS, (" **** Invalid Stop Bits ****\n") );
|
|
goto DoneWithIoctl;
|
|
}
|
|
DigiDump( DIGIIOCTL, ("%d\n", Lc->WordLength) );
|
|
|
|
DigiDump( DIGIIOCTL, (" -- returning stop bit = ") );
|
|
switch( (USHORT)CFlag & ~CFLAG_STOP_BIT )
|
|
{
|
|
case FEP_STOP_BIT_1:
|
|
Lc->StopBits = STOP_BIT_1;
|
|
DigiDump( DIGIIOCTL, ("1\n") );
|
|
break;
|
|
case FEP_STOP_BIT_2:
|
|
if( Lc->WordLength == 5 )
|
|
{
|
|
Lc->StopBits = STOP_BITS_1_5;
|
|
DigiDump( DIGIIOCTL, ("1.5\n") );
|
|
}
|
|
else
|
|
{
|
|
Lc->StopBits = STOP_BITS_2;
|
|
DigiDump( DIGIIOCTL, ("2\n") );
|
|
}
|
|
break;
|
|
|
|
default:
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
DigiDump( DIGIERRORS, (" **** Invalid Stop Bits ****\n") );
|
|
goto DoneWithIoctl;
|
|
}
|
|
|
|
DigiDump( DIGIIOCTL, (" -- returning parity = ") );
|
|
switch( (USHORT)CFlag & ~CFLAG_PARITY )
|
|
{
|
|
case FEP_NO_PARITY:
|
|
DigiDump( DIGIIOCTL, ("NONE\n") );
|
|
Lc->Parity = NO_PARITY;
|
|
break;
|
|
case FEP_ODD_PARITY:
|
|
DigiDump( DIGIIOCTL, ("ODD\n") );
|
|
Lc->Parity = ODD_PARITY;
|
|
break;
|
|
case FEP_EVEN_PARITY:
|
|
Lc->Parity = EVEN_PARITY;
|
|
DigiDump( DIGIIOCTL, ("EVEN\n") );
|
|
break;
|
|
default:
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
DigiDump( DIGIERRORS, (" **** Invalid Stop Bits ****\n") );
|
|
goto DoneWithIoctl;
|
|
}
|
|
|
|
Irp->IoStatus.Information = sizeof(SERIAL_LINE_CONTROL);
|
|
|
|
break;
|
|
} // end IOCTL_SERIAL_GET_LINE_CONTROL
|
|
|
|
case IOCTL_SERIAL_SET_TIMEOUTS:
|
|
{
|
|
PSERIAL_TIMEOUTS NewTimeouts =
|
|
((PSERIAL_TIMEOUTS)(Irp->AssociatedIrp.SystemBuffer));
|
|
|
|
DigiDump( DIGIIOCTL, (" IOCTL_SERIAL_SET_TIMEOUTS:\n") );
|
|
|
|
if (IrpSp->Parameters.DeviceIoControl.InputBufferLength <
|
|
sizeof(SERIAL_TIMEOUTS))
|
|
{
|
|
Status = STATUS_BUFFER_TOO_SMALL;
|
|
break;
|
|
}
|
|
|
|
KeAcquireSpinLock( &DeviceExt->ControlAccess, &OldIrql );
|
|
|
|
DeviceExt->Timeouts = *NewTimeouts;
|
|
|
|
if( (DeviceExt->SpecialFlags & DIGI_SPECIAL_FLAG_FAST_RAS) &&
|
|
DeviceExt->ParentControllerExt->ShortRasTimeout &&
|
|
DeviceExt->Timeouts.ReadIntervalTimeout>0 &&
|
|
DeviceExt->Timeouts.ReadIntervalTimeout!=0xffffffff &&
|
|
DeviceExt->Timeouts.ReadTotalTimeoutConstant!=0)
|
|
{
|
|
DigiDump( DIGIIOCTL, (" RAS Requested ReadInterval = %d\n"
|
|
" RAS Requested ReadMultiplier = %d\n",
|
|
DeviceExt->Timeouts.ReadIntervalTimeout,
|
|
DeviceExt->Timeouts.ReadTotalTimeoutMultiplier));
|
|
|
|
DeviceExt->Timeouts.ReadIntervalTimeout =
|
|
DeviceExt->Timeouts.ReadTotalTimeoutMultiplier = 0xffffffff;
|
|
}
|
|
|
|
DigiDump( DIGIIOCTL, (" New ReadIntervalTimeout = %d (ms)\n"
|
|
" New ReadTotalTimeoutMultiplier = %d\n"
|
|
" New ReadTotalTimeoutConstant = %d\n"
|
|
" New WriteTotalTimeoutMultiplier = %d\n"
|
|
" New WriteTotalTimeoutConstant = %d\n",
|
|
DeviceExt->Timeouts.ReadIntervalTimeout,
|
|
DeviceExt->Timeouts.ReadTotalTimeoutMultiplier,
|
|
DeviceExt->Timeouts.ReadTotalTimeoutConstant,
|
|
DeviceExt->Timeouts.WriteTotalTimeoutMultiplier,
|
|
DeviceExt->Timeouts.WriteTotalTimeoutConstant ) );
|
|
|
|
KeReleaseSpinLock( &DeviceExt->ControlAccess, OldIrql );
|
|
|
|
break;
|
|
} // end case IOCTL_SERIAL_SET_TIMEOUTS
|
|
|
|
case IOCTL_SERIAL_GET_TIMEOUTS:
|
|
{
|
|
DigiDump( DIGIIOCTL, (" IOCTL_SERIAL_GET_TIMEOUTS:\n") );
|
|
|
|
if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength <
|
|
sizeof(SERIAL_TIMEOUTS))
|
|
{
|
|
Status = STATUS_BUFFER_TOO_SMALL;
|
|
break;
|
|
}
|
|
|
|
KeAcquireSpinLock( &DeviceExt->ControlAccess, &OldIrql );
|
|
|
|
*((PSERIAL_TIMEOUTS)Irp->AssociatedIrp.SystemBuffer) = DeviceExt->Timeouts;
|
|
Irp->IoStatus.Information = sizeof(SERIAL_TIMEOUTS);
|
|
|
|
KeReleaseSpinLock( &DeviceExt->ControlAccess, OldIrql );
|
|
break;
|
|
} // case IOCTL_SERIAL_GET_TIMEOUTS
|
|
|
|
case IOCTL_SERIAL_SET_CHARS:
|
|
{
|
|
PSERIAL_CHARS NewChars = Irp->AssociatedIrp.SystemBuffer;
|
|
|
|
DigiDump( DIGIIOCTL, (" IOCTL_SERIAL_SET_CHARS:\n") );
|
|
|
|
if( IrpSp->Parameters.DeviceIoControl.InputBufferLength <
|
|
sizeof(SERIAL_CHARS))
|
|
{
|
|
Status = STATUS_BUFFER_TOO_SMALL;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// The only thing that can be wrong with the chars
|
|
// is that the xon and xoff characters are the
|
|
// same.
|
|
//
|
|
|
|
if( NewChars->XonChar == NewChars->XoffChar && NewChars->XonChar!=0 )
|
|
{
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
break;
|
|
}
|
|
|
|
KeAcquireSpinLock( &DeviceExt->ControlAccess, &OldIrql );
|
|
|
|
DeviceExt->SpecialChars = *NewChars;
|
|
|
|
KeReleaseSpinLock( &DeviceExt->ControlAccess, OldIrql );
|
|
|
|
DigiDump( (DIGIIOCTL|DIGIWAIT), (" EofChar = 0x%.2x\n"
|
|
" ErrorChar = 0x%.2x\n"
|
|
" BreakChar = 0x%.2x\n"
|
|
" EventChar = 0x%.2x\n"
|
|
" XonChar = 0x%.2x\n"
|
|
" XoffChar = 0x%.2x\n",
|
|
NewChars->EofChar,
|
|
NewChars->ErrorChar,
|
|
NewChars->BreakChar,
|
|
NewChars->EventChar,
|
|
NewChars->XonChar,
|
|
NewChars->XoffChar ) );
|
|
|
|
//
|
|
// Set the Xon & Xoff characters on the controller.
|
|
//
|
|
|
|
WriteCommandBytes( DeviceExt, SET_XON_XOFF_CHARACTERS,
|
|
NewChars->XonChar, NewChars->XoffChar );
|
|
|
|
break;
|
|
} // case IOCTL_SERIAL_SET_CHARS
|
|
|
|
case IOCTL_SERIAL_GET_CHARS:
|
|
{
|
|
PFEP_CHANNEL_STRUCTURE ChInfo;
|
|
UCHAR Xon, Xoff;
|
|
PSERIAL_CHARS pSerialChars;
|
|
|
|
DigiDump( DIGIIOCTL, (" IOCTL_SERIAL_GET_CHARS:\n") );
|
|
|
|
if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength <
|
|
sizeof(SERIAL_CHARS))
|
|
{
|
|
Status = STATUS_BUFFER_TOO_SMALL;
|
|
break;
|
|
}
|
|
|
|
ChInfo = (PFEP_CHANNEL_STRUCTURE)(ControllerExt->VirtualAddress +
|
|
DeviceExt->ChannelInfo.Offset);
|
|
|
|
EnableWindow( ControllerExt, DeviceExt->ChannelInfo.Window );
|
|
Xon = READ_REGISTER_UCHAR( &ChInfo->startc );
|
|
Xoff = READ_REGISTER_UCHAR( &ChInfo->stopc );
|
|
DisableWindow( ControllerExt );
|
|
|
|
pSerialChars = Irp->AssociatedIrp.SystemBuffer;
|
|
*pSerialChars = DeviceExt->SpecialChars;
|
|
pSerialChars->XonChar = Xon;
|
|
pSerialChars->XoffChar = Xoff;
|
|
|
|
Irp->IoStatus.Information = sizeof(SERIAL_CHARS);
|
|
|
|
DigiDump( DIGIIOCTL, (" EofChar = 0x%.2x\n"
|
|
" ErrorChar = 0x%.2x\n"
|
|
" BreakChar = 0x%.2x\n"
|
|
" EventChar = 0x%.2x\n"
|
|
" XonChar = 0x%.2x\n"
|
|
" XoffChar = 0x%.2x\n",
|
|
DeviceExt->SpecialChars.EofChar,
|
|
DeviceExt->SpecialChars.ErrorChar,
|
|
DeviceExt->SpecialChars.BreakChar,
|
|
DeviceExt->SpecialChars.EventChar,
|
|
Xon,
|
|
Xoff ) );
|
|
|
|
break;
|
|
} // end IOCTL_SERIAL_GET_CHARS
|
|
|
|
case IOCTL_SERIAL_CLR_DTR:
|
|
case IOCTL_SERIAL_SET_DTR:
|
|
{
|
|
KIRQL OldIrql;
|
|
TIME LineSignalTimeout;
|
|
UCHAR MStatSet, MStatClear;
|
|
|
|
if( IrpSp->Parameters.DeviceIoControl.IoControlCode ==
|
|
IOCTL_SERIAL_CLR_DTR )
|
|
DigiDump( DIGIIOCTL, (" IOCTL_SERIAL_CLR_DTR:\n") );
|
|
else
|
|
DigiDump( DIGIIOCTL, (" IOCTL_SERIAL_SET_DTR:\n") );
|
|
|
|
KeAcquireSpinLock( &DeviceExt->ControlAccess, &OldIrql );
|
|
|
|
if( (DeviceExt->ControlHandShake & SERIAL_DTR_MASK) ==
|
|
SERIAL_DTR_HANDSHAKE )
|
|
{
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
KeReleaseSpinLock( &DeviceExt->ControlAccess, OldIrql );
|
|
break;
|
|
}
|
|
|
|
if( IrpSp->Parameters.DeviceIoControl.IoControlCode ==
|
|
IOCTL_SERIAL_SET_DTR )
|
|
{
|
|
MStatSet = ControllerExt->ModemSignalTable[DTR_SIGNAL];
|
|
MStatClear = 0;
|
|
DeviceExt->WriteOnlyModemSignalValue |= MStatSet;
|
|
DeviceExt->CurrentModemSignals |= MStatSet;
|
|
}
|
|
else
|
|
{
|
|
MStatSet = 0;
|
|
MStatClear = ControllerExt->ModemSignalTable[DTR_SIGNAL];
|
|
DeviceExt->WriteOnlyModemSignalValue &= ~MStatClear;
|
|
DeviceExt->CurrentModemSignals &= ~MStatClear;
|
|
}
|
|
|
|
KeReleaseSpinLock( &DeviceExt->ControlAccess, OldIrql );
|
|
|
|
WriteCommandBytes( DeviceExt, SET_MODEM_LINES,
|
|
MStatSet, MStatClear );
|
|
|
|
//
|
|
// Delay for 50ms to ensure toggling of the lines last long enough.
|
|
//
|
|
|
|
// Create a 50 ms timeout interval
|
|
#if rmm < 807
|
|
LineSignalTimeout = RtlLargeIntegerNegate(
|
|
RtlConvertLongToLargeInteger( (LONG)(500 * 1000) ));
|
|
#else
|
|
LineSignalTimeout.QuadPart = Int32x32To64( -500, 1000 );
|
|
#endif
|
|
|
|
KeDelayExecutionThread( KernelMode,
|
|
FALSE,
|
|
&LineSignalTimeout );
|
|
|
|
break;
|
|
} // end IOCTL_SERIAL_SET_DTR
|
|
|
|
case IOCTL_SERIAL_RESET_DEVICE:
|
|
DigiDump( DIGIIOCTL, (" IOCTL_SERIAL_RESET_DEVICE:\n") );
|
|
break;
|
|
|
|
case IOCTL_SERIAL_CLR_RTS:
|
|
case IOCTL_SERIAL_SET_RTS:
|
|
{
|
|
KIRQL OldIrql;
|
|
TIME LineSignalTimeout;
|
|
UCHAR MStatSet, MStatClear;
|
|
|
|
if( IrpSp->Parameters.DeviceIoControl.IoControlCode ==
|
|
IOCTL_SERIAL_SET_RTS )
|
|
DigiDump( DIGIIOCTL, (" IOCTL_SERIAL_SET_RTS:\n") );
|
|
else
|
|
DigiDump( DIGIIOCTL, (" IOCTL_SERIAL_CLR_RTS:\n") );
|
|
|
|
KeAcquireSpinLock( &DeviceExt->ControlAccess, &OldIrql );
|
|
|
|
if( (DeviceExt->FlowReplace & SERIAL_RTS_MASK) ==
|
|
SERIAL_RTS_HANDSHAKE )
|
|
{
|
|
DigiDump( DIGIIOCTL, (" returning STATUS_INVALID_PARAMETER\n") );
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
KeReleaseSpinLock( &DeviceExt->ControlAccess, OldIrql );
|
|
break;
|
|
}
|
|
|
|
if( IrpSp->Parameters.DeviceIoControl.IoControlCode ==
|
|
IOCTL_SERIAL_SET_RTS )
|
|
{
|
|
MStatSet = ControllerExt->ModemSignalTable[RTS_SIGNAL];
|
|
MStatClear = 0;
|
|
DeviceExt->WriteOnlyModemSignalValue |= MStatSet;
|
|
DeviceExt->CurrentModemSignals |= MStatSet;
|
|
}
|
|
else
|
|
{
|
|
MStatSet = 0;
|
|
MStatClear = ControllerExt->ModemSignalTable[RTS_SIGNAL];
|
|
DeviceExt->WriteOnlyModemSignalValue &= ~MStatClear;
|
|
DeviceExt->CurrentModemSignals &= ~MStatClear;
|
|
}
|
|
|
|
KeReleaseSpinLock( &DeviceExt->ControlAccess, OldIrql );
|
|
|
|
WriteCommandBytes( DeviceExt, SET_MODEM_LINES,
|
|
MStatSet, MStatClear );
|
|
|
|
//
|
|
// Delay for 50ms to ensure toggling of the lines last long enough.
|
|
//
|
|
|
|
// Create a 50 ms timeout interval
|
|
#if rmm < 807
|
|
LineSignalTimeout = RtlLargeIntegerNegate(
|
|
RtlConvertLongToLargeInteger( (LONG)(500 * 1000) ));
|
|
#else
|
|
LineSignalTimeout.QuadPart = Int32x32To64( -500, 1000 );
|
|
#endif
|
|
KeDelayExecutionThread( KernelMode,
|
|
FALSE,
|
|
&LineSignalTimeout );
|
|
|
|
break;
|
|
} // end case IOCTL_SERIAL_SET_RTS
|
|
|
|
case IOCTL_SERIAL_SET_XOFF:
|
|
{
|
|
DigiDump( DIGIIOCTL, (" IOCTL_SERIAL_SET_XOFF:\n") );
|
|
WriteCommandWord( DeviceExt, PAUSE_TX, 0 );
|
|
break;
|
|
} // end case IOCTL_SERIAL_SET_XOFF
|
|
|
|
case IOCTL_SERIAL_SET_XON:
|
|
{
|
|
DigiDump( DIGIIOCTL, (" IOCTL_SERIAL_SET_XON:\n") );
|
|
WriteCommandWord( DeviceExt, RESUME_TX, 0 );
|
|
break;
|
|
} // end case IOCTL_SERIAL_SET_XON
|
|
|
|
case IOCTL_SERIAL_SET_BREAK_ON:
|
|
{
|
|
DigiDump( DIGIIOCTL, (" IOCTL_SERIAL_SET_BREAK_ON:\n") );
|
|
|
|
//
|
|
// We implement this under the assumption that a call to
|
|
// IOCTL_SERIAL_SET_BREAK_OFF will follow sometime in the
|
|
// future.
|
|
//
|
|
WriteCommandWord( DeviceExt, SEND_BREAK, INFINITE_FEP_BREAK );
|
|
|
|
break;
|
|
} // end IOCTL_SERIAL_SET_BREAK_ON
|
|
|
|
case IOCTL_SERIAL_SET_BREAK_OFF:
|
|
{
|
|
DigiDump( DIGIIOCTL, (" IOCTL_SERIAL_SET_BREAK_OFF:\n") );
|
|
|
|
WriteCommandWord( DeviceExt, SEND_BREAK, DEFAULT_FEP_BREAK );
|
|
|
|
break;
|
|
} // end IOCTL_SERIAL_SET_BREAK_OFF
|
|
|
|
case IOCTL_SERIAL_SET_QUEUE_SIZE:
|
|
{
|
|
PFEP_CHANNEL_STRUCTURE ChInfo;
|
|
USHORT Rmax, Tmax;
|
|
PSERIAL_QUEUE_SIZE Rs;
|
|
|
|
if( IrpSp->Parameters.DeviceIoControl.InputBufferLength <
|
|
sizeof(SERIAL_QUEUE_SIZE) )
|
|
{
|
|
Status = STATUS_BUFFER_TOO_SMALL;
|
|
break;
|
|
}
|
|
|
|
Rs = ((PSERIAL_QUEUE_SIZE)(Irp->AssociatedIrp.SystemBuffer));
|
|
|
|
DigiDump( DIGIIOCTL, (" IOCTL_SERIAL_SET_QUEUE_SIZE:\n") );
|
|
DigiDump( DIGIIOCTL, (" InSize = %u\n"
|
|
" OutSize = %u\n",
|
|
Rs->InSize, Rs->OutSize) );
|
|
|
|
ChInfo = (PFEP_CHANNEL_STRUCTURE)(ControllerExt->VirtualAddress +
|
|
DeviceExt->ChannelInfo.Offset);
|
|
|
|
EnableWindow( ControllerExt, DeviceExt->ChannelInfo.Window );
|
|
Rmax = READ_REGISTER_USHORT( &ChInfo->rmax );
|
|
Tmax = READ_REGISTER_USHORT( &ChInfo->tmax );
|
|
DisableWindow( ControllerExt );
|
|
|
|
// RAS sets the queue size for RAM-oriented drivers... This just screws us up.
|
|
if( !( DeviceExt->SpecialFlags & DIGI_SPECIAL_FLAG_FAST_RAS ) )
|
|
{
|
|
DeviceExt->RequestedQSize.InSize = Rs->InSize;
|
|
DeviceExt->RequestedQSize.OutSize = Rs->OutSize;
|
|
}
|
|
|
|
if( ((USHORT)(Rs->InSize) <= (Rmax+1)) &&
|
|
((USHORT)(Rs->OutSize) <= (Tmax+1)) )
|
|
{
|
|
Status = STATUS_SUCCESS;
|
|
DigiDump( DIGIIOCTL, (" Requested Queue sizes Valid.\n") );
|
|
}
|
|
else
|
|
{
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
DigiDump( DIGIIOCTL, (" Requested Queue sizes InValid.\n") );
|
|
}
|
|
|
|
Status = STATUS_SUCCESS; // DH Invalid queue sizes are accepted?
|
|
break;
|
|
} // end case IOCTL_SERIAL_SET_QUEUE_SIZE
|
|
|
|
case IOCTL_SERIAL_GET_WAIT_MASK:
|
|
{
|
|
DigiDump( (DIGIIOCTL|DIGIWAIT), (" IOCTL_SERIAL_GET_WAIT_MASK:\n") );
|
|
|
|
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) = DeviceExt->WaitMask;
|
|
|
|
break;
|
|
} // end IOCTL_SERIAL_GET_WAIT_MASK
|
|
|
|
case IOCTL_SERIAL_SET_WAIT_MASK:
|
|
{
|
|
PLIST_ENTRY WaitQueue;
|
|
ULONG NewMask;
|
|
KIRQL OldIrql;
|
|
DIGI_XFLAG IFlag;
|
|
|
|
DigiDump( (DIGIIOCTL|DIGIWAIT), (" IOCTL_SERIAL_SET_WAIT_MASK: DigiPort = %s\n",
|
|
DeviceExt->DeviceDbgString) );
|
|
|
|
if( IrpSp->Parameters.DeviceIoControl.InputBufferLength <
|
|
sizeof(ULONG) )
|
|
{
|
|
Status = STATUS_BUFFER_TOO_SMALL;
|
|
break;
|
|
}
|
|
|
|
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) )
|
|
{
|
|
DigiDump( DIGIWAIT, (" Invalid Wait mask, 0x%x\n", NewMask) );
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
break;
|
|
}
|
|
|
|
KeAcquireSpinLock( &DeviceExt->ControlAccess, &OldIrql );
|
|
|
|
if( (DeviceExt->WaitMask&SERIAL_EV_RXFLAG) && !(NewMask&SERIAL_EV_RXFLAG) )
|
|
{
|
|
DeviceExt->UnscannedRXFLAGPosition = MAXULONG;
|
|
}
|
|
|
|
if( NewMask )
|
|
DeviceExt->WaitMask = NewMask;
|
|
|
|
DeviceExt->HistoryWait &= NewMask;
|
|
|
|
KeReleaseSpinLock( &DeviceExt->ControlAccess, OldIrql );
|
|
|
|
//
|
|
// Now, make sure the controller and driver are set properly.
|
|
//
|
|
DigiDump( (DIGIIOCTL|DIGIWAIT), ( " Wait on Event 0x%x requested.\n", NewMask) );
|
|
if( NewMask & SERIAL_EV_RXCHAR )
|
|
{
|
|
DigiDump( (DIGIIOCTL|DIGIWAIT), ( " Wait on Event SERIAL_EV_RXCHAR requested.\n") );
|
|
}
|
|
|
|
if( NewMask & SERIAL_EV_RXFLAG )
|
|
{
|
|
DigiDump( (DIGIIOCTL|DIGIWAIT), ( " Wait on Event SERIAL_EV_RXFLAG requested, SpecialChar = 0x%hx.\n",
|
|
DeviceExt->SpecialChars.EventChar) );
|
|
|
|
}
|
|
|
|
if( NewMask & SERIAL_EV_TXEMPTY )
|
|
{
|
|
DigiDump( (DIGIIOCTL|DIGIWAIT), ( " Wait on Event SERIAL_EV_TXEMPTY requested.\n") );
|
|
}
|
|
|
|
if( NewMask & SERIAL_EV_CTS )
|
|
{
|
|
DigiDump( (DIGIIOCTL|DIGIWAIT), ( " Wait on Event SERIAL_EV_CTS requested.\n") );
|
|
}
|
|
|
|
if( NewMask & SERIAL_EV_DSR )
|
|
{
|
|
DigiDump( (DIGIIOCTL|DIGIWAIT), ( " Wait on Event SERIAL_EV_DSR requested.\n") );
|
|
}
|
|
|
|
if( NewMask & SERIAL_EV_RLSD )
|
|
{
|
|
DigiDump( (DIGIIOCTL|DIGIWAIT), ( " Wait on Event SERIAL_EV_RLSD requested.\n") );
|
|
}
|
|
|
|
// if( NewMask & SERIAL_EV_BREAK )
|
|
{
|
|
DIGI_XFLAG IFlag;
|
|
//
|
|
// We need to set the IFLAG variable on the controller so it
|
|
// will notify us when a BREAK has occurred.
|
|
//
|
|
// DigiDump( (DIGIIOCTL|DIGIWAIT), ( " Wait on Event SERIAL_EV_BREAK requested.\n") );
|
|
IFlag.Mask = MAXUSHORT;
|
|
IFlag.Src = IFLAG_BRKINT;
|
|
IFlag.Command = SET_IFLAGS;
|
|
SetXFlag( DeviceExt, &IFlag );
|
|
}
|
|
|
|
if( NewMask & SERIAL_EV_ERR )
|
|
{
|
|
//
|
|
// NOTE: This if should be after the SERIAL_EV_BREAK because
|
|
// we might need to turn Break event notification off.
|
|
//
|
|
// We need to turn on DOS mode to make sure we receive the parity
|
|
// errors in the data stream.
|
|
//
|
|
DigiDump( (DIGIIOCTL|DIGIWAIT), ( " Wait on Event SERIAL_EV_ERR requested.\n") );
|
|
|
|
if( !DeviceExt->EscapeChar )
|
|
{
|
|
//
|
|
// Turn on DOS mode
|
|
//
|
|
// Make sure we turn off break event notification. We
|
|
// will start processing break events in the data stream.
|
|
//
|
|
DigiDump( DIGIIOCTL, (" Turning DosMode ON!\n") );
|
|
IFlag.Src = ( IFLAG_PARMRK | IFLAG_INPCK | IFLAG_DOSMODE );
|
|
IFlag.Mask = (USHORT) ~( IFLAG_BRKINT );
|
|
IFlag.Command = SET_IFLAGS;
|
|
SetXFlag( DeviceExt, &IFlag );
|
|
}
|
|
}
|
|
else if( !DeviceExt->EscapeChar )
|
|
{
|
|
//
|
|
// Make sure we turn off DOSMode
|
|
//
|
|
DigiDump( DIGIIOCTL, (" Turning DosMode OFF!\n") );
|
|
IFlag.Mask = (USHORT)(~( IFLAG_PARMRK | IFLAG_INPCK | IFLAG_DOSMODE ));
|
|
IFlag.Src = 0;
|
|
// if( DeviceExt->WaitMask & SERIAL_EV_BREAK )
|
|
// {
|
|
//
|
|
// If we are suppose to notify on breaks, then reset the
|
|
// BRKINT flag to start getting the break notifications
|
|
// through the event queue.
|
|
//
|
|
IFlag.Src |= IFLAG_BRKINT;
|
|
// }
|
|
IFlag.Command = SET_IFLAGS;
|
|
SetXFlag( DeviceExt, &IFlag );
|
|
}
|
|
|
|
#if DBG
|
|
if( NewMask & SERIAL_EV_RING )
|
|
{
|
|
DigiDump( (DIGIIOCTL|DIGIWAIT), ( " Wait on Event SERIAL_EV_RING requested.\n") );
|
|
}
|
|
if( NewMask & SERIAL_EV_PERR )
|
|
{
|
|
DigiDump( (DIGINOTIMPLEMENTED|DIGIWAIT), ( " Wait on Event SERIAL_EV_PERR not implemented.\n") );
|
|
}
|
|
if( NewMask & SERIAL_EV_RX80FULL )
|
|
{
|
|
DigiDump( (DIGIIOCTL|DIGIWAIT), ( " Wait on Event SERIAL_EV_RX80FULL requested.\n") );
|
|
}
|
|
if( NewMask & SERIAL_EV_EVENT1 )
|
|
{
|
|
DigiDump( (DIGINOTIMPLEMENTED|DIGIWAIT), ( " Wait on Event SERIAL_EV_EVENT1 not implemented.\n") );
|
|
}
|
|
if( NewMask & SERIAL_EV_EVENT2 )
|
|
{
|
|
DigiDump( (DIGINOTIMPLEMENTED|DIGIWAIT), ( " Wait on Event SERIAL_EV_EVENT2 not implemented.\n") );
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// If there is a Wait IRP, complete it now.
|
|
//
|
|
// I placed this code after setting the new mask, because
|
|
// it is possible for us to be reentered when the
|
|
// Wait IRP is completed in the WAIT_ON_MASK and need
|
|
// to have the new masks in place if that happens.
|
|
//
|
|
WaitQueue = &DeviceExt->WaitQueue;
|
|
|
|
KeAcquireSpinLock( &DeviceExt->ControlAccess, &OldIrql );
|
|
|
|
if( !IsListEmpty( WaitQueue ) )
|
|
{
|
|
PIRP currentIrp = CONTAINING_RECORD(
|
|
WaitQueue->Flink,
|
|
IRP,
|
|
Tail.Overlay.ListEntry );
|
|
|
|
DigiDump( DIGIWAIT, (" Completing outstanding Wait IRP's\n") );
|
|
|
|
currentIrp->IoStatus.Information = sizeof(ULONG);
|
|
*(ULONG *)(currentIrp->AssociatedIrp.SystemBuffer) = 0L;
|
|
|
|
if( DeviceExt->HistoryWait )
|
|
{
|
|
DigiDump( (DIGIASSERT|DIGIWAIT), ("********** NON-Zero HistoryWait!!! **********\n") );
|
|
}
|
|
|
|
// This is a check to make sure there is only, at most,
|
|
// one Wait IRP on the WaitQueue list.
|
|
ASSERT( WaitQueue->Flink->Flink == WaitQueue );
|
|
|
|
//
|
|
// Increment because we know about the Irp.
|
|
//
|
|
DIGI_INC_REFERENCE( currentIrp );
|
|
|
|
DigiTryToCompleteIrp( DeviceExt, &OldIrql,
|
|
STATUS_SUCCESS, WaitQueue,
|
|
NULL, NULL,
|
|
NULL );
|
|
}
|
|
else
|
|
{
|
|
KeReleaseSpinLock( &DeviceExt->ControlAccess, OldIrql );
|
|
}
|
|
|
|
break;
|
|
} // end IOCTL_SERIAL_SET_WAIT_MASK
|
|
|
|
case IOCTL_SERIAL_WAIT_ON_MASK:
|
|
{
|
|
PFEP_CHANNEL_STRUCTURE ChInfo;
|
|
KIRQL OldIrql;
|
|
|
|
DigiDump( (DIGIIOCTL|DIGIWAIT), (" IOCTL_SERIAL_WAIT_ON_MASK: DigiPort = %s\n",
|
|
&DeviceExt->DeviceDbgString) );
|
|
|
|
if( IrpSp->Parameters.DeviceIoControl.OutputBufferLength <
|
|
sizeof(ULONG) )
|
|
{
|
|
Status = STATUS_BUFFER_TOO_SMALL;
|
|
break;
|
|
}
|
|
|
|
KeAcquireSpinLock( &DeviceExt->ControlAccess, &OldIrql );
|
|
|
|
if( !IsListEmpty( &DeviceExt->WaitQueue ) )
|
|
{
|
|
DigiDump( (DIGIIOCTL|DIGIWAIT), ("**** All ready have a WAIT_MASK, returning STATUS_INVALID_PARAMETER\n") );
|
|
KeReleaseSpinLock( &DeviceExt->ControlAccess, OldIrql );
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
break;
|
|
}
|
|
|
|
KeReleaseSpinLock( &DeviceExt->ControlAccess, OldIrql );
|
|
|
|
//
|
|
// Okay, turn on character receive and break events.
|
|
// Don't touch MINT on the controller. It is always left on
|
|
// so we recieve all modem events from the controller throughout
|
|
// the lifetime of the driver, even across device open and
|
|
// closes.
|
|
//
|
|
|
|
ChInfo = (PFEP_CHANNEL_STRUCTURE)(ControllerExt->VirtualAddress +
|
|
DeviceExt->ChannelInfo.Offset);
|
|
|
|
if( (DeviceExt->WaitMask & SERIAL_EV_RXCHAR) ||
|
|
(DeviceExt->WaitMask & SERIAL_EV_RXFLAG) )
|
|
{
|
|
EnableWindow( ControllerExt, DeviceExt->ChannelInfo.Window );
|
|
WRITE_REGISTER_UCHAR( &ChInfo->idata, TRUE );
|
|
DisableWindow( ControllerExt );
|
|
}
|
|
|
|
//
|
|
// Either start this irp or put it on the
|
|
// queue.
|
|
//
|
|
|
|
Status = DigiStartIrpRequest( ControllerExt,
|
|
DeviceExt,
|
|
&DeviceExt->WaitQueue, Irp,
|
|
StartWaitRequest );
|
|
|
|
if( DeviceExt->HistoryWait )
|
|
{
|
|
// Some events have occurred before WAIT_ON_MASK was called.
|
|
DigiSatisfyEvent( ControllerExt, DeviceExt, DeviceExt->HistoryWait );
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
|
|
#if DBG
|
|
KeQuerySystemTime( &CurrentSystemTime );
|
|
#endif
|
|
DigiDump( (DIGIFLOW|DIGIIOCTL), ("Exiting SerialIoControl: port = %s\t%u:%u\n",
|
|
DeviceExt->DeviceDbgString,
|
|
CurrentSystemTime.HighPart,
|
|
CurrentSystemTime.LowPart) );
|
|
|
|
// IRP will be completed by DigiSatisfyEvent.
|
|
return( Status );
|
|
} // end IOCTL_SERIAL_WAIT_ON_MASK
|
|
|
|
case IOCTL_SERIAL_IMMEDIATE_CHAR:
|
|
{
|
|
KIRQL OldIrql;
|
|
PLIST_ENTRY WriteQueue;
|
|
|
|
DigiDump( DIGIIOCTL, (" IOCTL_SERIAL_IMMEDIATE_CHAR:\n") );
|
|
|
|
if( IrpSp->Parameters.DeviceIoControl.InputBufferLength <
|
|
sizeof(UCHAR) )
|
|
{
|
|
Status = STATUS_BUFFER_TOO_SMALL;
|
|
break;
|
|
}
|
|
|
|
KeAcquireSpinLock( &DeviceExt->ControlAccess, &OldIrql );
|
|
|
|
WriteQueue = &DeviceExt->WriteQueue;
|
|
|
|
if( !IsListEmpty( WriteQueue ) )
|
|
{
|
|
PIRP WriteIrp;
|
|
PIO_STACK_LOCATION WriteIrpSp;
|
|
|
|
WriteIrp = CONTAINING_RECORD( WriteQueue->Flink,
|
|
IRP,
|
|
Tail.Overlay.ListEntry );
|
|
|
|
WriteIrpSp = IoGetCurrentIrpStackLocation( WriteIrp );
|
|
|
|
if( WriteIrpSp->Parameters.DeviceIoControl.IoControlCode == IOCTL_SERIAL_IMMEDIATE_CHAR
|
|
&& WriteIrpSp->MajorFunction == IRP_MJ_DEVICE_CONTROL )
|
|
{
|
|
//
|
|
// We are already processing an immediate char request.
|
|
//
|
|
KeReleaseSpinLock( &DeviceExt->ControlAccess, OldIrql );
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
break;
|
|
}
|
|
}
|
|
|
|
KeReleaseSpinLock( &DeviceExt->ControlAccess, OldIrql );
|
|
|
|
//
|
|
// Mark the IRP as being "not started."
|
|
// StartWriteRequest will set this field to zero.
|
|
// WriteTxBuffer updates the field to the number of bytes written.
|
|
//
|
|
Irp->IoStatus.Information = MAXULONG;
|
|
|
|
Status = DigiStartIrpRequest( ControllerExt, DeviceExt,
|
|
WriteQueue, Irp,
|
|
StartWriteRequest );
|
|
|
|
// Immediate char IRP is completed when its data is written.
|
|
return( Status );
|
|
} // end IOCTL_SERIAL_IMMEDIATE_CHAR
|
|
|
|
case IOCTL_SERIAL_PURGE:
|
|
{
|
|
ULONG PurgeBits;
|
|
|
|
if (IrpSp->Parameters.DeviceIoControl.InputBufferLength <
|
|
sizeof(ULONG))
|
|
{
|
|
Status = STATUS_BUFFER_TOO_SMALL;
|
|
break;
|
|
}
|
|
|
|
PurgeBits = *((ULONG *)(Irp->AssociatedIrp.SystemBuffer));
|
|
|
|
DigiDump( DIGIIOCTL, (" IOCTL_SERIAL_PURGE: 0x%x\n", PurgeBits) );
|
|
|
|
if( PurgeBits == 0
|
|
|| (PurgeBits & ~(SERIAL_PURGE_TXABORT |
|
|
SERIAL_PURGE_RXABORT |
|
|
SERIAL_PURGE_TXCLEAR |
|
|
SERIAL_PURGE_RXCLEAR) ) )
|
|
{
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
break;
|
|
}
|
|
|
|
// Order: cancel write irps, then write buffer, then read buffer, then read irps.
|
|
|
|
if( PurgeBits & SERIAL_PURGE_TXABORT )
|
|
DigiCancelIrpQueue( DeviceObject, &DeviceExt->WriteQueue );
|
|
|
|
if( PurgeBits & SERIAL_PURGE_TXCLEAR )
|
|
FlushTransmitBuffer( ControllerExt, DeviceExt );
|
|
|
|
if( PurgeBits & SERIAL_PURGE_RXCLEAR )
|
|
FlushReceiveBuffer( ControllerExt, DeviceExt );
|
|
|
|
if( PurgeBits & SERIAL_PURGE_RXABORT )
|
|
DigiCancelIrpQueue( DeviceObject, &DeviceExt->ReadQueue );
|
|
|
|
break;
|
|
} // end IOCTL_SERIAL_PURGE
|
|
|
|
case IOCTL_SERIAL_GET_HANDFLOW:
|
|
{
|
|
PSERIAL_HANDFLOW HandFlow = Irp->AssociatedIrp.SystemBuffer;
|
|
|
|
DigiDump( (DIGIIOCTL|DIGIFLOWCTRL), (" IOCTL_SERIAL_GET_HANDFLOW:\n") );
|
|
|
|
if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength <
|
|
sizeof(SERIAL_HANDFLOW))
|
|
{
|
|
Status = STATUS_BUFFER_TOO_SMALL;
|
|
break;
|
|
}
|
|
|
|
Irp->IoStatus.Information = sizeof(SERIAL_HANDFLOW);
|
|
|
|
HandFlow->ControlHandShake = DeviceExt->ControlHandShake;
|
|
HandFlow->FlowReplace = DeviceExt->FlowReplace;
|
|
HandFlow->XonLimit = DeviceExt->XonLimit;
|
|
HandFlow->XoffLimit = DeviceExt->XoffLimit;
|
|
|
|
DigiDump( (DIGIIOCTL|DIGIFLOWCTRL), (" -- returning ControlHandShake = 0x%x\n"
|
|
" -- returning FlowReplace = 0x%x\n"
|
|
" -- returning XonLimit = %d\n"
|
|
" -- returning XoffLimit = %d\n",
|
|
HandFlow->ControlHandShake,
|
|
HandFlow->FlowReplace,
|
|
HandFlow->XonLimit,
|
|
HandFlow->XoffLimit ) );
|
|
|
|
break;
|
|
} // case IOCTL_SERIAL_GET_HANDFLOW
|
|
|
|
case IOCTL_SERIAL_SET_HANDFLOW:
|
|
{
|
|
PSERIAL_HANDFLOW HandFlow = Irp->AssociatedIrp.SystemBuffer;
|
|
|
|
DigiDump( (DIGIIOCTL|DIGIFLOWCTRL), (" IOCTL_SERIAL_SET_HANDFLOW:\n") );
|
|
|
|
//
|
|
// Make sure that the hand shake and control is the
|
|
// right size.
|
|
//
|
|
|
|
if( IrpSp->Parameters.DeviceIoControl.InputBufferLength <
|
|
sizeof(SERIAL_HANDFLOW) )
|
|
{
|
|
|
|
Status = STATUS_BUFFER_TOO_SMALL;
|
|
break;
|
|
}
|
|
|
|
DigiDump( (DIGIIOCTL|DIGIFLOWCTRL), (" ControlHandShake = 0x%x\n"
|
|
" FlowReplace = 0x%x\n"
|
|
" XonLimit = %d\n"
|
|
" XoffLimit = %d\n",
|
|
HandFlow->ControlHandShake,
|
|
HandFlow->FlowReplace,
|
|
HandFlow->XonLimit,
|
|
HandFlow->XoffLimit ) );
|
|
|
|
|
|
Status = SetSerialHandflow( ControllerExt, DeviceObject,
|
|
HandFlow );
|
|
|
|
break;
|
|
}
|
|
|
|
case IOCTL_SERIAL_GET_MODEMSTATUS:
|
|
{
|
|
ULONG *ModemStatus = ((ULONG *)(Irp->AssociatedIrp.SystemBuffer));
|
|
ULONG CurrentModemStatus;
|
|
|
|
DigiDump( (DIGIIOCTL|DIGIWAIT), (" IOCTL_SERIAL_GET_MODEMSTATUS: DigiPort = %s\n",
|
|
DeviceExt->DeviceDbgString) );
|
|
|
|
if( IrpSp->Parameters.DeviceIoControl.OutputBufferLength <
|
|
sizeof(ULONG) )
|
|
{
|
|
Status = STATUS_BUFFER_TOO_SMALL;
|
|
break;
|
|
}
|
|
|
|
CurrentModemStatus = DeviceExt->CurrentModemSignals & (~DeviceExt->WriteOnlyModemSignalMask);
|
|
CurrentModemStatus |= DeviceExt->WriteOnlyModemSignalValue & DeviceExt->WriteOnlyModemSignalMask;
|
|
|
|
Irp->IoStatus.Information = sizeof(ULONG);
|
|
|
|
*ModemStatus = 0L;
|
|
|
|
if( CurrentModemStatus & ControllerExt->ModemSignalTable[DTR_SIGNAL] )
|
|
{
|
|
*ModemStatus |= SERIAL_DTR_STATE;
|
|
DigiDump( (DIGIIOCTL|DIGIWAIT), (" DTR") );
|
|
}
|
|
|
|
if( CurrentModemStatus & ControllerExt->ModemSignalTable[RTS_SIGNAL] )
|
|
{
|
|
*ModemStatus |= SERIAL_RTS_STATE;
|
|
DigiDump( (DIGIIOCTL|DIGIWAIT), (" RTS") );
|
|
}
|
|
|
|
if( CurrentModemStatus & ControllerExt->ModemSignalTable[CTS_SIGNAL] )
|
|
{
|
|
*ModemStatus |= SERIAL_CTS_STATE;
|
|
DigiDump( (DIGIIOCTL|DIGIWAIT), (" CTS") );
|
|
}
|
|
|
|
if( CurrentModemStatus & ControllerExt->ModemSignalTable[DSR_SIGNAL] )
|
|
{
|
|
*ModemStatus |= SERIAL_DSR_STATE;
|
|
DigiDump( (DIGIIOCTL|DIGIWAIT), (" DSR") );
|
|
}
|
|
|
|
if( CurrentModemStatus & ControllerExt->ModemSignalTable[RI_SIGNAL] )
|
|
{
|
|
*ModemStatus |= SERIAL_RI_STATE;
|
|
DigiDump( (DIGIIOCTL|DIGIWAIT), (" RI") );
|
|
}
|
|
|
|
if( CurrentModemStatus & ControllerExt->ModemSignalTable[DCD_SIGNAL] )
|
|
{
|
|
*ModemStatus |= SERIAL_DCD_STATE;
|
|
DigiDump( (DIGIIOCTL|DIGIWAIT), (" DCD") );
|
|
}
|
|
|
|
DigiDump( (DIGIIOCTL|DIGIWAIT), ("\n -- returning ModemStatus = 0x%x\n",
|
|
*ModemStatus) );
|
|
|
|
|
|
break;
|
|
} // end case IOCTL_SERIAL_GET_MODEMSTATUS
|
|
|
|
case IOCTL_SERIAL_GET_COMMSTATUS:
|
|
{
|
|
KIRQL OldIrql;
|
|
PSERIAL_STATUS Stat;
|
|
PFEP_CHANNEL_STRUCTURE ChInfo;
|
|
PLIST_ENTRY WriteQueue;
|
|
USHORT BufferedTxDataSize;
|
|
|
|
DigiDump( DIGIIOCTL, (" IOCTL_SERIAL_GET_COMMSTATUS:\n") );
|
|
|
|
if( IrpSp->Parameters.DeviceIoControl.OutputBufferLength <
|
|
sizeof(SERIAL_STATUS) )
|
|
{
|
|
Status = STATUS_BUFFER_TOO_SMALL;
|
|
break;
|
|
}
|
|
|
|
Irp->IoStatus.Information = sizeof(SERIAL_STATUS);
|
|
Stat = (PSERIAL_STATUS)(Irp->AssociatedIrp.SystemBuffer);
|
|
|
|
RtlZeroMemory( Stat, sizeof(SERIAL_STATUS) );
|
|
|
|
Stat->EofReceived = FALSE;
|
|
Stat->AmountInInQueue = NBytesInRecvBuffer( ControllerExt, DeviceExt );
|
|
Stat->HoldReasons = 0;
|
|
|
|
ChInfo = (PFEP_CHANNEL_STRUCTURE)(ControllerExt->VirtualAddress +
|
|
DeviceExt->ChannelInfo.Offset);
|
|
|
|
EnableWindow( ControllerExt, DeviceExt->ChannelInfo.Window );
|
|
BufferedTxDataSize = ( READ_REGISTER_USHORT( &ChInfo->tin ) - READ_REGISTER_USHORT( &ChInfo->tout ) )
|
|
& READ_REGISTER_USHORT( &ChInfo->tmax );
|
|
DisableWindow( ControllerExt );
|
|
|
|
WriteQueue = &DeviceExt->WriteQueue;
|
|
|
|
KeAcquireSpinLock( &DeviceExt->ControlAccess, &OldIrql );
|
|
|
|
if( IsListEmpty( WriteQueue ) )
|
|
{
|
|
Stat->WaitForImmediate = FALSE;
|
|
}
|
|
else
|
|
{
|
|
PIRP WriteIrp;
|
|
PIO_STACK_LOCATION WriteIrpSp;
|
|
|
|
WriteIrp = CONTAINING_RECORD( WriteQueue->Flink,
|
|
IRP,
|
|
Tail.Overlay.ListEntry );
|
|
|
|
WriteIrpSp = IoGetCurrentIrpStackLocation( WriteIrp );
|
|
|
|
Stat->WaitForImmediate = ( (WriteIrpSp->MajorFunction == IRP_MJ_DEVICE_CONTROL) &&
|
|
(WriteIrpSp->Parameters.DeviceIoControl.IoControlCode ==
|
|
IOCTL_SERIAL_IMMEDIATE_CHAR) );
|
|
|
|
}
|
|
|
|
if( (DeviceExt->ControlHandShake & SERIAL_CTS_HANDSHAKE)
|
|
&& !(DeviceExt->CurrentModemSignals & ControllerExt->ModemSignalTable[CTS_SIGNAL]) )
|
|
{
|
|
Stat->HoldReasons |= SERIAL_TX_WAITING_FOR_CTS;
|
|
}
|
|
if( (DeviceExt->ControlHandShake & SERIAL_DSR_HANDSHAKE)
|
|
&& !(DeviceExt->CurrentModemSignals & ControllerExt->ModemSignalTable[DSR_SIGNAL]) )
|
|
{
|
|
Stat->HoldReasons |= SERIAL_TX_WAITING_FOR_DSR;
|
|
}
|
|
if( (DeviceExt->ControlHandShake & SERIAL_DCD_HANDSHAKE)
|
|
&& !(DeviceExt->CurrentModemSignals & ControllerExt->ModemSignalTable[DCD_SIGNAL]) )
|
|
{
|
|
Stat->HoldReasons |= SERIAL_TX_WAITING_FOR_DCD;
|
|
}
|
|
|
|
// DH Should add support for
|
|
// SERIAL_TX_WAITING_FOR_XON, SERIAL_TX_WAITING_XOFF_SENT,
|
|
// SERIAL_TX_WAITING_ON_BREAK, and SERIAL_RX_WAITING_FOR_DSR.
|
|
|
|
Stat->AmountInOutQueue = DeviceExt->TotalCharsQueued + BufferedTxDataSize;
|
|
|
|
Stat->Errors = DeviceExt->ErrorWord;
|
|
DeviceExt->ErrorWord = 0;
|
|
|
|
KeReleaseSpinLock( &DeviceExt->ControlAccess, OldIrql );
|
|
|
|
DigiDump( DIGIIOCTL, (" returning COMMSTATUS:\n"
|
|
" Stat->AmountInInQueue = %d\n"
|
|
" Stat->AmountInOutQueue = %d\n"
|
|
" Stat->Errors = 0x%x\n"
|
|
" Stat->HoldReasons = 0x%x\n"
|
|
" Stat->EofReceived = %d\n"
|
|
" Stat->WaitForImmediate = %d\n",
|
|
Stat->AmountInInQueue,
|
|
Stat->AmountInOutQueue,
|
|
Stat->Errors,
|
|
Stat->HoldReasons,
|
|
Stat->EofReceived,
|
|
Stat->WaitForImmediate) );
|
|
|
|
break;
|
|
} // end IOCTL_SERIAL_GET_COMMSTATUS
|
|
|
|
case IOCTL_SERIAL_GET_PROPERTIES:
|
|
{
|
|
PFEP_CHANNEL_STRUCTURE ChInfo;
|
|
PSERIAL_COMMPROP Properties;
|
|
SERIAL_BAUD_RATES PossibleBaudRates;
|
|
|
|
DigiDump( DIGIIOCTL, (" IOCTL_SERIAL_GET_PROPERTIES:\n") );
|
|
|
|
if( IrpSp->Parameters.DeviceIoControl.OutputBufferLength <
|
|
sizeof(SERIAL_COMMPROP) )
|
|
{
|
|
Status = STATUS_BUFFER_TOO_SMALL;
|
|
break;
|
|
}
|
|
|
|
Properties = (PSERIAL_COMMPROP)Irp->AssociatedIrp.SystemBuffer;
|
|
RtlZeroMemory( Properties, sizeof(SERIAL_COMMPROP) );
|
|
|
|
Properties->PacketLength = sizeof(SERIAL_COMMPROP);
|
|
Properties->PacketVersion = 2;
|
|
Properties->ServiceMask = SERIAL_SP_SERIALCOMM;
|
|
|
|
// Get the buffer size from the controller
|
|
ChInfo = (PFEP_CHANNEL_STRUCTURE)(ControllerExt->VirtualAddress +
|
|
DeviceExt->ChannelInfo.Offset);
|
|
|
|
EnableWindow( ControllerExt, DeviceExt->ChannelInfo.Window );
|
|
|
|
Properties->MaxTxQueue =
|
|
Properties->CurrentTxQueue =
|
|
READ_REGISTER_USHORT( &ChInfo->tmax );
|
|
Properties->MaxRxQueue =
|
|
Properties->CurrentRxQueue =
|
|
READ_REGISTER_USHORT( &ChInfo->rmax );
|
|
|
|
DisableWindow( ControllerExt );
|
|
|
|
// Loop through the possible baud rates backwards until we
|
|
// find the max.
|
|
for( PossibleBaudRates = NUMBER_OF_BAUD_RATES - 1;
|
|
PossibleBaudRates != SerialBaud50; PossibleBaudRates-- )
|
|
{
|
|
if( ControllerExt->BaudTable[PossibleBaudRates] != -1 )
|
|
{
|
|
// Give a default value;
|
|
Properties->MaxBaud = SERIAL_BAUD_USER;
|
|
switch( PossibleBaudRates )
|
|
{
|
|
case SerialBaud50:
|
|
Properties->MaxBaud = SERIAL_BAUD_USER;
|
|
break;
|
|
case SerialBaud75:
|
|
Properties->MaxBaud = SERIAL_BAUD_075;
|
|
break;
|
|
case SerialBaud110:
|
|
Properties->MaxBaud = SERIAL_BAUD_110;
|
|
break;
|
|
case SerialBaud135_5:
|
|
Properties->MaxBaud = SERIAL_BAUD_134_5;
|
|
break;
|
|
case SerialBaud150:
|
|
Properties->MaxBaud = SERIAL_BAUD_150;
|
|
break;
|
|
case SerialBaud300:
|
|
Properties->MaxBaud = SERIAL_BAUD_300;
|
|
break;
|
|
case SerialBaud600:
|
|
Properties->MaxBaud = SERIAL_BAUD_600;
|
|
break;
|
|
case SerialBaud1200:
|
|
Properties->MaxBaud = SERIAL_BAUD_1200;
|
|
break;
|
|
case SerialBaud1800:
|
|
Properties->MaxBaud = SERIAL_BAUD_1800;
|
|
break;
|
|
case SerialBaud2000:
|
|
Properties->MaxBaud = SERIAL_BAUD_USER;
|
|
break;
|
|
case SerialBaud2400:
|
|
Properties->MaxBaud = SERIAL_BAUD_2400;
|
|
break;
|
|
case SerialBaud3600:
|
|
Properties->MaxBaud = SERIAL_BAUD_USER;
|
|
break;
|
|
case SerialBaud4800:
|
|
Properties->MaxBaud = SERIAL_BAUD_4800;
|
|
break;
|
|
case SerialBaud7200:
|
|
Properties->MaxBaud = SERIAL_BAUD_7200;
|
|
break;
|
|
case SerialBaud9600:
|
|
Properties->MaxBaud = SERIAL_BAUD_9600;
|
|
break;
|
|
case SerialBaud14400:
|
|
Properties->MaxBaud = SERIAL_BAUD_14400;
|
|
break;
|
|
case SerialBaud19200:
|
|
Properties->MaxBaud = SERIAL_BAUD_19200;
|
|
break;
|
|
case SerialBaud38400:
|
|
Properties->MaxBaud = SERIAL_BAUD_38400;
|
|
break;
|
|
case SerialBaud56000:
|
|
Properties->MaxBaud = SERIAL_BAUD_56K;
|
|
break;
|
|
case SerialBaud128000:
|
|
Properties->MaxBaud = SERIAL_BAUD_128K;
|
|
break;
|
|
case SerialBaud256000:
|
|
Properties->MaxBaud = SERIAL_BAUD_USER;
|
|
break;
|
|
case SerialBaud512000:
|
|
Properties->MaxBaud = SERIAL_BAUD_USER;
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
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 |
|
|
SERIAL_PCF_SPECIALCHARS;
|
|
|
|
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->SettableBaud = 0;
|
|
|
|
for( PossibleBaudRates = SerialBaud50;
|
|
PossibleBaudRates < NUMBER_OF_BAUD_RATES; PossibleBaudRates++ )
|
|
{
|
|
if( ControllerExt->BaudTable[PossibleBaudRates] != -1 )
|
|
switch( PossibleBaudRates )
|
|
{
|
|
case SerialBaud50:
|
|
Properties->SettableBaud |= SERIAL_BAUD_USER;
|
|
break;
|
|
case SerialBaud75:
|
|
Properties->SettableBaud |= SERIAL_BAUD_075;
|
|
break;
|
|
case SerialBaud110:
|
|
Properties->SettableBaud |= SERIAL_BAUD_110;
|
|
break;
|
|
case SerialBaud135_5:
|
|
Properties->SettableBaud |= SERIAL_BAUD_134_5;
|
|
break;
|
|
case SerialBaud150:
|
|
Properties->SettableBaud |= SERIAL_BAUD_150;
|
|
break;
|
|
case SerialBaud300:
|
|
Properties->SettableBaud |= SERIAL_BAUD_300;
|
|
break;
|
|
case SerialBaud600:
|
|
Properties->SettableBaud |= SERIAL_BAUD_600;
|
|
break;
|
|
case SerialBaud1200:
|
|
Properties->SettableBaud |= SERIAL_BAUD_1200;
|
|
break;
|
|
case SerialBaud1800:
|
|
Properties->SettableBaud |= SERIAL_BAUD_1800;
|
|
break;
|
|
case SerialBaud2000:
|
|
Properties->SettableBaud |= SERIAL_BAUD_USER;
|
|
break;
|
|
case SerialBaud2400:
|
|
Properties->SettableBaud |= SERIAL_BAUD_2400;
|
|
break;
|
|
case SerialBaud3600:
|
|
Properties->SettableBaud |= SERIAL_BAUD_USER;
|
|
break;
|
|
case SerialBaud4800:
|
|
Properties->SettableBaud |= SERIAL_BAUD_4800;
|
|
break;
|
|
case SerialBaud7200:
|
|
Properties->SettableBaud |= SERIAL_BAUD_7200;
|
|
break;
|
|
case SerialBaud9600:
|
|
Properties->SettableBaud |= SERIAL_BAUD_9600;
|
|
break;
|
|
case SerialBaud14400:
|
|
Properties->SettableBaud |= SERIAL_BAUD_14400;
|
|
break;
|
|
case SerialBaud19200:
|
|
Properties->SettableBaud |= SERIAL_BAUD_19200;
|
|
break;
|
|
case SerialBaud38400:
|
|
Properties->SettableBaud |= SERIAL_BAUD_38400;
|
|
break;
|
|
case SerialBaud56000:
|
|
Properties->SettableBaud |= SERIAL_BAUD_56K;
|
|
break;
|
|
case SerialBaud128000:
|
|
Properties->SettableBaud |= SERIAL_BAUD_128K;
|
|
break;
|
|
case SerialBaud256000:
|
|
Properties->SettableBaud |= SERIAL_BAUD_USER;
|
|
break;
|
|
case SerialBaud512000:
|
|
Properties->SettableBaud |= SERIAL_BAUD_USER;
|
|
break;
|
|
}
|
|
}
|
|
|
|
Properties->SettableData = ((USHORT)( SERIAL_DATABITS_5 |
|
|
SERIAL_DATABITS_6 |
|
|
SERIAL_DATABITS_7 |
|
|
SERIAL_DATABITS_8 ) );
|
|
|
|
Properties->SettableStopParity = ((USHORT)( SERIAL_STOPBITS_10 |
|
|
SERIAL_STOPBITS_20 |
|
|
SERIAL_PARITY_NONE |
|
|
SERIAL_PARITY_ODD |
|
|
SERIAL_PARITY_EVEN ) );
|
|
|
|
Irp->IoStatus.Information = sizeof(SERIAL_COMMPROP);
|
|
Irp->IoStatus.Status = STATUS_SUCCESS;
|
|
|
|
break;
|
|
} // end case IOCTL_SERIAL_GET_PROPERTIES
|
|
|
|
case IOCTL_SERIAL_XOFF_COUNTER: // see NTDDSER.H for algorithms
|
|
{
|
|
PSERIAL_XOFF_COUNTER Xc = Irp->AssociatedIrp.SystemBuffer;
|
|
|
|
if( IrpSp->Parameters.DeviceIoControl.InputBufferLength <
|
|
sizeof(SERIAL_XOFF_COUNTER) )
|
|
{
|
|
Status = STATUS_BUFFER_TOO_SMALL;
|
|
break;
|
|
}
|
|
|
|
DigiDump( (DIGIIOCTL),
|
|
(" IOCTL_SERIAL_XOFF_COUNTER:\n"
|
|
" Timeout = 0x%x\n"
|
|
" Counter = %d\n"
|
|
" XoffChar = 0x%.2x\n",
|
|
Xc->Timeout, Xc->Counter, Xc->XoffChar) );
|
|
|
|
if( Xc->Counter <= 0 )
|
|
{
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Mark the IRP as being "not started."
|
|
// StartWriteRequest will set this field to zero.
|
|
// WriteTxBuffer updates the field to the number of bytes written.
|
|
//
|
|
Irp->IoStatus.Information = MAXULONG;
|
|
|
|
return DigiStartIrpRequest( ControllerExt, DeviceExt,
|
|
&DeviceExt->WriteQueue, Irp,
|
|
StartWriteRequest );
|
|
} // end IOCTL_SERIAL_XOFF_COUNTER
|
|
|
|
case IOCTL_SERIAL_LSRMST_INSERT:
|
|
{
|
|
KIRQL OldIrql;
|
|
DIGI_XFLAG IFlag;
|
|
PUCHAR escapeChar = Irp->AssociatedIrp.SystemBuffer;
|
|
|
|
DigiDump( (DIGIIOCTL|DIGINOTIMPLEMENTED), (" IOCTL_SERIAL_LSRMST_INSERT:\n") );
|
|
|
|
//
|
|
// Make sure we get a byte.
|
|
//
|
|
|
|
if( IrpSp->Parameters.DeviceIoControl.InputBufferLength <
|
|
sizeof(UCHAR) )
|
|
{
|
|
Status = STATUS_BUFFER_TOO_SMALL;
|
|
break;
|
|
}
|
|
|
|
KeAcquireSpinLock( &DeviceExt->ControlAccess, &OldIrql );
|
|
|
|
if( *escapeChar )
|
|
{
|
|
|
|
if( (*escapeChar == DeviceExt->SpecialChars.XoffChar) ||
|
|
(*escapeChar == DeviceExt->SpecialChars.XonChar) ||
|
|
(DeviceExt->FlowReplace & SERIAL_ERROR_CHAR) )
|
|
{
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
KeReleaseSpinLock( &DeviceExt->ControlAccess, OldIrql );
|
|
break;
|
|
}
|
|
}
|
|
|
|
DeviceExt->EscapeChar = *escapeChar;
|
|
|
|
DigiDump( (DIGIIOCTL|DIGINOTIMPLEMENTED), (" Setting EscapeChar = 0x%x\n",
|
|
DeviceExt->EscapeChar) );
|
|
|
|
KeReleaseSpinLock( &DeviceExt->ControlAccess, OldIrql );
|
|
|
|
//
|
|
// Turn on/off DOS mode on the controller
|
|
//
|
|
if( DeviceExt->EscapeChar )
|
|
{
|
|
DigiDump( DIGIIOCTL, (" Turning DosMode ON!\n") );
|
|
//
|
|
// Turn on DOS mode
|
|
//
|
|
// Make sure we turn off break event notification. We
|
|
// will start processing break events in the data stream.
|
|
//
|
|
IFlag.Src = ( IFLAG_PARMRK | IFLAG_INPCK | IFLAG_DOSMODE );
|
|
IFlag.Mask = (USHORT) ~( IFLAG_BRKINT );
|
|
IFlag.Command = SET_IFLAGS;
|
|
SetXFlag( DeviceExt, &IFlag );
|
|
}
|
|
else if( !(DeviceExt->WaitMask & SERIAL_EV_ERR) )
|
|
{
|
|
//
|
|
// Turn off DOS mode
|
|
//
|
|
DigiDump( DIGIIOCTL, (" Turning DosMode OFF!\n") );
|
|
IFlag.Mask = (USHORT)(~( IFLAG_PARMRK | IFLAG_INPCK | IFLAG_DOSMODE ));
|
|
IFlag.Src = 0;
|
|
// if( DeviceExt->WaitMask & SERIAL_EV_BREAK )
|
|
// {
|
|
//
|
|
// If we are suppose to notify on breaks, then reset the
|
|
// BRKINT flag to start getting the break notifications
|
|
// through the command queue.
|
|
//
|
|
IFlag.Src |= IFLAG_BRKINT;
|
|
// }
|
|
IFlag.Command = SET_IFLAGS;
|
|
SetXFlag( DeviceExt, &IFlag );
|
|
}
|
|
|
|
#if DBG
|
|
{
|
|
PFEP_CHANNEL_STRUCTURE ChInfo;
|
|
USHORT DosMode;
|
|
|
|
ChInfo = (PFEP_CHANNEL_STRUCTURE)(ControllerExt->VirtualAddress +
|
|
DeviceExt->ChannelInfo.Offset);
|
|
|
|
EnableWindow( ControllerExt, DeviceExt->ChannelInfo.Window );
|
|
DosMode = READ_REGISTER_USHORT( &ChInfo->iflag );
|
|
DisableWindow( ControllerExt );
|
|
|
|
DigiDump( DIGIIOCTL, (" DosMode = 0x%x\n", DosMode) );
|
|
}
|
|
#endif
|
|
break;
|
|
} // end case IOCTL_SERIAL_LSRMST_INSERT
|
|
|
|
case IOCTL_DIGI_SPECIAL:
|
|
{
|
|
PDIGI_IOCTL DigiIoctl = (PDIGI_IOCTL)Irp->AssociatedIrp.SystemBuffer;
|
|
|
|
DigiDump( DIGIIOCTL, (" IOCTL_DIGI_SPECIAL:\n") );
|
|
|
|
if( DigiIoctl )
|
|
{
|
|
switch( DigiIoctl->dwCommand )
|
|
{
|
|
case DIGI_IOCTL_DBGOUT:
|
|
{
|
|
DigiDump( ~(DIGIBUGCHECK), ("%s", DigiIoctl->Char) );
|
|
break;
|
|
}
|
|
|
|
case DIGI_IOCTL_TRACE:
|
|
{
|
|
PULONG NewTraceLevel = (PULONG)&DigiIoctl->Char[0];
|
|
|
|
DigiDump( ~(DIGIBUGCHECK), (" Setting DigiDebugLevel = 0x%x\n",
|
|
*NewTraceLevel ) );
|
|
DigiDebugLevel = *NewTraceLevel;
|
|
break;
|
|
}
|
|
|
|
case DIGI_IOCTL_DBGBREAK:
|
|
{
|
|
DbgBreakPoint();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
Status = STATUS_SUCCESS;
|
|
break;
|
|
} // end IOCTL_DIGI_SPECIAL
|
|
|
|
case IOCTL_FAST_RAS:
|
|
{
|
|
ULONG WriteBufferingEnabled;
|
|
|
|
if (IrpSp->Parameters.DeviceIoControl.InputBufferLength <
|
|
sizeof(ULONG))
|
|
{
|
|
Status = STATUS_BUFFER_TOO_SMALL;
|
|
break;
|
|
}
|
|
|
|
WriteBufferingEnabled = *((ULONG*)(Irp->AssociatedIrp.SystemBuffer));
|
|
|
|
if (WriteBufferingEnabled == 1) {
|
|
DeviceExt->SpecialFlags &= ~DIGI_SPECIAL_FLAG_FAST_RAS;
|
|
} else {
|
|
DeviceExt->SpecialFlags |= DIGI_SPECIAL_FLAG_FAST_RAS;
|
|
}
|
|
|
|
DigiDump( DIGIIOCTL, (" IOCTL_FAST_RAS: 0x%x\n", WriteBufferingEnabled) );
|
|
break;
|
|
} // end IOCTL_RAS_PRIVATE
|
|
|
|
case IOCTL_SERIAL_GET_STATS:
|
|
{
|
|
KIRQL OldIrql;
|
|
PSERIALPERF_STATS SerialPerfStats;
|
|
|
|
DigiDump( DIGIIOCTL, (" IOCTL_MODEM_GET_STATS:\n") );
|
|
|
|
if( IrpSp->Parameters.DeviceIoControl.OutputBufferLength <
|
|
sizeof(SERIALPERF_STATS) )
|
|
{
|
|
Status = STATUS_BUFFER_TOO_SMALL;
|
|
}
|
|
else
|
|
{
|
|
Irp->IoStatus.Information = sizeof(SERIALPERF_STATS);
|
|
SerialPerfStats = (PSERIALPERF_STATS)Irp->AssociatedIrp.SystemBuffer;
|
|
|
|
RtlZeroMemory( SerialPerfStats, sizeof(SERIALPERF_STATS) );
|
|
SerialPerfStats->ReceivedCount = DeviceExt->PerfData.BytesRead - DeviceExt->SerialPerfStats.ReceivedCount;
|
|
SerialPerfStats->TransmittedCount = DeviceExt->PerfData.BytesWritten - DeviceExt->SerialPerfStats.TransmittedCount;
|
|
SerialPerfStats->ParityErrorCount = DeviceExt->PerfData.ParityErrorCount - DeviceExt->SerialPerfStats.ParityErrorCount;
|
|
SerialPerfStats->FrameErrorCount = DeviceExt->PerfData.FrameErrorCount - DeviceExt->SerialPerfStats.FrameErrorCount;
|
|
SerialPerfStats->BufferOverrunErrorCount = DeviceExt->PerfData.BufferOverrunErrorCount - DeviceExt->SerialPerfStats.BufferOverrunErrorCount;
|
|
SerialPerfStats->SerialOverrunErrorCount = DeviceExt->PerfData.SerialOverrunErrorCount - DeviceExt->SerialPerfStats.SerialOverrunErrorCount;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case IOCTL_SERIAL_CLEAR_STATS:
|
|
{
|
|
/*
|
|
** We already keep stats for perfmon, but UNIMODEM apparently wants the
|
|
** ability to reset the numbers it is being sent. We accomplish this
|
|
** by just recording whatever our current values are, and subtracting
|
|
** at the time we return them.
|
|
*/
|
|
DeviceExt->SerialPerfStats.ReceivedCount = DeviceExt->PerfData.BytesRead;
|
|
DeviceExt->SerialPerfStats.TransmittedCount = DeviceExt->PerfData.BytesWritten;
|
|
DeviceExt->SerialPerfStats.ParityErrorCount = DeviceExt->PerfData.ParityErrorCount;
|
|
DeviceExt->SerialPerfStats.FrameErrorCount = DeviceExt->PerfData.FrameErrorCount;
|
|
DeviceExt->SerialPerfStats.BufferOverrunErrorCount = DeviceExt->PerfData.BufferOverrunErrorCount;
|
|
DeviceExt->SerialPerfStats.SerialOverrunErrorCount = DeviceExt->PerfData.SerialOverrunErrorCount;
|
|
break;
|
|
}
|
|
|
|
default:
|
|
DigiDump( (DIGIERRORS|DIGIIOCTL), (" *** INVALID IOCTL PARAMETER (0x%x) ***\n",
|
|
IrpSp->Parameters.DeviceIoControl.IoControlCode) );
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
DbgPrint("DIGIFEP5: Invalid IOCTL parameter %8.8x\n", IrpSp->Parameters.DeviceIoControl.IoControlCode);
|
|
break;
|
|
}
|
|
|
|
DoneWithIoctl:
|
|
|
|
Irp->IoStatus.Status = Status;
|
|
|
|
DigiIoCompleteRequest( Irp, IO_NO_INCREMENT );
|
|
|
|
#if DBG
|
|
KeQuerySystemTime( &CurrentSystemTime );
|
|
#endif
|
|
DigiDump( (DIGIFLOW|DIGIIOCTL), ("Exiting SerialIoControl: port = %s\t%u:%u\n",
|
|
DeviceExt->DeviceDbgString,
|
|
CurrentSystemTime.HighPart,
|
|
CurrentSystemTime.LowPart) );
|
|
return( Status );
|
|
} // end SerialIoControl
|
|
|
|
NTSTATUS ControllerIoControl(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
|
|
{
|
|
NTSTATUS Status;
|
|
PIO_STACK_LOCATION IrpSp;
|
|
PDIGI_CONTROLLER_EXTENSION ControllerExt;
|
|
PDIGI_DEVICE_EXTENSION DeviceExt = DeviceObject->DeviceExtension;
|
|
|
|
#if DBG
|
|
LARGE_INTEGER CurrentSystemTime;
|
|
|
|
KeQuerySystemTime( &CurrentSystemTime );
|
|
#endif
|
|
DigiDump( (DIGIIRP|DIGIFLOW|DIGIIOCTL),
|
|
("Entering ControllerIoControl: port = %S\tIRP = 0x%x\t%u:%u\n",
|
|
DeviceExt->DeviceDbgString, Irp, CurrentSystemTime.HighPart, CurrentSystemTime.LowPart) );
|
|
|
|
IrpSp = IoGetCurrentIrpStackLocation( Irp );
|
|
Irp->IoStatus.Information = 0L;
|
|
Status = STATUS_SUCCESS;
|
|
|
|
ControllerExt = (PDIGI_CONTROLLER_EXTENSION)(DeviceExt->ParentControllerExt);
|
|
|
|
switch( IrpSp->Parameters.DeviceIoControl.IoControlCode )
|
|
{
|
|
case IOCTL_DIGI_GET_CONTROLLER_PERF_DATA:
|
|
{
|
|
if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(CONTROLLER_PERF_DATA))
|
|
{
|
|
Status = STATUS_BUFFER_TOO_SMALL;
|
|
}
|
|
else
|
|
{
|
|
memcpy(Irp->AssociatedIrp.SystemBuffer, &ControllerExt->PerfData, sizeof(ControllerExt->PerfData));
|
|
Irp->IoStatus.Information = sizeof(ControllerExt->PerfData);
|
|
}
|
|
break;
|
|
}
|
|
case IOCTL_DIGI_GET_PORT_PERF_DATA:
|
|
{
|
|
struct PORT_DATA
|
|
{
|
|
ULONG ComPort;
|
|
PORT_PERF_DATA PerfData;
|
|
} *PortData;
|
|
|
|
if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(struct PORT_DATA)*ControllerExt->NumberOfPorts)
|
|
{
|
|
Status = STATUS_BUFFER_TOO_SMALL;
|
|
}
|
|
else
|
|
{
|
|
PDEVICE_OBJECT DeviceObject = ControllerExt->HeadDeviceObject;
|
|
ULONG BytesReturned = 0;
|
|
|
|
PortData = (struct PORT_DATA *)Irp->AssociatedIrp.SystemBuffer;
|
|
Irp->IoStatus.Information = 0;
|
|
|
|
while (DeviceObject)
|
|
{
|
|
USHORT Tin, Tout, Tmax;
|
|
PFEP_CHANNEL_STRUCTURE ChInfo;
|
|
PDIGI_DEVICE_EXTENSION DeviceExt = DeviceObject->DeviceExtension;
|
|
|
|
if (BytesReturned+sizeof(struct PORT_DATA) > IrpSp->Parameters.DeviceIoControl.OutputBufferLength)
|
|
{
|
|
DigiDump( (DIGIERRORS|DIGIIOCTL), ("Trying to overflow ioctl buffer. BAD.") );
|
|
Status = STATUS_BUFFER_TOO_SMALL;
|
|
break;
|
|
}
|
|
|
|
// Gather some data directly from the fep.
|
|
|
|
ChInfo = (PFEP_CHANNEL_STRUCTURE)(ControllerExt->VirtualAddress +
|
|
DeviceExt->ChannelInfo.Offset);
|
|
|
|
EnableWindow( ControllerExt, DeviceExt->ChannelInfo.Window );
|
|
Tin = READ_REGISTER_USHORT( &ChInfo->tin );
|
|
Tout = READ_REGISTER_USHORT( &ChInfo->tout );
|
|
Tmax = READ_REGISTER_USHORT( &ChInfo->tmax );
|
|
DeviceExt->PerfData.SendBufferSize = Tmax;
|
|
DeviceExt->PerfData.BytesInSendBuffer = (Tin - Tout) & Tmax;
|
|
DisableWindow( ControllerExt );
|
|
|
|
PortData->ComPort = DeviceExt->ComPort;
|
|
PortData->PerfData = DeviceExt->PerfData;
|
|
|
|
BytesReturned += sizeof(struct PORT_DATA);
|
|
PortData++;
|
|
|
|
DeviceObject = DeviceExt->NextDeviceObject;
|
|
}
|
|
if (Status==STATUS_SUCCESS)
|
|
{
|
|
Irp->IoStatus.Information = BytesReturned;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case IOCTL_DIGI_GET_CONTROLLER_DATA:
|
|
{
|
|
if (IrpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(CONTROLLER_DATA))
|
|
{
|
|
Status = STATUS_BUFFER_TOO_SMALL;
|
|
}
|
|
else
|
|
{
|
|
CONTROLLER_DATA *Controller = Irp->AssociatedIrp.SystemBuffer;
|
|
ULONG BytesReturned = 0;
|
|
PDEVICE_OBJECT DeviceObject = ControllerExt->HeadDeviceObject;
|
|
WCHAR *p;
|
|
|
|
Controller->State = ControllerExt->ControllerState;
|
|
Controller->Type = ControllerExt->ControllerType;
|
|
Controller->NumberOfPorts = ControllerExt->NumberOfPorts;
|
|
|
|
BytesReturned = sizeof(CONTROLLER_DATA);
|
|
p = (WCHAR*)&Controller[1];
|
|
|
|
// Return a list of com port names. These are in the form \DosDevices\COMx,
|
|
// so trim the \DosDevices\ part. The list is terminated by a double null.
|
|
|
|
while (DeviceObject)
|
|
{
|
|
PDIGI_DEVICE_EXTENSION DeviceExt = DeviceObject->DeviceExtension;
|
|
|
|
BytesReturned += (wcslen(DeviceExt->SymbolicLinkName.Buffer)+1) * sizeof(WCHAR) + sizeof(ULONG);
|
|
|
|
if (BytesReturned<IrpSp->Parameters.DeviceIoControl.OutputBufferLength)
|
|
{
|
|
*(ULONG*)p = DeviceExt->ComPort;
|
|
p = (WCHAR*)((CHAR*)p+sizeof(ULONG));
|
|
wcscpy(p, (WCHAR*)((CHAR*)DeviceExt->SymbolicLinkName.Buffer));
|
|
p += wcslen(DeviceExt->SymbolicLinkName.Buffer) + 1;
|
|
}
|
|
else
|
|
{
|
|
// Ran out of space.
|
|
Status = STATUS_BUFFER_TOO_SMALL;
|
|
}
|
|
|
|
DeviceObject = DeviceExt->NextDeviceObject;
|
|
}
|
|
if (Status!=STATUS_BUFFER_TOO_SMALL)
|
|
{
|
|
if (BytesReturned+sizeof(WCHAR)<=IrpSp->Parameters.DeviceIoControl.OutputBufferLength)
|
|
{
|
|
*p = L'\0';
|
|
Irp->IoStatus.Information = BytesReturned + sizeof(WCHAR);
|
|
}
|
|
else
|
|
{
|
|
Status = STATUS_BUFFER_TOO_SMALL;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
DigiDump( (DIGIERRORS|DIGIIOCTL), (" *** INVALID IOCTL PARAMETER (%d) ***\n",
|
|
IrpSp->Parameters.DeviceIoControl.IoControlCode) );
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
break;
|
|
}
|
|
|
|
Irp->IoStatus.Status = Status;
|
|
DigiIoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
|
|
#if DBG
|
|
KeQuerySystemTime( &CurrentSystemTime );
|
|
#endif
|
|
DigiDump( (DIGIFLOW|DIGIIOCTL), ("Exiting ControlIoControl: port = %S\t%u:%u\n",
|
|
DeviceExt->DeviceDbgString,
|
|
CurrentSystemTime.HighPart,
|
|
CurrentSystemTime.LowPart) );
|
|
return Status;
|
|
}
|
|
|
|
|