/*++ ***************************************************************************** * * * 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 (BytesReturnedParameters.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; }